@@ -5,7 +5,7 @@ import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'
5
5
import { type FixtureTestContext } from '../../tests/utils/contexts.js'
6
6
import { generateRandomObjectID , startMockBlobStore } from '../../tests/utils/helpers.js'
7
7
8
- import { createRequestContext } from './handlers/request-context.cjs'
8
+ import { createRequestContext , type RequestContext } from './handlers/request-context.cjs'
9
9
import { setCacheControlHeaders , setVaryHeaders } from './headers.js'
10
10
11
11
beforeEach < FixtureTestContext > ( async ( ctx ) => {
@@ -194,25 +194,242 @@ describe('headers', () => {
194
194
describe ( 'setCacheControlHeaders' , ( ) => {
195
195
const defaultUrl = 'https://example.com'
196
196
197
+ describe ( 'Durable Cache feature flag disabled' , ( ) => {
198
+ test ( 'should set permanent, non-durable "netlify-cdn-cache-control" if "cache-control" is not set and "requestContext.usedFsRead" is truthy' , ( ) => {
199
+ const headers = new Headers ( )
200
+ const request = new Request ( defaultUrl )
201
+ vi . spyOn ( headers , 'set' )
202
+
203
+ const requestContext = createRequestContext ( )
204
+ requestContext . usedFsRead = true
205
+
206
+ setCacheControlHeaders ( headers , request , requestContext , false )
207
+
208
+ expect ( headers . set ) . toHaveBeenNthCalledWith (
209
+ 1 ,
210
+ 'cache-control' ,
211
+ 'public, max-age=0, must-revalidate' ,
212
+ )
213
+ expect ( headers . set ) . toHaveBeenNthCalledWith (
214
+ 2 ,
215
+ 'netlify-cdn-cache-control' ,
216
+ 'max-age=31536000' ,
217
+ )
218
+ } )
219
+
220
+ describe ( 'route handler responses with a specified `revalidate` value' , ( ) => {
221
+ test ( 'should set non-durable SWC=1yr with 1yr TTL if "{netlify-,}cdn-cache-control" is not present and `revalidate` is `false` (GET)' , ( ) => {
222
+ const headers = new Headers ( )
223
+ const request = new Request ( defaultUrl )
224
+ vi . spyOn ( headers , 'set' )
225
+
226
+ const ctx : RequestContext = { ...createRequestContext ( ) , routeHandlerRevalidate : false }
227
+ setCacheControlHeaders ( headers , request , ctx , false )
228
+
229
+ expect ( headers . set ) . toHaveBeenCalledTimes ( 1 )
230
+ expect ( headers . set ) . toHaveBeenNthCalledWith (
231
+ 1 ,
232
+ 'netlify-cdn-cache-control' ,
233
+ 's-maxage=31536000, stale-while-revalidate=31536000' ,
234
+ )
235
+ } )
236
+
237
+ test ( 'should set non-durable SWC=1yr with 1yr TTL if "{netlify-,}cdn-cache-control" is not present and `revalidate` is `false` (HEAD)' , ( ) => {
238
+ const headers = new Headers ( )
239
+ const request = new Request ( defaultUrl , { method : 'HEAD' } )
240
+ vi . spyOn ( headers , 'set' )
241
+
242
+ const ctx : RequestContext = { ...createRequestContext ( ) , routeHandlerRevalidate : false }
243
+ setCacheControlHeaders ( headers , request , ctx , false )
244
+
245
+ expect ( headers . set ) . toHaveBeenCalledTimes ( 1 )
246
+ expect ( headers . set ) . toHaveBeenNthCalledWith (
247
+ 1 ,
248
+ 'netlify-cdn-cache-control' ,
249
+ 's-maxage=31536000, stale-while-revalidate=31536000' ,
250
+ )
251
+ } )
252
+
253
+ test ( 'should set non-durable SWC=1yr with given TTL if "{netlify-,}cdn-cache-control" is not present and `revalidate` is a number (GET)' , ( ) => {
254
+ const headers = new Headers ( )
255
+ const request = new Request ( defaultUrl )
256
+ vi . spyOn ( headers , 'set' )
257
+
258
+ const ctx : RequestContext = { ...createRequestContext ( ) , routeHandlerRevalidate : 7200 }
259
+ setCacheControlHeaders ( headers , request , ctx , false )
260
+
261
+ expect ( headers . set ) . toHaveBeenCalledTimes ( 1 )
262
+ expect ( headers . set ) . toHaveBeenNthCalledWith (
263
+ 1 ,
264
+ 'netlify-cdn-cache-control' ,
265
+ 's-maxage=7200, stale-while-revalidate=31536000' ,
266
+ )
267
+ } )
268
+
269
+ test ( 'should set non-durable SWC=1yr with 1yr TTL if "{netlify-,}cdn-cache-control" is not present and `revalidate` is a number (HEAD)' , ( ) => {
270
+ const headers = new Headers ( )
271
+ const request = new Request ( defaultUrl , { method : 'HEAD' } )
272
+ vi . spyOn ( headers , 'set' )
273
+
274
+ const ctx : RequestContext = { ...createRequestContext ( ) , routeHandlerRevalidate : 7200 }
275
+ setCacheControlHeaders ( headers , request , ctx , false )
276
+
277
+ expect ( headers . set ) . toHaveBeenCalledTimes ( 1 )
278
+ expect ( headers . set ) . toHaveBeenNthCalledWith (
279
+ 1 ,
280
+ 'netlify-cdn-cache-control' ,
281
+ 's-maxage=7200, stale-while-revalidate=31536000' ,
282
+ )
283
+ } )
284
+ } )
285
+ } )
286
+
287
+ describe ( 'route handler responses with a specified `revalidate` value' , ( ) => {
288
+ test ( 'should not set any headers if "cdn-cache-control" is present' , ( ) => {
289
+ const givenHeaders = {
290
+ 'cdn-cache-control' : 'public, max-age=0, must-revalidate' ,
291
+ }
292
+ const headers = new Headers ( givenHeaders )
293
+ const request = new Request ( defaultUrl )
294
+ vi . spyOn ( headers , 'set' )
295
+
296
+ const ctx : RequestContext = { ...createRequestContext ( ) , routeHandlerRevalidate : false }
297
+ setCacheControlHeaders ( headers , request , ctx , true )
298
+
299
+ expect ( headers . set ) . toHaveBeenCalledTimes ( 0 )
300
+ } )
301
+
302
+ test ( 'should not set any headers if "netlify-cdn-cache-control" is present' , ( ) => {
303
+ const givenHeaders = {
304
+ 'netlify-cdn-cache-control' : 'public, max-age=0, must-revalidate' ,
305
+ }
306
+ const headers = new Headers ( givenHeaders )
307
+ const request = new Request ( defaultUrl )
308
+ vi . spyOn ( headers , 'set' )
309
+
310
+ const ctx : RequestContext = { ...createRequestContext ( ) , routeHandlerRevalidate : false }
311
+ setCacheControlHeaders ( headers , request , ctx , true )
312
+
313
+ expect ( headers . set ) . toHaveBeenCalledTimes ( 0 )
314
+ } )
315
+
316
+ test ( 'should mark content as stale if "{netlify-,}cdn-cache-control" is not present and "x-nextjs-cache" is "STALE" (GET)' , ( ) => {
317
+ const givenHeaders = {
318
+ 'x-nextjs-cache' : 'STALE' ,
319
+ }
320
+ const headers = new Headers ( givenHeaders )
321
+ const request = new Request ( defaultUrl )
322
+ vi . spyOn ( headers , 'set' )
323
+
324
+ const ctx : RequestContext = { ...createRequestContext ( ) , routeHandlerRevalidate : false }
325
+ setCacheControlHeaders ( headers , request , ctx , true )
326
+
327
+ expect ( headers . set ) . toHaveBeenCalledTimes ( 1 )
328
+ expect ( headers . set ) . toHaveBeenNthCalledWith (
329
+ 1 ,
330
+ 'netlify-cdn-cache-control' ,
331
+ 'public, max-age=0, must-revalidate' ,
332
+ )
333
+ } )
334
+
335
+ test ( 'should mark content as stale if "{netlify-,}cdn-cache-control" is not present and "x-nextjs-cache" is "STALE" (HEAD)' , ( ) => {
336
+ const givenHeaders = {
337
+ 'x-nextjs-cache' : 'STALE' ,
338
+ }
339
+ const headers = new Headers ( givenHeaders )
340
+ const request = new Request ( defaultUrl )
341
+ vi . spyOn ( headers , 'set' )
342
+
343
+ const ctx : RequestContext = { ...createRequestContext ( ) , routeHandlerRevalidate : false }
344
+ setCacheControlHeaders ( headers , request , ctx , true )
345
+
346
+ expect ( headers . set ) . toHaveBeenCalledTimes ( 1 )
347
+ expect ( headers . set ) . toHaveBeenNthCalledWith (
348
+ 1 ,
349
+ 'netlify-cdn-cache-control' ,
350
+ 'public, max-age=0, must-revalidate' ,
351
+ )
352
+ } )
353
+
354
+ test ( 'should set durable SWC=1yr with 1yr TTL if "{netlify-,}cdn-cache-control" is not present and `revalidate` is `false` (HEAD)' , ( ) => {
355
+ const headers = new Headers ( )
356
+ const request = new Request ( defaultUrl , { method : 'HEAD' } )
357
+ vi . spyOn ( headers , 'set' )
358
+
359
+ const ctx : RequestContext = { ...createRequestContext ( ) , routeHandlerRevalidate : false }
360
+ setCacheControlHeaders ( headers , request , ctx , true )
361
+
362
+ expect ( headers . set ) . toHaveBeenCalledTimes ( 1 )
363
+ expect ( headers . set ) . toHaveBeenNthCalledWith (
364
+ 1 ,
365
+ 'netlify-cdn-cache-control' ,
366
+ 's-maxage=31536000, stale-while-revalidate=31536000, durable' ,
367
+ )
368
+ } )
369
+
370
+ test ( 'should set durable SWC=1yr with given TTL if "{netlify-,}cdn-cache-control" is not present and `revalidate` is a number (GET)' , ( ) => {
371
+ const headers = new Headers ( )
372
+ const request = new Request ( defaultUrl )
373
+ vi . spyOn ( headers , 'set' )
374
+
375
+ const ctx : RequestContext = { ...createRequestContext ( ) , routeHandlerRevalidate : 7200 }
376
+ setCacheControlHeaders ( headers , request , ctx , true )
377
+
378
+ expect ( headers . set ) . toHaveBeenCalledTimes ( 1 )
379
+ expect ( headers . set ) . toHaveBeenNthCalledWith (
380
+ 1 ,
381
+ 'netlify-cdn-cache-control' ,
382
+ 's-maxage=7200, stale-while-revalidate=31536000, durable' ,
383
+ )
384
+ } )
385
+
386
+ test ( 'should set durable SWC=1yr with 1yr TTL if "{netlify-,}cdn-cache-control" is not present and `revalidate` is a number (HEAD)' , ( ) => {
387
+ const headers = new Headers ( )
388
+ const request = new Request ( defaultUrl , { method : 'HEAD' } )
389
+ vi . spyOn ( headers , 'set' )
390
+
391
+ const ctx : RequestContext = { ...createRequestContext ( ) , routeHandlerRevalidate : 7200 }
392
+ setCacheControlHeaders ( headers , request , ctx , true )
393
+
394
+ expect ( headers . set ) . toHaveBeenCalledTimes ( 1 )
395
+ expect ( headers . set ) . toHaveBeenNthCalledWith (
396
+ 1 ,
397
+ 'netlify-cdn-cache-control' ,
398
+ 's-maxage=7200, stale-while-revalidate=31536000, durable' ,
399
+ )
400
+ } )
401
+
402
+ test ( 'should not set any headers on POST request' , ( ) => {
403
+ const headers = new Headers ( )
404
+ const request = new Request ( defaultUrl , { method : 'POST' } )
405
+ vi . spyOn ( headers , 'set' )
406
+
407
+ const ctx : RequestContext = { ...createRequestContext ( ) , routeHandlerRevalidate : false }
408
+ setCacheControlHeaders ( headers , request , ctx , true )
409
+
410
+ expect ( headers . set ) . toHaveBeenCalledTimes ( 0 )
411
+ } )
412
+ } )
413
+
197
414
test ( 'should not set any headers if "cache-control" is not set and "requestContext.usedFsRead" is not truthy' , ( ) => {
198
415
const headers = new Headers ( )
199
416
const request = new Request ( defaultUrl )
200
417
vi . spyOn ( headers , 'set' )
201
418
202
- setCacheControlHeaders ( headers , request , createRequestContext ( ) )
419
+ setCacheControlHeaders ( headers , request , createRequestContext ( ) , true )
203
420
204
421
expect ( headers . set ) . toHaveBeenCalledTimes ( 0 )
205
422
} )
206
423
207
- test ( 'should set permanent "netlify-cdn-cache-control" if "cache-control" is not set and "requestContext.usedFsRead" is truthy' , ( ) => {
424
+ test ( 'should set permanent, durable "netlify-cdn-cache-control" if "cache-control" is not set and "requestContext.usedFsRead" is truthy' , ( ) => {
208
425
const headers = new Headers ( )
209
426
const request = new Request ( defaultUrl )
210
427
vi . spyOn ( headers , 'set' )
211
428
212
429
const requestContext = createRequestContext ( )
213
430
requestContext . usedFsRead = true
214
431
215
- setCacheControlHeaders ( headers , request , requestContext )
432
+ setCacheControlHeaders ( headers , request , requestContext , true )
216
433
217
434
expect ( headers . set ) . toHaveBeenNthCalledWith (
218
435
1 ,
@@ -222,7 +439,7 @@ describe('headers', () => {
222
439
expect ( headers . set ) . toHaveBeenNthCalledWith (
223
440
2 ,
224
441
'netlify-cdn-cache-control' ,
225
- 'max-age=31536000' ,
442
+ 'max-age=31536000, durable ' ,
226
443
)
227
444
} )
228
445
@@ -235,7 +452,7 @@ describe('headers', () => {
235
452
const request = new Request ( defaultUrl )
236
453
vi . spyOn ( headers , 'set' )
237
454
238
- setCacheControlHeaders ( headers , request , createRequestContext ( ) )
455
+ setCacheControlHeaders ( headers , request , createRequestContext ( ) , true )
239
456
240
457
expect ( headers . set ) . toHaveBeenCalledTimes ( 0 )
241
458
} )
@@ -249,7 +466,7 @@ describe('headers', () => {
249
466
const request = new Request ( defaultUrl )
250
467
vi . spyOn ( headers , 'set' )
251
468
252
- setCacheControlHeaders ( headers , request , createRequestContext ( ) )
469
+ setCacheControlHeaders ( headers , request , createRequestContext ( ) , true )
253
470
254
471
expect ( headers . set ) . toHaveBeenCalledTimes ( 0 )
255
472
} )
@@ -262,7 +479,7 @@ describe('headers', () => {
262
479
const request = new Request ( defaultUrl )
263
480
vi . spyOn ( headers , 'set' )
264
481
265
- setCacheControlHeaders ( headers , request , createRequestContext ( ) )
482
+ setCacheControlHeaders ( headers , request , createRequestContext ( ) , true )
266
483
267
484
expect ( headers . set ) . toHaveBeenNthCalledWith (
268
485
1 ,
@@ -272,7 +489,7 @@ describe('headers', () => {
272
489
expect ( headers . set ) . toHaveBeenNthCalledWith (
273
490
2 ,
274
491
'netlify-cdn-cache-control' ,
275
- 'public, max-age=0, must-revalidate' ,
492
+ 'public, max-age=0, must-revalidate, durable ' ,
276
493
)
277
494
} )
278
495
@@ -284,7 +501,7 @@ describe('headers', () => {
284
501
const request = new Request ( defaultUrl , { method : 'HEAD' } )
285
502
vi . spyOn ( headers , 'set' )
286
503
287
- setCacheControlHeaders ( headers , request , createRequestContext ( ) )
504
+ setCacheControlHeaders ( headers , request , createRequestContext ( ) , true )
288
505
289
506
expect ( headers . set ) . toHaveBeenNthCalledWith (
290
507
1 ,
@@ -294,7 +511,7 @@ describe('headers', () => {
294
511
expect ( headers . set ) . toHaveBeenNthCalledWith (
295
512
2 ,
296
513
'netlify-cdn-cache-control' ,
297
- 'public, max-age=0, must-revalidate' ,
514
+ 'public, max-age=0, must-revalidate, durable ' ,
298
515
)
299
516
} )
300
517
@@ -306,7 +523,7 @@ describe('headers', () => {
306
523
const request = new Request ( defaultUrl , { method : 'POST' } )
307
524
vi . spyOn ( headers , 'set' )
308
525
309
- setCacheControlHeaders ( headers , request , createRequestContext ( ) )
526
+ setCacheControlHeaders ( headers , request , createRequestContext ( ) , true )
310
527
311
528
expect ( headers . set ) . toHaveBeenCalledTimes ( 0 )
312
529
} )
@@ -319,13 +536,13 @@ describe('headers', () => {
319
536
const request = new Request ( defaultUrl )
320
537
vi . spyOn ( headers , 'set' )
321
538
322
- setCacheControlHeaders ( headers , request , createRequestContext ( ) )
539
+ setCacheControlHeaders ( headers , request , createRequestContext ( ) , true )
323
540
324
541
expect ( headers . set ) . toHaveBeenNthCalledWith ( 1 , 'cache-control' , 'public' )
325
542
expect ( headers . set ) . toHaveBeenNthCalledWith (
326
543
2 ,
327
544
'netlify-cdn-cache-control' ,
328
- 'public, s-maxage=604800' ,
545
+ 'public, s-maxage=604800, durable ' ,
329
546
)
330
547
} )
331
548
@@ -337,25 +554,25 @@ describe('headers', () => {
337
554
const request = new Request ( defaultUrl )
338
555
vi . spyOn ( headers , 'set' )
339
556
340
- setCacheControlHeaders ( headers , request , createRequestContext ( ) )
557
+ setCacheControlHeaders ( headers , request , createRequestContext ( ) , true )
341
558
342
559
expect ( headers . set ) . toHaveBeenNthCalledWith ( 1 , 'cache-control' , 'max-age=604800' )
343
560
expect ( headers . set ) . toHaveBeenNthCalledWith (
344
561
2 ,
345
562
'netlify-cdn-cache-control' ,
346
- 'max-age=604800, stale-while-revalidate=86400' ,
563
+ 'max-age=604800, stale-while-revalidate=86400, durable ' ,
347
564
)
348
565
} )
349
566
350
- test ( 'should set default "cache-control" header if it contains only "s-maxage" and "stale-whie -revalidate"' , ( ) => {
567
+ test ( 'should set default "cache-control" header if it contains only "s-maxage" and "stale-while -revalidate"' , ( ) => {
351
568
const givenHeaders = {
352
569
'cache-control' : 's-maxage=604800, stale-while-revalidate=86400' ,
353
570
}
354
571
const headers = new Headers ( givenHeaders )
355
572
const request = new Request ( defaultUrl )
356
573
vi . spyOn ( headers , 'set' )
357
574
358
- setCacheControlHeaders ( headers , request , createRequestContext ( ) )
575
+ setCacheControlHeaders ( headers , request , createRequestContext ( ) , true )
359
576
360
577
expect ( headers . set ) . toHaveBeenNthCalledWith (
361
578
1 ,
@@ -365,7 +582,7 @@ describe('headers', () => {
365
582
expect ( headers . set ) . toHaveBeenNthCalledWith (
366
583
2 ,
367
584
'netlify-cdn-cache-control' ,
368
- 's-maxage=604800, stale-while-revalidate=86400' ,
585
+ 's-maxage=604800, stale-while-revalidate=86400, durable ' ,
369
586
)
370
587
} )
371
588
} )
0 commit comments