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

379 lines
14 KiB
JavaScript

import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import assert from "node:assert/strict";
import { afterEach, describe, it } from "vitest";
import { formatHealthBaselineResult, verifyHealthBaseline } from "./verify-health-baseline.mjs";
const VALID_DOCKERFILE = `FROM node:20-bookworm-slim AS base
FROM base AS build
FROM base AS worker
HEALTHCHECK CMD node apps/worker/dist/health.js
CMD [ "node", "apps/worker/dist/index.js" ]
FROM base AS api
HEALTHCHECK CMD curl -fsS "http://127.0.0.1:\${API_PORT:-\${PORT}}/healthz"
CMD [ "pnpm", "--filter", "@toolctrl/api", "start" ]
FROM base AS web
HEALTHCHECK CMD curl -fsS "http://127.0.0.1:\${PORT}/healthz"
ENTRYPOINT [ "./start.sh" ]
`;
const VALID_WEB_HEALTHCHECK = `export const loader = async () => {
return new Response("OK");
};
`;
const VALID_WEB_HEALTHZ = `export const loader = async () => {
return Response.json({ status: "ok", service: "web", revision: process.env.APP_REVISION || "unknown" }, { status: 200 });
};
`;
const VALID_WEB_READYZ = `import { db } from "~/utils/db.server";
export const loader = async () => {
try {
await db.appConfiguration.count();
return Response.json({ status: "ready", service: "web" }, { status: 200 });
} catch {
return Response.json({ status: "not_ready", service: "web", error: "database_unavailable" }, { status: 503 });
}
};
`;
const VALID_API_APP = `import { Hono } from "hono";
import { getApiRuntimeConfig } from "@toolctrl/config";
export const app = new Hono();
function apiHealthResponse(c) {
return c.json({
status: "ok",
service: "api",
revision: getApiRuntimeConfig().revision,
requestId: c.get("requestId"),
});
}
app.get("/healthz", apiHealthResponse);
app.get("/readyz", async (c) => {
try {
await apiAppDependencies.checkReadiness();
return c.json({
status: "ready",
service: "api",
requestId: c.get("requestId"),
});
} catch {
return c.json(
{
status: "not_ready",
service: "api",
requestId: c.get("requestId"),
error: {
code: "dependency_unavailable",
message: "A required dependency is unavailable.",
},
},
503
);
}
});
app.get("/v1/health", apiHealthResponse);
`;
const VALID_WORKER_HEALTH = `import { getDbClient } from "@toolctrl/db/client.server";
export async function buildWorkerHealthReport(env = process.env) {
await getDbClient().$queryRaw\`SELECT 1\`;
return {
status: "ready",
revision: env.APP_REVISION || "unknown",
redisConfigured: true,
redisSource: "REDIS_URL",
redisParseStatus: "valid",
databaseConfigured: true,
databaseStatus: "ready",
errors: [],
};
}
export async function runWorkerHealthCli(io = process) {
const report = await buildWorkerHealthReport();
io.stdout.write(\`${"${JSON.stringify(report)}"}\\n\`);
return 0;
}
`;
const fixtures = [];
function writeFile(rootDir, relativePath, content) {
const absolutePath = path.join(rootDir, relativePath);
fs.mkdirSync(path.dirname(absolutePath), { recursive: true });
fs.writeFileSync(absolutePath, content);
}
function writeJson(rootDir, relativePath, value) {
writeFile(rootDir, relativePath, `${JSON.stringify(value, null, 2)}\n`);
}
function createFixture(overrides = {}) {
const rootDir = fs.mkdtempSync(path.join(os.tmpdir(), "health-baseline-"));
fixtures.push(rootDir);
writeFile(rootDir, "Dockerfile", overrides.dockerfile ?? VALID_DOCKERFILE);
writeFile(rootDir, "apps/web/app/routes/healthcheck.tsx", overrides.webHealthcheck ?? VALID_WEB_HEALTHCHECK);
writeFile(rootDir, "apps/web/app/routes/healthz.tsx", overrides.webHealthz ?? VALID_WEB_HEALTHZ);
writeFile(rootDir, "apps/web/app/routes/readyz.tsx", overrides.webReadyz ?? VALID_WEB_READYZ);
writeFile(rootDir, "apps/api/src/app.ts", overrides.apiApp ?? VALID_API_APP);
writeFile(rootDir, "apps/worker/src/health.ts", overrides.workerHealth ?? VALID_WORKER_HEALTH);
writeJson(
rootDir,
"apps/worker/package.json",
overrides.workerPackage ?? {
name: "@toolctrl/worker",
scripts: {
health: "node dist/health.js",
},
},
);
if (overrides.remove) {
for (const relativePath of overrides.remove) {
fs.rmSync(path.join(rootDir, relativePath), { recursive: true, force: true });
}
}
return rootDir;
}
afterEach(() => {
while (fixtures.length > 0) {
fs.rmSync(fixtures.pop(), { recursive: true, force: true });
}
});
describe("verify-health-baseline", () => {
it("passes enforced health surfaces without web/API gap observations", () => {
const rootDir = createFixture();
const result = verifyHealthBaseline(rootDir);
assert.deepEqual(result.failures, []);
assert.ok(result.passes.some((pass) => pass === "apps/web/app/routes/healthcheck.tsx: legacy liveness healthcheck avoids DB and self-fetch"));
assert.ok(result.passes.some((pass) => pass === "apps/web/app/routes/healthz.tsx: liveness route does not require DB"));
assert.ok(result.passes.some((pass) => pass === "apps/web/app/routes/healthz.tsx: liveness route reports ok web service identity"));
assert.ok(result.passes.some((pass) => pass === "apps/web/app/routes/readyz.tsx: readiness imports and checks db"));
assert.ok(result.passes.some((pass) => pass === "apps/api/src/app.ts: /healthz reports service \"api\""));
assert.ok(result.passes.some((pass) => pass === "apps/api/src/app.ts: /healthz includes requestId"));
assert.ok(result.passes.some((pass) => pass === "apps/api/src/app.ts: /readyz returns 503 not-ready dependency diagnostics"));
assert.ok(result.passes.some((pass) => pass === "apps/api/src/app.ts: /v1/health reports service \"api\""));
assert.ok(result.passes.some((pass) => pass === "apps/api/src/app.ts: /v1/health includes requestId"));
assert.ok(result.passes.some((pass) => pass === "apps/worker/package.json: script \"health\" is node dist/health.js"));
assert.ok(result.passes.some((pass) => pass === "apps/worker/src/health.ts: worker readiness checks DB"));
assert.ok(result.passes.some((pass) => pass === "Dockerfile: worker target contains HEALTHCHECK using compiled worker health"));
assert.doesNotMatch(result.observations.join("\n"), /Health Baseline target .*\/healthz route is not present yet/);
assert.doesNotMatch(result.observations.join("\n"), /Health Baseline target .*\/readyz route is not present yet/);
assert.doesNotMatch(result.observations.join("\n"), /apps\/worker\/src\/health\.ts: worker readiness does not check DB yet/);
});
it("fails when an existing web healthcheck surface regresses", () => {
const rootDir = createFixture({ remove: ["apps/web/app/routes/healthcheck.tsx"] });
const result = verifyHealthBaseline(rootDir);
assert.match(result.failures.join("\n"), /app\/routes\/healthcheck\.tsx: missing existing web healthcheck route/);
});
it("fails when legacy web healthcheck consumes DB or self-fetches", () => {
const rootDir = createFixture({
webHealthcheck: `import { db } from "~/utils/db.server";
export const loader = async () => {
await Promise.all([db.appConfiguration.count(), fetch("http://localhost/")]);
return new Response("OK");
};
`,
});
const result = verifyHealthBaseline(rootDir);
assert.match(result.failures.join("\n"), /legacy healthcheck must not import\/check DB or self-fetch the app/);
});
it("fails when API /v1/health stops reporting service and requestId", () => {
const rootDir = createFixture({
apiApp: `import { Hono } from "hono";
export const app = new Hono();
app.get("/v1/health", (c) => {
return c.json({ status: "ok" });
});
`,
});
const result = verifyHealthBaseline(rootDir);
assert.match(result.failures.join("\n"), /apps\/api\/src\/app\.ts: \/v1\/health must report service "api"/);
assert.match(result.failures.join("\n"), /apps\/api\/src\/app\.ts: \/v1\/health must include requestId/);
});
it("fails when API /v1/health route is missing", () => {
const rootDir = createFixture({
apiApp: VALID_API_APP.replace('app.get("/v1/health", apiHealthResponse);\n', ""),
});
const result = verifyHealthBaseline(rootDir);
assert.match(result.failures.join("\n"), /apps\/api\/src\/app\.ts: missing GET \/v1\/health route/);
});
it("fails when web /healthz is missing", () => {
const rootDir = createFixture({ remove: ["apps/web/app/routes/healthz.tsx"] });
const result = verifyHealthBaseline(rootDir);
assert.match(result.failures.join("\n"), /app\/routes\/healthz\.tsx: missing web \/healthz liveness route/);
});
it("fails when web /readyz is missing", () => {
const rootDir = createFixture({ remove: ["apps/web/app/routes/readyz.tsx"] });
const result = verifyHealthBaseline(rootDir);
assert.match(result.failures.join("\n"), /app\/routes\/readyz\.tsx: missing web \/readyz readiness route/);
});
it("fails when web /healthz imports db", () => {
const rootDir = createFixture({
webHealthz: `import { db } from "~/utils/db.server";
export const loader = async () => {
await db.appConfiguration.count();
return Response.json({ status: "ok", service: "web" }, { status: 200 });
};
`,
});
const result = verifyHealthBaseline(rootDir);
assert.match(result.failures.join("\n"), /app\/routes\/healthz\.tsx: liveness route must not import or check db/);
});
it("fails when web /healthz reports the wrong service identity", () => {
const rootDir = createFixture({
webHealthz: `export const loader = async () => {
return Response.json({ status: "ok", service: "oops", revision: process.env.APP_REVISION || "unknown" }, { status: 200 });
};
`,
});
const result = verifyHealthBaseline(rootDir);
assert.match(result.failures.join("\n"), /app\/routes\/healthz\.tsx: liveness route must report status ok and service web/);
});
it("fails when web /readyz does not check db", () => {
const rootDir = createFixture({
webReadyz: `export const loader = async () => {
return Response.json({ status: "ready", service: "web" }, { status: 200 });
};
`,
});
const result = verifyHealthBaseline(rootDir);
assert.match(result.failures.join("\n"), /app\/routes\/readyz\.tsx: readiness route must import and check db/);
});
it("fails when API /healthz is missing", () => {
const rootDir = createFixture({
apiApp: VALID_API_APP.replace('app.get("/healthz", apiHealthResponse);\n\n', ""),
});
const result = verifyHealthBaseline(rootDir);
assert.match(result.failures.join("\n"), /apps\/api\/src\/app\.ts: missing GET \/healthz route/);
});
it("fails when API /readyz is missing", () => {
const rootDir = createFixture({
apiApp: VALID_API_APP.replace(/app\.get\("\/readyz"[\s\S]*?\n\}\);\n\n/, ""),
});
const result = verifyHealthBaseline(rootDir);
assert.match(result.failures.join("\n"), /apps\/api\/src\/app\.ts: missing GET \/readyz route/);
});
it("fails when API /readyz lacks 503 dependency diagnostics", () => {
const rootDir = createFixture({
apiApp: VALID_API_APP.replace("status: \"not_ready\",", "status: \"ready\",").replace("code: \"dependency_unavailable\",", "code: \"unavailable\","),
});
const result = verifyHealthBaseline(rootDir);
assert.match(result.failures.join("\n"), /apps\/api\/src\/app\.ts: \/readyz must return 503 not_ready dependency diagnostics/);
});
it("fails when worker health exposes Redis secrets", () => {
const rootDir = createFixture({
workerHealth: VALID_WORKER_HEALTH.replace("redisSource: \"REDIS_URL\",", "redisUrl: process.env.REDIS_URL,"),
});
const result = verifyHealthBaseline(rootDir);
assert.match(result.failures.join("\n"), /apps\/worker\/src\/health\.ts: health JSON must not expose Redis URL or password values/);
});
it("allows helper parameters named redisUrl when the health report omits secrets", () => {
const rootDir = createFixture({
workerHealth: VALID_WORKER_HEALTH.replace(
"export async function runWorkerHealthCli(io = process) {",
"function probeRedis(redisUrl: string) { return Boolean(redisUrl); }\nexport async function runWorkerHealthCli(io = process) {"
),
});
const result = verifyHealthBaseline(rootDir);
assert.equal(result.failures.filter((failure) => /health JSON must not expose Redis URL/.test(failure)).length, 0);
});
it("fails when worker health omits DB readiness", () => {
const rootDir = createFixture({
workerHealth: `export function buildWorkerHealthReport() {
return {
status: "ready",
redisConfigured: true,
redisSource: "REDIS_URL",
redisParseStatus: "valid",
errors: [],
};
}
export function runWorkerHealthCli(io = process) {
const report = buildWorkerHealthReport();
io.stdout.write(\`${"${JSON.stringify(report)}"}\\n\`);
return 0;
}
`,
});
const result = verifyHealthBaseline(rootDir);
const output = formatHealthBaselineResult(result);
assert.match(result.failures.join("\n"), /apps\/worker\/src\/health\.ts: worker readiness must check DB/);
assert.match(output, /^FAIL Health baseline verification/);
assert.match(output, /FAIL apps\/worker\/src\/health\.ts: worker readiness must check DB/);
});
it("keeps web/API endpoint gaps out of passing output", () => {
const result = verifyHealthBaseline(createFixture());
const output = formatHealthBaselineResult(result);
assert.match(output, /^PASS Health baseline verification/);
assert.doesNotMatch(output, /Health Baseline target .*\/healthz route is not present yet/);
assert.doesNotMatch(output, /Health Baseline target .*\/readyz route is not present yet/);
assert.match(output, /Summary: \d+ passed, \d+ observed, 0 failed/);
});
});