@@ -315,49 +315,69 @@ function setupSecurityFetchMocks() {
315315
316316 vi . spyOn ( securityValidation , 'secureFetchWithPinnedIP' ) . mockImplementation (
317317 async ( url , _resolvedIP , options = { } ) => {
318- const fetchResponse = await global . fetch ( url , {
319- method : options . method ,
320- headers : options . headers as HeadersInit ,
321- body : options . body as BodyInit | null | undefined ,
322- } )
318+ let fetchResponse : any
319+ try {
320+ fetchResponse = await global . fetch ( url , {
321+ method : options . method ,
322+ headers : options . headers as HeadersInit ,
323+ body : options . body as BodyInit | null | undefined ,
324+ } )
325+ } catch ( error ) {
326+ // Keep parity with secure fetch timeout behavior expected by retry logic tests.
327+ if ( error instanceof Error && error . name === 'AbortError' ) {
328+ throw new Error ( 'Request timed out' )
329+ }
330+ throw error
331+ }
323332
324333 if ( ! fetchResponse ) {
325334 throw new Error ( 'Mock fetch returned no response' )
326335 }
327336
328- const headersRecord = responseHeadersToRecord ( ( fetchResponse as any ) . headers )
329- const status = ( fetchResponse as any ) . status ?? 200
337+ const headersRecord = responseHeadersToRecord ( fetchResponse . headers )
338+ const status = fetchResponse . status ?? 200
330339 const statusText = ( fetchResponse as any ) . statusText ?? ( status >= 200 ? 'OK' : 'Error' )
331- const ok = ( fetchResponse as any ) . ok ?? ( status >= 200 && status < 300 )
340+ const ok = fetchResponse . ok ?? ( status >= 200 && status < 300 )
341+ let cachedBodyText : string | null = null
342+
343+ const getBodyText = async ( ) : Promise < string > => {
344+ if ( cachedBodyText !== null ) return cachedBodyText
345+
346+ if ( typeof fetchResponse . text === 'function' ) {
347+ cachedBodyText = await fetchResponse . text ( )
348+ return cachedBodyText
349+ }
350+
351+ if ( typeof fetchResponse . json === 'function' ) {
352+ const jsonData = await fetchResponse . json ( )
353+ cachedBodyText = typeof jsonData === 'string' ? jsonData : JSON . stringify ( jsonData )
354+ return cachedBodyText
355+ }
356+
357+ if ( typeof fetchResponse . arrayBuffer === 'function' ) {
358+ const arr = await fetchResponse . arrayBuffer ( )
359+ cachedBodyText = new TextDecoder ( ) . decode ( arr )
360+ return cachedBodyText
361+ }
362+
363+ cachedBodyText = ''
364+ return cachedBodyText
365+ }
332366
333367 return {
334368 ok,
335369 status,
336370 statusText,
337371 headers : new securityValidation . SecureFetchHeaders ( headersRecord ) ,
338372 text : async ( ) => {
339- if ( typeof ( fetchResponse as any ) . text === 'function' ) {
340- return ( fetchResponse as any ) . text ( )
341- }
342- if ( typeof ( fetchResponse as any ) . json === 'function' ) {
343- return JSON . stringify ( await ( fetchResponse as any ) . json ( ) )
344- }
345- return ''
373+ return getBodyText ( )
346374 } ,
347375 json : async ( ) => {
348- if ( typeof ( fetchResponse as any ) . json === 'function' ) {
349- return ( fetchResponse as any ) . json ( )
350- }
351- const rawText =
352- typeof ( fetchResponse as any ) . text === 'function' ? await ( fetchResponse as any ) . text ( ) : ''
376+ const rawText = await getBodyText ( )
353377 return rawText ? JSON . parse ( rawText ) : { }
354378 } ,
355379 arrayBuffer : async ( ) => {
356- if ( typeof ( fetchResponse as any ) . arrayBuffer === 'function' ) {
357- return ( fetchResponse as any ) . arrayBuffer ( )
358- }
359- const rawText =
360- typeof ( fetchResponse as any ) . text === 'function' ? await ( fetchResponse as any ) . text ( ) : ''
380+ const rawText = await getBodyText ( )
361381 return new TextEncoder ( ) . encode ( rawText ) . buffer
362382 } ,
363383 }
0 commit comments