Files
e3edcc8afa feat: v2.3.0 add interactive platform selection to init (#38)
* feat: add interactive platform selection to init

* chore: remove completed OpenSpec artifacts

* chore: remove implemented design note

* fix: protect init lifecycle from unsafe config paths

* fix: support clearing init platforms from CLI

* docs: retire config command from repository rules

* docs: align init lifecycle and clear-platform contracts

* docs: keep repository guidance current-state focused

* docs: limit cleanup to repository-native documents

* docs: preserve historical version notes

---------

Co-authored-by: MerlinH <merlinh221@gmail.com>
2026-07-31 18:54:10 +10:00

236 lines
7.4 KiB
TypeScript

import { afterEach, describe, it } from "node:test";
import { expect } from "expect";
import { writeTruthmarkConfig } from "../helpers/truthmark-config.js";
import { buildImpactSet } from "../../src/impact/build.js";
import { runInit } from "../../src/init/init.js";
import { createTempRepo, type TempRepo } from "../helpers/temp-repo.js";
describe("buildImpactSet", () => {
const repos: TempRepo[] = [];
afterEach(async () => {
await Promise.all(repos.splice(0).map((repo) => repo.cleanup()));
});
it("maps changed files to routes, truth docs, and tests", async () => {
const repo = await createTempRepo();
repos.push(repo);
await repo.writeFile(
"src/math.ts",
"export function add(left: number, right: number) { return left + right; }\n",
);
await repo.writeFile(
"tests/math.test.ts",
"import { add } from '../src/math.js';\n",
);
await writeTruthmarkConfig(repo.rootDir);
await runInit(repo.rootDir);
await repo.runGit(["add", "."]);
await repo.runGit(["commit", "-m", "initial"]);
await repo.writeFile(
"src/math.ts",
"export function add(left: number, right: number) { return left + right + 0; }\n",
);
const impact = await buildImpactSet(repo.rootDir, { base: "main" });
expect(impact.schemaVersion).toBe("impact-set/v0");
expect(impact.changedFiles).toContainEqual(
expect.objectContaining({ path: "src/math.ts", status: "modified" }),
);
expect(impact.affectedTruthDocs.length).toBeGreaterThan(0);
expect(impact.affectedTests).toContain("tests/math.test.ts");
expect(impact).not.toHaveProperty("changedPublicSymbols");
});
it("maps changed routed truth docs to affected routes and truth docs", async () => {
const repo = await createTempRepo();
repos.push(repo);
await repo.writeFile("src/index.ts", "export const value = 1;\n");
await writeTruthmarkConfig(repo.rootDir);
await runInit(repo.rootDir);
await repo.runGit(["add", "."]);
await repo.runGit(["commit", "-m", "initial"]);
const truthDocPath =
"docs/truthmark/engineering/repository/bootstrap-routing.md";
await repo.writeFile(
truthDocPath,
`${await repo.readFile(truthDocPath)}\nUpdated direct truth-doc edit.\n`,
);
const impact = await buildImpactSet(repo.rootDir, { base: "main" });
expect(impact.changedFiles).toContainEqual(
expect.objectContaining({ path: truthDocPath, status: "modified" }),
);
expect(impact.affectedTruthDocs).toContain(truthDocPath);
expect(
impact.affectedRoutes.some((route) =>
route.truthDocs.includes(truthDocPath),
),
).toBe(true);
});
it("reports when a base ref cannot be compared", async () => {
const repo = await createTempRepo();
repos.push(repo);
await repo.writeFile("src/index.ts", "export const value = 1;\n");
await writeTruthmarkConfig(repo.rootDir);
const impact = await buildImpactSet(repo.rootDir, { base: "missing-ref" });
expect(impact.diagnostics).toContainEqual(
expect.objectContaining({
category: "impact",
severity: "error",
message: expect.stringContaining(
"Unable to compare base ref missing-ref",
),
}),
);
});
it("does not report TypeScript public-symbol diagnostics for route-owned changes", async () => {
const repo = await createTempRepo();
repos.push(repo);
await repo.writeFile("src/index.ts", "export const value = 1;\n");
await writeTruthmarkConfig(repo.rootDir);
await runInit(repo.rootDir);
await repo.runGit(["add", "."]);
await repo.runGit(["commit", "-m", "initial"]);
await repo.writeFile("src/index.ts", "export const changedValue = 2;\n");
const impact = await buildImpactSet(repo.rootDir, { base: "main" });
expect(impact).not.toHaveProperty("changedPublicSymbols");
expect(impact.diagnostics).not.toContainEqual(
expect.objectContaining({
category: "impact",
severity: "review",
file: "src/index.ts",
message: expect.stringContaining(
"affected truth docs but none were changed",
),
}),
);
});
it("treats changed test files as affected tests instead of unrouted code", async () => {
const repo = await createTempRepo();
repos.push(repo);
await repo.writeFile("src/index.ts", "export const value = 1;\n");
await repo.writeFile(
"tests/index.test.ts",
"import { value } from '../src/index.js';\n",
);
await writeTruthmarkConfig(repo.rootDir);
await runInit(repo.rootDir);
await repo.runGit(["add", "."]);
await repo.runGit(["commit", "-m", "initial"]);
await repo.writeFile(
"tests/index.test.ts",
"import { value } from '../src/index.js';\nvoid value;\n",
);
const impact = await buildImpactSet(repo.rootDir, { base: "main" });
expect(impact.affectedTests).toContain("tests/index.test.ts");
expect(impact.diagnostics).not.toContainEqual(
expect.objectContaining({
category: "impact",
file: "tests/index.test.ts",
}),
);
});
it("selects package-level tests for changed package files", async () => {
const repo = await createTempRepo();
repos.push(repo);
await repo.writeFile(
"src/repo-index/package-metadata.ts",
"export const manager = 'npm';\n",
);
await repo.writeFile(
"tests/repo-index/build.test.ts",
"import { describe, it } from 'node:test';\ndescribe('repo index package', () => { it('builds', () => undefined); });\n",
);
await writeTruthmarkConfig(repo.rootDir);
await runInit(repo.rootDir);
await repo.runGit(["add", "."]);
await repo.runGit(["commit", "-m", "initial"]);
await repo.writeFile(
"src/repo-index/package-metadata.ts",
"export const manager = 'pnpm';\n",
);
const impact = await buildImpactSet(repo.rootDir, { base: "main" });
expect(impact.affectedTests).toContain("tests/repo-index/build.test.ts");
});
it("keeps old and new route ownership for renamed files", async () => {
const repo = await createTempRepo();
repos.push(repo);
await repo.writeFile("src/old/api.ts", "export const oldApi = 1;\n");
await repo.writeFile("src/new/.gitkeep", "\n");
await writeTruthmarkConfig(repo.rootDir);
await runInit(repo.rootDir);
await repo.writeFile(
"docs/truthmark/routes/areas.md",
`# Truthmark Areas
## Old
Truth documents:
- docs/truthmark/truth/old.md
Code surface:
- src/old/**
Update truth when:
- old behavior changes
## New
Truth documents:
- docs/truthmark/truth/new.md
Code surface:
- src/new/**
Update truth when:
- new behavior changes
`,
);
await repo.writeFile("docs/truthmark/truth/old.md", "# Old\n");
await repo.writeFile("docs/truthmark/truth/new.md", "# New\n");
await repo.runGit(["add", "."]);
await repo.runGit(["commit", "-m", "initial"]);
await repo.runGit(["branch", "baseline"]);
await repo.runGit(["mv", "src/old/api.ts", "src/new/api.ts"]);
await repo.runGit(["commit", "-m", "move api"]);
const impact = await buildImpactSet(repo.rootDir, { base: "baseline" });
expect(impact.changedFiles).toContainEqual(
expect.objectContaining({
path: "src/new/api.ts",
previousPath: "src/old/api.ts",
status: "renamed",
}),
);
expect(impact.affectedTruthDocs).toEqual([
"docs/truthmark/truth/new.md",
"docs/truthmark/truth/old.md",
]);
expect(impact).not.toHaveProperty("changedPublicSymbols");
});
});