package store_test import ( "path/filepath" "testing" "time" "git.wntrmute.dev/kyle/crossbar/internal/store" ) func open(t *testing.T, dir string) *store.Store { s, err := store.Open(filepath.Join(dir, "crossbar.db")) if err != nil { t.Fatalf("Open: %v", err) } t.Cleanup(func() { _ = s.Close() }) return s } func TestOpenIsIdempotentAndWAL(t *testing.T) { dir := t.TempDir() s := open(t, dir) if got := s.JournalMode(); got != "wal" { t.Errorf("journal_mode = %q, want wal", got) } if err := s.Close(); err != nil { t.Fatal(err) } open(t, dir) // second open on the same file must not fail on existing tables } func TestLeasesSurviveReopen(t *testing.T) { dir := t.TempDir() s := open(t, dir) now := time.Date(2026, 9, 25, 10, 0, 0, 0, time.UTC) l := store.Lease{Route: "opencode-a", FP: "abc", Model: "m", Host: "alpha", State: store.Active, Created: now, LastUsed: now} if err := s.SaveLease(l); err != nil { t.Fatal(err) } l2 := l l2.FP = "def" l2.Host = "beta" l2.State = store.Pinned if err := s.SaveLease(l2); err != nil { t.Fatal(err) } // Saving the same key again replaces, not duplicates. l.Host = "beta" l.LastUsed = now.Add(time.Minute) if err := s.SaveLease(l); err != nil { t.Fatal(err) } if err := s.Close(); err != nil { t.Fatal(err) } s = open(t, dir) got, err := s.ListLeases() if err != nil { t.Fatal(err) } if len(got) != 2 { t.Fatalf("ListLeases = %d rows, want 2: %+v", len(got), got) } byFP := map[string]store.Lease{} for _, x := range got { byFP[x.FP] = x } if a := byFP["abc"]; a.Host != "beta" || !a.LastUsed.Equal(now.Add(time.Minute)) || a.State != store.Active { t.Errorf("abc = %+v", a) } if d := byFP["def"]; d.State != store.Pinned || d.Host != "beta" { t.Errorf("def = %+v", d) } if err := s.DeleteLease("opencode-a", "abc", "m"); err != nil { t.Fatal(err) } got, _ = s.ListLeases() if len(got) != 1 || got[0].FP != "def" { t.Errorf("after delete: %+v", got) } } func TestEventsAndRequestsAndUsage(t *testing.T) { s := open(t, t.TempDir()) t0 := time.Date(2026, 9, 25, 10, 0, 0, 0, time.UTC) must := func(err error) { if err != nil { t.Fatal(err) } } must(s.RecordEvent(store.LeaseEvent{TS: t0, Route: "r1", Model: "m", FromHost: "", ToHost: "alpha", Reason: store.ReasonNew})) must(s.RecordEvent(store.LeaseEvent{TS: t0.Add(time.Hour), Route: "r1", Model: "m", FromHost: "alpha", ToHost: "beta", Reason: store.ReasonUnhealthy})) reqs := []store.Request{ {Route: "r1", FP: "a", Model: "m", Host: "alpha", Started: t0, QueuedMs: 0, TTFBMs: 100, TotalMs: 1000, Status: 200, Streamed: true, PromptTokens: 1000, CachedTokens: 900, CompletionTokens: 50}, {Route: "r1", FP: "a", Model: "m", Host: "alpha", Started: t0.Add(time.Minute), QueuedMs: 40, TTFBMs: 120, TotalMs: 2000, Status: 200, Streamed: true, PromptTokens: 1100, CachedTokens: 1000, CompletionTokens: 60}, {Route: "r2", FP: "b", Model: "m", Host: "beta", Started: t0.Add(2 * time.Minute), TotalMs: 500, Status: 502, Err: "upstream failed"}, {Route: "r2", FP: "b", Model: "m", Host: "beta", Started: t0.Add(-48 * time.Hour), TotalMs: 300, Status: 200, PromptTokens: 10, CompletionTokens: 5}, } for _, r := range reqs { must(s.RecordRequest(r)) } must(s.RecordHostHealth(store.HostHealth{TS: t0, Host: "alpha", Healthy: true, Loaded: []string{"m"}})) rows, err := s.Usage(t0.Add(-time.Hour), store.ByRoute) must(err) if len(rows) != 2 { t.Fatalf("Usage by route since t0-1h: %d rows, want 2 (r1, r2): %+v", len(rows), rows) } byKey := map[string]store.UsageRow{} for _, r := range rows { byKey[r.Key] = r } r1 := byKey["r1"] if r1.Requests != 2 || r1.Errors != 0 || r1.BusyMs != 3000 || r1.QueuedMs != 40 { t.Errorf("r1 = %+v", r1) } if r1.PromptTokens != 2100 || r1.CachedTokens != 1900 || r1.CompletionTokens != 110 { t.Errorf("r1 tokens = %+v", r1) } if got := r1.CacheHitRatio(); got < 0.904 || got > 0.905 { t.Errorf("r1 cache hit ratio = %v, want 1900/2100", got) } r2 := byKey["r2"] if r2.Requests != 1 || r2.Errors != 1 || r2.BusyMs != 500 { t.Errorf("r2 = %+v (the 48h-old request is outside since)", r2) } if r2.CacheHitRatio() != 0 { t.Errorf("no prompt tokens: ratio must be 0, got %v", r2.CacheHitRatio()) } byHost, err := s.Usage(time.Time{}, store.ByHost) must(err) if len(byHost) != 2 { t.Errorf("by host, all time: %+v", byHost) } for _, r := range byHost { if r.Key == "beta" && r.Requests != 2 { t.Errorf("beta all-time requests = %d, want 2", r.Requests) } } byModel, err := s.Usage(time.Time{}, store.ByModel) must(err) if len(byModel) != 1 || byModel[0].Key != "m" || byModel[0].Requests != 4 { t.Errorf("by model: %+v", byModel) } ev, err := s.Events(t0.Add(-time.Minute), 10) must(err) if len(ev) != 2 || ev[0].Reason != store.ReasonNew || ev[1].ToHost != "beta" { t.Errorf("events = %+v", ev) } } func TestPruneRollsUpOldRequests(t *testing.T) { s := open(t, t.TempDir()) t0 := time.Date(2026, 9, 25, 10, 0, 0, 0, time.UTC) old := t0.Add(-200 * 24 * time.Hour) for i := 0; i < 3; i++ { if err := s.RecordRequest(store.Request{Route: "r", Model: "m", Host: "h", Started: old.Add(time.Duration(i) * time.Minute), TotalMs: 100, Status: 200, PromptTokens: 10, CachedTokens: 5, CompletionTokens: 1}); err != nil { t.Fatal(err) } } if err := s.RecordRequest(store.Request{Route: "r", Model: "m", Host: "h", Started: t0, TotalMs: 100, Status: 200}); err != nil { t.Fatal(err) } n, err := s.Prune(t0, 180*24*time.Hour) if err != nil { t.Fatal(err) } if n != 3 { t.Errorf("Prune removed %d rows, want 3", n) } rows, _ := s.Usage(time.Time{}, store.ByRoute) if len(rows) != 1 || rows[0].Requests != 4 || rows[0].PromptTokens != 30 { t.Errorf("usage must still include pruned traffic through the daily rollup: %+v", rows) } live, _ := s.Usage(old.Add(24*time.Hour), store.ByRoute) if len(live) != 1 || live[0].Requests != 1 { t.Errorf("recent-only usage = %+v", live) } } func TestBadPath(t *testing.T) { if _, err := store.Open(filepath.Join(t.TempDir(), "no", "such", "dir", "x.db")); err == nil { t.Fatal("Open must fail when the directory does not exist") } }