268 lines
13 KiB
JavaScript
268 lines
13 KiB
JavaScript
import { mkdtemp, mkdir, writeFile } from "node:fs/promises";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
import {
|
|
failureCount,
|
|
gates,
|
|
readinessDocsArchivedHandoffDirectory,
|
|
readinessDocsReadinessInventoryScanRoots,
|
|
readinessDocsStatusAuthorityContracts,
|
|
readinessDocsTruthContracts,
|
|
renderReport,
|
|
reportPath,
|
|
runReadinessDocsDocTruthGuard,
|
|
runReadinessDocsReadinessInventory,
|
|
runReadinessDocsStatusAuthorityGuard,
|
|
} from "./verify-readiness-docs-baseline.mjs";
|
|
|
|
const focusedSmokeTargets = ["scripts/check-api-inventory.test.ts", "apps/web/app/application/featureManifests.test.ts"];
|
|
|
|
describe("Readiness Docs baseline runner configuration", () => {
|
|
it("defines the required non-E2E gates and report path", () => {
|
|
expect(reportPath).toMatch(/\.tc\/baselines\/readiness-docs\/baseline-health\.md$/);
|
|
expect(gates.map((gate) => gate.id)).toEqual([
|
|
"typecheck",
|
|
"lint",
|
|
"vitest-readinessDocs-smoke",
|
|
"readinessDocs-status-authority-guard",
|
|
"readinessDocs-doc-truth-guard",
|
|
"readinessDocs-docs-readiness-inventory",
|
|
]);
|
|
expect(gates.some((gate) => gate.args?.some((arg) => /playwright|cypress|browser|e2e/i.test(arg)))).toBe(false);
|
|
expect(readinessDocsReadinessInventoryScanRoots).toEqual([
|
|
"docs/porting-guide.md",
|
|
"docs/porting-verification.md",
|
|
"docs/production-checklist.md",
|
|
"docs/feature-inventory.md",
|
|
"docs/env-access-inventory.md",
|
|
"docs/deployment.md",
|
|
]);
|
|
});
|
|
|
|
it("keeps focused smoke tests in the Vitest gate", () => {
|
|
const vitestGate = gates.find((gate) => gate.id === "vitest-readinessDocs-smoke");
|
|
|
|
expect(vitestGate?.args).toEqual(["exec", "vitest", "run", ...focusedSmokeTargets]);
|
|
});
|
|
});
|
|
|
|
describe("Readiness 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-readinessDocs-smoke", "Tests 2 failed | 8 passed", "", 1)).toBe(2);
|
|
expect(failureCount("vitest-readinessDocs-smoke", "Test Files 1 failed | 2 passed", "", 1)).toBe(1);
|
|
expect(failureCount("vitest-readinessDocs-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("readinessDocs-docs-readiness-inventory", "total matches: 12", "", 1)).toBe(0);
|
|
});
|
|
});
|
|
|
|
describe("Readiness Docs status authority guard", () => {
|
|
it("requires explicit authority markers on historical and canonical status docs", async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "readinessDocs-authority-guard-"));
|
|
|
|
for (const contract of readinessDocsStatusAuthorityContracts) {
|
|
const absolutePath = join(root, contract.path);
|
|
await mkdir(join(absolutePath, ".."), { recursive: true });
|
|
await writeFile(absolutePath, contract.requiredMarkers.join("\n\n"), "utf8");
|
|
}
|
|
|
|
const guard = await runReadinessDocsStatusAuthorityGuard(root);
|
|
|
|
expect(guard.checked).toEqual(readinessDocsStatusAuthorityContracts.map((contract) => contract.path));
|
|
expect(guard.failures).toEqual([]);
|
|
});
|
|
|
|
it("fails when authority markers drift out of the readiness docs", async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "readinessDocs-authority-guard-fail-"));
|
|
await mkdir(join(root, "docs", "improvement-plan"), { recursive: true });
|
|
await writeFile(join(root, "docs", "improvement-plan", "PROGRESS.md"), "# Progress\n", "utf8");
|
|
|
|
const guard = await runReadinessDocsStatusAuthorityGuard(root);
|
|
|
|
expect(guard.failures).toContain('docs/improvement-plan/PROGRESS.md: missing marker "READINESS_AUTHORITY: non-authoritative historical tracker"');
|
|
expect(guard.failures).toContain("docs/improvement-plan/BLOCKERS.md: missing authority-controlled document");
|
|
expect(guard.failures).toContain("docs/improvement-plan/OPERATOR-CHECKLIST.md: missing authority-controlled document");
|
|
expect(guard.failures).toContain("docs/improvement-plan/CURRENT-STATUS.md: missing authority-controlled document");
|
|
});
|
|
});
|
|
|
|
describe("Readiness Docs doc truth guard", () => {
|
|
it("requires current truth markers and historical handoff warnings", async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "readinessDocs-doc-truth-"));
|
|
|
|
for (const contract of readinessDocsTruthContracts) {
|
|
const absolutePath = join(root, contract.path);
|
|
await mkdir(join(absolutePath, ".."), { recursive: true });
|
|
await writeFile(absolutePath, contract.requiredMarkers.join("\n\n"), "utf8");
|
|
}
|
|
|
|
const handoffPath = join(root, readinessDocsArchivedHandoffDirectory, "HANDOFF-2026-05-20.md");
|
|
await mkdir(join(handoffPath, ".."), { recursive: true });
|
|
await writeFile(
|
|
handoffPath,
|
|
["Historical snapshot only", "Do not treat its deploy instructions as current work.", "Use OPERATOR-CHECKLIST.md for current work."].join("\n"),
|
|
"utf8"
|
|
);
|
|
|
|
const guard = await runReadinessDocsDocTruthGuard(root);
|
|
|
|
expect(guard.checked).toContain("docs/features/email-sequences.md");
|
|
expect(guard.checked).toContain("docs/improvement-plan/archive/handoffs/HANDOFF-2026-05-20.md");
|
|
expect(guard.failures).toEqual([]);
|
|
});
|
|
|
|
it("fails on stale current-status drift and unmarked archived handoffs", async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "readinessDocs-doc-truth-fail-"));
|
|
await mkdir(join(root, "docs", "features"), { recursive: true });
|
|
await writeFile(join(root, "docs", "features", "email-sequences.md"), "# Email Sequences\nproduction ready\n", "utf8");
|
|
await mkdir(join(root, readinessDocsArchivedHandoffDirectory), { recursive: true });
|
|
await writeFile(join(root, readinessDocsArchivedHandoffDirectory, "HANDOFF-2026-05-20.md"), "# Handoff\nDeploy now\n", "utf8");
|
|
|
|
const guard = await runReadinessDocsDocTruthGuard(root);
|
|
|
|
expect(guard.failures).toContain('docs/features/email-sequences.md: missing marker "foundation/admin-preview"');
|
|
expect(guard.failures).toContain("docs/improvement-plan/BLOCKERS.md: missing doc-truth-controlled document");
|
|
expect(guard.failures).toContain("docs/improvement-plan/archive/handoffs/HANDOFF-2026-05-20.md: missing historical snapshot marker");
|
|
expect(guard.failures).toContain('docs/improvement-plan/archive/handoffs/HANDOFF-2026-05-20.md: missing archived-handoff marker "OPERATOR-CHECKLIST.md"');
|
|
});
|
|
});
|
|
|
|
describe("Readiness Docs docs readiness inventory scan", () => {
|
|
it("scans configured docs, counts readiness terms, and ignores dependency, generated, and TC paths", async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "readinessDocs-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", "porting-guide.md"), "Readiness baseline verification health production-ready", "utf8");
|
|
await writeFile(join(root, "docs", "porting-verification.md"), "Deploy Docker Coolify Gitea registry workflow CI production release", "utf8");
|
|
await writeFile(
|
|
join(root, "docs", "production-checklist.md"),
|
|
"env environment secret DATABASE_URL DIRECT_URL REDIS_URL BULLMQ_REDIS_URL MAILERSEND_API_KEY TOKEN credential access",
|
|
"utf8"
|
|
);
|
|
await writeFile(
|
|
join(root, "docs", "feature-inventory.md"),
|
|
"feature manifest plugin plugin-candidate core remove-candidate inventory boundary extraction",
|
|
"utf8"
|
|
);
|
|
await writeFile(join(root, "docs", "env-access-inventory.md"), "TODO TBD FIXME XXX follow-up manual missing required risk blocked", "utf8");
|
|
await writeFile(join(root, "docs", "deployment.md"), "ready deploy environment feature TODO", "utf8");
|
|
await writeFile(join(root, "docs", "node_modules", "ignored", "ignored.md"), "ready deploy secret feature TODO", "utf8");
|
|
await writeFile(join(root, "docs", ".tc", "ignored.md"), "ready deploy secret feature TODO", "utf8");
|
|
await writeFile(join(root, "docs", "generated", "ignored.md"), "ready deploy secret feature TODO", "utf8");
|
|
|
|
const inventory = await runReadinessDocsReadinessInventory(root);
|
|
const allMatches = inventory.matches.join("\n");
|
|
|
|
expect(inventory.roots).toEqual(readinessDocsReadinessInventoryScanRoots);
|
|
expect(inventory.missingRoots).toEqual([]);
|
|
expect(inventory.filesScanned).toBe(6);
|
|
expect(inventory.counts).toMatchObject({
|
|
"readiness-signal": 6,
|
|
"deployment-signal": 11,
|
|
"environment-signal": 12,
|
|
"feature-plugin-signal": 10,
|
|
"risk-marker": 11,
|
|
});
|
|
expect(allMatches).toContain("docs/porting-guide.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 docs roots without failing the read-only inventory gate", async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "readinessDocs-baseline-missing-"));
|
|
const inventory = await runReadinessDocsReadinessInventory(root);
|
|
|
|
expect(inventory.roots).toEqual([]);
|
|
expect(inventory.missingRoots).toEqual(readinessDocsReadinessInventoryScanRoots);
|
|
expect(inventory.filesScanned).toBe(0);
|
|
expect(inventory.totalMatches).toBe(0);
|
|
expect(inventory.counts).toMatchObject({
|
|
"readiness-signal": 0,
|
|
"deployment-signal": 0,
|
|
"environment-signal": 0,
|
|
"feature-plugin-signal": 0,
|
|
"risk-marker": 0,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("Readiness 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: "readinessDocs-doc-truth-guard",
|
|
command: "static doc truth guard",
|
|
exitCode: 1,
|
|
durationMs: 20,
|
|
failureCount: 1,
|
|
docTruthGuard: {
|
|
checked: ["docs/features/email-sequences.md"],
|
|
failures: ["docs/features/email-sequences.md: missing marker"],
|
|
},
|
|
fingerprint: { hash: "abc456abc456", excerpt: "failures: 1" },
|
|
},
|
|
{
|
|
id: "readinessDocs-docs-readiness-inventory",
|
|
command: "read-only Readiness Docs docs readiness inventory",
|
|
exitCode: 0,
|
|
durationMs: 42,
|
|
failureCount: 0,
|
|
inventory: {
|
|
roots: ["docs/porting-guide.md"],
|
|
missingRoots: ["docs/deployment.md"],
|
|
filesScanned: 1,
|
|
counts: { "readiness-signal": 1, "risk-marker": 2 },
|
|
totalMatches: 3,
|
|
matches: [],
|
|
},
|
|
fingerprint: { hash: "fed456abc123", excerpt: "readiness-signal: 1" },
|
|
},
|
|
]);
|
|
|
|
expect(report).toContain("# Readiness Docs Baseline Health");
|
|
expect(report).toContain("non-E2E baseline gates for Readiness Docs baseline");
|
|
expect(report).toContain("readiness, deployment, environment, feature/plugin, and risk-marker terms");
|
|
expect(report).toContain("## typecheck");
|
|
expect(report).toContain("## readinessDocs-docs-readiness-inventory");
|
|
expect(report).toContain("- Exitcode: 2");
|
|
expect(report).toContain("- Failure Count: 7");
|
|
expect(report).toContain("- Output Fingerprint: abc123def456");
|
|
expect(report).toContain("- Doc Truth Docs Checked: docs/features/email-sequences.md");
|
|
expect(report).toContain("- Doc Truth Guard Failures: 1");
|
|
expect(report).toContain(" - docs/features/email-sequences.md: missing marker");
|
|
expect(report).toContain("- Inventory Roots: docs/porting-guide.md");
|
|
expect(report).toContain("- Missing Roots Skipped: docs/deployment.md");
|
|
expect(report).toContain(" - readiness-signal: 1");
|
|
expect(report).toContain("- Total Inventory Matches: 3");
|
|
});
|
|
});
|