1
1
// @ts -check
2
+
2
3
import path from "path"
3
4
import zlib from "zlib"
4
5
import os from "os"
@@ -77,6 +78,8 @@ async function getFileContent(file, req, options = {}) {
77
78
}
78
79
}
79
80
81
+ let attempts503 = 0
82
+
80
83
const server = setupServer (
81
84
rest . get ( `http://external.com/logo.svg` , async ( req , res , ctx ) => {
82
85
const { content, contentLength } = await getFileContent (
@@ -140,7 +143,44 @@ const server = setupServer(
140
143
ctx . status ( 500 ) ,
141
144
ctx . body ( content )
142
145
)
143
- } )
146
+ } ) ,
147
+ rest . get ( `http://external.com/503-twice.svg` , async ( req , res , ctx ) => {
148
+ const errorContent = `Server error`
149
+ attempts503 ++
150
+
151
+ if ( attempts503 < 3 ) {
152
+ return res (
153
+ ctx . set ( `Content-Type` , `text/html` ) ,
154
+ ctx . set ( `Content-Length` , String ( errorContent . length ) ) ,
155
+ ctx . status ( 503 ) ,
156
+ ctx . body ( errorContent )
157
+ )
158
+ }
159
+
160
+ const { content, contentLength } = await getFileContent (
161
+ path . join ( __dirname , `./fixtures/gatsby-logo.svg` ) ,
162
+ req
163
+ )
164
+
165
+ return res (
166
+ ctx . set ( `Content-Type` , `image/svg+xml` ) ,
167
+ ctx . set ( `Content-Length` , contentLength ) ,
168
+ ctx . status ( 200 ) ,
169
+ ctx . body ( content )
170
+ )
171
+ } ) ,
172
+ rest . get ( `http://external.com/503-forever.svg` , async ( req , res , ctx ) => {
173
+ const errorContent = `Server error`
174
+ return res (
175
+ ctx . set ( `Content-Type` , `text/html` ) ,
176
+ ctx . set ( `Content-Length` , String ( errorContent . length ) ) ,
177
+ ctx . status ( 503 ) ,
178
+ ctx . body ( errorContent )
179
+ )
180
+ } ) ,
181
+ rest . get ( `http://external.com/network-error.svg` , ( req , res ) =>
182
+ res . networkError ( `ECONNREFUSED` )
183
+ )
144
184
)
145
185
146
186
function getFetchInWorkerContext ( workerId ) {
@@ -205,6 +245,10 @@ describe(`fetch-remote-file`, () => {
205
245
} )
206
246
} )
207
247
248
+ afterEach ( ( ) => {
249
+ jest . useRealTimers ( )
250
+ } )
251
+
208
252
it ( `downloads and create a file` , async ( ) => {
209
253
const filePath = await fetchRemoteFile ( {
210
254
url : `http://external.com/logo.svg` ,
@@ -304,8 +348,6 @@ describe(`fetch-remote-file`, () => {
304
348
jest . runAllTimers ( )
305
349
await requests [ 0 ]
306
350
307
- jest . useRealTimers ( )
308
-
309
351
// we still expect 2 fetches because cache can't save fast enough
310
352
expect ( gotStream ) . toBeCalledTimes ( 2 )
311
353
expect ( fsMove ) . toBeCalledTimes ( 1 )
@@ -365,18 +407,12 @@ describe(`fetch-remote-file`, () => {
365
407
jest . runAllTimers ( )
366
408
await requests [ 0 ]
367
409
368
- jest . useRealTimers ( )
369
-
370
410
// we still expect 4 fetches because cache can't save fast enough
371
411
expect ( gotStream ) . toBeCalledTimes ( 4 )
372
412
expect ( fsMove ) . toBeCalledTimes ( 2 )
373
413
} )
374
414
375
415
it ( `doesn't keep lock when file download failed` , async ( ) => {
376
- // we don't want to wait for polling to finish
377
- jest . useFakeTimers ( )
378
- jest . runAllTimers ( )
379
-
380
416
const cacheInternals = new Map ( )
381
417
const workerCache = {
382
418
get ( key ) {
@@ -398,18 +434,14 @@ describe(`fetch-remote-file`, () => {
398
434
} )
399
435
) . rejects . toThrow ( )
400
436
401
- jest . runAllTimers ( )
402
-
403
437
await expect (
404
438
fetchRemoteFileInstanceTwo ( {
405
439
url : `http://external.com/500.jpg` ,
406
440
cache : workerCache ,
407
441
} )
408
442
) . rejects . toThrow ( )
409
443
410
- jest . useRealTimers ( )
411
-
412
- expect ( gotStream ) . toBeCalledTimes ( 1 )
444
+ expect ( gotStream ) . toBeCalledTimes ( 3 )
413
445
expect ( fsMove ) . toBeCalledTimes ( 0 )
414
446
} )
415
447
@@ -428,7 +460,30 @@ describe(`fetch-remote-file`, () => {
428
460
url : `http://external.com/500.jpg` ,
429
461
cache,
430
462
} )
431
- ) . rejects . toThrow ( `Response code 500 (Internal Server Error)` )
463
+ ) . rejects . toThrowErrorMatchingInlineSnapshot ( `
464
+ "Unable to fetch:
465
+ http://external.com/500.jpg
466
+ ---
467
+ Reason: Response code 500 (Internal Server Error)
468
+ ---
469
+ Fetch details:
470
+ {
471
+ \\"attempt\\": 3,
472
+ \\"method\\": \\"GET\\",
473
+ \\"responseStatusCode\\": 500,
474
+ \\"responseStatusMessage\\": \\"Internal Server Error\\",
475
+ \\"requestHeaders\\": {
476
+ \\"user-agent\\": \\"got (https://github.com/sindresorhus/got)\\",
477
+ \\"accept-encoding\\": \\"gzip, deflate, br\\"
478
+ },
479
+ \\"responseHeaders\\": {
480
+ \\"x-powered-by\\": \\"msw\\",
481
+ \\"content-length\\": \\"12\\",
482
+ \\"content-type\\": \\"text/html\\"
483
+ }
484
+ }
485
+ ---"
486
+ ` )
432
487
} )
433
488
434
489
describe ( `retries the download` , ( ) => {
@@ -457,5 +512,81 @@ describe(`fetch-remote-file`, () => {
457
512
)
458
513
expect ( gotStream ) . toBeCalledTimes ( 2 )
459
514
} )
515
+
516
+ it ( `Retries when server returns 503 error till server returns 200` , async ( ) => {
517
+ const fetchRemoteFileInstance = fetchRemoteFile ( {
518
+ url : `http://external.com/503-twice.svg` ,
519
+ cache,
520
+ } )
521
+
522
+ const filePath = await fetchRemoteFileInstance
523
+
524
+ expect ( path . basename ( filePath ) ) . toBe ( `503-twice.svg` )
525
+ expect ( getFileSize ( filePath ) ) . resolves . toBe (
526
+ await getFileSize ( path . join ( __dirname , `./fixtures/gatsby-logo.svg` ) )
527
+ )
528
+ expect ( gotStream ) . toBeCalledTimes ( 3 )
529
+ } )
530
+
531
+ it ( `Stops retry when maximum attempts is reached` , async ( ) => {
532
+ await expect (
533
+ fetchRemoteFile ( {
534
+ url : `http://external.com/503-forever.svg` ,
535
+ cache,
536
+ } )
537
+ ) . rejects . toThrowErrorMatchingInlineSnapshot ( `
538
+ "Unable to fetch:
539
+ http://external.com/503-forever.svg
540
+ ---
541
+ Reason: Response code 503 (Service Unavailable)
542
+ ---
543
+ Fetch details:
544
+ {
545
+ \\"attempt\\": 3,
546
+ \\"method\\": \\"GET\\",
547
+ \\"responseStatusCode\\": 503,
548
+ \\"responseStatusMessage\\": \\"Service Unavailable\\",
549
+ \\"requestHeaders\\": {
550
+ \\"user-agent\\": \\"got (https://github.com/sindresorhus/got)\\",
551
+ \\"accept-encoding\\": \\"gzip, deflate, br\\"
552
+ },
553
+ \\"responseHeaders\\": {
554
+ \\"x-powered-by\\": \\"msw\\",
555
+ \\"content-length\\": \\"12\\",
556
+ \\"content-type\\": \\"text/html\\"
557
+ }
558
+ }
559
+ ---"
560
+ ` )
561
+
562
+ expect ( gotStream ) . toBeCalledTimes ( 3 )
563
+ } )
564
+ // @todo retry on network errors
565
+ it ( `Retries on network errors` , async ( ) => {
566
+ await expect (
567
+ fetchRemoteFile ( {
568
+ url : `http://external.com/network-error.svg` ,
569
+ cache,
570
+ } )
571
+ ) . rejects . toThrowErrorMatchingInlineSnapshot ( `
572
+ "Unable to fetch:
573
+ http://external.com/network-error.svg
574
+ ---
575
+ Reason: ECONNREFUSED
576
+ ---
577
+ Fetch details:
578
+ {
579
+ \\"attempt\\": 3,
580
+ \\"method\\": \\"GET\\",
581
+ \\"requestHeaders\\": {
582
+ \\"user-agent\\": \\"got (https://github.com/sindresorhus/got)\\",
583
+ \\"accept-encoding\\": \\"gzip, deflate, br\\"
584
+ }
585
+ }
586
+ ---"
587
+ ` )
588
+
589
+ expect ( gotStream ) . toBeCalledTimes ( 3 )
590
+ } )
460
591
} )
461
592
} )
0 commit comments