feat(FN-3234): refactor test-changed script and add workflow tests

This merge refactors the test-changed script and adds new CLI tests (FN-3234), keeping automation on the full-suite path. It includes a new test file for the root test command, updates to the CI workflow, and documentation improvements.

Fusion-Task-Id: FN-3234
This commit is contained in:
Fusion
2026-05-03 05:42:16 -07:00
committed by gsxdsm
parent 4ed60ba889
commit 4d98b12243
8 changed files with 332 additions and 22 deletions

View File

@@ -103,11 +103,12 @@ describe("CI workflow (.github/workflows/ci.yml)", () => {
});
it("keeps contributing docs aligned with verification and slow-lane contracts", () => {
expect(contributingContent).toContain("pnpm test` must be runnable in a clean worktree without requiring a prior `pnpm build`.");
expect(contributingContent).toContain("pnpm test:full` must be runnable in a clean worktree without requiring a prior `pnpm build`.");
expect(contributingContent).toContain("`pnpm verify:workspace` is the canonical pre-merge gate");
expect(contributingContent).toContain("1. `pnpm lint`");
expect(contributingContent).toContain("2. `pnpm test`");
expect(contributingContent).toContain("2. `pnpm test:full`");
expect(contributingContent).toContain("3. `pnpm build`");
expect(contributingContent).toContain("`pnpm test` now uses a changed-only entrypoint");
expect(contributingContent).toContain("pnpm test:slow-cli");
expect(contributingContent).toContain("test:pre-release");
@@ -154,6 +155,35 @@ describe("CI workflow (.github/workflows/ci.yml)", () => {
});
});
describe("PR checks workflow (.github/workflows/pr-checks.yml)", () => {
let workflow: any;
let content: string;
let steps: any[];
beforeAll(() => {
const result = loadWorkflow("pr-checks.yml");
workflow = result.parsed;
content = result.content;
steps = workflow.jobs?.checks?.steps ?? [];
});
it("is valid YAML", () => {
expect(workflow).toBeDefined();
expect(typeof workflow).toBe("object");
});
it("runs on pull requests targeting main", () => {
expect(workflow.on?.pull_request?.branches).toContain("main");
});
it("runs explicit full-suite tests (not changed-only root test)", () => {
const testStep = steps.find((step: any) => step.name?.includes("Test"));
expect(testStep).toBeDefined();
expect(testStep.run).toBe("pnpm test:full");
expect(content).not.toContain("run: pnpm test\n");
});
});
describe("Version & Release workflow (.github/workflows/version.yml)", () => {
let workflow: any;
let content: string;

View File

@@ -114,19 +114,18 @@ describe("Scoped @fusion/* packages publishing config", () => {
describe("Workspace bootstrap script contract", () => {
const rootPkg = loadRootPackageJson();
it("keeps root test self-sufficient (no implicit pre-build dependency)", () => {
const testScript = rootPkg.scripts?.test;
expect(testScript).toBeDefined();
expect(testScript).toContain("pnpm -r");
expect(testScript).not.toContain("pnpm build");
it("makes root test changed-only while keeping explicit full-suite command", () => {
expect(rootPkg.scripts?.test).toBe("node scripts/test-changed.mjs");
expect(rootPkg.scripts?.["test:full"]).toContain("pnpm -r --workspace-concurrency=2 test");
expect(rootPkg.scripts?.["test:full"]).not.toContain("pnpm build");
});
it("defines verify:workspace in lint -> test -> build order", () => {
it("defines verify:workspace in lint -> test:full -> build order", () => {
const verifyScript = rootPkg.scripts?.["verify:workspace"];
expect(verifyScript).toBe("pnpm lint && pnpm test && pnpm build");
expect(verifyScript).toBe("pnpm lint && pnpm test:full && pnpm build");
const lintIdx = verifyScript.indexOf("pnpm lint");
const testIdx = verifyScript.indexOf("pnpm test");
const testIdx = verifyScript.indexOf("pnpm test:full");
const buildIdx = verifyScript.indexOf("pnpm build");
expect(lintIdx).toBeGreaterThanOrEqual(0);

View File

@@ -0,0 +1,57 @@
import { describe, expect, it } from "vitest";
import {
decideExecutionPlan,
resolveAffectedPackages,
shouldForceFullSuite,
} from "../../../../scripts/test-changed.mjs";
describe("root test command changed-only planning", () => {
it("uses changed mode when package-only changes are detected", () => {
const packageMap = new Map([
["core", "@fusion/core"],
["engine", "@fusion/engine"],
]);
const plan = decideExecutionPlan({
forceFullSuite: false,
comparisonBase: "abc123",
changedFiles: ["packages/core/src/store.ts", "packages/engine/src/index.ts"],
packageNameByDir: packageMap,
});
expect(plan).toEqual({ mode: "changed", packages: ["@fusion/core", "@fusion/engine"] });
});
it("falls back to full suite when shared test infra changes", () => {
const plan = decideExecutionPlan({
forceFullSuite: false,
comparisonBase: "abc123",
changedFiles: ["scripts/test-with-lock.mjs"],
packageNameByDir: new Map([["core", "@fusion/core"]]),
});
expect(plan).toEqual({ mode: "full", reason: "shared-infra-changed" });
});
it("falls back to full suite when comparison base cannot be resolved", () => {
const plan = decideExecutionPlan({
forceFullSuite: false,
comparisonBase: null,
changedFiles: null,
packageNameByDir: new Map(),
});
expect(plan).toEqual({ mode: "full", reason: "missing-comparison-base" });
});
it("treats unknown package directories as full-suite fallback", () => {
const resolved = resolveAffectedPackages(["packages/unknown/src/index.ts"], new Map());
expect(resolved).toBeNull();
});
it("marks root workflow/config changes as full-suite triggers", () => {
expect(shouldForceFullSuite([".github/workflows/ci.yml"])).toBe(true);
expect(shouldForceFullSuite(["package.json"])).toBe(true);
expect(shouldForceFullSuite(["packages/core/src/store.ts"])).toBe(false);
});
});