@@ -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