mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-04-28 03:20:11 +00:00
231 lines
5.5 KiB
Go
231 lines
5.5 KiB
Go
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.Now().UTC().Truncate(time.Second)
|
|
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)
|
|
}
|
|
}
|
|
|
|
func TestNewStoreDefersStartupMaintenance(t *testing.T) {
|
|
previousHook := startupMaintenanceHook
|
|
defer func() {
|
|
startupMaintenanceHook = previousHook
|
|
}()
|
|
|
|
started := make(chan struct{})
|
|
release := make(chan struct{})
|
|
startupMaintenanceHook = func() {
|
|
close(started)
|
|
<-release
|
|
}
|
|
|
|
dir := t.TempDir()
|
|
cfg := DefaultConfig(dir)
|
|
cfg.FlushInterval = time.Hour
|
|
|
|
done := make(chan struct{})
|
|
var (
|
|
store *Store
|
|
err error
|
|
)
|
|
go func() {
|
|
store, err = NewStore(cfg)
|
|
close(done)
|
|
}()
|
|
|
|
select {
|
|
case <-done:
|
|
case <-time.After(200 * time.Millisecond):
|
|
t.Fatal("NewStore blocked on startup maintenance")
|
|
}
|
|
|
|
if err != nil {
|
|
t.Fatalf("NewStore returned error: %v", err)
|
|
}
|
|
|
|
select {
|
|
case <-started:
|
|
case <-time.After(time.Second):
|
|
t.Fatal("startup maintenance was not scheduled")
|
|
}
|
|
|
|
close(release)
|
|
defer store.Close()
|
|
}
|