/** * KHAELOR * File: tests/repository/recent.test.ts * Description: RecentFilesTracker tests — LRU bounds, frecency decay, repeated-access boosts. * * Author: Simon-Pierre Boucher * Contact: contact@spboucher.ai */ import { describe, expect, it } from "vitest"; import { RecentFilesTracker } from "../../src/repository/index.js"; const T0 = 1_700_000_000_000; const MINUTE = 60_000; const HOUR = 60 * MINUTE; const DAY = 24 * HOUR; function tracker(maxEntries = 256, maxTimestampsPerFile = 16): RecentFilesTracker { return new RecentFilesTracker({ maxEntries, maxTimestampsPerFile, now: () => T0 }); } describe("RecentFilesTracker LRU bound", () => { it("evicts the least recently accessed path at capacity", () => { const t = tracker(3); t.noteAccess("a", T0 - 4 * MINUTE); t.noteAccess("b", T0 - 3 * MINUTE); t.noteAccess("c", T0 - 2 * MINUTE); t.noteAccess("d", T0 - MINUTE); expect(t.size()).toBe(3); expect(t.has("a")).toBe(false); expect(t.has("d")).toBe(true); }); it("re-accessing a path refreshes its LRU position", () => { const t = tracker(3); t.noteAccess("a", T0 - 4 * MINUTE); t.noteAccess("b", T0 - 3 * MINUTE); t.noteAccess("c", T0 - 2 * MINUTE); t.noteAccess("a", T0 - MINUTE); // refresh a t.noteAccess("d", T0); // should evict b, not a expect(t.has("a")).toBe(true); expect(t.has("b")).toBe(false); }); }); describe("RecentFilesTracker frecency", () => { it("scores recent accesses far higher than old ones", () => { const t = tracker(); t.noteAccess("old.ts", T0 - 30 * DAY); t.noteAccess("recent.ts", T0 - 10 * MINUTE); expect(t.frecency("recent.ts")).toBeGreaterThan(t.frecency("old.ts")); expect(t.recent().map((r) => r.path)).toEqual(["recent.ts", "old.ts"]); }); it("accumulates over repeated accesses", () => { const t = tracker(); t.noteAccess("hot.ts", T0 - 3 * MINUTE); t.noteAccess("hot.ts", T0 - 2 * MINUTE); t.noteAccess("hot.ts", T0 - MINUTE); t.noteAccess("cold.ts", T0 - MINUTE); expect(t.frecency("hot.ts")).toBe(3 * t.frecency("cold.ts")); const hot = t.recent()[0]; expect(hot).toMatchObject({ path: "hot.ts", count: 3 }); }); it("bounds the per-file timestamp window", () => { const t = tracker(256, 2); for (let i = 5; i >= 1; i -= 1) t.noteAccess("f.ts", T0 - i * MINUTE); expect(t.frecency("f.ts")).toBe(16); // only 2 retained accesses × weight 8 }); it("returns 0 for unknown paths", () => { expect(tracker().frecency("nope.ts")).toBe(0); }); });