1// @effect-diagnostics globalDate:off -- A fixed instant keeps calendar-window assertions deterministic.
2import { describe, expect, it, vi } from "vite-plus/test";
3import type { UsageSource } from "@t3tools/contracts";
4
5import { formatHourShort, formatTokens, makeWindow, usageSourceMessages } from "./usageFormat.ts";
6
7function malformedSource(count: number, overrides: Partial<UsageSource> = {}): UsageSource {
8 return {
9 fingerprint: {
10 hostId: "demo-host",
11 provider: "codex",
12 resolvedHomePath: "/demo/codex",
13 volumeId: "demo-volume",
14 },
15 status: "partial",
16 scannedFiles: 1,
17 skippedFiles: 0,
18 malformedRecords: count,
19 distinctSessions: 1,
20 message: `${count} malformed or inconsistent usage record(s) were skipped; usage may be incomplete.`,
21 ...overrides,
22 };
23}
24
25describe("usage source messages", () => {
26 it("combines different skipped-record counts into one warning", () => {
27 const codex = malformedSource(1);
28 const claude = malformedSource(11, {
29 fingerprint: { ...codex.fingerprint, provider: "claude", resolvedHomePath: "/demo/claude" },
30 });
31
32 expect(usageSourceMessages([codex, claude])).toEqual([
33 "12 malformed or inconsistent usage record(s) were skipped; usage may be incomplete.",
34 ]);
35 });
36
37 it.each([
38 { first: 1, second: 1, total: 2 },
39 { first: 1, second: 11, total: 12 },
40 ])(
41 "combines hosts reporting $first and $second skipped records once per source",
42 ({ first, second, total }) => {
43 const source = malformedSource(first);
44 const otherHost = malformedSource(second, {
45 fingerprint: { ...source.fingerprint, hostId: "other-demo-host" },
46 });
47
48 expect(usageSourceMessages([source, source, otherHost, otherHost])).toEqual([
49 `${total} malformed or inconsistent usage record(s) were skipped; usage may be incomplete.`,
50 ]);
51 expect(usageSourceMessages([source, source])).toEqual([
52 `${first} malformed or inconsistent usage record(s) were skipped; usage may be incomplete.`,
53 ]);
54 expect(usageSourceMessages([otherHost, otherHost])).toEqual([
55 `${second} malformed or inconsistent usage record(s) were skipped; usage may be incomplete.`,
56 ]);
57 },
58 );
59
60 it("keeps different machines with the same hostname and home path distinct", () => {
61 const source = malformedSource(1);
62 const otherMachine = malformedSource(11, {
63 fingerprint: { ...source.fingerprint, volumeId: "other-demo-volume" },
64 });
65
66 expect(usageSourceMessages([source, otherMachine])).toEqual([
67 "12 malformed or inconsistent usage record(s) were skipped; usage may be incomplete.",
68 ]);
69 });
70
71 it("uses the largest count when connected environments report the same source", () => {
72 const source = malformedSource(1);
73
74 expect(usageSourceMessages([source, malformedSource(11), source])).toEqual([
75 "11 malformed or inconsistent usage record(s) were skipped; usage may be incomplete.",
76 ]);
77 });
78
79 it("retains other diagnostics beside the combined warning", () => {
80 const source = malformedSource(1);
81 const readFailure = "2 transcript path(s) could not be read; usage may be incomplete.";
82 const cursor = malformedSource(0, {
83 fingerprint: { ...source.fingerprint, provider: "cursor" },
84 status: "ok",
85 message: "Cursor history is limited to the last 30 days.",
86 });
87
88 expect(
89 usageSourceMessages([
90 { ...source, message: `${readFailure} ${source.message}` },
91 { ...source, malformedRecords: 0, message: readFailure },
92 cursor,
93 ]),
94 ).toEqual([
95 readFailure,
96 cursor.message,
97 "1 malformed or inconsistent usage record(s) were skipped; usage may be incomplete.",
98 ]);
99 });
100
101 it("includes cached diagnostics when a transcript directory is missing", () => {
102 const source = malformedSource(1);
103 const missing = malformedSource(2, {
104 fingerprint: { ...source.fingerprint, hostId: "other-demo-host" },
105 status: "missing",
106 scannedFiles: 0,
107 skippedFiles: 1,
108 distinctSessions: 0,
109 message:
110 "No transcript directory on this environment. 2 malformed or inconsistent usage record(s) were skipped; usage may be incomplete.",
111 });
112
113 expect(usageSourceMessages([source, missing, missing])).toEqual([
114 "No transcript directory on this environment.",
115 "3 malformed or inconsistent usage record(s) were skipped; usage may be incomplete.",
116 ]);
117 });
118
119 it("omits empty missing sources and healthy sources", () => {
120 expect(
121 usageSourceMessages([
122 malformedSource(0, { status: "missing" }),
123 malformedSource(1, { status: "ok" }),
124 ]),
125 ).toEqual([]);
126 expect(usageSourceMessages([])).toEqual([]);
127 });
128});
129
130describe("hourly usage formatting", () => {
131 it("keeps requested zones separate when formatting repeated calls", () => {
132 const instant = "2026-08-11T12:37:00.000Z";
133 for (const zone of ["UTC", "America/New_York", "Asia/Kathmandu", "UTC"]) {
134 expect(formatHourShort(instant, zone)).toBe(
135 new Intl.DateTimeFormat("en-US", { timeZone: zone, hour: "numeric" }).format(
136 new Date(instant),
137 ),
138 );
139 }
140 expect(() => formatHourShort(instant, "Etc/Unknown")).toThrow(RangeError);
141 expect(formatHourShort("invalid", "UTC")).toBe("invalid");
142 });
143
144 it("disambiguates repeated hours during a fall-back transition", () => {
145 expect(formatHourShort("2026-11-01T05:37:00.000Z", "America/New_York")).toBe("1 AM EDT");
146 expect(formatHourShort("2026-11-01T06:37:00.000Z", "America/New_York")).toBe("1 AM EST");
147 expect(formatHourShort("2026-11-01T05:37:00.000Z", "America/New_York", false)).toBe(
148 "01:00 EDT",
149 );
150 expect(formatHourShort("2026-11-01T06:37:00.000Z", "America/New_York", false)).toBe(
151 "01:00 EST",
152 );
153 });
154
155 it("builds an exact minute-aligned 24-hour request", () => {
156 const window = makeWindow(1, new Date("2026-08-11T12:37:42.123Z"), "hour");
157
158 expect(window.resolution).toBe("hour");
159 expect(window.sinceTime).toBe("2026-08-10T12:37:00.000Z");
160 expect(window.untilTime).toBe("2026-08-11T12:37:00.000Z");
161 });
162
163 it("degrades an unknown resolved zone to UTC instead of crashing", () => {
164 const resolved = new Intl.DateTimeFormat().resolvedOptions();
165 const resolvedOptions = vi
166 .spyOn(Intl.DateTimeFormat.prototype, "resolvedOptions")
167 .mockReturnValue({ ...resolved, timeZone: "Etc/Unknown" });
168
169 try {
170 const now = new Date("2026-08-11T12:37:42.123Z");
171
172 expect(makeWindow(1, now, "hour").timeZone).toBe("UTC");
173 expect(makeWindow(30, now).timeZone).toBe("UTC");
174 } finally {
175 resolvedOptions.mockRestore();
176 }
177 });
178});
179
180describe("token formatting", () => {
181 it("moves to the next unit when rounding reaches it", () => {
182 expect(formatTokens(999_999)).toBe("1M");
183 expect(formatTokens(999_999_999)).toBe("1B");
184 expect(formatTokens(-999_999)).toBe("-1M");
185 });
186});