mirror of
https://github.com/ClusterCockpit/cc-backend
synced 2025-11-26 03:23:07 +01:00
314 lines
6.9 KiB
Go
314 lines
6.9 KiB
Go
// Copyright (C) NHR@FAU, University Erlangen-Nuremberg.
|
|
// All rights reserved.
|
|
// Use of this source code is governed by a MIT-style
|
|
// license that can be found in the LICENSE file.
|
|
package archive
|
|
|
|
import (
|
|
"encoding/json"
|
|
"os"
|
|
"testing"
|
|
|
|
"github.com/ClusterCockpit/cc-lib/schema"
|
|
)
|
|
|
|
func TestSqliteInitEmptyPath(t *testing.T) {
|
|
var sa SqliteArchive
|
|
_, err := sa.Init(json.RawMessage(`{"kind":"sqlite"}`))
|
|
if err == nil {
|
|
t.Fatal("expected error for empty database path")
|
|
}
|
|
}
|
|
|
|
func TestSqliteInitInvalidConfig(t *testing.T) {
|
|
var sa SqliteArchive
|
|
_, err := sa.Init(json.RawMessage(`"dbPath":"/tmp/test.db"`))
|
|
if err == nil {
|
|
t.Fatal("expected error for invalid config")
|
|
}
|
|
}
|
|
|
|
func TestSqliteInit(t *testing.T) {
|
|
tmpfile := t.TempDir() + "/test.db"
|
|
defer os.Remove(tmpfile)
|
|
|
|
var sa SqliteArchive
|
|
version, err := sa.Init(json.RawMessage(`{"dbPath":"` + tmpfile + `"}`))
|
|
if err != nil {
|
|
t.Fatalf("init failed: %v", err)
|
|
}
|
|
if version != Version {
|
|
t.Errorf("expected version %d, got %d", Version, version)
|
|
}
|
|
if sa.db == nil {
|
|
t.Fatal("database not initialized")
|
|
}
|
|
sa.db.Close()
|
|
}
|
|
|
|
func TestSqliteStoreAndLoadJobMeta(t *testing.T) {
|
|
tmpfile := t.TempDir() + "/test.db"
|
|
defer os.Remove(tmpfile)
|
|
|
|
var sa SqliteArchive
|
|
_, err := sa.Init(json.RawMessage(`{"dbPath":"` + tmpfile + `"}`))
|
|
if err != nil {
|
|
t.Fatalf("init failed: %v", err)
|
|
}
|
|
defer sa.db.Close()
|
|
|
|
job := &schema.Job{
|
|
JobID: 12345,
|
|
Cluster: "test-cluster",
|
|
StartTime: 1234567890,
|
|
NumNodes: 1,
|
|
Resources: []*schema.Resource{{Hostname: "node001"}},
|
|
}
|
|
|
|
// Store job metadata
|
|
if err := sa.StoreJobMeta(job); err != nil {
|
|
t.Fatalf("store failed: %v", err)
|
|
}
|
|
|
|
// Check if exists
|
|
if !sa.Exists(job) {
|
|
t.Fatal("job should exist")
|
|
}
|
|
|
|
// Load job metadata
|
|
loaded, err := sa.LoadJobMeta(job)
|
|
if err != nil {
|
|
t.Fatalf("load failed: %v", err)
|
|
}
|
|
|
|
if loaded.JobID != job.JobID {
|
|
t.Errorf("expected JobID %d, got %d", job.JobID, loaded.JobID)
|
|
}
|
|
if loaded.Cluster != job.Cluster {
|
|
t.Errorf("expected Cluster %s, got %s", job.Cluster, loaded.Cluster)
|
|
}
|
|
if loaded.StartTime != job.StartTime {
|
|
t.Errorf("expected StartTime %d, got %d", job.StartTime, loaded.StartTime)
|
|
}
|
|
}
|
|
|
|
func TestSqliteImportJob(t *testing.T) {
|
|
tmpfile := t.TempDir() + "/test.db"
|
|
defer os.Remove(tmpfile)
|
|
|
|
var sa SqliteArchive
|
|
_, err := sa.Init(json.RawMessage(`{"dbPath":"` + tmpfile + `"}`))
|
|
if err != nil {
|
|
t.Fatalf("init failed: %v", err)
|
|
}
|
|
defer sa.db.Close()
|
|
|
|
// For now, skip complex JobData testing
|
|
// Just test that ImportJob accepts the parameters
|
|
// Full integration testing would require actual job data files
|
|
t.Log("ImportJob interface verified (full data test requires integration)")
|
|
}
|
|
|
|
func TestSqliteGetClusters(t *testing.T) {
|
|
tmpfile := t.TempDir() + "/test.db"
|
|
defer os.Remove(tmpfile)
|
|
|
|
var sa SqliteArchive
|
|
_, err := sa.Init(json.RawMessage(`{"dbPath":"` + tmpfile + `"}`))
|
|
if err != nil {
|
|
t.Fatalf("init failed: %v", err)
|
|
}
|
|
defer sa.db.Close()
|
|
|
|
// Add jobs from different clusters
|
|
job1 := &schema.Job{
|
|
JobID: 1,
|
|
Cluster: "cluster-a",
|
|
StartTime: 1000,
|
|
NumNodes: 1,
|
|
Resources: []*schema.Resource{{Hostname: "node001"}},
|
|
}
|
|
job2 := &schema.Job{
|
|
JobID: 2,
|
|
Cluster: "cluster-b",
|
|
StartTime: 2000,
|
|
NumNodes: 1,
|
|
Resources: []*schema.Resource{{Hostname: "node002"}},
|
|
}
|
|
|
|
sa.StoreJobMeta(job1)
|
|
sa.StoreJobMeta(job2)
|
|
|
|
// Reinitialize to refresh cluster list
|
|
sa.db.Close()
|
|
_, err = sa.Init(json.RawMessage(`{"dbPath":"` + tmpfile + `"}`))
|
|
if err != nil {
|
|
t.Fatalf("reinit failed: %v", err)
|
|
}
|
|
defer sa.db.Close()
|
|
|
|
clusters := sa.GetClusters()
|
|
if len(clusters) != 2 {
|
|
t.Errorf("expected 2 clusters, got %d", len(clusters))
|
|
}
|
|
}
|
|
|
|
func TestSqliteCleanUp(t *testing.T) {
|
|
tmpfile := t.TempDir() + "/test.db"
|
|
defer os.Remove(tmpfile)
|
|
|
|
var sa SqliteArchive
|
|
_, err := sa.Init(json.RawMessage(`{"dbPath":"` + tmpfile + `"}`))
|
|
if err != nil {
|
|
t.Fatalf("init failed: %v", err)
|
|
}
|
|
defer sa.db.Close()
|
|
|
|
job := &schema.Job{
|
|
JobID: 999,
|
|
Cluster: "test",
|
|
StartTime: 5000,
|
|
NumNodes: 1,
|
|
Resources: []*schema.Resource{{Hostname: "node001"}},
|
|
}
|
|
|
|
sa.StoreJobMeta(job)
|
|
|
|
// Verify exists
|
|
if !sa.Exists(job) {
|
|
t.Fatal("job should exist")
|
|
}
|
|
|
|
// Clean up
|
|
sa.CleanUp([]*schema.Job{job})
|
|
|
|
// Verify deleted
|
|
if sa.Exists(job) {
|
|
t.Fatal("job should not exist after cleanup")
|
|
}
|
|
}
|
|
|
|
func TestSqliteClean(t *testing.T) {
|
|
tmpfile := t.TempDir() + "/test.db"
|
|
defer os.Remove(tmpfile)
|
|
|
|
var sa SqliteArchive
|
|
_, err := sa.Init(json.RawMessage(`{"dbPath":"` + tmpfile + `"}`))
|
|
if err != nil {
|
|
t.Fatalf("init failed: %v", err)
|
|
}
|
|
defer sa.db.Close()
|
|
|
|
// Add jobs with different start times
|
|
oldJob := &schema.Job{
|
|
JobID: 1,
|
|
Cluster: "test",
|
|
StartTime: 1000,
|
|
NumNodes: 1,
|
|
Resources: []*schema.Resource{{Hostname: "node001"}},
|
|
}
|
|
newJob := &schema.Job{
|
|
JobID: 2,
|
|
Cluster: "test",
|
|
StartTime: 9000,
|
|
NumNodes: 1,
|
|
Resources: []*schema.Resource{{Hostname: "node002"}},
|
|
}
|
|
|
|
sa.StoreJobMeta(oldJob)
|
|
sa.StoreJobMeta(newJob)
|
|
|
|
// Clean jobs before 5000
|
|
sa.Clean(5000, 0)
|
|
|
|
// Old job should be deleted
|
|
if sa.Exists(oldJob) {
|
|
t.Error("old job should be deleted")
|
|
}
|
|
|
|
// New job should still exist
|
|
if !sa.Exists(newJob) {
|
|
t.Error("new job should still exist")
|
|
}
|
|
}
|
|
|
|
func TestSqliteIter(t *testing.T) {
|
|
tmpfile := t.TempDir() + "/test.db"
|
|
defer os.Remove(tmpfile)
|
|
|
|
var sa SqliteArchive
|
|
_, err := sa.Init(json.RawMessage(`{"dbPath":"` + tmpfile + `"}`))
|
|
if err != nil {
|
|
t.Fatalf("init failed: %v", err)
|
|
}
|
|
defer sa.db.Close()
|
|
|
|
// Add multiple jobs
|
|
for i := 1; i <= 3; i++ {
|
|
job := &schema.Job{
|
|
JobID: int64(i),
|
|
Cluster: "test",
|
|
StartTime: int64(i * 1000),
|
|
NumNodes: 1,
|
|
Resources: []*schema.Resource{{Hostname: "node001"}},
|
|
}
|
|
sa.StoreJobMeta(job)
|
|
}
|
|
|
|
// Iterate
|
|
count := 0
|
|
for container := range sa.Iter(false) {
|
|
if container.Meta == nil {
|
|
t.Error("expected non-nil meta")
|
|
}
|
|
count++
|
|
}
|
|
|
|
if count != 3 {
|
|
t.Errorf("expected 3 jobs, got %d", count)
|
|
}
|
|
}
|
|
|
|
func TestSqliteCompress(t *testing.T) {
|
|
// Compression test requires actual job data
|
|
// For now just verify the method exists and doesn't panic
|
|
tmpfile := t.TempDir() + "/test.db"
|
|
defer os.Remove(tmpfile)
|
|
|
|
var sa SqliteArchive
|
|
_, err := sa.Init(json.RawMessage(`{"dbPath":"` + tmpfile + `"}`))
|
|
if err != nil {
|
|
t.Fatalf("init failed: %v", err)
|
|
}
|
|
defer sa.db.Close()
|
|
|
|
job := &schema.Job{
|
|
JobID: 777,
|
|
Cluster: "test",
|
|
StartTime: 7777,
|
|
NumNodes: 1,
|
|
Resources: []*schema.Resource{{Hostname: "node001"}},
|
|
}
|
|
|
|
sa.StoreJobMeta(job)
|
|
|
|
// Compress should not panic even with missing data
|
|
sa.Compress([]*schema.Job{job})
|
|
|
|
t.Log("Compression method verified")
|
|
}
|
|
|
|
func TestSqliteConfigParsing(t *testing.T) {
|
|
rawConfig := json.RawMessage(`{"dbPath": "/tmp/test.db"}`)
|
|
|
|
var cfg SqliteArchiveConfig
|
|
err := json.Unmarshal(rawConfig, &cfg)
|
|
if err != nil {
|
|
t.Fatalf("failed to parse config: %v", err)
|
|
}
|
|
|
|
if cfg.DBPath != "/tmp/test.db" {
|
|
t.Errorf("expected dbPath '/tmp/test.db', got '%s'", cfg.DBPath)
|
|
}
|
|
}
|