Skip to content

Commit c054ea0

Browse files
author
Peter Steinberger
committed
refactor(usage): resolve validated date ranges once
1 parent 1424ca0 commit c054ea0

2 files changed

Lines changed: 107 additions & 170 deletions

File tree

src/gateway/server-methods/usage.test.ts

Lines changed: 69 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,23 @@ describe("gateway usage helpers", () => {
7272
});
7373

7474
function expectUtcDateRange(
75-
range: ReturnType<typeof testApi.parseDateRange>,
75+
result: ReturnType<typeof testApi.resolveDateRange>,
7676
startDate: string,
7777
endDate: string,
7878
) {
79+
const range = expectDateRange(result);
7980
expect(range.startMs).toBe(testApi.parseDateToMs(startDate));
8081
expect(range.endMs).toBe(testApi.parseDateToMs(endDate)! + dayMs - 1);
8182
}
8283

84+
function expectDateRange(result: ReturnType<typeof testApi.resolveDateRange>) {
85+
expect(result.ok).toBe(true);
86+
if (!result.ok) {
87+
throw new Error(result.error);
88+
}
89+
return result.value;
90+
}
91+
8392
beforeEach(() => {
8493
testApi.costUsageCache.clear();
8594
vi.useRealTimers();
@@ -106,48 +115,18 @@ describe("gateway usage helpers", () => {
106115
expect(testApi.parseDateToMs("2024-02-29")).toBe(Date.UTC(2024, 1, 29));
107116
});
108117

109-
it("findInvalidExplicitDate flags provided-but-unparseable dates and ignores absent/valid ones", () => {
110-
// Explicitly provided invalid dates (bad format or impossible calendar date) are reported.
111-
expect(testApi.findInvalidExplicitDate({ startDate: "2026-02-30" })).toBe("startDate");
112-
expect(testApi.findInvalidExplicitDate({ endDate: "2026-2-5" })).toBe("endDate");
113-
expect(testApi.findInvalidExplicitDate({ startDate: 0 })).toBe("startDate");
114-
expect(testApi.findInvalidExplicitDate({ endDate: [] })).toBe("endDate");
115-
expect(
116-
testApi.findInvalidExplicitDate({ startDate: "2026-02-01", endDate: "2026-13-01" }),
117-
).toBe("endDate");
118-
// Absent or valid dates are not flagged, so they still fall through to the default range.
119-
expect(testApi.findInvalidExplicitDate({})).toBeUndefined();
120-
expect(testApi.findInvalidExplicitDate({ startDate: "", endDate: null })).toBeUndefined();
121-
expect(
122-
testApi.findInvalidExplicitDate({ startDate: "2026-02-01", endDate: "2026-02-02" }),
123-
).toBeUndefined();
124-
});
125-
126-
it("hasInvertedExplicitDateRange rejects startDate after endDate only when both dates are valid", () => {
127-
expect(
128-
testApi.hasInvertedExplicitDateRange({
129-
startDate: "2026-02-03",
130-
endDate: "2026-02-02",
131-
}),
132-
).toBe(true);
133-
expect(
134-
testApi.hasInvertedExplicitDateRange({
135-
startDate: "2026-02-02",
136-
endDate: "2026-02-02",
137-
}),
138-
).toBe(false);
139-
expect(
140-
testApi.hasInvertedExplicitDateRange({
141-
startDate: "2026-02-01",
142-
endDate: "2026-02-02",
143-
}),
144-
).toBe(false);
145-
expect(
146-
testApi.hasInvertedExplicitDateRange({
147-
startDate: "2026-02-30",
148-
endDate: "2026-02-02",
149-
}),
150-
).toBe(false);
118+
it.each([
119+
[{ startDate: "2026-02-30" }, "invalid startDate"],
120+
[{ endDate: "2026-2-5" }, "invalid endDate"],
121+
[{ startDate: 0 }, "invalid startDate"],
122+
[{ endDate: [] }, "invalid endDate"],
123+
[{ startDate: "2026-02-01", endDate: "2026-13-01" }, "invalid endDate"],
124+
[{ startDate: "2026-02-03", endDate: "2026-02-02" }, "startDate must not be after endDate"],
125+
])("resolveDateRange rejects invalid explicit ranges", (params, error) => {
126+
expect(testApi.resolveDateRange(params)).toEqual({
127+
ok: false,
128+
error: expect.stringContaining(error),
129+
});
151130
});
152131

153132
it("usage.cost rejects an explicitly provided invalid date with INVALID_REQUEST", async () => {
@@ -206,80 +185,91 @@ describe("gateway usage helpers", () => {
206185
expect(testApi.parseDays("nope")).toBeUndefined();
207186
});
208187

209-
it("parseDateRange uses explicit start/end as UTC when mode is missing (backward compatible)", () => {
210-
const range = testApi.parseDateRange({ startDate: "2026-02-01", endDate: "2026-02-02" });
211-
expectUtcDateRange(range, "2026-02-01", "2026-02-02");
212-
});
213-
214-
it("parseDateRange uses explicit UTC mode", () => {
215-
const range = testApi.parseDateRange({
188+
it("resolveDateRange uses explicit start/end as UTC when mode is missing (backward compatible)", () => {
189+
const result = testApi.resolveDateRange({
216190
startDate: "2026-02-01",
217191
endDate: "2026-02-02",
218-
mode: "utc",
219192
});
220-
expectUtcDateRange(range, "2026-02-01", "2026-02-02");
193+
expectUtcDateRange(result, "2026-02-01", "2026-02-02");
221194
});
222195

223-
it("parseDateRange uses specific UTC offset for explicit dates", () => {
224-
const range = testApi.parseDateRange({
196+
it("resolveDateRange uses explicit UTC mode", () => {
197+
const result = testApi.resolveDateRange({
225198
startDate: "2026-02-01",
226199
endDate: "2026-02-02",
227-
mode: "specific",
228-
utcOffset: "UTC+5:30",
200+
mode: "utc",
229201
});
202+
expectUtcDateRange(result, "2026-02-01", "2026-02-02");
203+
});
204+
205+
it("resolveDateRange uses specific UTC offset for explicit dates", () => {
206+
const range = expectDateRange(
207+
testApi.resolveDateRange({
208+
startDate: "2026-02-01",
209+
endDate: "2026-02-02",
210+
mode: "specific",
211+
utcOffset: "UTC+5:30",
212+
}),
213+
);
230214
const start = Date.UTC(2026, 1, 1) - 5.5 * 60 * 60 * 1000;
231215
const endStart = Date.UTC(2026, 1, 2) - 5.5 * 60 * 60 * 1000;
232216
expect(range.startMs).toBe(start);
233217
expect(range.endMs).toBe(endStart + dayMs - 1);
234218
});
235219

236-
it("parseDateRange falls back to UTC when specific mode offset is missing or invalid", () => {
237-
const missingOffset = testApi.parseDateRange({
238-
startDate: "2026-02-01",
239-
endDate: "2026-02-02",
240-
mode: "specific",
241-
});
242-
const invalidOffset = testApi.parseDateRange({
243-
startDate: "2026-02-01",
244-
endDate: "2026-02-02",
245-
mode: "specific",
246-
utcOffset: "bad-value",
247-
});
220+
it("resolveDateRange falls back to UTC when specific mode offset is missing or invalid", () => {
221+
const missingOffset = expectDateRange(
222+
testApi.resolveDateRange({
223+
startDate: "2026-02-01",
224+
endDate: "2026-02-02",
225+
mode: "specific",
226+
}),
227+
);
228+
const invalidOffset = expectDateRange(
229+
testApi.resolveDateRange({
230+
startDate: "2026-02-01",
231+
endDate: "2026-02-02",
232+
mode: "specific",
233+
utcOffset: "bad-value",
234+
}),
235+
);
248236
expect(missingOffset.startMs).toBe(Date.UTC(2026, 1, 1));
249237
expect(missingOffset.endMs).toBe(Date.UTC(2026, 1, 2) + dayMs - 1);
250238
expect(invalidOffset.startMs).toBe(Date.UTC(2026, 1, 1));
251239
expect(invalidOffset.endMs).toBe(Date.UTC(2026, 1, 2) + dayMs - 1);
252240
});
253241

254-
it("parseDateRange uses specific offset for today/day math after UTC midnight", () => {
242+
it("resolveDateRange uses specific offset for today/day math after UTC midnight", () => {
255243
vi.useFakeTimers();
256244
vi.setSystemTime(new Date("2026-02-17T03:57:00.000Z"));
257-
const range = testApi.parseDateRange({
258-
days: 1,
259-
mode: "specific",
260-
utcOffset: "UTC-5",
261-
});
245+
const range = expectDateRange(
246+
testApi.resolveDateRange({
247+
days: 1,
248+
mode: "specific",
249+
utcOffset: "UTC-5",
250+
}),
251+
);
262252
expect(range.startMs).toBe(Date.UTC(2026, 1, 16, 5, 0, 0, 0));
263253
expect(range.endMs).toBe(Date.UTC(2026, 1, 17, 4, 59, 59, 999));
264254
});
265255

266-
it("parseDateRange uses gateway local day boundaries in gateway mode", () => {
256+
it("resolveDateRange uses gateway local day boundaries in gateway mode", () => {
267257
vi.useFakeTimers();
268258
vi.setSystemTime(new Date("2026-02-05T12:34:56.000Z"));
269-
const range = testApi.parseDateRange({ days: 1, mode: "gateway" });
259+
const range = expectDateRange(testApi.resolveDateRange({ days: 1, mode: "gateway" }));
270260
const expectedStart = new Date(2026, 1, 5).getTime();
271261
expect(range.startMs).toBe(expectedStart);
272262
expect(range.endMs).toBe(expectedStart + dayMs - 1);
273263
});
274264

275-
it("parseDateRange clamps days to at least 1 and defaults to 30 days", () => {
265+
it("resolveDateRange clamps days to at least 1 and defaults to 30 days", () => {
276266
vi.useFakeTimers();
277267
vi.setSystemTime(new Date("2026-02-05T12:34:56.000Z"));
278-
const oneDay = testApi.parseDateRange({ days: 0 });
268+
const oneDay = expectDateRange(testApi.resolveDateRange({ days: 0 }));
279269
expect(oneDay.endMs).toBe(Date.UTC(2026, 1, 5) + dayMs - 1);
280270
expect(oneDay.startMs).toBe(Date.UTC(2026, 1, 5));
281271

282-
const def = testApi.parseDateRange({});
272+
const def = expectDateRange(testApi.resolveDateRange({}));
283273
expect(def.endMs).toBe(Date.UTC(2026, 1, 5) + dayMs - 1);
284274
expect(def.startMs).toBe(Date.UTC(2026, 1, 5) - 29 * dayMs);
285275
});

0 commit comments

Comments
 (0)