@@ -4,9 +4,21 @@ import { cp } from 'node:fs/promises'
44import { createRequire } from 'node:module'
55import { join } from 'node:path'
66import { gunzipSync } from 'node:zlib'
7+ import { HttpResponse , http , passthrough } from 'msw'
8+ import { setupServer } from 'msw/node'
79import { gt , prerelease } from 'semver'
810import { v4 } from 'uuid'
9- import { Mock , afterAll , beforeAll , beforeEach , describe , expect , test , vi } from 'vitest'
11+ import {
12+ Mock ,
13+ afterAll ,
14+ afterEach ,
15+ beforeAll ,
16+ beforeEach ,
17+ describe ,
18+ expect ,
19+ test ,
20+ vi ,
21+ } from 'vitest'
1022import { getPatchesToApply } from '../../src/build/content/server.js'
1123import { type FixtureTestContext } from '../utils/contexts.js'
1224import {
@@ -36,9 +48,32 @@ vi.mock('node:fs/promises', async (importOriginal) => {
3648 }
3749} )
3850
51+ let server : ReturnType < typeof setupServer >
52+
3953// Disable the verbose logging of the lambda-local runtime
4054getLogger ( ) . level = 'alert'
4155
56+ const purgeAPI = vi . fn ( )
57+
58+ beforeAll ( ( ) => {
59+ server = setupServer (
60+ http . post ( 'https://api.netlify.com/api/v1/purge' , async ( { request } ) => {
61+ purgeAPI ( await request . json ( ) )
62+
63+ return HttpResponse . json ( {
64+ ok : true ,
65+ } )
66+ } ) ,
67+ http . all ( / .* / , ( ) => passthrough ( ) ) ,
68+ )
69+ server . listen ( )
70+ } )
71+
72+ afterAll ( ( ) => {
73+ // Disable API mocking after the tests are done.
74+ server . close ( )
75+ } )
76+
4277beforeEach < FixtureTestContext > ( async ( ctx ) => {
4378 // set for each test a new deployID and siteID
4479 ctx . deployID = generateRandomObjectID ( )
@@ -48,9 +83,15 @@ beforeEach<FixtureTestContext>(async (ctx) => {
4883 // hide debug logs in tests
4984 vi . spyOn ( console , 'debug' ) . mockImplementation ( ( ) => { } )
5085
86+ purgeAPI . mockClear ( )
87+
5188 await startMockBlobStore ( ctx )
5289} )
5390
91+ afterEach ( ( ) => {
92+ vi . unstubAllEnvs ( )
93+ } )
94+
5495test < FixtureTestContext > ( 'Test that the simple next app is working' , async ( ctx ) => {
5596 await createFixture ( 'simple' , ctx )
5697 await runPlugin ( ctx )
@@ -210,6 +251,39 @@ test<FixtureTestContext>('cacheable route handler is cached on cdn (revalidate=f
210251 )
211252} )
212253
254+ test < FixtureTestContext > ( 'purge API is not used when unstable_cache cache entry gets stale' , async ( ctx ) => {
255+ await createFixture ( 'simple' , ctx )
256+ await runPlugin ( ctx )
257+
258+ // set the NETLIFY_PURGE_API_TOKEN to get pass token check and allow fetch call to be made
259+ vi . stubEnv ( 'NETLIFY_PURGE_API_TOKEN' , 'mock' )
260+
261+ const page1 = await invokeFunction ( ctx , {
262+ url : '/unstable_cache' ,
263+ } )
264+ const data1 = load ( page1 . body ) ( 'pre' ) . text ( )
265+
266+ // allow for cache entry to get stale
267+ await new Promise ( ( res ) => setTimeout ( res , 2000 ) )
268+
269+ const page2 = await invokeFunction ( ctx , {
270+ url : '/unstable_cache' ,
271+ } )
272+ const data2 = load ( page2 . body ) ( 'pre' ) . text ( )
273+
274+ const page3 = await invokeFunction ( ctx , {
275+ url : '/unstable_cache' ,
276+ } )
277+ const data3 = load ( page3 . body ) ( 'pre' ) . text ( )
278+
279+ expect ( purgeAPI , 'Purge API should not be hit' ) . toHaveBeenCalledTimes ( 0 )
280+ expect (
281+ data2 ,
282+ 'Should use stale cache entry for current request and invalidate it in background' ,
283+ ) . toBe ( data1 )
284+ expect ( data3 , 'Should use updated cache entry' ) . not . toBe ( data2 )
285+ } )
286+
213287test < FixtureTestContext > ( 'cacheable route handler is cached on cdn (revalidate=15)' , async ( ctx ) => {
214288 await createFixture ( 'simple' , ctx )
215289 await runPlugin ( ctx )
0 commit comments