Merge manifest hygiene and reconcile runtime exclusions with CSP response capture
This commit is contained in:
@@ -20,6 +20,7 @@ writeFileSync(join(publicDir, 'root.css'), 'body{color:red}');
|
||||
writeFileSync(join(publicDir, 'sw.js'), "const BUILD_TAG = typeof __BUILD_TAG__ !== 'undefined' ? __BUILD_TAG__ : 'v-test';\n");
|
||||
writeFileSync(join(publicDir, 'fonts', 'font.woff2'), 'font');
|
||||
writeFileSync(join(publicDir, 'icons', 'icon.png'), 'icon');
|
||||
for (const name of ['probe.test.js', 'README.md', 'admin.html', 'fonts/font-OFL.txt']) writeFileSync(join(publicDir, name), 'direct-only file');
|
||||
|
||||
async function freePort() {
|
||||
return await new Promise((resolvePort, reject) => {
|
||||
@@ -65,19 +66,35 @@ beforeAll(async () => { server = await startServer(); });
|
||||
afterAll(async () => { await stopServer(server); rmSync(root, { recursive: true, force: true }); });
|
||||
|
||||
describe('static delivery characterization', () => {
|
||||
test('build tag is stable for the same tree and changes when any served file changes', async () => {
|
||||
test('build tag is stable for the same tree and changes only when runtime files change', async () => {
|
||||
const first = await (await fetch(`${server.base}/api/version`)).json();
|
||||
await stopServer(server);
|
||||
server = await startServer();
|
||||
const same = await (await fetch(`${server.base}/api/version`)).json();
|
||||
expect(same.buildTag).toBe(first.buildTag);
|
||||
writeFileSync(join(publicDir, 'unlisted.txt'), 'included in recursive build tag');
|
||||
writeFileSync(join(publicDir, 'unlisted.js'), 'runtime build input');
|
||||
await stopServer(server);
|
||||
server = await startServer();
|
||||
const changed = await (await fetch(`${server.base}/api/version`)).json();
|
||||
expect(changed.buildTag).not.toBe(first.buildTag);
|
||||
});
|
||||
|
||||
test('excluded files retain direct bodies, hashes, compression and cache headers', async () => {
|
||||
const { createHash } = await import('node:crypto');
|
||||
const expected = createHash('sha256').update('direct-only file').digest('hex').slice(0, 10);
|
||||
const manifest = await (await fetch(`${server.base}/api/manifest`)).json();
|
||||
for (const path of ['/probe.test.js', '/README.md', '/admin.html', '/fonts/font-OFL.txt']) {
|
||||
expect(manifest.files[path]).toBeUndefined();
|
||||
for (const query of ['', `?v=${expected}`, '?v=stale']) {
|
||||
const response = await fetch(`${server.base}${path}${query}`);
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.headers.get('x-asset-hash')).toBe(expected);
|
||||
expect(await response.text()).toBe('direct-only file');
|
||||
expect(response.headers.get('cache-control')).toBe(query === '?v=stale' ? 'no-store' : query ? 'public, max-age=31536000, immutable' : path.startsWith('/fonts/') ? 'public, max-age=2592000' : 'no-cache');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
test('unreadable public tree uses the fixed dev-build fallback', async () => {
|
||||
const moved = join(root, 'public.saved');
|
||||
Bun.spawnSync(['mv', publicDir, moved]);
|
||||
|
||||
Reference in New Issue
Block a user