FN-8626: validate committed test timing snapshot

Add an automated guard that keeps CI test-sharding timings usable.

- Validate snapshot structure, freshness, and recorded test-file paths.
- Confirm planning loads the snapshot and shard dry-runs use it without stale warnings.

Files changed:
 scripts/__tests__/ci-test-shard-timings.test.mjs | 50 +++++++++++++++++++++++-
 1 file changed, 49 insertions(+), 1 deletion(-)

Fusion-Task-Id: FN-8626

Fusion-Task-Lineage: ada6525f-8dcc-4494-8586-3aa2e41618f3

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
gsxdsm
2026-07-26 20:42:30 -07:00
parent 3f33cb000f
commit f1a2d9ae1f

View File

@@ -7,9 +7,11 @@
import test from "node:test";
import assert from "node:assert/strict";
import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, rmSync } from "node:fs";
import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, rmSync, existsSync } from "node:fs";
import { tmpdir } from "node:os";
import path from "node:path";
import { spawnSync } from "node:child_process";
import { fileURLToPath } from "node:url";
import {
bucketDuration,
@@ -18,9 +20,13 @@ import {
buildTimingsSnapshot,
writeTimings,
TIMINGS_SNAPSHOT_RELATIVE,
TIMINGS_STALENESS_DAYS,
discoverWorkspaceTimingFiles,
loadPlanningTimings,
} from "../ci-test-shard.mjs";
const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
const PACKAGES = [
{ name: "@fusion/core", dir: "packages/core" },
{ name: "@fusion/engine", dir: "packages/engine" },
@@ -42,6 +48,48 @@ function tmpRoot() {
return mkdtempSync(path.join(tmpdir(), "fusion-timings-test-"));
}
/*
* FNXC:CITestSharding 2026-07-27-03:20:
* The committed timing snapshot must remain current and refer only to live test
* files. Stale or phantom-path entries silently degrade CI shard balancing and
* can hide merge-gate regressions, so this guard fails before that drift lands.
*/
test("committed timing snapshot is fresh, parseable, and references live test files", () => {
const snapshotPath = path.join(REPO_ROOT, TIMINGS_SNAPSHOT_RELATIVE);
const snapshot = JSON.parse(readFileSync(snapshotPath, "utf8"));
assert.ok(snapshot.packages && typeof snapshot.packages === "object");
assert.ok(Object.keys(snapshot.packages).length > 0, "snapshot must contain package timings");
const capturedAtMs = new Date(snapshot.capturedAt).getTime();
assert.ok(Number.isFinite(capturedAtMs), `capturedAt must be a valid ISO timestamp: ${snapshot.capturedAt}`);
assert.ok(capturedAtMs <= Date.now(), `capturedAt must not be future-dated: ${snapshot.capturedAt}`);
assert.ok(
(Date.now() - capturedAtMs) / 86_400_000 <= TIMINGS_STALENESS_DAYS,
`capturedAt exceeds the ${TIMINGS_STALENESS_DAYS}-day staleness budget: ${snapshot.capturedAt}`,
);
const recordedFiles = Object.values(snapshot.packages).flatMap((pkg) => Object.keys(pkg.files ?? {}));
const missingFiles = recordedFiles.filter((file) => !existsSync(path.join(REPO_ROOT, file)));
assert.deepEqual(missingFiles, [], `timing snapshot references missing test files:\n${missingFiles.join("\n")}`);
const timings = loadPlanningTimings({ projectRoot: REPO_ROOT });
assert.equal(timings.present, true);
assert.equal(timings.stale, false);
assert.ok(timings.fileDurations.size > 0, "committed snapshot must expose duration-weighted files");
const dryRun = spawnSync(
process.execPath,
[path.join(REPO_ROOT, "scripts/ci-test-shard.mjs"), "--dry-run", "--total", "4"],
{ cwd: REPO_ROOT, encoding: "utf8" },
);
assert.equal(dryRun.status, 0, dryRun.stderr);
assert.doesNotMatch(
`${dryRun.stdout}\n${dryRun.stderr}`,
/timing snapshot is stale|no timing snapshot found/i,
"dry-run must use the committed duration snapshot without missing or stale warnings",
);
});
test("bucketDuration rounds to nearest 100ms, floors non-zero to one bucket", () => {
assert.equal(bucketDuration(0), 0);
assert.equal(bucketDuration(40), 100); // sub-bucket non-zero floors up