import { describe, expect, it } from "bun:test"; import { getUsageMetadata, computeUsageStatus, buildUsagePatch, recordMemoryUsage, recordReconstructionUsage, deriveUsageStatus, USAGE_COLD_INJECTION_THRESHOLD, type UsageMetadata, type UsageStorePort, } from "../usage-tracker.js"; import type { MemoryEntry } from "../store.js"; // --------------------------------------------------------------------------- // Helpers // --------------------------------------------------------------------------- const ID = "11111111-1111-1111-1111-111111111111"; function makeEntry(id: string, meta: Record = {}): MemoryEntry { return { id, text: "t", vector: [1, 0, 0], category: "events", scope: "project:test", importance: 0.7, timestamp: 1000, metadata: JSON.stringify(meta), }; } function createMockStore(entries: MemoryEntry[]): { store: UsageStorePort; data: Map; updates: Array<{ id: string; metadata: string }>; } { const data = new Map(entries.map(e => [e.id, { ...e }])); const updates: Array<{ id: string; metadata: string }> = []; // 模拟 store.patchMetadata 的真实语义:读最新 metadata → patchFn → 写回。 const store: UsageStorePort = { async patchMetadata(id, patchFn, _scopeFilter) { const entry = data.get(id); if (!entry) return null; let meta: Record; try { const parsed: unknown = JSON.parse(entry.metadata || "{}"); meta = parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? (parsed as Record) : {}; } catch { meta = {}; } const patched = patchFn(meta, entry); entry.metadata = JSON.stringify(patched); updates.push({ id, metadata: entry.metadata }); return entry; }, }; return { store, data, updates }; } // --------------------------------------------------------------------------- // Tests // --------------------------------------------------------------------------- describe("usage-tracker", () => { describe("getUsageMetadata", () => { it("returns useCount 0 for entry without usage", () => { expect(getUsageMetadata(makeEntry(ID)).useCount).toBe(0); }); it("returns 0 for malformed metadata", () => { const e: MemoryEntry = { ...makeEntry(ID), metadata: "not-json" }; expect(getUsageMetadata(e).useCount).toBe(0); }); it("reads stored usage fields", () => { const e = makeEntry(ID, { usage: { useCount: 3, firstUsedAt: 1, lastUsedAt: 5, usageStatus: "warm" } }); const u = getUsageMetadata(e); expect(u.useCount).toBe(3); expect(u.lastUsedAt).toBe(5); expect(u.usageStatus).toBe("warm"); }); it("ignores invalid usageStatus value", () => { const e = makeEntry(ID, { usage: { useCount: 1, usageStatus: "bogus" } }); expect(getUsageMetadata(e).usageStatus).toBeUndefined(); }); it("malformed metadata='null' does not crash", () => { const e: MemoryEntry = { ...makeEntry(ID), metadata: "null" }; expect(getUsageMetadata(e).useCount).toBe(0); expect(() => buildUsagePatch(e, 100)).not.toThrow(); }); it("malformed array metadata does not crash", () => { const e: MemoryEntry = { ...makeEntry(ID), metadata: "[1,2,3]" }; expect(getUsageMetadata(e).useCount).toBe(0); expect(deriveUsageStatus(e)).toBe("unused"); }); }); describe("computeUsageStatus (复刻 Orb decay_tick)", () => { it("unused: no injections, no uses", () => { expect(computeUsageStatus(0, 0)).toBe("unused"); }); it("unused: just below cold threshold, zero uses", () => { expect(computeUsageStatus(USAGE_COLD_INJECTION_THRESHOLD - 1, 0)).toBe("unused"); }); it("cold: AT injection threshold with zero uses (核心边界 inj=6 & use=0)", () => { expect(computeUsageStatus(USAGE_COLD_INJECTION_THRESHOLD, 0)).toBe("cold"); }); it("cold: well above threshold, zero uses", () => { expect(computeUsageStatus(20, 0)).toBe("cold"); }); it("warm: used but ratio below hot (1/10=0.1)", () => { expect(computeUsageStatus(10, 1)).toBe("warm"); }); it("hot: use/injection ratio meets HOT_RATIO (3/10=0.3)", () => { expect(computeUsageStatus(10, 3)).toBe("hot"); }); it("warm: used with zero injections (no div-by-zero → hot)", () => { expect(computeUsageStatus(0, 2)).toBe("warm"); }); }); describe("buildUsagePatch", () => { it("returns null for null entry", () => { expect(buildUsagePatch(null, 100)).toBeNull(); }); it("first use: 0 -> 1, sets first/lastUsedAt", () => { const patch = buildUsagePatch(makeEntry(ID), 100); expect(patch).not.toBeNull(); const usage = patch!.metadata.usage as UsageMetadata; expect(usage.useCount).toBe(1); expect(usage.firstUsedAt).toBe(100); expect(usage.lastUsedAt).toBe(100); expect(patch!.update.oldUseCount).toBe(0); expect(patch!.update.newUseCount).toBe(1); }); it("accumulates useCount, preserves firstUsedAt, updates lastUsedAt", () => { const e = makeEntry(ID, { usage: { useCount: 2, firstUsedAt: 50 } }); const patch = buildUsagePatch(e, 200); const usage = patch!.metadata.usage as UsageMetadata; expect(usage.useCount).toBe(3); expect(usage.firstUsedAt).toBe(50); expect(usage.lastUsedAt).toBe(200); }); it("双计数互不污染:不动 accessCount / confidence / evolution / tier", () => { const e = makeEntry(ID, { accessCount: 9, confidence: 0.9, evolution: { accessCount: 4 }, tier: "core", }); const m = buildUsagePatch(e, 100)!.metadata; expect(m.accessCount).toBe(9); expect(m.confidence).toBe(0.9); expect((m.evolution as { accessCount: number }).accessCount).toBe(4); expect(m.tier).toBe("core"); }); it("不写 usageStatus(status 留 deriveUsageStatus 离线算,避免假 cold)", () => { const e = makeEntry(ID, { accessCount: 8 }); const patch = buildUsagePatch(e, 100); const usage = patch!.metadata.usage as UsageMetadata; expect(usage.usageStatus).toBeUndefined(); expect("status" in patch!.update).toBe(false); }); it("does not emit any ranking multiplier / score field (影子定位)", () => { const patch = buildUsagePatch(makeEntry(ID), 100); const update = patch!.update as Record; expect(update.scoreMultiplier).toBeUndefined(); expect(update.weight).toBeUndefined(); }); }); describe("deriveUsageStatus (离线按需,cold 可达)", () => { it("cold: accessCount>=6 且从未被引用(无 usage 字段)", () => { expect(deriveUsageStatus(makeEntry(ID, { accessCount: 8 }))).toBe("cold"); }); it("cold: accessCount=6 边界 + useCount 0", () => { expect(deriveUsageStatus(makeEntry(ID, { accessCount: 6, usage: { useCount: 0 } }))).toBe("cold"); }); it("unused: 低 injection 从未被引用", () => { expect(deriveUsageStatus(makeEntry(ID, { accessCount: 2 }))).toBe("unused"); }); it("warm: 被引用但比例低", () => { expect(deriveUsageStatus(makeEntry(ID, { accessCount: 10, usage: { useCount: 1 } }))).toBe("warm"); }); it("hot: use/injection 达标", () => { expect(deriveUsageStatus(makeEntry(ID, { accessCount: 10, usage: { useCount: 3 } }))).toBe("hot"); }); }); describe("recordMemoryUsage", () => { it("returns null and skips update when entry not found", async () => { const { store, updates } = createMockStore([]); const res = await recordMemoryUsage(store, "missing", 100); expect(res).toBeNull(); expect(updates.length).toBe(0); }); it("writes full metadata back with usage, preserving other fields", async () => { const e = makeEntry(ID, { accessCount: 3, confidence: 0.8 }); const { store, updates, data } = createMockStore([e]); const res = await recordMemoryUsage(store, ID, 100); expect(res!.newUseCount).toBe(1); expect(updates.length).toBe(1); const written = JSON.parse(updates[0].metadata) as Record; expect((written.usage as UsageMetadata).useCount).toBe(1); expect(written.accessCount).toBe(3); expect(written.confidence).toBe(0.8); const persisted = JSON.parse(data.get(ID)!.metadata ?? "{}") as Record; expect((persisted.usage as UsageMetadata).useCount).toBe(1); }); it("second call accumulates useCount to 2", async () => { const { store } = createMockStore([makeEntry(ID)]); await recordMemoryUsage(store, ID, 100); const res2 = await recordMemoryUsage(store, ID, 200); expect(res2!.newUseCount).toBe(2); }); it("passes scope through to store.patchMetadata when provided", async () => { const captured: Array = []; const base = createMockStore([makeEntry(ID)]); const store: UsageStorePort = { async patchMetadata(id, patchFn, scopeFilter) { captured.push(scopeFilter); return base.store.patchMetadata(id, patchFn, scopeFilter); }, }; await recordMemoryUsage(store, ID, 100, "project:test"); expect(captured[0]).toEqual(["project:test"]); }); }); describe("recordReconstructionUsage (批量影子)", () => { it("no-op on empty array", async () => { const { store, updates } = createMockStore([]); await recordReconstructionUsage(store, [], 100); expect(updates.length).toBe(0); }); it("records useCount=1 for each distinct cited id", async () => { const { store, data } = createMockStore([makeEntry("mem-a"), makeEntry("mem-b")]); await recordReconstructionUsage(store, ["mem-a", "mem-b"], 100); const a = JSON.parse(data.get("mem-a")!.metadata ?? "{}") as Record; const b = JSON.parse(data.get("mem-b")!.metadata ?? "{}") as Record; expect((a.usage as UsageMetadata).useCount).toBe(1); expect((b.usage as UsageMetadata).useCount).toBe(1); }); it("dedupes repeated ids within one call (writes once)", async () => { const { store, updates } = createMockStore([makeEntry(ID)]); await recordReconstructionUsage(store, [ID, ID, ID], 100); expect(updates.length).toBe(1); }); it("one failing id does not block others (allSettled, no throw)", async () => { const base = createMockStore([makeEntry(ID)]); const store: UsageStorePort = { async patchMetadata(id, patchFn, scopeFilter) { if (id === "bad") throw new Error("boom"); return base.store.patchMetadata(id, patchFn, scopeFilter); }, }; await recordReconstructionUsage(store, ["bad", ID], 100); const good = JSON.parse(base.data.get(ID)!.metadata ?? "{}") as Record; expect((good.usage as UsageMetadata).useCount).toBe(1); }); }); });