test: add deterministic resilience matrix

This commit is contained in:
2026-09-02 07:24:48 +03:00
parent 5abd1c19ae
commit 8089d061ec
21 changed files with 809 additions and 22 deletions

View File

@@ -0,0 +1,112 @@
import { expect, test } from '../helpers/serve-dist';
import {
assertSemanticFaultOracle,
authorizedFaultUrl,
collectBrowserErrors,
missingTokenFaultUrl,
readFaultSnapshot,
readUnhandledRejections,
wrongTokenFaultUrl,
} from '../helpers/test-build-auth';
import { installSceneInstrumentation, readSceneInstrumentation } from '../helpers/scene-instrumentation';
import type { PortfolioFaultId } from '@/testing/fault-hooks';
const assertNoEscapedErrors = async (
page: Parameters<typeof readUnhandledRejections>[0],
pageErrors: readonly string[],
): Promise<void> => {
expect(pageErrors).toEqual([]);
expect(await readUnhandledRejections(page)).toEqual([]);
expect((await readSceneInstrumentation(page)).unhandledRejections).toEqual([]);
};
test.describe('authenticated fault-build boundary', () => {
test.beforeEach(async ({}, testInfo) => {
test.skip(testInfo.project.name !== 'desktop-chromium', 'The complete F1–F13 matrix runs once on current desktop Chromium.');
});
test('missing token exposes no fault API and leaves the portfolio operational', async ({ page, distUrl }) => {
await page.goto(missingTokenFaultUrl(distUrl, 'F1-scene-chunk'), { waitUntil: 'domcontentloaded' });
expect(await page.evaluate(() => '__PORTFOLIO_TEST_HOOKS__' in window)).toBe(false);
await expect(page.locator('.journey-world')).toHaveAttribute('data-scene-state', 'running');
await assertSemanticFaultOracle(page);
});
test('wrong token exposes no fault API and leaves the portfolio operational', async ({ page, distUrl }) => {
await page.goto(wrongTokenFaultUrl(distUrl, 'F1-scene-chunk'), { waitUntil: 'domcontentloaded' });
expect(await page.evaluate(() => '__PORTFOLIO_TEST_HOOKS__' in window)).toBe(false);
await expect(page.locator('.journey-world')).toHaveAttribute('data-scene-state', 'running');
await assertSemanticFaultOracle(page);
});
test('correct token exposes only the frozen named fault interface', async ({ page, distUrl }) => {
await page.goto(authorizedFaultUrl(distUrl, 'F9-hydration-failure'), { waitUntil: 'domcontentloaded' });
await expect.poll(() => page.evaluate(() => '__PORTFOLIO_TEST_HOOKS__' in window)).toBe(true);
const shape = await page.evaluate(() => {
const hooks = (window as unknown as { __PORTFOLIO_TEST_HOOKS__: object }).__PORTFOLIO_TEST_HOOKS__;
return { frozen: Object.isFrozen(hooks), keys: Object.keys(hooks).sort() };
});
expect(shape).toEqual({ frozen: true, keys: ['fault', 'snapshot', 'trigger', 'version'] });
expect((await readFaultSnapshot(page)).fault).toBe('F9-hydration-failure');
await expect(page.locator('.drive-controls')).toHaveCount(1);
await assertSemanticFaultOracle(page);
});
});
test.describe('scene failure matrix', () => {
test.beforeEach(async ({}, testInfo) => {
test.skip(testInfo.project.name !== 'desktop-chromium', 'The complete F1–F13 matrix runs once on current desktop Chromium.');
});
for (const fault of [
'F1-scene-chunk',
'F1-shader',
'F1-car',
'F1-texture',
] as const satisfies readonly PortfolioFaultId[]) {
test(`${fault} enters one static fallback without escaping an error`, async ({ page, distUrl }) => {
await installSceneInstrumentation(page);
const errors = await collectBrowserErrors(page);
await page.goto(authorizedFaultUrl(distUrl, fault), { waitUntil: 'domcontentloaded' });
await expect(page.locator('.journey-world')).toHaveAttribute('data-scene-state', 'static');
await expect(page.locator('.journey-world')).toHaveAttribute('data-static-fallback', 'true');
await expect(page.locator('.journey-poster')).toHaveCount(1);
await expect(page.locator('.journey-canvas')).toHaveCount(0);
await expect(page.locator('.drive-controls__status')).toHaveText('Visuals unavailable');
await assertSemanticFaultOracle(page);
const resources = await readSceneInstrumentation(page);
expect(resources.activeAnimationFrames).toBe(0);
expect(resources.maximumCanvasCount).toBeLessThanOrEqual(1);
expect((await readFaultSnapshot(page)).staticFallbackCount).toBe(1);
await assertNoEscapedErrors(page, errors.pageErrors);
});
}
test('F2 enforces the literal five-second first-frame deadline', async ({ page, distUrl }) => {
test.setTimeout(12_000);
await installSceneInstrumentation(page);
const errors = await collectBrowserErrors(page);
const startedAt = Date.now();
await page.goto(authorizedFaultUrl(distUrl, 'F2-first-frame-timeout'), { waitUntil: 'domcontentloaded' });
await expect(page.locator('.journey-world')).toHaveAttribute('data-scene-state', 'static', { timeout: 7_000 });
expect(Date.now() - startedAt).toBeGreaterThanOrEqual(4_800);
await expect(page.locator('.journey-world')).toHaveAttribute('data-static-fallback', 'true');
await expect(page.locator('.journey-canvas')).toHaveCount(0);
await assertSemanticFaultOracle(page);
await assertNoEscapedErrors(page, errors.pageErrors);
});
test('F3 contains a post-first-frame exception on the next frame', async ({ page, distUrl }) => {
await installSceneInstrumentation(page);
const errors = await collectBrowserErrors(page);
await page.goto(authorizedFaultUrl(distUrl, 'F3-post-first-frame-exception'), { waitUntil: 'domcontentloaded' });
await expect(page.locator('.journey-world')).toHaveAttribute('data-scene-state', 'static');
await expect(page.locator('.journey-world')).toHaveAttribute('data-static-fallback', 'true');
await expect(page.locator('.journey-canvas')).toHaveCount(0);
await assertSemanticFaultOracle(page);
expect((await readSceneInstrumentation(page)).activeAnimationFrames).toBe(0);
await assertNoEscapedErrors(page, errors.pageErrors);
});
});