Files
Michał Pierzchała b15121ffc8 test(ios): establish snapshot convergence baselines and permanent evidence (#2204)
* test(ios): add snapshot convergence evidence harness

* fix(ios): satisfy benchmark CI guards

* fix(ios): constrain benchmark proxy routes

* fix(ios-benchmark): enforce cell admission evidence

* fix(ios-benchmark): protect benchmark state ownership

* fix(ios-benchmark): use proxy port flag

* fix(ios-benchmark): let proxy choose an ephemeral port

* test(ios-benchmark): keep CLI process seam local

* fix(ios-benchmark): parse proxy startup envelope

* fix(ios-benchmark): bind proxy lease to simulator

* fix(ios-benchmark): keep fresh proxy CLI sessions isolated

* fix(ios-benchmark): preserve async timeout evidence

* docs(ios-benchmark): retain exact-head evidence

* test(ios): reveal offscreen alert fixture controls

* test(ios): reset alert between relaunch samples

* test(ios): admit native alert snapshots

* docs(ios): publish snapshot convergence corpus

* chore(ios): format benchmark evidence

* fix(ios-benchmark): admit proxy fixture anchors

* docs(ios): republish exact-head benchmark corpus

* fix(size): make publish asset evidence hermetic

* style(size): format package evidence test

* test(size): update publish preparation contracts

* fix: retire stale utils layering zone

* test: pin shared publish asset owner

* test: verify preserved size reporter closure

* fix: move mutation ownership to snapshot module

* test(ios): add snapshot convergence evidence harness

* fix(ios): satisfy benchmark CI guards

* fix(ios): constrain benchmark proxy routes

* fix(ios-benchmark): enforce cell admission evidence

* fix(ios-benchmark): protect benchmark state ownership

* fix(ios-benchmark): use proxy port flag

* fix(ios-benchmark): let proxy choose an ephemeral port

* test(ios-benchmark): keep CLI process seam local

* fix(ios-benchmark): parse proxy startup envelope

* fix(ios-benchmark): bind proxy lease to simulator

* fix(ios-benchmark): keep fresh proxy CLI sessions isolated

* fix(ios-benchmark): preserve async timeout evidence

* docs(ios-benchmark): retain exact-head evidence

* test(ios): reveal offscreen alert fixture controls

* test(ios): reset alert between relaunch samples

* test(ios): admit native alert snapshots

* docs(ios): publish snapshot convergence corpus

* chore(ios): format benchmark evidence

* fix(ios-benchmark): admit proxy fixture anchors

* docs(ios): republish exact-head benchmark corpus

* fix(size): make publish asset evidence hermetic

* test(size): update publish preparation contracts

* fix: keep git-state gates out of mutation sandboxes
2026-09-01 21:39:05 +02:00

107 lines
3.9 KiB
TypeScript

import crypto from 'node:crypto';
import { fileURLToPath } from 'node:url';
import {
ARTIFACT_FILENAME,
materializeNodes,
readDeepButtonFixtureArtifact,
type DeepButtonArtifact,
} from './deep-button-artifact.ts';
import type { DeepButtonEvidence, DeepButtonObservation } from './types.ts';
const INVALID_SHALLOW_COMMAND = 'pnpm bench:ios-snapshot:deep-button -- --rule invalid-shallow';
const SAFE_FULL_COMMAND = 'pnpm bench:ios-snapshot:deep-button -- --rule safe-full';
const INVALID_ASSERTION =
'AssertionError: changed descendant was omitted by shallow observation; no-effect claim is invalid.';
export { readDeepButtonFixtureArtifact } from './deep-button-artifact.ts';
export function deepButtonFixtureEvidence(): DeepButtonEvidence {
const artifact = readDeepButtonFixtureArtifact();
return {
issue: '#1626',
fixture: 'deep-button-v1',
artifact: ARTIFACT_FILENAME,
depth: artifact.depth,
changedDescendant: artifact.changedDescendant,
invalidShallowRule: {
command: INVALID_SHALLOW_COMMAND,
exitCode: 1,
assertion: INVALID_ASSERTION,
},
safeFullRule: {
command: SAFE_FULL_COMMAND,
exitCode: 0,
assertion: 'full observation changed and includes the changed descendant.',
},
before: observation(artifact, artifact.before),
after: observation(artifact, artifact.after),
};
}
export function assertInvalidShallowRuleFails(): never {
const evidence = deepButtonFixtureEvidence();
if (
evidence.before.surfaceDigest === evidence.after.surfaceDigest &&
evidence.before.fullDigest !== evidence.after.fullDigest &&
!evidence.after.surfaceNodeIds.includes(evidence.changedDescendant) &&
evidence.after.fullNodeIds.includes(evidence.changedDescendant)
) {
throw new Error(INVALID_ASSERTION);
}
throw new Error('AssertionError: fixture did not produce a changed omitted descendant.');
}
export function assertSafeFullRulePasses(): void {
const evidence = deepButtonFixtureEvidence();
if (evidence.before.fullDigest === evidence.after.fullDigest) {
throw new Error('AssertionError: full observation did not record the changed descendant.');
}
if (!evidence.after.fullNodeIds.includes(evidence.changedDescendant)) {
throw new Error('AssertionError: full observation omitted the changed descendant.');
}
}
function observation(
artifact: DeepButtonArtifact,
source: DeepButtonArtifact['before'],
): DeepButtonObservation {
const surfaceNodes = materializeNodes(artifact.nodes, source.shallowNodeIds, source.changedNode);
const fullNodes = materializeNodes(artifact.nodes, source.fullNodeIds, source.changedNode);
return {
depth: artifact.depth,
surfaceDigest: digest(surfaceNodes),
fullDigest: digest(fullNodes),
state: source.state,
surfaceNodeIds: source.shallowNodeIds,
fullNodeIds: source.fullNodeIds,
};
}
function digest(nodes: DeepButtonArtifact['nodes']): string {
return crypto.createHash('sha256').update(JSON.stringify(nodes)).digest('hex');
}
function readRule(argv: string[]): 'invalid-shallow' | 'safe-full' {
if (argv[0] === '--') argv = argv.slice(1);
const index = argv.indexOf('--rule');
const rule = argv[index + 1];
if (rule === 'invalid-shallow' || rule === 'safe-full') return rule;
throw new Error('Usage: pnpm bench:ios-snapshot:deep-button -- --rule invalid-shallow|safe-full');
}
function runDeepButtonRule(argv: string[]): void {
const rule = readRule(argv);
if (rule === 'invalid-shallow') assertInvalidShallowRuleFails();
assertSafeFullRulePasses();
process.stdout.write('safe-full: changed descendant observed in the full snapshot.\n');
}
if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
try {
runDeepButtonRule(process.argv.slice(2));
} catch (error) {
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
process.exitCode = 1;
}
}