@@ -3,20 +3,29 @@ import { HttpClient, createDefaultRetryConfig, buildHttpConfig } from '../../src
33import type { HttpConfig } from '../../src/core/types' ;
44import { TEST_API_KEY } from '../setup' ;
55
6+ // Helper to create mock Headers object
7+ function createMockHeaders ( entries : [ string , string ] [ ] ) : any {
8+ const map = new Map ( entries ) ;
9+ return {
10+ get : ( key : string ) => map . get ( key . toLowerCase ( ) ) || null ,
11+ has : ( key : string ) => map . has ( key . toLowerCase ( ) ) ,
12+ entries : ( ) => map . entries ( ) ,
13+ keys : ( ) => map . keys ( ) ,
14+ values : ( ) => map . values ( ) ,
15+ } ;
16+ }
17+
618describe ( 'HttpClient' , ( ) => {
719 let httpClient : HttpClient ;
820 let fetchMock : ReturnType < typeof vi . fn > ;
921 let config : HttpConfig ;
1022
1123 beforeEach ( ( ) => {
12- // Use fake timers to speed up retry tests
13- vi . useFakeTimers ( ) ;
14-
1524 config = buildHttpConfig (
1625 TEST_API_KEY ,
1726 'https://api.nfe.io/v1' ,
1827 10000 ,
19- createDefaultRetryConfig ( )
28+ { maxRetries : 3 , baseDelay : 10 , maxDelay : 100 } // Delays curtos para testes rápidos
2029 ) ;
2130
2231 httpClient = new HttpClient ( config ) ;
@@ -27,7 +36,6 @@ describe('HttpClient', () => {
2736 } ) ;
2837
2938 afterEach ( ( ) => {
30- vi . useRealTimers ( ) ;
3139 vi . restoreAllMocks ( ) ;
3240 } ) ;
3341
@@ -38,7 +46,7 @@ describe('HttpClient', () => {
3846 ok : true ,
3947 status : 200 ,
4048 statusText : 'OK' ,
41- headers : new Map ( [ [ 'content-type' , 'application/json' ] ] ) ,
49+ headers : createMockHeaders ( [ [ 'content-type' , 'application/json' ] ] ) ,
4250 json : async ( ) => mockData ,
4351 } ) ;
4452
@@ -58,7 +66,7 @@ describe('HttpClient', () => {
5866 fetchMock . mockResolvedValue ( {
5967 ok : true ,
6068 status : 200 ,
61- headers : new Map ( [ [ 'content-type' , 'application/json' ] ] ) ,
69+ headers : createMockHeaders ( [ [ 'content-type' , 'application/json' ] ] ) ,
6270 json : async ( ) => ( [ ] ) ,
6371 } ) ;
6472
@@ -73,7 +81,7 @@ describe('HttpClient', () => {
7381 fetchMock . mockResolvedValue ( {
7482 ok : true ,
7583 status : 200 ,
76- headers : new Map ( [ [ 'content-type' , 'application/json' ] ] ) ,
84+ headers : createMockHeaders ( [ [ 'content-type' , 'application/json' ] ] ) ,
7785 json : async ( ) => ( [ ] ) ,
7886 } ) ;
7987
@@ -99,7 +107,7 @@ describe('HttpClient', () => {
99107 ok : true ,
100108 status : 201 ,
101109 statusText : 'Created' ,
102- headers : new Map ( [ [ 'content-type' , 'application/json' ] ] ) ,
110+ headers : createMockHeaders ( [ [ 'content-type' , 'application/json' ] ] ) ,
103111 json : async ( ) => responseBody ,
104112 } ) ;
105113
@@ -146,7 +154,7 @@ describe('HttpClient', () => {
146154 fetchMock . mockResolvedValue ( {
147155 ok : true ,
148156 status : 200 ,
149- headers : new Map ( [ [ 'content-type' , 'application/json' ] ] ) ,
157+ headers : createMockHeaders ( [ [ 'content-type' , 'application/json' ] ] ) ,
150158 json : async ( ) => responseBody ,
151159 } ) ;
152160
@@ -179,7 +187,7 @@ describe('HttpClient', () => {
179187 fetchMock . mockResolvedValue ( {
180188 ok : true ,
181189 status : 200 ,
182- headers : new Map ( [ [ 'content-type' , 'application/json' ] ] ) ,
190+ headers : createMockHeaders ( [ [ 'content-type' , 'application/json' ] ] ) ,
183191 json : async ( ) => ( { } ) ,
184192 } ) ;
185193
@@ -194,7 +202,7 @@ describe('HttpClient', () => {
194202 ok : false ,
195203 status : 401 ,
196204 statusText : 'Unauthorized' ,
197- headers : new Map ( [ [ 'content-type' , 'application/json' ] ] ) ,
205+ headers : createMockHeaders ( [ [ 'content-type' , 'application/json' ] ] ) ,
198206 json : async ( ) => ( { error : 'Invalid API key' } ) ,
199207 } ) ;
200208
@@ -215,7 +223,7 @@ describe('HttpClient', () => {
215223 ok : false ,
216224 status : 400 ,
217225 statusText : 'Bad Request' ,
218- headers : new Map ( [ [ 'content-type' , 'application/json' ] ] ) ,
226+ headers : createMockHeaders ( [ [ 'content-type' , 'application/json' ] ] ) ,
219227 json : async ( ) => ( {
220228 error : 'Validation failed' ,
221229 details : { field : 'required' } ,
@@ -236,7 +244,7 @@ describe('HttpClient', () => {
236244 ok : false ,
237245 status : 404 ,
238246 statusText : 'Not Found' ,
239- headers : new Map ( [ [ 'content-type' , 'application/json' ] ] ) ,
247+ headers : createMockHeaders ( [ [ 'content-type' , 'application/json' ] ] ) ,
240248 json : async ( ) => ( { error : 'Resource not found' } ) ,
241249 } ) ;
242250
@@ -264,9 +272,6 @@ describe('HttpClient', () => {
264272
265273 const promise = httpClient . get ( '/test' ) ;
266274
267- // Rate limits are retried, so advance timers to complete all retries
268- await vi . runAllTimersAsync ( ) ;
269-
270275 // Should fail after max retries
271276 await expect ( promise ) . rejects . toMatchObject ( {
272277 name : 'RateLimitError' ,
@@ -283,15 +288,12 @@ describe('HttpClient', () => {
283288 ok : false ,
284289 status : 500 ,
285290 statusText : 'Internal Server Error' ,
286- headers : new Map ( [ [ 'content-type' , 'application/json' ] ] ) ,
291+ headers : createMockHeaders ( [ [ 'content-type' , 'application/json' ] ] ) ,
287292 json : async ( ) => ( { error : 'Server error' } ) ,
288293 } ) ;
289294
290295 const promise = httpClient . get ( '/test' ) ;
291296
292- // Server errors are retried, so advance timers
293- await vi . runAllTimersAsync ( ) ;
294-
295297 // Should fail after max retries
296298 await expect ( promise ) . rejects . toMatchObject ( {
297299 name : 'ServerError' ,
@@ -307,9 +309,6 @@ describe('HttpClient', () => {
307309
308310 const promise = httpClient . get ( '/test' ) ;
309311
310- // Network errors are retried, so advance timers
311- await vi . runAllTimersAsync ( ) ;
312-
313312 // Should fail after max retries
314313 await expect ( promise ) . rejects . toMatchObject ( {
315314 name : 'ConnectionError' ,
@@ -326,9 +325,6 @@ describe('HttpClient', () => {
326325
327326 const promise = httpClient . get ( '/test' ) ;
328327
329- // Timeout errors are retried, so advance timers
330- await vi . runAllTimersAsync ( ) ;
331-
332328 // Should fail after max retries
333329 await expect ( promise ) . rejects . toMatchObject ( {
334330 name : 'TimeoutError' ,
@@ -345,28 +341,25 @@ describe('HttpClient', () => {
345341 ok : false ,
346342 status : 503 ,
347343 statusText : 'Service Unavailable' ,
348- headers : new Map ( [ [ 'content-type' , 'application/json' ] ] ) ,
344+ headers : createMockHeaders ( [ [ 'content-type' , 'application/json' ] ] ) ,
349345 json : async ( ) => ( { error : 'Temporarily unavailable' } ) ,
350346 } )
351347 . mockResolvedValueOnce ( {
352348 ok : false ,
353349 status : 503 ,
354350 statusText : 'Service Unavailable' ,
355- headers : new Map ( [ [ 'content-type' , 'application/json' ] ] ) ,
351+ headers : createMockHeaders ( [ [ 'content-type' , 'application/json' ] ] ) ,
356352 json : async ( ) => ( { error : 'Temporarily unavailable' } ) ,
357353 } )
358354 . mockResolvedValueOnce ( {
359355 ok : true ,
360356 status : 200 ,
361- headers : new Map ( [ [ 'content-type' , 'application/json' ] ] ) ,
357+ headers : createMockHeaders ( [ [ 'content-type' , 'application/json' ] ] ) ,
362358 json : async ( ) => ( { success : true } ) ,
363359 } ) ;
364360
365361 const promise = httpClient . get < { success : boolean } > ( '/test' ) ;
366362
367- // Fast-forward through retry delays
368- await vi . runAllTimersAsync ( ) ;
369-
370363 const response = await promise ;
371364
372365 expect ( response . data ) . toEqual ( { success : true } ) ;
@@ -380,15 +373,12 @@ describe('HttpClient', () => {
380373 . mockResolvedValueOnce ( {
381374 ok : true ,
382375 status : 200 ,
383- headers : new Map ( [ [ 'content-type' , 'application/json' ] ] ) ,
376+ headers : createMockHeaders ( [ [ 'content-type' , 'application/json' ] ] ) ,
384377 json : async ( ) => ( { success : true } ) ,
385378 } ) ;
386379
387380 const promise = httpClient . get < { success : boolean } > ( '/test' ) ;
388381
389- // Fast-forward through retry delays
390- await vi . runAllTimersAsync ( ) ;
391-
392382 const response = await promise ;
393383
394384 expect ( response . data ) . toEqual ( { success : true } ) ;
@@ -400,7 +390,7 @@ describe('HttpClient', () => {
400390 ok : false ,
401391 status : 400 ,
402392 statusText : 'Bad Request' ,
403- headers : new Map ( [ [ 'content-type' , 'application/json' ] ] ) ,
393+ headers : createMockHeaders ( [ [ 'content-type' , 'application/json' ] ] ) ,
404394 json : async ( ) => ( { error : 'Invalid input' } ) ,
405395 } ) ;
406396
@@ -415,15 +405,12 @@ describe('HttpClient', () => {
415405 ok : false ,
416406 status : 503 ,
417407 statusText : 'Service Unavailable' ,
418- headers : new Map ( [ [ 'content-type' , 'application/json' ] ] ) ,
408+ headers : createMockHeaders ( [ [ 'content-type' , 'application/json' ] ] ) ,
419409 json : async ( ) => ( { error : 'Unavailable' } ) ,
420410 } ) ;
421411
422412 const promise = httpClient . get ( '/test' ) ;
423413
424- // Fast-forward through all retry attempts
425- await vi . runAllTimersAsync ( ) ;
426-
427414 await expect ( promise ) . rejects . toThrow ( ) ;
428415 // Initial request + 3 retries = 4 total
429416 expect ( fetchMock ) . toHaveBeenCalledTimes ( 4 ) ;
@@ -443,15 +430,12 @@ describe('HttpClient', () => {
443430 . mockResolvedValueOnce ( {
444431 ok : true ,
445432 status : 200 ,
446- headers : new Map ( [ [ 'content-type' , 'application/json' ] ] ) ,
433+ headers : createMockHeaders ( [ [ 'content-type' , 'application/json' ] ] ) ,
447434 json : async ( ) => ( { success : true } ) ,
448435 } ) ;
449436
450437 const promise = httpClient . get < { success : boolean } > ( '/test' ) ;
451438
452- // Fast-forward through retry delay
453- await vi . runAllTimersAsync ( ) ;
454-
455439 const response = await promise ;
456440 expect ( response . data ) . toEqual ( { success : true } ) ;
457441 expect ( fetchMock ) . toHaveBeenCalledTimes ( 2 ) ;
@@ -463,7 +447,7 @@ describe('HttpClient', () => {
463447 fetchMock . mockResolvedValue ( {
464448 ok : true ,
465449 status : 200 ,
466- headers : new Map ( [ [ 'content-type' , 'application/json' ] ] ) ,
450+ headers : createMockHeaders ( [ [ 'content-type' , 'application/json' ] ] ) ,
467451 json : async ( ) => ( { } ) ,
468452 } ) ;
469453
@@ -486,7 +470,7 @@ describe('HttpClient', () => {
486470 fetchMock . mockResolvedValue ( {
487471 ok : true ,
488472 status : 200 ,
489- headers : new Map ( [ [ 'content-type' , 'application/json' ] ] ) ,
473+ headers : createMockHeaders ( [ [ 'content-type' , 'application/json' ] ] ) ,
490474 json : async ( ) => ( { } ) ,
491475 } ) ;
492476
@@ -503,7 +487,7 @@ describe('HttpClient', () => {
503487 fetchMock . mockResolvedValue ( {
504488 ok : true ,
505489 status : 200 ,
506- headers : new Map ( [ [ 'content-type' , 'application/json' ] ] ) ,
490+ headers : createMockHeaders ( [ [ 'content-type' , 'application/json' ] ] ) ,
507491 json : async ( ) => jsonData ,
508492 } ) ;
509493
@@ -515,7 +499,7 @@ describe('HttpClient', () => {
515499 fetchMock . mockResolvedValue ( {
516500 ok : true ,
517501 status : 200 ,
518- headers : new Map ( [ [ 'content-type' , 'text/plain' ] ] ) ,
502+ headers : createMockHeaders ( [ [ 'content-type' , 'text/plain' ] ] ) ,
519503 text : async ( ) => 'Plain text response' ,
520504 } ) ;
521505
@@ -530,7 +514,7 @@ describe('HttpClient', () => {
530514 fetchMock . mockResolvedValue ( {
531515 ok : true ,
532516 status : 200 ,
533- headers : new Map ( [ [ 'content-type' , 'application/pdf' ] ] ) ,
517+ headers : createMockHeaders ( [ [ 'content-type' , 'application/pdf' ] ] ) ,
534518 arrayBuffer : async ( ) => arrayBuffer ,
535519 } ) ;
536520
@@ -547,7 +531,7 @@ describe('HttpClient', () => {
547531 fetchMock . mockResolvedValue ( {
548532 ok : true ,
549533 status : 200 ,
550- headers : new Map ( [ [ 'content-type' , 'application/xml' ] ] ) ,
534+ headers : createMockHeaders ( [ [ 'content-type' , 'application/xml' ] ] ) ,
551535 arrayBuffer : async ( ) => arrayBuffer ,
552536 } ) ;
553537
@@ -563,7 +547,7 @@ describe('HttpClient', () => {
563547 fetchMock . mockResolvedValue ( {
564548 ok : true ,
565549 status : 200 ,
566- headers : new Map ( [ [ 'content-type' , 'application/json' ] ] ) ,
550+ headers : createMockHeaders ( [ [ 'content-type' , 'application/json' ] ] ) ,
567551 json : async ( ) => ( { } ) ,
568552 } ) ;
569553
@@ -578,7 +562,7 @@ describe('HttpClient', () => {
578562 fetchMock . mockResolvedValue ( {
579563 ok : true ,
580564 status : 200 ,
581- headers : new Map ( [ [ 'content-type' , 'application/json' ] ] ) ,
565+ headers : createMockHeaders ( [ [ 'content-type' , 'application/json' ] ] ) ,
582566 json : async ( ) => ( { } ) ,
583567 } ) ;
584568
@@ -592,7 +576,7 @@ describe('HttpClient', () => {
592576 fetchMock . mockResolvedValue ( {
593577 ok : true ,
594578 status : 201 ,
595- headers : new Map ( [ [ 'content-type' , 'application/json' ] ] ) ,
579+ headers : createMockHeaders ( [ [ 'content-type' , 'application/json' ] ] ) ,
596580 json : async ( ) => ( { } ) ,
597581 } ) ;
598582
0 commit comments