Skip to content

Commit a1d7948

Browse files
committed
test: add integration test for site-wide purge calls
1 parent 6a2c39c commit a1d7948

File tree

2 files changed

+87
-0
lines changed

2 files changed

+87
-0
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { unstable_cache } from 'next/cache'
2+
3+
export const dynamic = 'force-dynamic'
4+
5+
const getData = unstable_cache(
6+
async () => {
7+
return {
8+
timestamp: Date.now(),
9+
}
10+
},
11+
[],
12+
{
13+
revalidate: 1,
14+
},
15+
)
16+
17+
export default async function Page() {
18+
const data = await getData()
19+
20+
return <pre>{JSON.stringify(data, null, 2)}</pre>
21+
}

tests/integration/simple-app.test.ts

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { cp } from 'node:fs/promises'
44
import { createRequire } from 'node:module'
55
import { join } from 'node:path'
66
import { gunzipSync } from 'node:zlib'
7+
import { HttpResponse, http, passthrough } from 'msw'
8+
import { setupServer } from 'msw/node'
79
import { gt, prerelease } from 'semver'
810
import { v4 } from 'uuid'
911
import { Mock, afterAll, beforeAll, beforeEach, describe, expect, test, vi } from 'vitest'
@@ -22,6 +24,8 @@ import {
2224
startMockBlobStore,
2325
} from '../utils/helpers.js'
2426
import { nextVersionSatisfies } from '../utils/next-version-helpers.mjs'
27+
import { purgeCache } from '@netlify/functions'
28+
import { afterEach } from 'node:test'
2529

2630
const mockedCp = cp as Mock<
2731
Parameters<(typeof import('node:fs/promises'))['cp']>,
@@ -36,9 +40,32 @@ vi.mock('node:fs/promises', async (importOriginal) => {
3640
}
3741
})
3842

43+
let server: ReturnType<typeof setupServer>
44+
3945
// Disable the verbose logging of the lambda-local runtime
4046
getLogger().level = 'alert'
4147

48+
const purgeAPI = vi.fn()
49+
50+
beforeAll(() => {
51+
server = setupServer(
52+
http.post('https://api.netlify.com/api/v1/purge', async ({ request }) => {
53+
purgeAPI(await request.json())
54+
55+
return HttpResponse.json({
56+
ok: true,
57+
})
58+
}),
59+
http.all(/.*/, () => passthrough()),
60+
)
61+
server.listen()
62+
})
63+
64+
afterAll(() => {
65+
// Disable API mocking after the tests are done.
66+
server.close()
67+
})
68+
4269
beforeEach<FixtureTestContext>(async (ctx) => {
4370
// set for each test a new deployID and siteID
4471
ctx.deployID = generateRandomObjectID()
@@ -48,9 +75,15 @@ beforeEach<FixtureTestContext>(async (ctx) => {
4875
// hide debug logs in tests
4976
vi.spyOn(console, 'debug').mockImplementation(() => {})
5077

78+
purgeAPI.mockClear()
79+
5180
await startMockBlobStore(ctx)
5281
})
5382

83+
afterEach(() => {
84+
vi.unstubAllEnvs()
85+
})
86+
5487
test<FixtureTestContext>('Test that the simple next app is working', async (ctx) => {
5588
await createFixture('simple', ctx)
5689
await runPlugin(ctx)
@@ -210,6 +243,39 @@ test<FixtureTestContext>('cacheable route handler is cached on cdn (revalidate=f
210243
)
211244
})
212245

246+
test<FixtureTestContext>('purge API is not used when unstable_cache cache entry gets stale', async (ctx) => {
247+
await createFixture('simple', ctx)
248+
await runPlugin(ctx)
249+
250+
// set the NETLIFY_PURGE_API_TOKEN to get pass token check and allow fetch call to be made
251+
vi.stubEnv('NETLIFY_PURGE_API_TOKEN', 'mock')
252+
253+
const page1 = await invokeFunction(ctx, {
254+
url: '/unstable_cache',
255+
})
256+
const data1 = load(page1.body)('pre').text()
257+
258+
// allow for cache entry to get stale
259+
await new Promise((res) => setTimeout(res, 2000))
260+
261+
const page2 = await invokeFunction(ctx, {
262+
url: '/unstable_cache',
263+
})
264+
const data2 = load(page2.body)('pre').text()
265+
266+
const page3 = await invokeFunction(ctx, {
267+
url: '/unstable_cache',
268+
})
269+
const data3 = load(page3.body)('pre').text()
270+
271+
expect(purgeAPI, 'Purge API should not be hit').toHaveBeenCalledTimes(0)
272+
expect(
273+
data2,
274+
'Should use stale cache entry for current request and invalidate it in background',
275+
).toBe(data1)
276+
expect(data3, 'Should use updated cache entry').not.toBe(data2)
277+
})
278+
213279
test<FixtureTestContext>('cacheable route handler is cached on cdn (revalidate=15)', async (ctx) => {
214280
await createFixture('simple', ctx)
215281
await runPlugin(ctx)

0 commit comments

Comments
 (0)