import { describe, expect, it } from "@effect/vitest"; import { decodeScanCache, dedupeWithinFile, encodeScanCache, pruneScanCache, type ScanCache, } from "./usageScanCache.ts"; import type { UsageRecord } from "./usageTranscripts.ts"; function record(overrides: Partial = {}): UsageRecord { return { provider: "claude", timestampMs: 1_786_000_000_000, model: "claude-fable-5", sessionId: "session-a", totals: { uncachedInputTokens: 2, cachedInputTokens: 1000, cacheCreationTokens: 10, outputTokens: 50, reasoningTokens: 0, }, reportedCostUsd: null, dedupeKey: "msg_1:", ...overrides, }; } function cacheWith(entries: readonly [string, number, readonly UsageRecord[]][]): ScanCache { const cache: ScanCache = new Map(); for (const [path, mtimeMs, records] of entries) { cache.set(path, { size: records.length * 10, mtimeMs, provider: "claude", records }); } return cache; } describe("scan cache round trip", () => { it("restores records unchanged", () => { const original = cacheWith([ ["/a.jsonl", 100, [record(), record({ dedupeKey: "msg_2:", model: "claude-opus-5" })]], ["/b.jsonl", 200, [record({ sessionId: "session-b", reportedCostUsd: 1.5 })]], ]); original.set("/grok.jsonl", { size: 40, mtimeMs: 300, provider: "grok", records: [ record({ provider: "grok", model: "grok-4.5-build", dedupeKey: "s:p:grok-4.5-build" }), ], }); const restored = decodeScanCache(JSON.parse(JSON.stringify(encodeScanCache(original)))); expect(restored.size).toBe(3); expect(restored.get("/a.jsonl")).toEqual(original.get("/a.jsonl")); expect(restored.get("/b.jsonl")).toEqual(original.get("/b.jsonl")); expect(restored.get("/grok.jsonl")).toEqual(original.get("/grok.jsonl")); }); it("interns repeated model and session strings", () => { const encoded = encodeScanCache( cacheWith([["/a.jsonl", 100, [record(), record({ dedupeKey: "msg_2:" }), record()]]]), ); expect(encoded.models).toEqual(["claude-fable-5"]); expect(encoded.sessions).toEqual(["session-a"]); }); it("treats a corrupt or foreign document as an empty cache", () => { // A bad cache should cost one cold scan, never a broken page. expect(decodeScanCache(null).size).toBe(0); expect(decodeScanCache("nonsense").size).toBe(0); expect(decodeScanCache({ version: 999, models: [], sessions: [], files: {} }).size).toBe(0); }); it("skips malformed file entries but keeps good ones", () => { const encoded = encodeScanCache(cacheWith([["/good.jsonl", 100, [record()]]])); const withJunk = { ...encoded, files: { ...encoded.files, "/bad.jsonl": { s: "nope", m: 1, p: "claude", r: [] } }, }; const restored = decodeScanCache(JSON.parse(JSON.stringify(withJunk))); expect([...restored.keys()]).toEqual(["/good.jsonl"]); }); it("rejects the whole cache when an intern table holds a non-string", () => { // models: [1] would pass the undefined guard, put a number in a record's // model, and crash normalizeModelName at aggregate time. const encoded = encodeScanCache(cacheWith([["/a.jsonl", 100, [record()]]])); const poisoned = { ...encoded, models: [1] }; expect(decodeScanCache(JSON.parse(JSON.stringify(poisoned))).size).toBe(0); }); it("drops the whole entry when any row is corrupt, forcing a cold re-parse", () => { // Keeping the surviving rows under the original (size, mtime) would read // as a valid warm hit and the file would never be re-parsed. const encoded = encodeScanCache( cacheWith([["/a.jsonl", 100, [record(), record({ dedupeKey: "msg_2:" })]]]), ); const rows = encoded.files["/a.jsonl"]!.r; const poisoned = { ...encoded, files: { "/a.jsonl": { ...encoded.files["/a.jsonl"]!, r: [rows[0]!, [...rows[1]!.slice(0, 3), "not-a-number", ...rows[1]!.slice(4)]], }, }, }; const restored = decodeScanCache(JSON.parse(JSON.stringify(poisoned))); expect(restored.has("/a.jsonl")).toBe(false); }); }); describe("pruneScanCache", () => { const retentionCutoffMs = 1000; it("drops entries older than retention", () => { const cache = cacheWith([["/old.jsonl", 500, [record()]]]); const removed = pruneScanCache(cache, { livePaths: new Set(), walkedRoots: ["/"], windowStartMs: 400, retentionCutoffMs, }); expect(removed).toBe(1); expect(cache.size).toBe(0); }); it("drops in-window entries whose file has disappeared", () => { const cache = cacheWith([["/gone.jsonl", 5000, [record()]]]); pruneScanCache(cache, { livePaths: new Set(), walkedRoots: ["/"], windowStartMs: 4000, retentionCutoffMs, }); expect(cache.size).toBe(0); }); it("keeps entries outside the walked window that are still within retention", () => { // Viewing 7 days must not evict the 30-day entries, which that walk never // looked for and so cannot prove are gone. const cache = cacheWith([["/older-but-valid.jsonl", 2000, [record()]]]); const removed = pruneScanCache(cache, { livePaths: new Set(), walkedRoots: ["/"], windowStartMs: 4000, retentionCutoffMs, }); expect(removed).toBe(0); expect(cache.size).toBe(1); }); it("keeps entries the walk saw", () => { const cache = cacheWith([["/live.jsonl", 5000, [record()]]]); pruneScanCache(cache, { livePaths: new Set(["/live.jsonl"]), walkedRoots: ["/"], windowStartMs: 4000, retentionCutoffMs, }); expect(cache.size).toBe(1); }); }); describe("pruneScanCache with an unwalked root", () => { it("keeps in-window entries for a provider whose directory was not walked", () => { // A missing provider root or failed settings read leaves livePaths without // that provider's files. Its warm entries must survive the pass. const cache = cacheWith([["/codex/sessions/a.jsonl", 5000, [record()]]]); const removed = pruneScanCache(cache, { livePaths: new Set(), walkedRoots: ["/claude/projects"], windowStartMs: 4000, retentionCutoffMs: 1000, }); expect(removed).toBe(0); expect(cache.size).toBe(1); }); }); describe("dedupeWithinFile", () => { it("keeps the first record per dedupe key", () => { const kept = dedupeWithinFile([ record({ totals: { ...record().totals, outputTokens: 1 } }), record({ totals: { ...record().totals, outputTokens: 999 } }), record({ dedupeKey: "msg_2:" }), ]); expect(kept).toHaveLength(2); expect(kept[0]?.totals.outputTokens).toBe(1); }); it("keeps every record that has no dedupe key", () => { expect( dedupeWithinFile([record({ dedupeKey: null }), record({ dedupeKey: null })]), ).toHaveLength(2); }); });