import assert from "node:assert/strict"; import test from "node:test"; import { assertReadonlyKubectlArgs, buildDevRuntimeHotfixAuditReport, parseArgs } from "./dev-runtime-hotfix-audit.mjs"; test("plan mode emits a bounded read-only plan and runs no kubectl commands", async () => { let commandCount = 0; const report = await buildDevRuntimeHotfixAuditReport(parseArgs(["--plan"]), { runCommand: async () => { commandCount += 1; throw new Error("plan mode must not execute commands"); } }); assert.equal(commandCount, 0); assert.equal(report.mode, "plan"); assert.equal(report.safety.k8sWriteAttempted, false); assert.deepEqual(report.classifications, [ "source-artifact-expected", "unknown-needs-manual-readonly-check" ]); assert.ok(report.commandPlan.readonly.every((command) => command.includes("KUBECONFIG=/etc/rancher/k3s/k3s.yaml"))); assert.ok(report.commandPlan.forbiddenDefaultOperations.some((command) => command.includes("kubectl patch"))); }); test("collect-readonly classifies active ConfigMap mount and pod marker without write verbs", async () => { const commands = []; const report = await buildDevRuntimeHotfixAuditReport(parseArgs(["--collect-readonly"]), { runCommand: async ({ args, displayCommand }) => { commands.push(displayCommand); return fixtureResult(args); } }); assert.equal(report.mode, "collect-readonly"); assert.equal(report.safety.k8sWriteAttempted, false); assert.ok(report.classifications.includes("hotfix-configmap-present")); assert.ok(report.classifications.includes("deployment-mounts-hotfix")); assert.ok(report.classifications.includes("pod-loads-hotfix-marker")); assert.ok(report.classifications.includes("rollback-required")); assert.equal(report.conclusion.status, "hotfix-detected"); assert.ok(commands.length > 0); assertNoWriteVerbs(commands); }); test("collect-readonly reports no-hotfix-detected when ConfigMap and mount are absent", async () => { const report = await buildDevRuntimeHotfixAuditReport(parseArgs(["--collect-readonly"]), { runCommand: async ({ args }) => fixtureResult(args, { noHotfix: true }) }); assert.deepEqual(report.classifications, [ "source-artifact-expected", "no-hotfix-detected" ]); assert.equal(report.conclusion.status, "clear"); }); test("collect-readonly keeps unknown classification when D601 node cannot be proven", async () => { const report = await buildDevRuntimeHotfixAuditReport(parseArgs(["--collect-readonly"]), { runCommand: async ({ args }) => { if (args.includes("nodes")) { return { ok: false, exitCode: 1, stdout: "", stderr: "unable to connect to the server" }; } return fixtureResult(args); } }); assert.ok(report.classifications.includes("unknown-needs-manual-readonly-check")); assert.notEqual(report.conclusion.status, "clear"); }); test("kubectl write verbs are rejected by the audit helper", () => { assert.throws(() => assertReadonlyKubectlArgs(["-n", "hwlab-dev", "patch", "deployment", "hwlab-cloud-api"]), /forbidden/u); assert.throws(() => assertReadonlyKubectlArgs(["-n", "hwlab-dev", "rollout", "restart", "deployment", "hwlab-cloud-api"]), /forbidden/u); }); function fixtureResult(args, options = {}) { const text = args.join(" "); if (text.includes("get nodes")) { return okJson({ items: [{ metadata: { name: "d601" } }] }); } if (text.includes("get configmap")) { if (options.noHotfix) { return { ok: false, exitCode: 1, stdout: "", stderr: "Error from server (NotFound): configmaps \"hwlab-cloud-api-code-agent-hotfix\" not found" }; } return okJson({ data: { "code-agent-chat.mjs": "// runtime-hotfix-pc-gateway-shell" } }); } if (text.includes("get deployment")) { return okJson(deploymentFixture({ noHotfix: options.noHotfix })); } if (text.includes("get pods")) { return okJson({ items: [{ metadata: { name: "hwlab-cloud-api-abc", creationTimestamp: "2026-05-24T00:00:00Z" }, status: { phase: "Running" }, spec: { nodeName: "d601" } }] }); } if (text.includes("exec") && text.includes("grep")) { return options.noHotfix ? { ok: false, exitCode: 1, stdout: "", stderr: "" } : { ok: true, exitCode: 0, stdout: "1:runtime-hotfix-pc-gateway-shell\n", stderr: "" }; } if (text.includes("exec") && text.includes("node --check")) { return { ok: true, exitCode: 0, stdout: "", stderr: "" }; } throw new Error(`unexpected fixture command: ${text}`); } function deploymentFixture({ noHotfix = false } = {}) { return { metadata: { generation: 9 }, spec: { template: { metadata: { annotations: noHotfix ? {} : { "hwlab.pikastech.local/pc-gateway-shell-hotfix": "runtime-hotfix" } }, spec: { volumes: noHotfix ? [] : [{ name: "code-agent-hotfix", configMap: { name: "hwlab-cloud-api-code-agent-hotfix", items: [{ key: "code-agent-chat.mjs", path: "code-agent-chat.mjs" }] } }], containers: [{ name: "hwlab-cloud-api", image: "127.0.0.1:5000/hwlab/hwlab-cloud-api:dev", volumeMounts: noHotfix ? [] : [{ name: "code-agent-hotfix", mountPath: "/app/internal/cloud/code-agent-chat.mjs", subPath: "code-agent-chat.mjs", readOnly: true }] }] } } } }; } function okJson(value) { return { ok: true, exitCode: 0, stdout: JSON.stringify(value), stderr: "" }; } function assertNoWriteVerbs(commands) { const forbidden = /\bkubectl\s+(?:apply|patch|rollout|delete|create|replace|scale|set|edit|annotate|label)\b/u; for (const command of commands) { assert.doesNotMatch(command, forbidden, command); } }