import { mkdtemp, mkdir, rm, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { describe, expect, it } from "vitest"; import { failureCount, gates, portingDocsDocumentationInventoryScanRoots, renderReport, reportPath, runPortingDocsDocumentationInventory, } from "./verify-porting-docs-baseline.mjs"; const focusedSmokeTargets = ["scripts/check-api-inventory.test.ts", "apps/web/app/application/featureManifests.test.ts"]; describe("Porting Docs baseline runner configuration", () => { it("defines the required non-E2E gates and report path", () => { expect(reportPath).toMatch(/\.tc\/baselines\/porting-docs\/baseline-health\.md$/); expect(gates.map((gate) => gate.id)).toEqual(["typecheck", "lint", "vitest-portingDocs-smoke", "portingDocs-documentation-porting-inventory"]); expect(gates.some((gate) => gate.args?.some((arg) => /playwright|cypress|browser|e2e/i.test(arg)))).toBe(false); expect(portingDocsDocumentationInventoryScanRoots).toEqual([ "docs/configuration.md", "docs/deployment.md", "docs/db-ci.md", "docs/mail-auth.md", "docs/env-access-inventory.md", "docs/feature-inventory.md", ]); }); it("keeps focused smoke tests in the Vitest gate", () => { const vitestGate = gates.find((gate) => gate.id === "vitest-portingDocs-smoke"); expect(vitestGate?.args).toEqual(["exec", "vitest", "run", ...focusedSmokeTargets]); }); }); describe("Porting Docs baseline runner failure counting", () => { it("counts failed typecheck diagnostics conservatively", () => { expect(failureCount("typecheck", "src/a.ts(1,1): error TS2307: Cannot find module\nsrc/b.ts(2,2): error TS2322: Type mismatch", "", 1)).toBe(2); expect(failureCount("typecheck", "Build failed", "", 1)).toBe(1); }); it("counts failed ESLint diagnostics conservatively", () => { expect(failureCount("lint", "✖ 4 problems (3 errors, 1 warning)", "", 1)).toBe(3); expect(failureCount("lint", "2 errors and 0 warnings", "", 1)).toBe(2); expect(failureCount("lint", "Lint crashed", "", 1)).toBe(1); }); it("counts failed Vitest diagnostics conservatively", () => { expect(failureCount("vitest-portingDocs-smoke", "Tests 2 failed | 8 passed", "", 1)).toBe(2); expect(failureCount("vitest-portingDocs-smoke", "Test Files 1 failed | 2 passed", "", 1)).toBe(1); expect(failureCount("vitest-portingDocs-smoke", "Vitest crashed", "", 1)).toBe(1); }); it("returns zero for clean exits and read-only inventory gates", () => { expect(failureCount("lint", "warning only", "", 0)).toBe(0); expect(failureCount("portingDocs-documentation-porting-inventory", "total matches: 12", "", 1)).toBe(0); }); }); describe("Porting Docs documentation inventory scan", () => { it("scans configured docs, counts porting terms, and ignores dependency and TC paths", async () => { const root = await mkdtemp(join(tmpdir(), "portingDocs-baseline-inventory-")); await mkdir(join(root, "docs"), { recursive: true }); await mkdir(join(root, "docs", "node_modules", "ignored"), { recursive: true }); await mkdir(join(root, "docs", ".tc"), { recursive: true }); await mkdir(join(root, "docs", "generated"), { recursive: true }); await writeFile( join(root, "docs", "configuration.md"), [ "# Configuration for toolctrl-boilerplate", "ToolCTRL uses @toolctrl/mail and https://tool-ctrl.de as placeholders.", "DATABASE_URL PostgreSQL Prisma Supabase Redis BULLMQ_REDIS_URL REDIS_URL", "MailerSend MAILERSEND_API_KEY webhook signing Svix Postmark", "Gitea Coolify Docker deployment registry CONTAINER_REGISTRY workflow_dispatch toolctrl-ci", "branding logo favicon terms privacy imprint legal copyright trademark", "TODO TBD FIXME XXX follow-up Follow-up", ].join("\n"), "utf8", ); await writeFile(join(root, "docs", "deployment.md"), "SaasRock and saasrock.com appear in deployment docs.", "utf8"); await writeFile(join(root, "docs", "db-ci.md"), "Prisma database DATABASE_URL", "utf8"); await writeFile(join(root, "docs", "node_modules", "ignored", "ignored.md"), "ToolCTRL MailerSend TODO", "utf8"); await writeFile(join(root, "docs", ".tc", "ignored.md"), "ToolCTRL MailerSend TODO", "utf8"); await writeFile(join(root, "docs", "generated", "ignored.md"), "ToolCTRL MailerSend TODO", "utf8"); const inventory = await runPortingDocsDocumentationInventory(root); const allMatches = inventory.matches.join("\n"); expect(inventory.roots).toEqual(["docs/configuration.md", "docs/deployment.md", "docs/db-ci.md"]); expect(inventory.missingRoots).toEqual(["docs/mail-auth.md", "docs/env-access-inventory.md", "docs/feature-inventory.md"]); expect(inventory.filesScanned).toBe(3); expect(inventory.counts).toMatchObject({ "project-package-name": 6, "domain-reference": 2, "database-provider-term": 10, "mail-provider-term": 5, "gitea-coolify-deployment-term": 9, "legal-branding-term": 9, "todo-tbd-marker": 6, }); expect(allMatches).toContain("docs/configuration.md"); expect(allMatches).toContain("docs/deployment.md"); expect(allMatches).not.toContain("node_modules/ignored/ignored.md"); expect(allMatches).not.toContain(".tc/ignored.md"); expect(allMatches).not.toContain("generated/ignored.md"); }); it("records absent documentation roots for fail-closed inventory diagnostics", async () => { const root = await mkdtemp(join(tmpdir(), "portingDocs-baseline-missing-")); await mkdir(join(root, "docs"), { recursive: true }); await writeFile(join(root, "docs", "configuration.md"), "ToolCTRL MailerSend TODO", "utf8"); await rm(join(root, "docs", "configuration.md")); const inventory = await runPortingDocsDocumentationInventory(root); expect(inventory.roots).toEqual([]); expect(inventory.missingRoots).toEqual(portingDocsDocumentationInventoryScanRoots); expect(inventory.filesScanned).toBe(0); expect(inventory.totalMatches).toBe(0); }); }); describe("Porting Docs report rendering", () => { it("renders gate metadata, fingerprints, and inventory counts", () => { const report = renderReport([ { id: "typecheck", command: "pnpm run typecheck", exitCode: 2, durationMs: 1500, failureCount: 7, fingerprint: { hash: "abc123def456", excerpt: "error TS2322" } }, { id: "portingDocs-documentation-porting-inventory", command: "read-only Porting Docs documentation porting inventory", exitCode: 0, durationMs: 42, failureCount: 0, inventory: { roots: ["docs/configuration.md"], missingRoots: ["docs/deployment.md"], filesScanned: 1, counts: { "project-package-name": 1, "mail-provider-term": 2 }, totalMatches: 3, matches: [], }, fingerprint: { hash: "fed456abc123", excerpt: "project-package-name: 1" }, }, ]); expect(report).toContain("# Porting Docs Baseline Health"); expect(report).toContain("non-E2E baseline gates for Porting Docs baseline"); expect(report).toContain("porting-relevant project/package names"); expect(report).toContain("## typecheck"); expect(report).toContain("## portingDocs-documentation-porting-inventory"); expect(report).toContain("- Exitcode: 2"); expect(report).toContain("- Failure Count: 7"); expect(report).toContain("- Output Fingerprint: abc123def456"); expect(report).toContain("- Inventory Roots: docs/configuration.md"); expect(report).toContain("- Missing Roots Skipped: docs/deployment.md"); expect(report).toContain(" - project-package-name: 1"); expect(report).toContain("- Total Inventory Matches: 3"); }); });