@@ -181,6 +181,80 @@ describe("fetchWithRetry", () => {
181181 } ) ;
182182 } ) ;
183183
184+ // ─── Server error (502/503/504) retry ───────────────────────────────
185+
186+ describe ( "server error (502/503/504) retry" , ( ) => {
187+ it ( "should retry 502 and succeed on next attempt" , async ( ) => {
188+ mockFetch
189+ . mockResolvedValueOnce ( createMockResponse ( 502 , "Bad Gateway" ) )
190+ . mockResolvedValueOnce ( createMockResponse ( 200 , "ok" ) ) ;
191+
192+ const result = await retryFetch ( "http://test.com" , { } , mockFetch , {
193+ maxTransportRetries : 3 ,
194+ initialRetryDelay : 1 ,
195+ } ) ;
196+
197+ expect ( result . status ) . toBe ( 200 ) ;
198+ expect ( mockFetch ) . toHaveBeenCalledTimes ( 2 ) ;
199+ } ) ;
200+
201+ it ( "should retry 503 and succeed on next attempt" , async ( ) => {
202+ mockFetch
203+ . mockResolvedValueOnce ( createMockResponse ( 503 , "Service Unavailable" ) )
204+ . mockResolvedValueOnce ( createMockResponse ( 200 , "ok" ) ) ;
205+
206+ const result = await retryFetch ( "http://test.com" , { } , mockFetch , {
207+ maxTransportRetries : 3 ,
208+ initialRetryDelay : 1 ,
209+ } ) ;
210+
211+ expect ( result . status ) . toBe ( 200 ) ;
212+ expect ( mockFetch ) . toHaveBeenCalledTimes ( 2 ) ;
213+ } ) ;
214+
215+ it ( "should retry 504 and succeed on next attempt" , async ( ) => {
216+ mockFetch
217+ . mockResolvedValueOnce ( createMockResponse ( 504 , "Gateway Timeout" ) )
218+ . mockResolvedValueOnce ( createMockResponse ( 200 , "ok" ) ) ;
219+
220+ const result = await retryFetch ( "http://test.com" , { } , mockFetch , {
221+ maxTransportRetries : 3 ,
222+ initialRetryDelay : 1 ,
223+ } ) ;
224+
225+ expect ( result . status ) . toBe ( 200 ) ;
226+ expect ( mockFetch ) . toHaveBeenCalledTimes ( 2 ) ;
227+ } ) ;
228+
229+ it ( "should exhaust retries and return last 5xx response" , async ( ) => {
230+ mockFetch . mockResolvedValue ( createMockResponse ( 502 , "Bad Gateway" ) ) ;
231+
232+ const result = await retryFetch ( "http://test.com" , { } , mockFetch , {
233+ maxTransportRetries : 2 ,
234+ initialRetryDelay : 1 ,
235+ } ) ;
236+
237+ expect ( result . status ) . toBe ( 502 ) ;
238+ // 1 initial + 2 retries = 3
239+ expect ( mockFetch ) . toHaveBeenCalledTimes ( 3 ) ;
240+ } ) ;
241+
242+ it ( "should handle transport error then 502 then success" , async ( ) => {
243+ mockFetch
244+ . mockRejectedValueOnce ( new Error ( "ECONNRESET" ) )
245+ . mockResolvedValueOnce ( createMockResponse ( 502 , "Bad Gateway" ) )
246+ . mockResolvedValueOnce ( createMockResponse ( 200 , "ok" ) ) ;
247+
248+ const result = await retryFetch ( "http://test.com" , { } , mockFetch , {
249+ maxTransportRetries : 3 ,
250+ initialRetryDelay : 1 ,
251+ } ) ;
252+
253+ expect ( result . status ) . toBe ( 200 ) ;
254+ expect ( mockFetch ) . toHaveBeenCalledTimes ( 3 ) ;
255+ } ) ;
256+ } ) ;
257+
184258 // ─── Auth failure (401/403) retry ──────────────────────────────────
185259
186260 describe ( "auth failure (401/403) retry" , ( ) => {
0 commit comments