@@ -20,8 +20,10 @@ import Sinon, { match, restore, stub, useFakeTimers } from 'sinon';
2020import sinonChai from 'sinon-chai' ;
2121import chaiAsPromised from 'chai-as-promised' ;
2222import {
23+ ABORT_ERROR_NAME ,
2324 RequestURL ,
2425 ServerPromptTemplateTask ,
26+ TIMEOUT_EXPIRED_MESSAGE ,
2527 Task ,
2628 getHeaders ,
2729 makeRequest
@@ -306,7 +308,7 @@ describe('request methods', () => {
306308 const signal = options ! . signal ;
307309 return new Promise ( ( _resolve , reject ) : void => {
308310 const abortListener = ( ) : void => {
309- reject ( new DOMException ( signal ?. reason || 'Aborted' , 'AbortError' ) ) ;
311+ reject ( new DOMException ( signal ?. reason || 'Aborted' , ABORT_ERROR_NAME ) ) ;
310312 } ;
311313
312314 signal ?. addEventListener ( 'abort' , abortListener , { once : true } ) ;
@@ -343,7 +345,7 @@ describe('request methods', () => {
343345 fetchStub . resolves ( {
344346 ok : false ,
345347 status : 500 ,
346- statusText : 'AbortError'
348+ statusText : ABORT_ERROR_NAME
347349 } as Response ) ;
348350
349351 try {
@@ -363,7 +365,7 @@ describe('request methods', () => {
363365 expect ( ( e as AIError ) . code ) . to . equal ( AIErrorCode . FETCH_ERROR ) ;
364366 expect ( ( e as AIError ) . customErrorData ?. status ) . to . equal ( 500 ) ;
365367 expect ( ( e as AIError ) . customErrorData ?. statusText ) . to . equal (
366- 'AbortError'
368+ ABORT_ERROR_NAME
367369 ) ;
368370 expect ( ( e as AIError ) . message ) . to . include ( '500 AbortError' ) ;
369371 }
@@ -512,7 +514,7 @@ describe('request methods', () => {
512514
513515 expect ( fetchStub ) . not . to . have . been . called ;
514516 } ) ;
515- it ( 'should abort fetch if external signal aborts during request' , async ( ) => {
517+ it ( 'should throw DOMException if external signal aborts during request' , async ( ) => {
516518 fetchStub . callsFake ( fetchAborter ) ;
517519 const controller = new AbortController ( ) ;
518520 const abortReason = 'Aborted during request' ;
@@ -531,7 +533,10 @@ describe('request methods', () => {
531533 await clock . tickAsync ( 0 ) ;
532534 controller . abort ( abortReason ) ;
533535
534- await expect ( requestPromise ) . to . be . rejectedWith ( 'Aborted during request' ) ;
536+ await expect ( requestPromise ) . to . be . rejectedWith (
537+ DOMException ,
538+ abortReason
539+ ) ;
535540 } ) ;
536541
537542 it ( 'should abort fetch if timeout expires during request' , async ( ) => {
@@ -552,16 +557,16 @@ describe('request methods', () => {
552557 await clock . tickAsync ( timeoutDuration + 100 ) ;
553558
554559 await expect ( requestPromise ) . to . be . rejectedWith (
555- 'AbortError' ,
556- 'Timeout has expired'
560+ DOMException ,
561+ TIMEOUT_EXPIRED_MESSAGE
557562 ) ;
558563
559564 expect ( fetchStub ) . to . have . been . calledOnce ;
560565 const fetchOptions = fetchStub . firstCall . args [ 1 ] as RequestInit ;
561566 const internalSignal = fetchOptions . signal ;
562567
563568 expect ( internalSignal ?. aborted ) . to . be . true ;
564- expect ( ( internalSignal ?. reason as Error ) . name ) . to . equal ( 'AbortError' ) ;
569+ expect ( ( internalSignal ?. reason as Error ) . name ) . to . equal ( ABORT_ERROR_NAME ) ;
565570 expect ( ( internalSignal ?. reason as Error ) . message ) . to . equal (
566571 'Timeout has expired.'
567572 ) ;
@@ -596,34 +601,6 @@ describe('request methods', () => {
596601 expect ( fetchStub ) . to . have . been . calledOnce ;
597602 } ) ;
598603
599- it ( 'should succeed and clear timeout/listener if fetch completes with signal provided but not aborted' , async ( ) => {
600- const controller = new AbortController ( ) ;
601- const mockResponse = new Response ( '{}' , {
602- status : 200 ,
603- statusText : 'OK'
604- } ) ;
605- const fetchPromise = Promise . resolve ( mockResponse ) ;
606- fetchStub . resolves ( fetchPromise ) ;
607-
608- const requestPromise = makeRequest (
609- {
610- model : 'models/model-name' ,
611- task : Task . GENERATE_CONTENT ,
612- apiSettings : fakeApiSettings ,
613- stream : false ,
614- singleRequestOptions : { signal : controller . signal } // Generous timeout
615- } ,
616- '{}'
617- ) ;
618-
619- // Advance time slightly
620- await clock . tickAsync ( 10 ) ;
621-
622- const response = await requestPromise ;
623- expect ( response . ok ) . to . be . true ;
624- expect ( fetchStub ) . to . have . been . calledOnce ;
625- } ) ;
626-
627604 it ( 'should use external signal abort reason if it occurs before timeout' , async ( ) => {
628605 const controller = new AbortController ( ) ;
629606 const abortReason = 'External Abort Wins' ;
@@ -648,7 +625,10 @@ describe('request methods', () => {
648625 await clock . tickAsync ( timeoutDuration / 2 ) ;
649626 controller . abort ( abortReason ) ;
650627
651- await expect ( requestPromise ) . to . be . rejectedWith ( abortReason ) ;
628+ await expect ( requestPromise ) . to . be . rejectedWith (
629+ DOMException ,
630+ abortReason
631+ ) ;
652632 } ) ;
653633
654634 it ( 'should use timeout reason if it occurs before external signal abort' , async ( ) => {
@@ -678,8 +658,8 @@ describe('request methods', () => {
678658 await clock . tickAsync ( timeoutDuration + 1 ) ;
679659
680660 await expect ( requestPromise ) . to . be . rejectedWith (
681- 'AbortError' ,
682- 'Timeout has expired'
661+ DOMException ,
662+ TIMEOUT_EXPIRED_MESSAGE
683663 ) ;
684664 } ) ;
685665
@@ -707,80 +687,6 @@ describe('request methods', () => {
707687 expect ( fetchOptions . signal ?. aborted ) . to . be . false ;
708688 } ) ;
709689
710- it ( 'should remove abort listener on successful completion to prevent memory leaks' , async ( ) => {
711- const controller = new AbortController ( ) ;
712- const addSpy = Sinon . spy ( controller . signal , 'addEventListener' ) ;
713- const removeSpy = Sinon . spy ( controller . signal , 'removeEventListener' ) ;
714-
715- const mockResponse = new Response ( '{}' , {
716- status : 200 ,
717- statusText : 'OK'
718- } ) ;
719- fetchStub . resolves ( mockResponse ) ;
720-
721- await makeRequest (
722- {
723- model : 'models/model-name' ,
724- task : Task . GENERATE_CONTENT ,
725- apiSettings : fakeApiSettings ,
726- stream : false ,
727- singleRequestOptions : { signal : controller . signal }
728- } ,
729- '{}'
730- ) ;
731-
732- expect ( addSpy ) . to . have . been . calledOnceWith ( 'abort' ) ;
733- expect ( removeSpy ) . to . have . been . calledOnceWith ( 'abort' ) ;
734- } ) ;
735-
736- it ( 'should remove listener if fetch itself rejects' , async ( ) => {
737- const controller = new AbortController ( ) ;
738- const removeSpy = Sinon . spy ( controller . signal , 'removeEventListener' ) ;
739- const error = new Error ( 'Network failure' ) ;
740- fetchStub . rejects ( error ) ;
741-
742- const requestPromise = makeRequest (
743- {
744- model : 'models/model-name' ,
745- task : Task . GENERATE_CONTENT ,
746- apiSettings : fakeApiSettings ,
747- stream : false ,
748- singleRequestOptions : { signal : controller . signal }
749- } ,
750- '{}'
751- ) ;
752-
753- await expect ( requestPromise ) . to . be . rejectedWith (
754- AIError ,
755- / N e t w o r k f a i l u r e /
756- ) ;
757- expect ( removeSpy ) . to . have . been . calledOnce ;
758- } ) ;
759-
760- it ( 'should remove listener if response is not ok' , async ( ) => {
761- const controller = new AbortController ( ) ;
762- const removeSpy = Sinon . spy ( controller . signal , 'removeEventListener' ) ;
763- const mockResponse = new Response ( '{}' , {
764- status : 500 ,
765- statusText : 'Internal Server Error'
766- } ) ;
767- fetchStub . resolves ( mockResponse ) ;
768-
769- const requestPromise = makeRequest (
770- {
771- model : 'models/model-name' ,
772- task : Task . GENERATE_CONTENT ,
773- apiSettings : fakeApiSettings ,
774- stream : false ,
775- singleRequestOptions : { signal : controller . signal }
776- } ,
777- '{}'
778- ) ;
779-
780- await expect ( requestPromise ) . to . be . rejectedWith ( AIError , / 5 0 0 / ) ;
781- expect ( removeSpy ) . to . have . been . calledOnce ;
782- } ) ;
783-
784690 it ( 'should abort immediately if timeout is 0' , async ( ) => {
785691 fetchStub . callsFake ( fetchAborter ) ;
786692 const requestPromise = makeRequest (
@@ -797,19 +703,21 @@ describe('request methods', () => {
797703 // Tick the clock just enough to trigger a timeout(0)
798704 await clock . tickAsync ( 1 ) ;
799705
800- await expect ( requestPromise ) . to . be . rejectedWith ( 'AbortError' ) ;
706+ await expect ( requestPromise ) . to . be . rejectedWith (
707+ DOMException ,
708+ TIMEOUT_EXPIRED_MESSAGE
709+ ) ;
801710 } ) ;
802711
803712 it ( 'should not error if signal is aborted after completion' , async ( ) => {
804713 const controller = new AbortController ( ) ;
805- const removeSpy = Sinon . spy ( controller . signal , 'removeEventListener' ) ;
806714 const mockResponse = new Response ( '{}' , {
807715 status : 200 ,
808716 statusText : 'OK'
809717 } ) ;
810718 fetchStub . resolves ( mockResponse ) ;
811719
812- await makeRequest (
720+ const response = await makeRequest (
813721 {
814722 model : 'models/model-name' ,
815723 task : Task . GENERATE_CONTENT ,
@@ -823,7 +731,7 @@ describe('request methods', () => {
823731 // Listener should be removed, so this abort should do nothing.
824732 controller . abort ( 'Too late' ) ;
825733
826- expect ( removeSpy ) . to . have . been . calledOnce ;
734+ expect ( response . ok ) . to . be . true ;
827735 } ) ;
828736 } ) ;
829737} ) ;
0 commit comments