@@ -6,9 +6,33 @@ import { v4 } from 'uuid'
6
6
import { afterAll , beforeAll , beforeEach , expect , test , vi } from 'vitest'
7
7
import { type FixtureTestContext } from '../utils/contexts.js'
8
8
import { createFixture , invokeFunction , runPlugin , runPluginStep } from '../utils/fixture.js'
9
- import { generateRandomObjectID , startMockBlobStore } from '../utils/helpers.js'
9
+ import { generateRandomObjectID , getBlobServerGets , startMockBlobStore } from '../utils/helpers.js'
10
10
import { nextVersionSatisfies } from '../utils/next-version-helpers.mjs'
11
11
12
+ function isFetch ( key : string ) {
13
+ // exclude tag manifests (starting with `_N_T_`), pages (starting with `/`) and static html files (keys including `.html`)
14
+ return ! key . startsWith ( '_N_T_' ) && ! key . startsWith ( '/' ) && ! key . includes ( '.html' )
15
+ }
16
+
17
+ expect . extend ( {
18
+ toBeDistinct ( received : string [ ] ) {
19
+ const { isNot } = this
20
+ const pass = new Set ( received ) . size === received . length
21
+ return {
22
+ pass,
23
+ message : ( ) => `${ received } is${ isNot ? ' not' : '' } array with distinct values` ,
24
+ }
25
+ } ,
26
+ } )
27
+
28
+ interface CustomMatchers < R = unknown > {
29
+ toBeDistinct ( ) : R
30
+ }
31
+
32
+ declare module 'vitest' {
33
+ interface Assertion < T = any > extends CustomMatchers < T > { }
34
+ }
35
+
12
36
// Disable the verbose logging of the lambda-local runtime
13
37
getLogger ( ) . level = 'alert'
14
38
@@ -304,3 +328,71 @@ test<FixtureTestContext>('if the fetch call is cached correctly (cached page res
304
328
} ) ,
305
329
)
306
330
} )
331
+
332
+ test < FixtureTestContext > ( 'does not fetch same cached fetch data from blobs twice for the same request' , async ( ctx ) => {
333
+ await createFixture ( 'revalidate-fetch' , ctx )
334
+ await runPluginStep ( ctx , 'onPreBuild' )
335
+ await runPlugin ( ctx )
336
+
337
+ handlerCalled = 0
338
+ const request1 = await invokeFunction ( ctx , {
339
+ url : 'same-fetch-multiple-times/99' ,
340
+ } )
341
+
342
+ const request1FetchDate = load ( request1 . body ) ( '[data-testid="date-from-response"]' ) . text ( )
343
+ const request1Name = load ( request1 . body ) ( '[data-testid="name"]' ) . text ( )
344
+
345
+ expect ( request1 . statusCode , 'Tested page should work' ) . toBe ( 200 )
346
+ expect ( request1Name , 'Test setup should use test API mock' ) . toBe ( 'Fake response' )
347
+
348
+ expect (
349
+ handlerCalled ,
350
+ 'Cache should be empty, and we should hit mock endpoint once to warm up the cache' ,
351
+ ) . toBe ( 1 )
352
+
353
+ const request1FetchCacheKeys = getBlobServerGets ( ctx , isFetch )
354
+
355
+ expect (
356
+ request1FetchCacheKeys . length ,
357
+ 'tested page should be doing 3 fetch calls to render single page - we should only try to get cached fetch data from blobs once' ,
358
+ ) . toBe ( 1 )
359
+
360
+ const request1AllCacheKeys = getBlobServerGets ( ctx )
361
+ expect (
362
+ request1AllCacheKeys ,
363
+ 'expected blobs for all types of values to be retrieved at most once per key (including fetch data, tag manifests, static files)' ,
364
+ ) . toBeDistinct ( )
365
+
366
+ ctx . blobServerGetSpy . mockClear ( )
367
+ handlerCalled = 0
368
+ const request2 = await invokeFunction ( ctx , {
369
+ url : 'same-fetch-multiple-times/99' ,
370
+ } )
371
+
372
+ const request2FetchDate = load ( request2 . body ) ( '[data-testid="date-from-response"]' ) . text ( )
373
+ const request2Name = load ( request2 . body ) ( '[data-testid="name"]' ) . text ( )
374
+
375
+ expect ( request2 . statusCode , 'Tested page should work' ) . toBe ( 200 )
376
+ expect ( request2Name , 'Test setup should use test API mock' ) . toBe ( 'Fake response' )
377
+ expect ( request2FetchDate , 'Cached fetch data should be used for second request' ) . toBe (
378
+ request1FetchDate ,
379
+ )
380
+
381
+ expect ( handlerCalled , 'Cache should be warm, and we should not hit mock endpoint' ) . toBe ( 0 )
382
+
383
+ const request2FetchCacheKeys = getBlobServerGets ( ctx , isFetch )
384
+
385
+ expect (
386
+ request2FetchCacheKeys . length ,
387
+ 'We should not reuse in-memory cache from first request and have one fetch blob call for second request' ,
388
+ ) . toBe ( 1 )
389
+ expect ( request2FetchCacheKeys , 'Same fetch keys should be used in both requests' ) . toEqual (
390
+ request1FetchCacheKeys ,
391
+ )
392
+
393
+ const request2AllCacheKeys = getBlobServerGets ( ctx )
394
+ expect (
395
+ request2AllCacheKeys ,
396
+ 'expected blobs for all types of values to be retrieved at most once per key (including fetch data, tag manifests, static files)' ,
397
+ ) . toBeDistinct ( )
398
+ } )
0 commit comments