import { execFileSync } from "node:child_process"; import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { afterEach, describe, expect, it } from "vitest"; const root = process.cwd(); const verifierPath = path.join(root, "scripts/verify-first-approved-small-feature-cut.mjs"); let fixtureRoot; function writeFixtureFile(relativePath, content = "") { const filePath = path.join(fixtureRoot, relativePath); fs.mkdirSync(path.dirname(filePath), { recursive: true }); fs.writeFileSync(filePath, content); } function createPassingFixture() { fixtureRoot = fs.mkdtempSync(path.join(os.tmpdir(), "first-approved-small-feature-cut-")); writeFixtureFile("apps/web/app/routes/index.tsx", "export default function Index() { return null; }\n"); writeFixtureFile("apps/web/app/modules/blog/README.md", "Blog runtime has been removed.\n"); writeFixtureFile("docs/feature-inventory.md", "Feature Decision/S06 BlogApi remove-from-core note: the orphan Blog API stub is removed.\n"); writeFixtureFile("docs/feature-manifests/remove-candidates.json", JSON.stringify({ notes: ["Feature Decision/S06 BlogApi remove-from-core note: orphan API stub removed."] }, null, 2)); return fixtureRoot; } function runVerifier(targetRoot = fixtureRoot) { return execFileSync(process.execPath, [verifierPath, targetRoot], { cwd: root, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], }); } describe("verify-first-approved-small-feature-cut", () => { afterEach(() => { if (fixtureRoot) { fs.rmSync(fixtureRoot, { recursive: true, force: true }); fixtureRoot = undefined; } }); it("passes for a fixture with the BlogApi cut applied and documented", () => { createPassingFixture(); expect(runVerifier()).toContain("PASS first approved small feature cut verification"); }); it("fails when the removed BlogApi.ts file exists", () => { createPassingFixture(); writeFixtureFile("apps/web/app/utils/api/.server/BlogApi.ts", "export class BlogApi {}\n"); expect(() => runVerifier()).toThrow(/removed Blog API file still exists: apps\/web\/app\/utils\/api\/\.server\/BlogApi\.ts/); }); it("fails when an app source file imports the blog module", () => { createPassingFixture(); writeFixtureFile("apps/web/app/routes/stale.tsx", "import { loader } from \"~\/modules\/blog\";\nexport { loader };\n"); expect(() => runVerifier()).toThrow(/stale Blog source reference: apps\/web\/app\/routes\/stale\.tsx:1 references ~\/modules\/blog/); }); });