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

343 lines
15 KiB
JavaScript

import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import assert from "node:assert/strict";
import { execFileSync } from "node:child_process";
import { afterEach, describe, it } from "vitest";
import { formatProductionDocsDocsResult, verifyProductionDocsDocs } from "./verify-production-docs.mjs";
const fixtures = [];
const VALID_DOCS = {
"README.md": [
"# Project",
"See [Quickstart](docs/quickstart.md), [New Project](docs/new-project.md), and [Local Dev](docs/local-dev.md).",
"Run Corepack with pnpm@10.33.0 before installing dependencies.",
].join("\n"),
"docs/quickstart.md": [
"# Quickstart",
"Create `.env` from `.env.example` with placeholder values and no production secrets.",
"Use DATABASE_URL for PostgreSQL and Prisma setup.",
"Run `pnpm seed`, then `pnpm dev`, and open localhost:3000.",
].join("\n"),
"docs/new-project.md": [
"# New Project",
"Set WORKER_ENABLED and BULLMQ_REDIS_URL only when optional Redis worker behavior is in scope.",
"Browser, Playwright, Cypress, and E2E checks are manual unless explicitly approved.",
].join("\n"),
"docs/local-dev.md": [
"# Local Development",
"Use focused non-E2E verification during local development.",
"Troubleshooting covers PostgreSQL, Prisma, seed, and Redis issues.",
].join("\n"),
"docs/env-vars.md": [
"# Environment Variables",
"Environment and .env keys include API_KEY and TOKEN style secrets, but diagnostics must use redacted or masked output.",
"Production troubleshooting must never print raw secrets or secret values.",
].join("\n"),
"docs/database.md": [
"# Database Operations",
"Database PostgreSQL operations cover migration deploy steps, status health checks, backup routines, and restore notes.",
].join("\n"),
"docs/mail.md": [
"# Mail",
"MailerSend handles outbound email and mail delivery.",
"Webhook events require signing signature verification before processing.",
].join("\n"),
"docs/worker.md": [
"# Worker Runtime",
"WORKER_ENABLED controls worker enablement with BULLMQ_REDIS_URL or Redis configured for queue jobs.",
"Queue health diagnostics stay redacted for production failure investigation.",
"Separate Coolify workers include worker-general, worker-mail, and worker-plugin. The permanent mail worker uses WORKER_QUEUE_GROUP=mail; docs/ops/worker-queues.md is the canonical topology reference.",
].join("\n"),
"docs/api.md": [
"# API Documentation",
"The production API requires authentication with Bearer tokens or API key credentials.",
"OpenAPI and swagger references describe endpoints and every response includes request-id correlation through X-Request-ID.",
].join("\n"),
"docs/deployment.md": [
"# Deployment",
"Deployment uses a Docker container image published for Coolify release rollout.",
"Manual-only runtime checks document the start command and service boundary before traffic is switched.",
"Coolify patches pre_deployment_command for `pnpm exec prisma migrate deploy` and post_deployment_command for `node packages/db/dist/production-seed.server.js`.",
].join("\n"),
"docs/deployments/boiler-saas-ctrl-com.md": [
"# boiler.saas-ctrl.com",
"Coolify patches pre_deployment_command for `cd /myapp && pnpm exec prisma migrate deploy --config prisma.config.ts`.",
"Coolify patches post_deployment_command for `cd /myapp && NODE_ENV=production node packages/db/dist/production-seed.server.js`.",
"Manual-only runtime checks document Coolify image containers, release rollout, start command, and service boundary.",
].join("\n"),
"docs/security.md": [
"# Security",
"Security headers include CSP and HSTS guidance.",
"CORS allowed origins are explicit, and TOKEN, API_KEY, and other secrets are masked or redacted in diagnostics.",
].join("\n"),
"docs/observability.md": [
"# Observability",
"Observability diagnostics are redacted for health and readiness checks.",
"Queue and job inspection supports incident failure triage without exposing raw values.",
].join("\n"),
"docs/plugins.md": [
"# Plugin Candidate Boundaries",
"Plugin candidates are extraction contracts, not already-packaged plugins.",
"They document core, plugin-candidate, and remove-candidate boundaries with non-E2E verification.",
].join("\n"),
"docs/upgrades.md": [
"# Upgrade Runbook",
"Upgrade guidance keeps pnpm@10.33.0 pinned while reviewing Prisma database, runtime, and provider changes.",
"The CI-safe workflow runs non-E2E checks including verify:docs-links before manual runtime review.",
].join("\n"),
"docs/troubleshooting.md": [
"# Production Troubleshooting Runbook",
"This troubleshooting runbook defines triage order for production incidents.",
"Safe evidence collection records redacted diagnostics only and never raw secrets.",
].join("\n"),
};
function writeFixtureFile(rootDir, relativePath, content) {
const absolutePath = path.join(rootDir, relativePath);
fs.mkdirSync(path.dirname(absolutePath), { recursive: true });
fs.writeFileSync(absolutePath, content);
}
function createFixture(overrides = {}) {
const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "productionDocs-docs-"));
fixtures.push(rootDir);
for (const [relativePath, content] of Object.entries(VALID_DOCS)) {
if (overrides[relativePath] === null) continue;
writeFixtureFile(rootDir, relativePath, overrides[relativePath] ?? content);
}
return rootDir;
}
afterEach(() => {
while (fixtures.length > 0) {
fs.rmSync(fixtures.pop(), { recursive: true, force: true });
}
});
describe("verify-production-docs", () => {
it("passes for documentation that satisfies the Production Docs production documentation contract", () => {
const rootDir = createFixture();
const result = verifyProductionDocsDocs(rootDir);
const output = formatProductionDocsDocsResult(result);
assert.deepEqual(result.failures, []);
assert.match(output, /^PASS Production Docs production documentation verification/);
assert.match(output, /16 required documentation files/);
assert.match(output, /docs\/api\.md/);
assert.match(output, /docs\/deployment\.md/);
assert.match(output, /docs\/deployments\/boiler-saas-ctrl-com\.md/);
assert.match(output, /docs\/security\.md/);
assert.match(output, /docs\/observability\.md/);
assert.match(output, /docs\/plugins\.md/);
assert.match(output, /docs\/upgrades\.md/);
assert.match(output, /docs\/troubleshooting\.md/);
});
it("fails when a required documentation file is missing", () => {
const rootDir = createFixture({ "docs/worker.md": null });
const result = verifyProductionDocsDocs(rootDir);
assert.match(result.failures.join("\n"), /docs\/worker\.md: missing required documentation file/);
});
it("fails when an S04 documentation entrypoint is missing", () => {
const rootDir = createFixture({ "docs/api.md": null });
const result = verifyProductionDocsDocs(rootDir);
assert.match(result.failures.join("\n"), /docs\/api\.md: missing required documentation file/);
});
it("fails when forbidden placeholder markers are present", () => {
const rootDir = createFixture({ "docs/quickstart.md": `${VALID_DOCS["docs/quickstart.md"]}\nTODO fill later` });
const result = verifyProductionDocsDocs(rootDir);
assert.match(result.failures.join("\n"), /docs\/quickstart\.md:\d+:\d+: forbidden placeholder marker "TODO"/);
});
it("fails when a required readiness term group is incomplete", () => {
const rootDir = createFixture({
"docs/new-project.md": "# New Project\nManual browser checks are explicitly approved only.",
"docs/worker.md": "# Worker Runtime\nQueue health diagnostics stay redacted for production failure investigation.",
});
const result = verifyProductionDocsDocs(rootDir);
assert.match(result.failures.join("\n"), /required term group "optional-worker-redis"/);
});
it("fails when S03 mail operations are incomplete", () => {
const rootDir = createFixture({ "docs/mail.md": "# Mail\nOutbound email delivery uses redacted production diagnostics." });
const result = verifyProductionDocsDocs(rootDir);
assert.match(result.failures.join("\n"), /required term group "s03-mailersend-boundaries"/);
});
it("fails when S04 API documentation coverage is incomplete", () => {
const rootDir = createFixture({ "docs/api.md": "# API Documentation\nThe production API requires authentication only." });
const result = verifyProductionDocsDocs(rootDir);
assert.match(result.failures.join("\n"), /required term group "s04-api-auth-openapi-request-id"/);
});
it("fails when S04 deployment documentation coverage is incomplete", () => {
const rootDir = createFixture();
const result = verifyProductionDocsDocs(rootDir, {
termGroups: [
{
id: "s04-deployment-image-coolify-runtime",
description: "deployment image, Coolify, and manual runtime boundaries",
terms: [/\bdeploy(?:ment)?\b|\brelease\b/i, /\bmust-not-exist-in-fixture\b/i],
},
],
});
assert.match(result.failures.join("\n"), /required term group "s04-deployment-image-coolify-runtime"/);
});
it("fails when deployment docs combine seed into the pre-deploy command slot", () => {
const rootDir = createFixture({
"docs/deployment.md": [
"# Deployment",
"Deployment uses a Docker container image published for Coolify release rollout.",
"Manual-only runtime checks document the start command and service boundary before traffic is switched.",
"Pre-deploy command: `cd /myapp && pnpm exec prisma migrate deploy --config prisma.config.ts && NODE_ENV=production node packages/db/dist/production-seed.server.js`.",
].join("\n"),
});
const result = verifyProductionDocsDocs(rootDir);
assert.match(result.failures.join("\n"), /docs\/deployment\.md: must document Coolify post_deployment_command as the guarded seed slot/);
assert.match(result.failures.join("\n"), /docs\/deployment\.md: must not describe the guarded seed as combined into the pre-deploy command/);
});
it("fails when separate worker group documentation is incomplete", () => {
const rootDir = createFixture({
"docs/worker.md": "# Worker Runtime\nWORKER_ENABLED controls worker enablement with BULLMQ_REDIS_URL or Redis configured for queue jobs.\nQueue health diagnostics stay redacted for production failure investigation.",
});
const result = verifyProductionDocsDocs(rootDir);
assert.match(result.failures.join("\n"), /required term group "s04-separate-worker-groups"/);
});
it("fails when live docs repeat superseded status blockers", () => {
const rootDir = createFixture({
"docs/worker.md": `${VALID_DOCS["docs/worker.md"]}\nRemaining gap: persistent Coolify mail-worker service.`,
});
const result = verifyProductionDocsDocs(rootDir);
assert.match(result.failures.join("\n"), /superseded B-014 mail-worker blocker wording/);
});
it("fails when plugin docs claim production enablement before signoff", () => {
const rootDir = createFixture({
"docs/plugins.md": [
"# Plugin Candidate Boundaries",
"Plugin candidates are extraction contracts, not already-packaged plugins.",
"Third-party plugin execution is production-enabled with non-E2E verification.",
].join("\n"),
});
const result = verifyProductionDocsDocs(rootDir);
assert.match(result.failures.join("\n"), /third-party plugin execution must stay gated/);
});
it("fails when S04 security documentation coverage is incomplete", () => {
const rootDir = createFixture({ "docs/security.md": "# Security\nSecurity headers are configured." });
const result = verifyProductionDocsDocs(rootDir);
assert.match(result.failures.join("\n"), /required term group "s04-security-headers-cors-secrets"/);
});
it("fails when S04 observability documentation coverage is incomplete", () => {
const rootDir = createFixture();
const result = verifyProductionDocsDocs(rootDir, {
termGroups: [
{
id: "s04-observability-diagnostics-health-queue-triage",
description: "redacted diagnostics, health/readiness, queue inspection, and failure triage",
terms: [/\bobservability\b/i, /\bmust-not-exist-in-fixture\b/i],
},
],
});
assert.match(result.failures.join("\n"), /required term group "s04-observability-diagnostics-health-queue-triage"/);
});
it("fails when plugin candidate documentation implies packaged plugins", () => {
const rootDir = createFixture({ "docs/plugins.md": "# Plugin Candidate Boundaries\nPlugin candidates are installed packages with non-E2E verification." });
const result = verifyProductionDocsDocs(rootDir);
assert.match(result.failures.join("\n"), /required term group "s05-plugin-candidates"/);
});
it("fails when upgrade guidance is incomplete", () => {
const rootDir = createFixture();
const result = verifyProductionDocsDocs(rootDir, {
termGroups: [
{
id: "s05-upgrade-guidance",
description: "upgrade runbook covers pinned pnpm, database/runtime/provider upgrades, and non-E2E checks",
terms: [/\bupgrade(?:s|d)?\b/i, /\bmust-not-exist-in-fixture\b/i],
},
],
});
assert.match(result.failures.join("\n"), /required term group "s05-upgrade-guidance"/);
});
it("fails when troubleshooting runbook redaction guidance is incomplete", () => {
const rootDir = createFixture({ "docs/troubleshooting.md": "# Production Troubleshooting Runbook\nTriage order exists." });
const result = verifyProductionDocsDocs(rootDir);
assert.match(result.failures.join("\n"), /required term group "s05-troubleshooting-runbook"/);
});
it("fails when docs link-check CI guidance is absent", () => {
const rootDir = createFixture({ "docs/upgrades.md": "# Upgrade Runbook\nUpgrade guidance keeps pnpm@10.33.0 pinned while reviewing Prisma database changes.\nUse non-E2E checks before manual runtime review." });
const result = verifyProductionDocsDocs(rootDir);
assert.match(result.failures.join("\n"), /required term group "s05-docs-link-check-ci"/);
});
it("fails for automatic E2E and browser command recommendations but allows manual notes", () => {
const rootDir = createFixture({
"docs/local-dev.md": `${VALID_DOCS["docs/local-dev.md"]}\nManual Playwright review may be recorded.\n\`pnpm exec playwright test\``,
});
const result = verifyProductionDocsDocs(rootDir);
assert.match(result.failures.join("\n"), /forbidden automatic browser\/E2E command \(playwright-test-command\)/);
assert.doesNotMatch(result.failures.join("\n"), /Manual Playwright review/);
});
it("exits non-zero from the CLI when checks fail", () => {
const rootDir = createFixture({ "README.md": "" });
assert.throws(
() => execFileSync(process.execPath, [path.resolve("scripts/verify-production-docs.mjs")], { cwd: rootDir, encoding: "utf8", stdio: "pipe" }),
/documentation file is empty/,
);
});
});