Files
HiveGrid-Wireframes/hivegrid-cloud/scripts/verify-release-docs-baseline.test.mjs
T

215 lines
10 KiB
JavaScript

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,
releaseDocsReleaseDocumentationInventoryPatterns,
releaseDocsReleaseDocumentationInventoryScanRoots,
renderReport,
reportPath,
runReleaseDocsReleaseDocumentationInventory,
} from "./verify-release-docs-baseline.mjs";
const focusedTargets = ["scripts/verify-release-docs-baseline.test.mjs", "scripts/verify-ci-baseline.test.mjs", "scripts/verify-production-docs.test.mjs"];
const forbiddenCommandPattern = /playwright|cypress|browser|e2e/i;
describe("Release Docs baseline runner configuration", () => {
it("defines the required non-E2E gates and report path", () => {
expect(reportPath).toMatch(/\.tc\/baselines\/release-docs\/baseline-health\.md$/);
expect(gates.map((gate) => gate.id)).toEqual(["typecheck", "lint", "vitest-releaseDocs-focused", "releaseDocs-release-docs-inventory"]);
expect(gates.some((gate) => [gate.command, ...(gate.args ?? [])].some((arg) => forbiddenCommandPattern.test(arg)))).toBe(false);
expect(releaseDocsReleaseDocumentationInventoryScanRoots).toEqual([
"CHANGELOG.md",
"docs/versioning.md",
"docs/upgrade.md",
"docs/releases",
"docs/migration-registry.md",
"docs/release-checklist.md",
"docs/boilerplate-version-tracking.md",
"docs/production-checklist.md",
"docs/upgrades.md",
"README.md",
]);
});
it("keeps exact focused Vitest targets and excludes browser/E2E commands", () => {
const vitestGate = gates.find((gate) => gate.id === "vitest-releaseDocs-focused");
expect(vitestGate?.args).toEqual(["exec", "vitest", "run", ...focusedTargets]);
expect(vitestGate?.args?.join(" ")).not.toMatch(forbiddenCommandPattern);
});
it("defines release-documentation inventory term buckets", () => {
expect(releaseDocsReleaseDocumentationInventoryPatterns.map(({ id }) => id)).toEqual([
"semver-versioning",
"breaking-change-policy",
"changelog",
"release-checklist",
"upgrade-guide",
"migration-registry",
"boilerplate-version-tracking",
"production-readiness",
"docs-link-command",
"todo-tbd-marker",
]);
});
});
describe("Release 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-releaseDocs-focused", "Tests 2 failed | 8 passed", "", 1)).toBe(2);
expect(failureCount("vitest-releaseDocs-focused", "Test Files 1 failed | 2 passed", "", 1)).toBe(1);
expect(failureCount("vitest-releaseDocs-focused", "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("releaseDocs-release-docs-inventory", "total matches: 12", "", 1)).toBe(0);
});
});
describe("Release Docs release documentation inventory scan", () => {
it("recognizes S03 release documentation roots and contract term buckets in the repository", async () => {
const inventory = await runReleaseDocsReleaseDocumentationInventory(process.cwd());
const allMatches = inventory.matches.join("\n");
expect(inventory.roots).toEqual(expect.arrayContaining(["docs/upgrade.md", "docs/releases", "docs/migration-registry.md"]));
expect(inventory.counts["release-checklist"]).toBeGreaterThan(0);
expect(inventory.counts["upgrade-guide"]).toBeGreaterThan(0);
expect(inventory.counts["migration-registry"]).toBeGreaterThan(0);
expect(allMatches).toContain("docs/upgrade.md");
expect(allMatches).toContain("docs/releases/README.md");
expect(allMatches).toContain("docs/releases/2026-01.md");
expect(allMatches).toContain("docs/migration-registry.md");
});
it("scans configured roots, counts release-contract terms, and ignores dependency/generated/TC paths", async () => {
const root = await mkdtemp(join(tmpdir(), "releaseDocs-baseline-inventory-"));
await mkdir(join(root, "docs", "releases"), { 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, "CHANGELOG.md"),
[
"# Changelog",
"Keep a Changelog with Added Changed Deprecated Removed Fixed Security sections.",
"Semver semantic versioning major minor patch version policy.",
"Breaking change deprecation compatibility backward compatible notes.",
"Release checklist pre-release post-release cut release tag artifact.",
].join("\n"),
"utf8",
);
await writeFile(join(root, "docs", "versioning.md"), "Semver versioning and breaking-change policy for every release.", "utf8");
await writeFile(join(root, "docs", "upgrade.md"), "Upgrade guide migration guide rollout rollback steps.", "utf8");
await writeFile(join(root, "docs", "releases", "2026-01.md"), "Release checklist artifact and tag with production readiness.", "utf8");
await writeFile(join(root, "docs", "migration-registry.md"), "Migration registry migration id migration entry owner status deadline.", "utf8");
await writeFile(join(root, "docs", "boilerplate-version-tracking.md"), "Boilerplate version template version source version ported project downstream project baseline version.", "utf8");
await writeFile(join(root, "docs", "production-checklist.md"), "Production checklist deployment CI non-E2E verification manual runtime.", "utf8");
await writeFile(join(root, "README.md"), "See [upgrade](docs/upgrade.md) and run `pnpm run verify:release-docs-baseline`. TODO follow-up", "utf8");
await writeFile(join(root, "docs", "node_modules", "ignored", "ignored.md"), "TODO changelog release", "utf8");
await writeFile(join(root, "docs", ".tc", "ignored.md"), "TODO changelog release", "utf8");
await writeFile(join(root, "docs", "generated", "ignored.md"), "TODO changelog release", "utf8");
const inventory = await runReleaseDocsReleaseDocumentationInventory(root);
const allMatches = inventory.matches.join("\n");
expect(inventory.roots).toEqual([
"CHANGELOG.md",
"docs/versioning.md",
"docs/upgrade.md",
"docs/releases",
"docs/migration-registry.md",
"docs/boilerplate-version-tracking.md",
"docs/production-checklist.md",
"README.md",
]);
expect(inventory.missingRoots).toEqual(["docs/release-checklist.md", "docs/upgrades.md"]);
expect(inventory.filesScanned).toBe(8);
expect(inventory.counts).toMatchObject({
"semver-versioning": 12,
"breaking-change-policy": 6,
changelog: 8,
"release-checklist": 9,
"upgrade-guide": 9,
"migration-registry": 6,
"boilerplate-version-tracking": 6,
"production-readiness": 7,
"docs-link-command": 2,
"todo-tbd-marker": 2,
});
expect(inventory.totalMatches).toBe(67);
expect(allMatches).toContain("CHANGELOG.md");
expect(allMatches).toContain("docs/releases/2026-01.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 release-documentation roots for fail-closed inventory diagnostics", async () => {
const root = await mkdtemp(join(tmpdir(), "releaseDocs-baseline-missing-"));
await mkdir(join(root, "docs"), { recursive: true });
await writeFile(join(root, "README.md"), "Release", "utf8");
await rm(join(root, "README.md"));
const inventory = await runReleaseDocsReleaseDocumentationInventory(root);
expect(inventory.roots).toEqual([]);
expect(inventory.missingRoots).toEqual(releaseDocsReleaseDocumentationInventoryScanRoots);
expect(inventory.filesScanned).toBe(0);
expect(inventory.totalMatches).toBe(0);
});
});
describe("Release 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: "releaseDocs-release-docs-inventory",
command: "read-only Release Docs release documentation inventory",
exitCode: 0,
durationMs: 42,
failureCount: 0,
inventory: {
roots: ["CHANGELOG.md"],
missingRoots: ["docs/upgrade.md"],
filesScanned: 1,
counts: { "semver-versioning": 1, changelog: 2 },
totalMatches: 3,
matches: [],
},
fingerprint: { hash: "fed456abc123", excerpt: "semver-versioning: 1" },
},
]);
expect(report).toContain("# Release Docs Baseline Health");
expect(report).toContain("non-E2E baseline gates for Release Docs baseline");
expect(report).toContain("release-contract readiness terms");
expect(report).toContain("## typecheck");
expect(report).toContain("## releaseDocs-release-docs-inventory");
expect(report).toContain("- Exitcode: 2");
expect(report).toContain("- Failure Count: 7");
expect(report).toContain("- Output Fingerprint: abc123def456");
expect(report).toContain("- Inventory Roots: CHANGELOG.md");
expect(report).toContain("- Missing Roots Skipped: docs/upgrade.md");
expect(report).toContain(" - semver-versioning: 1");
expect(report).toContain("- Total Inventory Matches: 3");
});
});