import { beforeEach, describe, expect, it } from "vitest"; import { bumpKeepStats, clearKeepStats, copyKeepStats, keepStatPercent, keepStatText, keepStatTotal, readKeepStats, sortKeepStats, } from "../web/chat-keep-stats.js"; const STORAGE_KEY = "wa-direct-chat-keep-stats-v1"; function fakeStorage() { const map = new Map(); return { getItem: (k) => (map.has(k) ? map.get(k) : null), setItem: (k, v) => map.set(k, String(v)), removeItem: (k) => map.delete(k), clear: () => map.clear(), }; } const gpt = { profileId: "p-gpt", profileName: "主配置", model: "gpt-4o" }; const claude = { profileId: "p-claude", profileName: "备用", model: "claude-3-7" }; beforeEach(() => { globalThis.localStorage = fakeStorage(); }); describe("chat keep stats", () => { it("counts keeps per conversation and sorts them", () => { bumpKeepStats("conv-a", gpt); bumpKeepStats("conv-a", claude); bumpKeepStats("conv-a", claude); const stats = readKeepStats("conv-a"); expect(stats.map((s) => s.label)).toEqual(["备用", "主配置"]); expect(stats[0].count).toBe(2); expect(keepStatTotal(stats)).toBe(3); }); it("keeps conversations apart", () => { bumpKeepStats("conv-a", gpt); bumpKeepStats("conv-b", claude); expect(readKeepStats("conv-a").map((s) => s.label)).toEqual(["主配置"]); expect(readKeepStats("conv-b").map((s) => s.label)).toEqual(["备用"]); clearKeepStats("conv-a"); expect(readKeepStats("conv-a")).toEqual([]); expect(readKeepStats("conv-b")).toHaveLength(1); }); it("subtracts on unkeep and drops the model at zero", () => { bumpKeepStats("conv-a", gpt); bumpKeepStats("conv-a", gpt); expect(readKeepStats("conv-a")[0].count).toBe(2); bumpKeepStats("conv-a", gpt, -1); expect(readKeepStats("conv-a")[0].count).toBe(1); bumpKeepStats("conv-a", gpt, -1); expect(readKeepStats("conv-a")).toEqual([]); }); it("treats the same profile + model as the same model even if the window id changes", () => { bumpKeepStats("conv-a", { ...gpt, id: "w1" }); bumpKeepStats("conv-a", { ...gpt, id: "w2" }); expect(readKeepStats("conv-a")).toHaveLength(1); expect(readKeepStats("conv-a")[0].count).toBe(2); }); it("falls back to the profile name when the model is the same string", () => { const text = keepStatText({ profileName: "本地", model: "本地" }); expect(text.label).toBe("本地"); expect(text.model).toBe(""); }); it("moves the counts when a conversation is copied or promoted", () => { bumpKeepStats("conv-a", gpt); bumpKeepStats("conv-a", claude); bumpKeepStats("conv-a", claude); copyKeepStats("conv-a", "conv-copy"); expect(readKeepStats("conv-copy").map((s) => s.count)).toEqual([2, 1]); expect(readKeepStats("conv-a")).toEqual([]); }); it("survives a broken storage payload", () => { globalThis.localStorage.setItem(STORAGE_KEY, "{not json"); expect(readKeepStats("conv-a")).toEqual([]); bumpKeepStats("conv-a", gpt); expect(readKeepStats("conv-a")).toHaveLength(1); }); it("sorts equal counts by label and scales bar widths", () => { const sorted = sortKeepStats([ { id: "b", label: "乙", model: "", count: 1 }, { id: "a", label: "甲", model: "", count: 1 }, { id: "c", label: "丙", model: "", count: 3 }, ]); expect(sorted.map((s) => s.id)).toEqual(["c", "a", "b"]); expect(keepStatPercent(3, 3)).toBe(100); expect(keepStatPercent(1, 3)).toBe(33); expect(keepStatPercent(1, 100)).toBe(6); expect(keepStatPercent(0, 3)).toBe(0); expect(keepStatPercent(1, 0)).toBe(0); }); });