import { describe, expect, it } from "vitest"; import { createLocalStats, dayKey, recordInto, streakOf, weeklySum, type Buckets } from "../src/lib/readingStats"; describe("dayKey", () => { it("本地时区 YYYY-MM-DD", () => { expect(dayKey(new Date(2026, 0, 5))).toBe("2026-01-05"); expect(dayKey(new Date(2026, 10, 23))).toBe("2026-11-23"); }); }); describe("recordInto", () => { it("不可变累加", () => { const b: Buckets = { "2026-01-01": 60 }; const nb = recordInto(b, "2026-01-01", 30); expect(nb["2026-01-01"]).toBe(90); expect(b["2026-01-01"]).toBe(60); // 原桶不变 expect(recordInto(nb, "2026-01-02", 5)["2026-01-02"]).toBe(5); expect(recordInto(nb, "2026-01-03", -5)).toEqual({ "2026-01-01": 90, "2026-01-03": 0 }); // 负值钳 0 }); }); describe("weeklySum", () => { it("近 7 天含今天、缺日补 0、合计", () => { const today = new Date(2026, 8, 16); // 2026-09-16(周三) const b: Buckets = { "2026-09-16": 120, "2026-09-12": 300 }; const w = weeklySum(b, today); expect(w.perDay).toHaveLength(7); expect(w.perDay[6]).toEqual({ day: "2026-09-16", seconds: 120 }); expect(w.perDay[2]).toEqual({ day: "2026-09-12", seconds: 300 }); expect(w.perDay[0].seconds).toBe(0); expect(w.total).toBe(420); }); }); describe("streakOf", () => { it("今天有记录 → 连续天数", () => { const today = new Date(2026, 8, 16); const b: Buckets = { "2026-09-16": 10, "2026-09-15": 10, "2026-09-14": 10 }; expect(streakOf(b, today)).toBe(3); }); it("今天无但昨天有 → 连续天数(含昨天)", () => { const today = new Date(2026, 8, 16); const b: Buckets = { "2026-09-15": 10, "2026-09-14": 10 }; expect(streakOf(b, today)).toBe(2); }); it("断档即止", () => { const today = new Date(2026, 8, 16); const b: Buckets = { "2026-09-15": 10, "2026-09-13": 10 }; expect(streakOf(b, today)).toBe(1); }); it("空 → 0", () => { const today = new Date(2026, 8, 16); expect(streakOf({}, today)).toBe(0); expect(streakOf({ "2026-09-14": 10 }, today)).toBe(0); }); }); describe("createLocalStats", () => { it("record→weekly/streak 走 localStorage(内存桩)", () => { const mem: Record = {}; globalThis.localStorage ??= { getItem: (k: string) => (k in mem ? mem[k] : null), setItem: (k: string, v: string) => (mem[k] = String(v)), clear: () => Object.keys(mem).forEach((k) => delete mem[k]), } as unknown as typeof localStorage; globalThis.localStorage.clear(); const s = createLocalStats(); s.record(30); s.record(20); const w = s.weekly(); expect(w.perDay.at(-1)?.seconds).toBe(50); expect(s.streak()).toBe(1); s.record(0); // 无时间不落盘 expect(s.weekly().total).toBe(50); }); });