| 1 | package usagecatalog |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "os" |
| 6 | "path/filepath" |
| 7 | "testing" |
| 8 | ) |
| 9 | |
| 10 | func TestRebuildReplacesStaleUsageProjection(t *testing.T) { |
| 11 | t.Parallel() |
| 12 | ctx := context.Background() |
| 13 | databasePath := filepath.Join(t.TempDir(), "usage.sqlite") |
| 14 | staleDir := t.TempDir() |
| 15 | currentDir := t.TempDir() |
| 16 | if err := os.WriteFile(filepath.Join(staleDir, "2026-08-09.jsonl"), |
| 17 | []byte("{\"ts\":\"2026-08-09T10:00:00Z\",\"source\":\"desktop\",\"total\":9}\n"), 0o600); err != nil { |
| 18 | t.Fatal(err) |
| 19 | } |
| 20 | if err := os.WriteFile(filepath.Join(currentDir, "2026-08-10.jsonl"), |
| 21 | []byte("{\"ts\":\"2026-08-10T10:00:00Z\",\"source\":\"desktop\",\"total\":42}\n"), 0o600); err != nil { |
| 22 | t.Fatal(err) |
| 23 | } |
| 24 | seed, err := Open(ctx, databasePath) |
| 25 | if err != nil { |
| 26 | t.Fatal(err) |
| 27 | } |
| 28 | if err := seed.ReconcileDir(ctx, staleDir); err != nil { |
| 29 | t.Fatal(err) |
| 30 | } |
| 31 | if err := seed.Close(ctx); err != nil { |
| 32 | t.Fatal(err) |
| 33 | } |
| 34 | if _, err := Rebuild(ctx, databasePath, currentDir); err != nil { |
| 35 | t.Fatal(err) |
| 36 | } |
| 37 | rebuilt, err := Open(ctx, databasePath) |
| 38 | if err != nil { |
| 39 | t.Fatal(err) |
| 40 | } |
| 41 | t.Cleanup(func() { _ = rebuilt.Close(context.Background()) }) |
| 42 | stale, err := rebuilt.Query(ctx, "2026-08-09", "2026-08-09", "desktop") |
| 43 | if err != nil || len(stale) != 0 { |
| 44 | t.Fatalf("stale rows=%#v err=%v", stale, err) |
| 45 | } |
| 46 | current, err := rebuilt.Query(ctx, "2026-08-10", "2026-08-10", "desktop") |
| 47 | if err != nil || len(current) != 1 || current[0].Total != 42 { |
| 48 | t.Fatalf("current rows=%#v err=%v", current, err) |
| 49 | } |
| 50 | } |
| 51 |