@@ -80,6 +80,23 @@ function createPendingFetchQueue() {
8080 return { fetch, requests } ;
8181}
8282
83+ function createNonAbortableFetchQueue ( ) {
84+ const requests = [ ] ;
85+
86+ function fetch ( url , options = { } ) {
87+ return new Promise ( ( resolve , reject ) => {
88+ requests . push ( {
89+ url,
90+ options,
91+ resolve,
92+ reject
93+ } ) ;
94+ } ) ;
95+ }
96+
97+ return { fetch, requests } ;
98+ }
99+
83100function jsonResponse ( payload ) {
84101 return {
85102 ok : true ,
@@ -192,6 +209,71 @@ test('fetchModels aborts stale in-flight requests before applying new data', asy
192209 ) ;
193210} ) ;
194211
212+ test ( 'fetchModels ignores stale unauthorized responses from superseded requests' , async ( ) => {
213+ const queue = createNonAbortableFetchQueue ( ) ;
214+ const app = loadDashboardApp ( { fetch : queue . fetch } ) ;
215+ const originalModels = [ { provider_type : 'openai' , model : { id : 'existing-model' } } ] ;
216+ app . models = originalModels . slice ( ) ;
217+
218+ const firstFetch = app . fetchModels ( ) ;
219+ assert . equal ( queue . requests . length , 1 ) ;
220+ const firstSignal = queue . requests [ 0 ] . options . signal ;
221+
222+ const secondFetch = app . fetchModels ( ) ;
223+ assert . equal ( queue . requests . length , 2 ) ;
224+ assert . equal ( firstSignal . aborted , true ) ;
225+
226+ queue . requests [ 0 ] . resolve ( {
227+ ok : false ,
228+ status : 401 ,
229+ statusText : 'Unauthorized' ,
230+ json : async ( ) => ( { } )
231+ } ) ;
232+ await firstFetch ;
233+
234+ assert . equal ( app . authError , false ) ;
235+ assert . equal ( app . needsAuth , false ) ;
236+ assert . equal ( JSON . stringify ( app . models ) , JSON . stringify ( originalModels ) ) ;
237+
238+ queue . requests [ 1 ] . resolve ( jsonResponse ( [ { provider_type : 'openai' , model : { id : 'gpt-5' } } ] ) ) ;
239+ await secondFetch ;
240+
241+ assert . equal ( app . authError , false ) ;
242+ assert . equal ( app . needsAuth , false ) ;
243+ assert . equal (
244+ JSON . stringify ( app . models ) ,
245+ JSON . stringify ( [ { provider_type : 'openai' , model : { id : 'gpt-5' } } ] )
246+ ) ;
247+ } ) ;
248+
249+ test ( 'fetchModels ignores stale errors from superseded requests' , async ( ) => {
250+ const queue = createNonAbortableFetchQueue ( ) ;
251+ const app = loadDashboardApp ( { fetch : queue . fetch } ) ;
252+ const originalModels = [ { provider_type : 'openai' , model : { id : 'existing-model' } } ] ;
253+ app . models = originalModels . slice ( ) ;
254+
255+ const firstFetch = app . fetchModels ( ) ;
256+ assert . equal ( queue . requests . length , 1 ) ;
257+ const firstSignal = queue . requests [ 0 ] . options . signal ;
258+
259+ const secondFetch = app . fetchModels ( ) ;
260+ assert . equal ( queue . requests . length , 2 ) ;
261+ assert . equal ( firstSignal . aborted , true ) ;
262+
263+ queue . requests [ 0 ] . reject ( new Error ( 'stale models failure' ) ) ;
264+ await firstFetch ;
265+
266+ assert . equal ( JSON . stringify ( app . models ) , JSON . stringify ( originalModels ) ) ;
267+
268+ queue . requests [ 1 ] . resolve ( jsonResponse ( [ { provider_type : 'openai' , model : { id : 'gpt-5' } } ] ) ) ;
269+ await secondFetch ;
270+
271+ assert . equal (
272+ JSON . stringify ( app . models ) ,
273+ JSON . stringify ( [ { provider_type : 'openai' , model : { id : 'gpt-5' } } ] )
274+ ) ;
275+ } ) ;
276+
195277test ( 'fetchCalendarData aborts stale in-flight requests before applying new data' , async ( ) => {
196278 const queue = createPendingFetchQueue ( ) ;
197279 const app = loadDashboardApp ( { fetch : queue . fetch } ) ;
@@ -213,3 +295,70 @@ test('fetchCalendarData aborts stale in-flight requests before applying new data
213295 JSON . stringify ( [ { date : '2026-03-29' , total_tokens : 11 } ] )
214296 ) ;
215297} ) ;
298+
299+ test ( 'fetchCalendarData ignores stale unauthorized responses while a newer request is active' , async ( ) => {
300+ const queue = createNonAbortableFetchQueue ( ) ;
301+ const app = loadDashboardApp ( { fetch : queue . fetch } ) ;
302+ const originalCalendarData = [ { date : '2026-03-28' , total_tokens : 3 } ] ;
303+ app . calendarData = originalCalendarData . slice ( ) ;
304+
305+ const firstFetch = app . fetchCalendarData ( ) ;
306+ assert . equal ( queue . requests . length , 1 ) ;
307+ const firstSignal = queue . requests [ 0 ] . options . signal ;
308+
309+ const secondFetch = app . fetchCalendarData ( ) ;
310+ assert . equal ( queue . requests . length , 2 ) ;
311+ assert . equal ( firstSignal . aborted , true ) ;
312+
313+ queue . requests [ 0 ] . resolve ( {
314+ ok : false ,
315+ status : 401 ,
316+ statusText : 'Unauthorized' ,
317+ json : async ( ) => ( { } )
318+ } ) ;
319+ await firstFetch ;
320+
321+ assert . equal ( JSON . stringify ( app . calendarData ) , JSON . stringify ( originalCalendarData ) ) ;
322+ assert . equal ( app . calendarLoading , true ) ;
323+ assert . notEqual ( app . _calendarFetchController , null ) ;
324+
325+ queue . requests [ 1 ] . resolve ( jsonResponse ( [ { date : '2026-03-29' , total_tokens : 11 } ] ) ) ;
326+ await secondFetch ;
327+
328+ assert . equal ( app . calendarLoading , false ) ;
329+ assert . equal (
330+ JSON . stringify ( app . calendarData ) ,
331+ JSON . stringify ( [ { date : '2026-03-29' , total_tokens : 11 } ] )
332+ ) ;
333+ } ) ;
334+
335+ test ( 'fetchCalendarData ignores stale errors while a newer request is active' , async ( ) => {
336+ const queue = createNonAbortableFetchQueue ( ) ;
337+ const app = loadDashboardApp ( { fetch : queue . fetch } ) ;
338+ const originalCalendarData = [ { date : '2026-03-28' , total_tokens : 3 } ] ;
339+ app . calendarData = originalCalendarData . slice ( ) ;
340+
341+ const firstFetch = app . fetchCalendarData ( ) ;
342+ assert . equal ( queue . requests . length , 1 ) ;
343+ const firstSignal = queue . requests [ 0 ] . options . signal ;
344+
345+ const secondFetch = app . fetchCalendarData ( ) ;
346+ assert . equal ( queue . requests . length , 2 ) ;
347+ assert . equal ( firstSignal . aborted , true ) ;
348+
349+ queue . requests [ 0 ] . reject ( new Error ( 'stale calendar failure' ) ) ;
350+ await firstFetch ;
351+
352+ assert . equal ( JSON . stringify ( app . calendarData ) , JSON . stringify ( originalCalendarData ) ) ;
353+ assert . equal ( app . calendarLoading , true ) ;
354+ assert . notEqual ( app . _calendarFetchController , null ) ;
355+
356+ queue . requests [ 1 ] . resolve ( jsonResponse ( [ { date : '2026-03-29' , total_tokens : 11 } ] ) ) ;
357+ await secondFetch ;
358+
359+ assert . equal ( app . calendarLoading , false ) ;
360+ assert . equal (
361+ JSON . stringify ( app . calendarData ) ,
362+ JSON . stringify ( [ { date : '2026-03-29' , total_tokens : 11 } ] )
363+ ) ;
364+ } ) ;
0 commit comments