mirror of
https://github.com/callstack/agent-device.git
synced 2026-09-14 20:06:34 +08:00
b15121ffc8
* 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
107 lines
3.9 KiB
TypeScript
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;
|
|
}
|
|
}
|