| 1 | import test from "node:test"; |
| 2 | import assert from "node:assert/strict"; |
| 3 | import { aggregateShards, completeShard, memoryProtocol, MEMORY_FIXTURES } from "./app-memory-shards.mjs"; |
| 4 | |
| 5 | const identity = { sourceSHA: "a".repeat(40), trackedDiffSHA256: "clean-diff", untrackedSourceSHA256: "clean-untracked", buildSHA256: "shared-build", node: "v24", platform: "linux", arch: "x64", sourceStatus: "" }; |
| 6 | |
| 7 | function fixture(profile = "full", executionId = "123:1") { |
| 8 | const protocol = memoryProtocol(profile); |
| 9 | const manifest = { identity, protocol, executionId }; |
| 10 | const report = id => { |
| 11 | const sample = (phase, roundTrips) => ({ phase, roundTrips, |
| 12 | lifecycle: { liveRenderTokenIds: [1], liveRenderTokens: 1, activeOperations: 0, activeSubscriptions: 2, overflow: false, invariantViolations: 0 }, |
| 13 | dom: { nodes: 10, jsEventListeners: 2 }, heap: { usedSize: 100 } }); |
| 14 | const samples = [sample("baseline", 0)]; |
| 15 | for (const phase of ["full", "windowed", "safety", "mixed"]) { |
| 16 | const count = phase === "mixed" ? protocol.mixedCycles : protocol.cycles; |
| 17 | for (let round = 32; round <= count; round += 32) samples.push(sample(phase, round)); |
| 18 | } |
| 19 | samples.push(sample("settled", protocol.mixedCycles)); |
| 20 | return { identity: structuredClone(identity), fixtures: structuredClone(MEMORY_FIXTURES), protocol: structuredClone(protocol), |
| 21 | shard: { id, total: protocol.shards, executionId }, cycles: protocol.cycles, mixedCycles: protocol.mixedCycles, |
| 22 | shardComplete: true, protocolComplete: false, verdict: "SHARD_PASS", processes: [{ process: id, browser: "chromium-fixed", samples, |
| 23 | snapshots: ["baseline", "full", "windowed", "safety", "mixed"].map(phase => ({ file: `${id}-${phase}.heapsnapshot`, summary: {} })), |
| 24 | checks: { evidenceIntegrity: true, instrumentedOperationsReleased: true, noPageErrors: true }, metrics: { pageErrors: [] } }] }; |
| 25 | }; |
| 26 | return { manifest, protocol, report, aggregate: reports => aggregateShards(reports, manifest, identity.sourceSHA) }; |
| 27 | } |
| 28 | |
| 29 | test("full screening preserves three independent complete shards", () => { |
| 30 | const { aggregate, report } = fixture("full"); |
| 31 | const result = aggregate([report(3), report(1), report(2)]); |
| 32 | assert.equal(result.verdict, "PASS"); |
| 33 | assert.equal(result.protocolComplete, true); |
| 34 | assert.equal(result.screeningLevel, "full"); |
| 35 | assert.deepEqual(result.processes.map(run => run.process), [1, 2, 3]); |
| 36 | assert.ok(result.processes.every(run => run.attribution.reasons.includes("heap-retainer-and-control-evidence-required"))); |
| 37 | }); |
| 38 | |
| 39 | test("short screening is one complete 32/128 shard and remains explicitly labeled", () => { |
| 40 | const { aggregate, report } = fixture("short"); |
| 41 | assert.equal(completeShard(report(1)), true); |
| 42 | const result = aggregate([report(1)]); |
| 43 | assert.equal(result.verdict, "PASS"); |
| 44 | assert.equal(result.screeningLevel, "short"); |
| 45 | assert.equal(result.protocol.shards, 1); |
| 46 | assert.equal(result.processes.length, 1); |
| 47 | }); |
| 48 | |
| 49 | test("each profile rejects another profile's shard count and protocol", () => { |
| 50 | const full = fixture("full"); |
| 51 | const short = fixture("short"); |
| 52 | assert.throws(() => full.aggregate([full.report(1)]), /3 complete/); |
| 53 | assert.throws(() => short.aggregate([short.report(1), short.report(1)]), /1 complete/); |
| 54 | assert.throws(() => aggregateShards([short.report(1)], full.manifest, identity.sourceSHA)); |
| 55 | assert.throws(() => memoryProtocol("unknown"), /unknown App memory profile/); |
| 56 | }); |
| 57 | |
| 58 | for (const [name, mutate] of [ |
| 59 | ["duplicate shard", reports => { reports[2] = fixture("full").report(1); }], |
| 60 | ["different commit", reports => { reports[1].identity.sourceSHA = "b".repeat(40); }], |
| 61 | ["different build", reports => { reports[1].identity.buildSHA256 = "other-build"; }], |
| 62 | ["dirty source", reports => { reports[1].identity.sourceStatus = " M source.ts"; }], |
| 63 | ["another workflow attempt", reports => { reports[1].shard.executionId = "123:2"; }], |
| 64 | ["different fixture", reports => { reports[1].fixtures.windowed.label = "short-fixture"; }], |
| 65 | ["old protocol", reports => { reports[1].protocol.version = 3; }], |
| 66 | ["short cycles", reports => { reports[1].cycles = 127; }], |
| 67 | ["missing checkpoint", reports => { reports[1].processes[0].samples.splice(5, 1); }], |
| 68 | ["missing heap snapshot", reports => { reports[1].processes[0].snapshots.pop(); }], |
| 69 | ["different browser", reports => { reports[1].processes[0].browser = "another-browser"; }], |
| 70 | ["missing checks", reports => { reports[1].processes[0].checks = {}; }], |
| 71 | ["page error", reports => { reports[1].processes[0].metrics.pageErrors.push("boom"); }], |
| 72 | ["persistent DOM drift despite claimed pass", reports => { reports[1].processes[0].samples.at(-1).dom.nodes++; }], |
| 73 | ["unfinished operations despite claimed pass", reports => { reports[1].processes[0].samples.at(-1).lifecycle.activeOperations++; }], |
| 74 | ["missing token identity", reports => { delete reports[1].processes[0].samples[0].lifecycle.liveRenderTokenIds; }], |
| 75 | ]) test(`aggregate rejects ${name}`, () => { |
| 76 | const { aggregate, report } = fixture("full"); |
| 77 | const reports = [report(1), report(2), report(3)]; |
| 78 | mutate(reports); |
| 79 | assert.throws(() => aggregate(reports)); |
| 80 | }); |
| 81 | |
| 82 | test("requested head and manifest protocol must match", () => { |
| 83 | const { manifest, report } = fixture("full"); |
| 84 | assert.throws(() => aggregateShards([report(1), report(2), report(3)], manifest, "wrong-head")); |
| 85 | assert.throws(() => aggregateShards([report(1), report(2), report(3)], { ...manifest, protocol: { ...manifest.protocol, viewport: { width: 1, height: 1 } } }, identity.sourceSHA)); |
| 86 | }); |
| 87 |