@@ -145,49 +145,69 @@ function setupSecurityFetchMocks() {
145145
146146 vi . spyOn ( securityValidation , 'secureFetchWithPinnedIP' ) . mockImplementation (
147147 async ( url , _resolvedIP , options = { } ) => {
148- const fetchResponse = await global . fetch ( url , {
149- method : options . method ,
150- headers : options . headers as HeadersInit ,
151- body : options . body as BodyInit | null | undefined ,
152- } )
148+ let fetchResponse : any
149+ try {
150+ fetchResponse = await global . fetch ( url , {
151+ method : options . method ,
152+ headers : options . headers as HeadersInit ,
153+ body : options . body as BodyInit | null | undefined ,
154+ } )
155+ } catch ( error ) {
156+ // Keep parity with secure fetch timeout behavior expected by retry logic tests.
157+ if ( error instanceof Error && error . name === 'AbortError' ) {
158+ throw new Error ( 'Request timed out' )
159+ }
160+ throw error
161+ }
153162
154163 if ( ! fetchResponse ) {
155164 throw new Error ( 'Mock fetch returned no response' )
156165 }
157166
158- const headersRecord = responseHeadersToRecord ( ( fetchResponse as any ) . headers )
159- const status = ( fetchResponse as any ) . status ?? 200
167+ const headersRecord = responseHeadersToRecord ( fetchResponse . headers )
168+ const status = fetchResponse . status ?? 200
160169 const statusText = ( fetchResponse as any ) . statusText ?? ( status >= 200 ? 'OK' : 'Error' )
161- const ok = ( fetchResponse as any ) . ok ?? ( status >= 200 && status < 300 )
170+ const ok = fetchResponse . ok ?? ( status >= 200 && status < 300 )
171+ let cachedBodyText : string | null = null
172+
173+ const getBodyText = async ( ) : Promise < string > => {
174+ if ( cachedBodyText !== null ) return cachedBodyText
175+
176+ if ( typeof fetchResponse . text === 'function' ) {
177+ cachedBodyText = await fetchResponse . text ( )
178+ return cachedBodyText
179+ }
180+
181+ if ( typeof fetchResponse . json === 'function' ) {
182+ const jsonData = await fetchResponse . json ( )
183+ cachedBodyText = typeof jsonData === 'string' ? jsonData : JSON . stringify ( jsonData )
184+ return cachedBodyText
185+ }
186+
187+ if ( typeof fetchResponse . arrayBuffer === 'function' ) {
188+ const arr = await fetchResponse . arrayBuffer ( )
189+ cachedBodyText = new TextDecoder ( ) . decode ( arr )
190+ return cachedBodyText
191+ }
192+
193+ cachedBodyText = ''
194+ return cachedBodyText
195+ }
162196
163197 return {
164198 ok,
165199 status,
166200 statusText,
167201 headers : new securityValidation . SecureFetchHeaders ( headersRecord ) ,
168202 text : async ( ) => {
169- if ( typeof ( fetchResponse as any ) . text === 'function' ) {
170- return ( fetchResponse as any ) . text ( )
171- }
172- if ( typeof ( fetchResponse as any ) . json === 'function' ) {
173- return JSON . stringify ( await ( fetchResponse as any ) . json ( ) )
174- }
175- return ''
203+ return getBodyText ( )
176204 } ,
177205 json : async ( ) => {
178- if ( typeof ( fetchResponse as any ) . json === 'function' ) {
179- return ( fetchResponse as any ) . json ( )
180- }
181- const rawText =
182- typeof ( fetchResponse as any ) . text === 'function' ? await ( fetchResponse as any ) . text ( ) : ''
206+ const rawText = await getBodyText ( )
183207 return rawText ? JSON . parse ( rawText ) : { }
184208 } ,
185209 arrayBuffer : async ( ) => {
186- if ( typeof ( fetchResponse as any ) . arrayBuffer === 'function' ) {
187- return ( fetchResponse as any ) . arrayBuffer ( )
188- }
189- const rawText =
190- typeof ( fetchResponse as any ) . text === 'function' ? await ( fetchResponse as any ) . text ( ) : ''
210+ const rawText = await getBodyText ( )
191211 return new TextEncoder ( ) . encode ( rawText ) . buffer
192212 } ,
193213 }
0 commit comments