// 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) } }