fix(FN-5048): keep Fusion test verification bounded

This commit is contained in:
gsxdsm
2026-06-21 10:05:12 -07:00
parent 7e478fb473
commit ce90cc9b62
8 changed files with 202 additions and 9 deletions

View File

@@ -366,6 +366,56 @@ test("decideExecutionPlan: expands changed packages with reverse dependents", ()
assert.deepEqual(plan.packages, ["@fusion/core", "@fusion/engine", "@fusion/dashboard"]);
});
// FNXC:TestInfrastructure 2026-06-21-10:42: a foundational-package edit must NOT
// reverse-expand into a whole-workspace vitest sweep. Cap to the directly changed
// package and delegate reverse-dependent coverage to the merge-gate suite.
test("decideExecutionPlan: foundational-package edit reverse-blast is capped to direct packages", () => {
// 10-package workspace where @fusion/core is depended on by 8 others (>=60%).
const dependents = ["p1", "p2", "p3", "p4", "p5", "p6", "p7", "p8"];
const reverseDependencyMap = new Map([
["@fusion/core", dependents],
...dependents.map((d) => [d, []]),
["@fusion/standalone", []],
]);
const plan = decideExecutionPlan({
forceFullSuite: false,
comparisonBase: "abc123",
changedFiles: ["packages/core/src/store.ts"],
packageNameByDir: basePackageMap,
reverseDependencyMap,
});
assert.equal(plan.mode, "changed");
assert.equal(plan.reason, "reverse-dependent-blast-capped");
assert.deepEqual(plan.packages, ["@fusion/core"]);
});
// A leaf-ish change with only a couple of dependents in a large workspace must
// still expand normally — the cap is for foundational blast, not any expansion.
test("decideExecutionPlan: narrow reverse-dependent expansion is NOT capped", () => {
const reverseDependencyMap = new Map([
["@fusion/engine", ["@fusion/dashboard"]],
["@fusion/dashboard", []],
["@fusion/core", []],
["p1", []],
["p2", []],
["p3", []],
["p4", []],
["p5", []],
]);
const plan = decideExecutionPlan({
forceFullSuite: false,
comparisonBase: "abc123",
changedFiles: ["packages/engine/src/index.ts"],
packageNameByDir: basePackageMap,
reverseDependencyMap,
});
assert.equal(plan.mode, "changed");
assert.equal(plan.reason, undefined);
assert.deepEqual(plan.packages, ["@fusion/engine", "@fusion/dashboard"]);
});
test("decideExecutionPlan: no affected package resolved → gate", () => {
const plan = decideExecutionPlan({
forceFullSuite: false,

View File

@@ -27,6 +27,12 @@ governs hand-written source.
//
// Run `node scripts/check-file-line-count.mjs --update` to rewrite the baseline
// after an intentional, reviewed change to the set of oversized files.
//
// FNXC:TestInfrastructure 2026-06-21-10:00:
// Line-count drift remains visible through the explicit check:line-count audit,
// but it must not block `pnpm test` from reaching the real test runner. The test
// preflight owns fast safety checks; broad god-file cleanup is tracked separately
// so unrelated task completion is not stuck before tests start.
import { readFileSync, writeFileSync } from "node:fs";
import { spawnSync } from "node:child_process";
import { fileURLToPath, URL } from "node:url";

View File

@@ -0,0 +1,32 @@
#!/usr/bin/env node
import { globSync } from "node:fs";
import { spawn } from "node:child_process";
/*
FNXC:TestInfrastructure 2026-06-21-10:00:
Script-test verification must honor forwarded file arguments so targeted checks stay fast inside Fusion tasks.
The old package script always expanded scripts/__tests__/*.test.mjs before forwarded args, turning `pnpm test:scripts -- scripts/__tests__/x.test.mjs` into the full script suite and making task completion look stalled.
*/
const forwarded = process.argv.slice(2).filter((arg) => arg !== "--");
const testFiles = forwarded.length > 0
? forwarded
: globSync("scripts/__tests__/*.test.mjs").sort();
const child = spawn(process.execPath, ["--test", ...testFiles], {
stdio: "inherit",
});
child.on("exit", (code, signal) => {
if (signal) {
process.kill(process.pid, signal);
return;
}
process.exit(code ?? 1);
});
child.on("error", (error) => {
console.error(error);
process.exit(1);
});

View File

@@ -1209,6 +1209,17 @@ const fullSuiteEnv = {
FUSION_TEST_CONCURRENCY: process.env.FUSION_TEST_CONCURRENCY || String(concurrency),
};
/*
FNXC:TestInfrastructure 2026-06-21-10:42:
Reverse-dependent blast cap thresholds. A foundational-package edit (e.g.
@fusion/core) reverse-expands to ~the whole workspace; capping past 60% of a
workspace of at least 8 packages keeps a one-line core edit from triggering a
25-package vitest sweep, while leaving leaf-package expansion (a few dependents)
and small synthetic test fixtures untouched.
*/
export const WIDE_REVERSE_DEPENDENT_FRACTION = 0.6;
export const MIN_WORKSPACE_FOR_BLAST_CAP = 8;
export function decideExecutionPlan({
forceFullSuite,
comparisonBase,
@@ -1228,12 +1239,34 @@ export function decideExecutionPlan({
const affectedPackages = resolveAffectedPackages(changedFiles, packageNameByDir);
if (!affectedPackages || affectedPackages.length === 0) return { mode: "gate", reason: "no-affected-package" };
return {
mode: "changed",
packages: reverseDependencyMap
? expandWithReverseDependents(affectedPackages, reverseDependencyMap)
: affectedPackages,
};
if (!reverseDependencyMap) return { mode: "changed", packages: affectedPackages };
const expanded = expandWithReverseDependents(affectedPackages, reverseDependencyMap);
/*
FNXC:TestInfrastructure 2026-06-21-10:42:
Cap the reverse-dependent fan-out for foundational-package edits. A single
`@fusion/core` source change reverse-expands to ~the entire workspace (every
package imports core), so `pnpm test` bundled all 25 packages into one
`vitest --changed` invocation that ran for the full 20-min `changed`-class
watchdog ceiling and pinned the task (the engine runs project testCommand
"pnpm test" as its verification gate; on timeout the task fully restarts and
re-runs the sweep, stacking into hours). When expansion balloons past most of
a real (non-fixture) workspace, test only the DIRECTLY changed packages scoped
and delegate cross-cutting reverse-dependent coverage to the merge-gate suite,
which already runs first in changed mode and is the project's thin/trusted net.
Guarded by MIN_WORKSPACE_FOR_BLAST_CAP so tiny synthetic maps still expand fully.
*/
const totalPackages = reverseDependencyMap.size;
const expandedBeyondDirect = expanded.length > affectedPackages.length;
const isWideBlast =
totalPackages >= MIN_WORKSPACE_FOR_BLAST_CAP &&
expanded.length >= Math.ceil(totalPackages * WIDE_REVERSE_DEPENDENT_FRACTION);
if (expandedBeyondDirect && isWideBlast) {
return { mode: "changed", packages: affectedPackages, reason: "reverse-dependent-blast-capped" };
}
return { mode: "changed", packages: expanded };
}
/**
@@ -1467,6 +1500,13 @@ export async function main(argv = process.argv.slice(2)) {
label: "test:gate (pre-affected)",
});
if (plan.reason === "reverse-dependent-blast-capped") {
// FNXC:TestInfrastructure 2026-06-21-10:42: surface the cap so coverage is never silently dropped.
console.log(
"[test-changed] reverse-dependent fan-out capped: a foundational-package edit reverse-expanded to most of the workspace. " +
"Testing only the directly changed packages scoped; cross-cutting reverse-dependent coverage is delegated to the merge-gate suite (ran above).",
);
}
console.log(`[test-changed] running tests for changed packages: ${activePackages.join(", ")}`);
if (cachedPackages.length > 0) {
console.log(`[test-changed] skipping cached packages: ${cachedPackages.join(", ")}`);