Skip to content

test: add some test cases ensuring next.config redirects/rewrites don't result in cache poisoning #2879

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 57 additions & 1 deletion tests/e2e/simple-app.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { expect, type Locator } from '@playwright/test'
import { expect, type Locator, type Response } from '@playwright/test'
import { nextVersionSatisfies } from '../utils/next-version-helpers.mjs'
import { test } from '../utils/playwright-helpers.js'

Expand Down Expand Up @@ -282,3 +282,59 @@ test('can require CJS module that is not bundled', async ({ simple }) => {
expect(parsedBody.notBundledCJSModule.isBundled).toEqual(false)
expect(parsedBody.bundledCJSModule.isBundled).toEqual(true)
})

test.describe('RSC cache poisoning', () => {
test('Next.config.js rewrite', async ({ page, simple }) => {
const prefetchResponsePromise = new Promise<Response>((resolve) => {
page.on('response', (response) => {
if (response.url().includes('/config-rewrite/source')) {
resolve(response)
}
})
})
await page.goto(`${simple.url}/config-rewrite`)

// ensure prefetch
await page.hover('text=NextConfig.rewrite')

// wait for prefetch request to finish
const prefetchResponse = await prefetchResponsePromise

// ensure prefetch respond with RSC data
expect(prefetchResponse.headers()['content-type']).toMatch(/text\/x-component/)
expect(prefetchResponse.headers()['netlify-cdn-cache-control']).toMatch(/s-maxage=31536000/)

const htmlResponse = await page.goto(`${simple.url}/config-rewrite/source`)

// ensure we get HTML response
expect(htmlResponse?.headers()['content-type']).toMatch(/text\/html/)
expect(htmlResponse?.headers()['netlify-cdn-cache-control']).toMatch(/s-maxage=31536000/)
})

test('Next.config.js redirect', async ({ page, simple }) => {
const prefetchResponsePromise = new Promise<Response>((resolve) => {
page.on('response', (response) => {
if (response.url().includes('/config-redirect/dest')) {
resolve(response)
}
})
})
await page.goto(`${simple.url}/config-redirect`)

// ensure prefetch
await page.hover('text=NextConfig.redirect')

// wait for prefetch request to finish
const prefetchResponse = await prefetchResponsePromise

// ensure prefetch respond with RSC data
expect(prefetchResponse.headers()['content-type']).toMatch(/text\/x-component/)
expect(prefetchResponse.headers()['netlify-cdn-cache-control']).toMatch(/s-maxage=31536000/)

const htmlResponse = await page.goto(`${simple.url}/config-rewrite/source`)

// ensure we get HTML response
expect(htmlResponse?.headers()['content-type']).toMatch(/text\/html/)
expect(htmlResponse?.headers()['netlify-cdn-cache-control']).toMatch(/s-maxage=31536000/)
})
})
9 changes: 9 additions & 0 deletions tests/fixtures/simple/app/config-redirect/dest/page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export default function Page() {
return (
<main>
<h1>Hello redirect target</h1>
</main>
)
}

export const dynamic = 'force-static'
9 changes: 9 additions & 0 deletions tests/fixtures/simple/app/config-redirect/page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import Link from 'next/link'

export default function Home() {
return (
<div>
<Link href="/config-redirect/source">NextConfig.redirect</Link>
</div>
)
}
9 changes: 9 additions & 0 deletions tests/fixtures/simple/app/config-rewrite/dest/page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export default function Page() {
return (
<main>
<h1>Hello rewrite target</h1>
</main>
)
}

export const dynamic = 'force-static'
9 changes: 9 additions & 0 deletions tests/fixtures/simple/app/config-rewrite/page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import Link from 'next/link'

export default function Home() {
return (
<div>
<Link href="/config-rewrite/source">NextConfig.rewrite</Link>
</div>
)
}
13 changes: 13 additions & 0 deletions tests/fixtures/simple/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,19 @@ const nextConfig = {
destination: 'https://example.vercel.sh',
basePath: false,
},
{
source: '/config-rewrite/source',
destination: '/config-rewrite/dest',
},
]
},
async redirects() {
return [
{
source: '/config-redirect/source',
destination: '/config-redirect/dest',
permanent: true,
},
]
},
}
Expand Down
4 changes: 4 additions & 0 deletions tests/integration/simple-app.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@ test<FixtureTestContext>('Test that the simple next app is working', async (ctx)
'/404',
'/api/cached-permanent',
'/api/cached-revalidate',
'/config-redirect',
'/config-redirect/dest',
'/config-rewrite',
'/config-rewrite/dest',
'/image/local',
'/image/migration-from-v4-runtime',
'/image/remote-domain',
Expand Down
Loading