import { execFile } from "node:child_process"; import { mkdtemp, mkdir, writeFile } from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { promisify } from "node:util"; import { describe, expect, it } from "vitest"; const execFileAsync = promisify(execFile); const verifierSourcePath = path.resolve("scripts/verify-porting-guide.mjs"); async function makeRepo(guideContent) { const root = await mkdtemp(path.join(os.tmpdir(), "portingDocs-porting-guide-")); await mkdir(path.join(root, "scripts"), { recursive: true }); await mkdir(path.join(root, "docs"), { recursive: true }); await writeFile(path.join(root, "scripts/verify-porting-guide.mjs"), await import("node:fs/promises").then((fs) => fs.readFile(verifierSourcePath, "utf8"))); if (guideContent !== null) await writeFile(path.join(root, "docs/porting-guide.md"), guideContent); return root; } function validGuide(rowCount = 12) { const rows = Array.from({ length: rowCount }, (_, index) => `| legacy-${index} | preferred-${index} | when-${index} | note-${index} |`).join("\n"); return `# ToolCTRL Documentation Porting Guide ## Rename/Replace Map | Legacy | Preferred | Apply in docs when | Notes | | --- | --- | --- | --- | ${rows} ## Do-Not-Replace Rules - Keep runtime identifiers stable. ## Verification - Run the verifier. `; } async function runVerifier(root) { return execFileAsync(process.execPath, [path.join(root, "scripts/verify-porting-guide.mjs")], { cwd: root }); } describe("verify-porting-guide", () => { it("passes for a complete guide with enough rename-map rows", async () => { const root = await makeRepo(validGuide(12)); await expect(runVerifier(root)).resolves.toMatchObject({ stdout: expect.stringContaining("Verified docs/porting-guide.md: 13 Rename/Replace Map table rows."), }); }); it("fails when the guide is missing", async () => { const root = await makeRepo(null); await expect(runVerifier(root)).rejects.toMatchObject({ stderr: expect.stringContaining("missing docs/porting-guide.md") }); }); it("fails for unresolved markers and underspecified maps", async () => { const root = await makeRepo(validGuide(3).replace("- Run the verifier.", "TODO: decide later.")); await expect(runVerifier(root)).rejects.toMatchObject({ stderr: expect.stringContaining("unresolved TODO/TBD/FIXME markers") }); }); });