package metrics import ( "path/filepath" "testing" "time" ) func TestDefaultConfig_UsesLargerWriteBuffer(t *testing.T) { cfg := DefaultConfig("/tmp/pulse") if cfg.WriteBufferSize != 500 { t.Fatalf("WriteBufferSize = %d, want 500", cfg.WriteBufferSize) } } func TestStoreCoalesceQueuedBatches(t *testing.T) { store := &Store{ writeCh: make(chan writeRequest, 4), } initial := writeRequest{ metrics: []bufferedMetric{ {resourceType: "vm", resourceID: "vm-1", metricType: "cpu", value: 10}, }, } store.writeCh <- writeRequest{metrics: []bufferedMetric{ {resourceType: "vm", resourceID: "vm-1", metricType: "memory", value: 20}, }} store.writeCh <- writeRequest{metrics: []bufferedMetric{ {resourceType: "vm", resourceID: "vm-2", metricType: "cpu", value: 30}, }} combined := store.coalesceQueuedRequests(initial) if len(combined) != 3 { t.Fatalf("expected 3 combined requests, got %d", len(combined)) } totalMetrics := 0 for _, req := range combined { totalMetrics += len(req.metrics) } if totalMetrics != 3 { t.Fatalf("expected 3 combined metrics, got %d", totalMetrics) } if len(store.writeCh) != 0 { t.Fatalf("expected queued batches to be drained, got %d remaining", len(store.writeCh)) } } func TestStoreWriteBatchSync(t *testing.T) { dir := t.TempDir() store, err := NewStore(DefaultConfig(dir)) if err != nil { t.Fatalf("NewStore returned error: %v", err) } defer store.Close() ts := time.Unix(1000, 0) metrics := []WriteMetric{ {ResourceType: "vm", ResourceID: "v1", MetricType: "cpu", Value: 10.0, Timestamp: ts, Tier: TierRaw}, {ResourceType: "vm", ResourceID: "v1", MetricType: "mem", Value: 50.0, Timestamp: ts, Tier: TierRaw}, } store.WriteBatchSync(metrics) // Verify data was written points, err := store.Query("vm", "v1", "cpu", ts.Add(-time.Second), ts.Add(time.Second), 0) if err != nil { t.Fatalf("Query failed: %v", err) } if len(points) != 1 || points[0].Value != 10.0 { t.Fatalf("expected 1 point with value 10.0, got %v", points) } } func TestStoreClear(t *testing.T) { dir := t.TempDir() store, err := NewStore(DefaultConfig(dir)) if err != nil { t.Fatalf("NewStore returned error: %v", err) } defer store.Close() // Write some data store.Write("vm", "v1", "cpu", 10.0, time.Now()) store.Flush() // Write some data store.Write("vm", "v1", "cpu", 10.0, time.Now()) store.Flush() stats := store.GetStats() if stats.RawCount == 0 { t.Fatal("expected data to be written before clearing") } // clear if err := store.Clear(); err != nil { t.Fatalf("Clear failed: %v", err) } // Verify empty stats = store.GetStats() if stats.RawCount != 0 { t.Fatalf("expected empty store, got %d raw records", stats.RawCount) } } func TestStoreSetMaxOpenConns(t *testing.T) { dir := t.TempDir() store, err := NewStore(DefaultConfig(dir)) if err != nil { t.Fatalf("NewStore returned error: %v", err) } defer store.Close() // Just verifying it doesn't panic store.SetMaxOpenConns(5) } func TestStoreRunRollupManually(t *testing.T) { // Tests the runRollup function wrapper which was showing 0% coverage dir := t.TempDir() cfg := DefaultConfig(dir) // Create separate DB for this test cfg.DBPath = filepath.Join(dir, "metrics-rollup-manual.db") store, err := NewStore(cfg) if err != nil { t.Fatalf("NewStore returned error: %v", err) } defer store.Close() // Trigger manual rollup - should not panic store.runRollup() } func TestStoreGetMetaIntInvalid(t *testing.T) { dir := t.TempDir() store, err := NewStore(DefaultConfig(dir)) if err != nil { t.Fatalf("NewStore returned error: %v", err) } defer store.Close() // Insert invalid int value _, err = store.db.Exec("INSERT INTO metrics_meta (key, value) VALUES (?, ?)", "bad_key", "not_an_int") if err != nil { t.Fatalf("failed to insert invalid meta: %v", err) } val, ok := store.getMetaInt("bad_key") if ok { t.Fatalf("expected getMetaInt to fail for invalid int, got %d", val) } } func TestStoreFlushMakesQueuedWritesVisible(t *testing.T) { dir := t.TempDir() cfg := DefaultConfig(dir) cfg.WriteBufferSize = 1 cfg.FlushInterval = time.Hour store, err := NewStore(cfg) if err != nil { t.Fatalf("NewStore returned error: %v", err) } defer store.Close() ts := time.Unix(2000, 0) store.Write("node", "node-1", "cpu", 42, ts) // With a buffer size of 1, the write above is already queued asynchronously. // Flush must still wait for that queued batch to be committed. store.Flush() points, err := store.Query("node", "node-1", "cpu", ts.Add(-time.Second), ts.Add(time.Second), 0) if err != nil { t.Fatalf("Query failed: %v", err) } if len(points) != 1 || points[0].Value != 42 { t.Fatalf("expected flushed metric to be immediately visible, got %v", points) } }