fix: harden prerender publication boundary
This commit is contained in:
46
tests/unit/entry-client.test.ts
Normal file
46
tests/unit/entry-client.test.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { resolve } from 'node:path';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { readProductionInputsSnapshot } from '@/entry-client';
|
||||
import { serializeJsonLd } from '@/security/serialize-json-ld';
|
||||
import type { ProductionInputs } from '@/content/types';
|
||||
|
||||
const validInputs = (): ProductionInputs => ({
|
||||
content: JSON.parse(readFileSync(resolve(process.cwd(), 'content/public/portfolio-content.json'), 'utf8')),
|
||||
manifest: JSON.parse(readFileSync(resolve(process.cwd(), 'content/public/asset-manifest.json'), 'utf8')),
|
||||
site: JSON.parse(readFileSync(resolve(process.cwd(), 'config/site.preview.json'), 'utf8')),
|
||||
}) as ProductionInputs;
|
||||
|
||||
const snapshotDocument = (value: unknown): Document => new DOMParser().parseFromString(
|
||||
`<script id="portfolio-inputs" type="application/json">${serializeJsonLd(value)}</script>`,
|
||||
'text/html',
|
||||
);
|
||||
|
||||
describe('client production-input snapshot', () => {
|
||||
it('schema-validates and recursively freezes the complete browser snapshot', () => {
|
||||
const inputs = readProductionInputsSnapshot(snapshotDocument(validInputs()));
|
||||
|
||||
expect(Object.isFrozen(inputs)).toBe(true);
|
||||
expect(Object.isFrozen(inputs.content.projects.selected)).toBe(true);
|
||||
expect(Object.isFrozen(inputs.content.projects.selected[0]?.technicalDecisions)).toBe(true);
|
||||
expect(Object.isFrozen(inputs.manifest.assets)).toBe(true);
|
||||
expect(Object.isFrozen(inputs.site)).toBe(true);
|
||||
expect(() => {
|
||||
(inputs.content.identity as { role: string }).role = 'mutated';
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
it('rejects JSON that does not satisfy the production-input schema', () => {
|
||||
const invalid = validInputs() as ProductionInputs & { unexpected?: string };
|
||||
invalid.unexpected = 'not allowed';
|
||||
|
||||
expect(() => readProductionInputsSnapshot(snapshotDocument(invalid))).toThrow(/unrecognized|invalid|expected/i);
|
||||
});
|
||||
|
||||
it('rejects a supplied asset reference absent from the supplied public manifest', () => {
|
||||
const invalid = validInputs();
|
||||
invalid.content.projects.selected[0]!.assetIds = ['unmanifested-snapshot-asset'];
|
||||
|
||||
expect(() => readProductionInputsSnapshot(snapshotDocument(invalid))).toThrow(/unmanifested asset reference/i);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user