import { describe, expect, it } from "vitest"; import { MAX_TOOL_RUNS, recordToolRun, readToolHistory, type ToolRunMetadata } from "../src/tool-history"; function createStorage(seed: string | null = null) { let value = seed; return { getItem: () => value, setItem: (_key: string, next: string) => { value = next; }, value: () => value, }; } const run = (overrides: Partial = {}): ToolRunMetadata => ({ tool: "gitea.pullrequest.status", project: "ludarium", environment: "prod", ok: true, traceId: "trace-123", at: "2026-09-30T10:00:00.000Z", ...overrides, }); describe("bounded recent tool history", () => { it("stores only safe invocation metadata and never serializes arguments or results", () => { const storage = createStorage(); recordToolRun({ ...run(), arguments: { token: "fixture-do-not-store" }, result: "private output" } as ToolRunMetadata, storage); expect(JSON.parse(storage.value() ?? "null")).toEqual([run()]); expect(storage.value()).not.toContain("fixture-do-not-store"); expect(storage.value()).not.toContain("private output"); }); it("bounds history to the newest twenty valid entries", () => { const storage = createStorage(); for (let index = 0; index < MAX_TOOL_RUNS + 4; index += 1) { recordToolRun(run({ traceId: `trace-${index}`, at: `2026-09-30T10:${String(index).padStart(2, "0")}:00.000Z` }), storage); } const history = readToolHistory(storage); expect(history).toHaveLength(MAX_TOOL_RUNS); expect(history[0]?.traceId).toBe(`trace-${MAX_TOOL_RUNS + 3}`); expect(history.at(-1)?.traceId).toBe("trace-4"); }); it("drops malformed and oversized stored entries without exposing extra fields", () => { const storage = createStorage(JSON.stringify([ { ...run(), arguments: { apiKey: "secret" }, content: "result" }, { ...run(), traceId: "trace\nsecret" }, { ...run(), at: "yesterday" }, null, ])); expect(readToolHistory(storage)).toEqual([run()]); }); it("treats corrupt or unavailable browser storage as an empty history", () => { expect(readToolHistory(createStorage("not-json"))).toEqual([]); expect(() => readToolHistory({ getItem: () => { throw new Error("blocked"); }, setItem: () => undefined })).not.toThrow(); expect(() => recordToolRun(run(), { getItem: () => null, setItem: () => { throw new Error("blocked"); } })).not.toThrow(); }); });