Files
fusion/scripts/__tests__/pg-setup-boundary-probe.test.mjs
gsxdsm 6d7b4a3ac3 FN-9140: make Vitest timeout ownership surveys deterministic
Replace ambiguous setup-boundary observations with calibrated, repeatable timeout ownership evidence.

- record fixture lifecycle events in an append-only JSONL ledger with process-safe ordering
- classify four timeout-budget arms across repeated isolate-mode cells and fail closed on incomplete evidence
- expand connectionless unit coverage and document the terminal insufficient-data survey result

Files changed:
 .../test-failures/postgres-ddl-admission-bound.md  |   2 +
 .../vitest-setup-boundary-timeout-ownership.md     |  37 +++
 docs/testing.md                                    |   2 +-
 scripts/__tests__/pg-setup-boundary-probe.test.mjs | 195 ++++++++----
 scripts/pg-setup-boundary-probe.mjs                | 341 ++++++++++++---------
 5 files changed, 371 insertions(+), 206 deletions(-)

Fusion-Task-Id: FN-9140

Fusion-Task-Lineage: 9c6970b8-af80-400a-b2b2-49718d4fe87f

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
2026-08-16 22:47:53 -07:00

154 lines
9.9 KiB
JavaScript

import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import test from "node:test";
import { URL } from "node:url";
import {
classifyArm,
decideSurveyVerdict,
deriveBudgets,
fixtureFiles,
parseLedger,
parseProbeArgs,
resolveOwnership,
resolveVitestRunner,
runFixtureCell,
runSurvey,
summarizeLedger,
summarizeRepeats,
} from "../pg-setup-boundary-probe.mjs";
const event = (overrides = {}) => ({ type: "boundary", boundary: "sample", phase: "start", pid: 1, file: "one", epochMs: 1000, monotonicNs: "100", ...overrides });
const options = { workers: 2, files: 6, repeats: 3, isolate: true, delayMs: 4000, orderingMarginMs: 50, runTimeoutMs: 60_000 };
/*
FNXC:PgSetupBoundaryProbe 2026-08-17-23:40:
FN-9140 keeps survey verification connectionless by using synthetic ledgers for ownership and
ordering. The opt-in smoke is the only test that starts Vitest, so normal script checks remain fast.
*/
test("arguments derive a discriminating matrix and reject undecidable file/worker counts", () => {
assert.deepEqual(deriveBudgets(4000), { D: 4000, SMALL: 1000, LARGE: 20000 });
assert.deepEqual(parseProbeArgs([]), { ...options, isolate: "both", json: undefined, runTimeoutMs: 74_000 });
assert.throws(() => parseProbeArgs(["--files", "2", "--workers", "2"]), /indeterminate configuration/);
assert.throws(() => parseProbeArgs(["--isolate", "maybe"]), /isolate/);
});
test("ledger parser ignores malformed or truncated final records", () => {
const text = `${JSON.stringify(event())}\n{"type":"boundary"`;
assert.deepEqual(parseLedger(text), [{ ...event(), ledgerIndex: 0 }]);
});
test("ledger granularity distinguishes worker and file execution with six files", () => {
const workerEvents = ["0", "1"].flatMap((file, pid) => [event({ pid: pid + 1, file }), event({ pid: pid + 1, file, phase: "end", epochMs: 5000, monotonicNs: "500" })]);
assert.equal(summarizeLedger("sample", workerEvents, options).granularity, "per worker");
const fileEvents = Array.from({ length: 6 }, (_, index) => event({ pid: index % 2 + 1, file: String(index) }));
assert.equal(summarizeLedger("sample", fileEvents, options).granularity, "per file");
});
test("setup-file granularity requires a real test-file identity across worker lifecycles", () => {
/*
FNXC:PgSetupBoundaryProbe 2026-08-17-05:34:
Persistent workers can execute several files; each setup callback must retain the active path.
*/
const persistentWorkers = [event({ pid: 1, file: "a.test.mjs" }), event({ pid: 1, file: "b.test.mjs" }), event({ pid: 2, file: "c.test.mjs" })];
assert.equal(summarizeLedger("sample", persistentWorkers, { ...options, files: 3 }).granularity, "per file");
/*
FNXC:PgSetupBoundaryProbe 2026-08-17-05:34:
Recycled workers may produce as many PIDs as files. Empty setup-file paths must not fake per-file proof.
*/
const recycledWorkers = [event({ pid: 11, file: "" }), event({ pid: 12, file: "" }), event({ pid: 13, file: "" })];
assert.equal(summarizeLedger("sample", recycledWorkers, { ...options, files: 3 }).granularity, "indeterminate");
});
test("ordering uses same-process monotonic time and cross-process epoch margin", () => {
const samePid = [event(), event({ type: "test", boundary: "test", pid: 1, phase: "start", monotonicNs: "200", epochMs: 900 })];
assert.equal(summarizeLedger("sample", samePid, options).ordering, "before-first-test");
const near = [event({ pid: 1, epochMs: 1000 }), event({ type: "test", boundary: "test", pid: 2, phase: "start", epochMs: 1020, monotonicNs: "1" })];
assert.equal(summarizeLedger("sample", near, options).ordering, "indeterminate");
const before = [event({ pid: 1, epochMs: 1000 }), event({ type: "test", boundary: "test", pid: 2, phase: "start", epochMs: 1100, monotonicNs: "1" })];
assert.equal(summarizeLedger("sample", before, options).ordering, "before-first-test");
assert.equal(summarizeLedger("sample", [event()], options).ordering, "not-observed");
});
test("unrecognized failures retain their diagnostic and never become off-budget", () => {
const result = classifyArm({ exitCode: 1, output: "Error: unrelated fixture failure", events: [], boundary: "sample", delayMs: 4000 });
assert.deepEqual(result, { outcome: "failed-unclassified", diagnostic: "Error: unrelated fixture failure" });
assert.equal(resolveOwnership({ A: result, B: { outcome: "passed" }, C: { outcome: "passed" }, R: { outcome: "passed" } }), "failed-unclassified");
});
test("only a duration-verified both-small pass establishes falsifying off-budget evidence", () => {
const passed = { outcome: "passed" };
assert.equal(resolveOwnership({ A: { outcome: "timed-out-test" }, B: passed, C: passed, R: passed }), "failed-unclassified");
assert.equal(resolveOwnership({ R: passed }), "completed-within-budget");
assert.equal(resolveOwnership({ A: passed, B: passed, C: passed, R: passed }), "off-budget");
const short = classifyArm({ exitCode: 0, events: [event(), event({ phase: "end", epochMs: 2000, monotonicNs: "200" })], boundary: "sample", delayMs: 4000 });
assert.equal(short.outcome, "failed-unclassified");
assert.equal(resolveOwnership({ A: passed, B: { outcome: "timed-out-test" }, C: passed, R: passed }), "failed-unclassified");
});
test("repeat disagreement, bad calibration, and indeterminate ordering force insufficient data", () => {
const stable = {
ownership: "off-budget",
arms: { A: { outcome: "passed" }, B: { outcome: "passed" }, C: { outcome: "passed" }, R: { outcome: "passed" } },
summaries: Object.fromEntries(["A", "B", "C", "R"].map((arm) => [arm, { ordering: "before-first-test", granularity: "per worker", durationMs: 4000 }])),
};
const flapping = summarizeRepeats("sample", [stable, { ...stable, ownership: "test-timeout" }]);
assert.equal(flapping.deterministic, false);
assert.equal(decideSurveyVerdict({ calibration: "passed", cells: [flapping] }), "insufficient-data");
assert.equal(decideSurveyVerdict({ calibration: "failed", cells: [{ ...stable, deterministic: true, ordering: "before-first-test", ownership: "off-budget" }] }), "insufficient-data");
assert.equal(decideSurveyVerdict({ calibration: "passed", cells: [{ ...stable, deterministic: true, ordering: "indeterminate", ownership: "off-budget" }] }), "insufficient-data");
assert.equal(decideSurveyVerdict({ calibration: "passed", cells: [{ ...stable, deterministic: true, ordering: "after-first-test", ownership: "completed-within-budget" }] }), "prerequisite-not-established");
assert.equal(decideSurveyVerdict({ calibration: "passed", cells: [{ ...stable, deterministic: true, ordering: "before-first-test", ownership: "off-budget" }] }), "prerequisite-established");
});
test("arm-level ordering and outcomes must agree across repeats", () => {
const repeat = {
ownership: "off-budget",
arms: { A: { outcome: "passed" }, B: { outcome: "passed" }, C: { outcome: "passed" }, R: { outcome: "passed" } },
summaries: Object.fromEntries(["A", "B", "C", "R"].map((arm) => [arm, { ordering: "before-first-test", granularity: "per invocation", durationMs: 4000 }])),
};
const aAfterTest = JSON.parse(JSON.stringify(repeat));
aAfterTest.summaries.A.ordering = "after-first-test";
const armFlap = JSON.parse(JSON.stringify(repeat));
armFlap.arms.B.outcome = "timed-out-test";
assert.equal(summarizeRepeats("sample", [repeat, aAfterTest]).deterministic, false);
assert.equal(summarizeRepeats("sample", [repeat, armFlap]).deterministic, false);
});
test("fixture-local config contains derived budgets and survey remains repository-inert", () => {
const fixture = fixtureFiles({ boundary: "setup-top-level-await", arm: "A", options, ledgerPath: "/tmp/ledger", vitestApiUrl: "vitest", control: undefined });
assert.match(fixture["vitest.config.mjs"], /testTimeout:1000/);
assert.match(fixture["vitest.config.mjs"], /hookTimeout:1000/);
const perFileFixture = fixtureFiles({ boundary: "setup-before-all", arm: "A", options, ledgerPath: "/tmp/ledger", vitestApiUrl: "vitest", control: undefined });
assert.match(perFileFixture["setup.mjs"], /expect\.getState\(\)\.testPath/);
const source = readFileSync(new URL("../pg-setup-boundary-probe.mjs", import.meta.url), "utf8");
assert.doesNotMatch(source, /pg-test-harness|pg-preadmission-campaign|packages\/core\/src\/__test-utils__|FUSION_PG_TEST_/);
assert.match(source, /mkdtempSync\(join\(tmpdir\(\)/);
});
test("unresolvable Vitest becomes a retained insufficient-data report", async () => {
const runner = resolveVitestRunner(() => { throw new Error("test runner unavailable"); });
assert.equal(runner.vitestVersion, "unresolvable");
const result = await runFixtureCell({ boundary: "global-setup", arm: "A", options, runner });
assert.equal(result.arm.outcome, "failed-unclassified");
assert.match(result.arm.diagnostic, /Vitest resolution failed: test runner unavailable/);
const report = await runSurvey({ ...options, repeats: 1, isolate: "true" }, runner);
assert.equal(report.calibration, "failed");
assert.equal(report.calibrationFailureReason, "classifier-uncalibrated");
assert.equal(report.verdict, "insufficient-data");
assert.match(report.runnerResolutionError, /test runner unavailable/);
assert.ok(report.cells.every((cell) => cell.ownership === "failed-unclassified"));
});
test("live fixture cells preserve setup-file identities on the installed runner", { skip: process.env.FUSION_BOUNDARY_SURVEY_LIVE !== "1" }, async () => {
const liveOptions = { ...options, files: 3, workers: 2, delayMs: 100, runTimeoutMs: 60_000, isolate: false };
const global = await runFixtureCell({ boundary: "global-setup", arm: "A", options: liveOptions });
assert.ok(global.events.length > 0);
assert.equal(global.arm.outcome, "passed");
const setup = await runFixtureCell({ boundary: "setup-before-all", arm: "R", options: liveOptions });
const summary = summarizeLedger("setup-before-all", setup.events, liveOptions);
assert.equal(setup.arm.outcome, "passed");
assert.equal(summary.granularity, "per file");
assert.equal(summary.filesObserved, 3);
});