import assert from "node:assert/strict"; import test from "node:test"; import { buildKubectlCommandPrefix, compareRuntimeIdentityEnv, decideDevLegacySimulatorDeploymentCleanup, decideDevTemplateJobReplacement, formatKubeconfigAccessFailure, inspectCodeAgentProviderDesiredState, inspectCodeAgentProviderLiveDeployment, parseArgs, redactSensitiveText, resolveApplySourceCommit, resolveDevKubeconfigSelection } from "./dev-deploy-apply.mjs"; import { devCdTransactionGuardFailure, hasDevCdTransactionEnv, requireDevCdTransactionForSideEffect } from "./dev-cd-transaction-guard.mjs"; const codeAgentBaseUrl = "http://172.26.26.227:17680/v1/responses"; function codeAgentDeploy(env = {}) { return { services: [ { serviceId: "hwlab-cloud-api", env: { HWLAB_CODE_AGENT_PROVIDER: "codex-stdio", HWLAB_CODE_AGENT_MODEL: "gpt-5.5", HWLAB_CODE_AGENT_OPENAI_BASE_URL: codeAgentBaseUrl, OPENAI_API_KEY: "secretRef:hwlab-code-agent-provider/openai-api-key", ...env } } ] }; } function codeAgentWorkloads(envOverrides = []) { return { kind: "List", items: [ { kind: "Deployment", metadata: { name: "hwlab-cloud-api", namespace: "hwlab-dev", labels: { "hwlab.pikastech.local/service-id": "hwlab-cloud-api" } }, spec: { template: { spec: { containers: [ { name: "hwlab-cloud-api", env: [ { name: "HWLAB_CODE_AGENT_PROVIDER", value: "codex-stdio" }, { name: "HWLAB_CODE_AGENT_MODEL", value: "gpt-5.5" }, { name: "HWLAB_CODE_AGENT_OPENAI_BASE_URL", value: codeAgentBaseUrl }, { name: "OPENAI_API_KEY", valueFrom: { secretKeyRef: { name: "hwlab-code-agent-provider", key: "openai-api-key" } } }, ...envOverrides ] } ] } } } } ] }; } test("allowlisted suspended template Job image change plans replacement", () => { const decision = decideDevTemplateJobReplacement({ jobName: "hwlab-agent-worker-template", jobNamespace: "hwlab-dev", desiredSuspended: true, liveExists: true, liveSuspended: true, oldImages: [ { name: "hwlab-agent-worker", serviceId: "hwlab-agent-worker", image: "127.0.0.1:5000/hwlab/hwlab-agent-worker:old1234" } ], newImages: [ { name: "hwlab-agent-worker", serviceId: "hwlab-agent-worker", image: "127.0.0.1:5000/hwlab/hwlab-agent-worker:new5678" } ] }); assert.equal(decision.replace, true); assert.equal(decision.result, "planned"); assert.equal(decision.namespace, "hwlab-dev"); assert.equal(decision.jobName, "hwlab-agent-worker-template"); assert.equal(decision.oldImage, "127.0.0.1:5000/hwlab/hwlab-agent-worker:old1234"); assert.equal(decision.newImage, "127.0.0.1:5000/hwlab/hwlab-agent-worker:new5678"); }); test("DEV CD transaction guard rejects direct legacy side-effect calls", () => { assert.equal(hasDevCdTransactionEnv({}), false); assert.equal(hasDevCdTransactionEnv({ HWLAB_CD_TRANSACTION_ID: "tx-123" }), true); const failure = requireDevCdTransactionForSideEffect({ env: {}, script: "scripts/dev-deploy-apply.mjs", mode: "--apply" }); assert.deepEqual(failure, devCdTransactionGuardFailure({ script: "scripts/dev-deploy-apply.mjs", mode: "--apply" })); assert.equal(failure.error, "cd-transaction-required"); assert.equal(failure.mutationAttempted, false); assert.match(failure.entrypoint, /scripts\/dev-cd-apply\.mjs/u); assert.equal( requireDevCdTransactionForSideEffect({ env: { HWLAB_CD_TRANSACTION_ID: "tx-123" }, script: "scripts/dev-deploy-apply.mjs", mode: "--apply" }), null ); }); test("matching allowlisted suspended template Job image does not replace", () => { const image = "127.0.0.1:5000/hwlab/hwlab-cli:73b379f"; const decision = decideDevTemplateJobReplacement({ jobName: "hwlab-cli-template", jobNamespace: "hwlab-dev", desiredSuspended: true, liveExists: true, liveSuspended: true, oldImages: [{ name: "hwlab-cli", serviceId: "hwlab-cli", image }], newImages: [{ name: "hwlab-cli", serviceId: "hwlab-cli", image }] }); assert.equal(decision.replace, false); assert.equal(decision.result, "not_needed"); }); test("replacement policy refuses non-allowlisted or active Jobs", () => { const ignored = decideDevTemplateJobReplacement({ jobName: "some-other-job", jobNamespace: "hwlab-dev", desiredSuspended: true, liveExists: true, liveSuspended: true, oldImages: [{ name: "worker", serviceId: "worker", image: "image:old" }], newImages: [{ name: "worker", serviceId: "worker", image: "image:new" }] }); assert.equal(ignored.replace, false); assert.equal(ignored.result, "ignored"); const active = decideDevTemplateJobReplacement({ jobName: "hwlab-cli-template", jobNamespace: "hwlab-dev", desiredSuspended: true, liveExists: true, liveSuspended: false, oldImages: [{ name: "hwlab-cli", serviceId: "hwlab-cli", image: "image:old" }], newImages: [{ name: "hwlab-cli", serviceId: "hwlab-cli", image: "image:new" }] }); assert.equal(active.replace, false); assert.equal(active.result, "blocked"); }); test("allowlisted stale simulator Deployment plans cleanup only with StatefulSet replacement", () => { const decision = decideDevLegacySimulatorDeploymentCleanup({ deploymentName: "hwlab-box-simu", deploymentNamespace: "hwlab-dev", desiredReplacementExists: true, desiredReplacementKind: "StatefulSet", liveExists: true, oldImages: [ { name: "hwlab-box-simu", serviceId: "hwlab-box-simu", image: "127.0.0.1:5000/hwlab/hwlab-box-simu:old1234" } ], newImages: [ { name: "hwlab-box-simu", serviceId: "hwlab-box-simu", image: "127.0.0.1:5000/hwlab/hwlab-box-simu:new5678" } ] }); assert.equal(decision.cleanup, true); assert.equal(decision.result, "planned"); assert.equal(decision.namespace, "hwlab-dev"); assert.equal(decision.deploymentName, "hwlab-box-simu"); assert.equal(decision.serviceId, "hwlab-box-simu"); assert.equal(decision.desiredReplacementKind, "StatefulSet"); assert.equal(decision.oldImage, "127.0.0.1:5000/hwlab/hwlab-box-simu:old1234"); assert.equal(decision.newImage, "127.0.0.1:5000/hwlab/hwlab-box-simu:new5678"); }); test("legacy simulator cleanup refuses non-DEV or missing replacement cleanup", () => { const wrongNamespace = decideDevLegacySimulatorDeploymentCleanup({ deploymentName: "hwlab-gateway-simu", deploymentNamespace: "hwlab-prod", desiredReplacementExists: true, desiredReplacementKind: "StatefulSet", liveExists: true }); assert.equal(wrongNamespace.cleanup, false); assert.equal(wrongNamespace.result, "blocked"); const missingReplacement = decideDevLegacySimulatorDeploymentCleanup({ deploymentName: "hwlab-gateway-simu", deploymentNamespace: "hwlab-dev", desiredReplacementExists: false, desiredReplacementKind: null, liveExists: true }); assert.equal(missingReplacement.cleanup, false); assert.equal(missingReplacement.result, "blocked"); const ignored = decideDevLegacySimulatorDeploymentCleanup({ deploymentName: "hwlab-cloud-api", deploymentNamespace: "hwlab-dev", desiredReplacementExists: true, desiredReplacementKind: "StatefulSet", liveExists: true }); assert.equal(ignored.cleanup, false); assert.equal(ignored.result, "ignored"); }); test("apply source commit follows deploy/catalog artifact identity", () => { assert.equal( resolveApplySourceCommit({ commitId: "73b379f" }, { commitId: "73b379f" }, "cb35ada68606"), "73b379f" ); assert.equal( resolveApplySourceCommit({ commitId: "73b379f" }, { commitId: "deadbee" }, "cb35ada68606"), "cb35ada68606" ); }); test("runtime identity env comparator accepts matching commit image and tag", () => { const comparison = compareRuntimeIdentityEnv( { HWLAB_COMMIT_ID: "7de6edd", HWLAB_IMAGE: "127.0.0.1:5000/hwlab/hwlab-cloud-web:7de6edd", HWLAB_IMAGE_TAG: "7de6edd" }, { HWLAB_COMMIT_ID: "7de6edd2c41fb50dcb007353780338be8832b27e", HWLAB_IMAGE: "127.0.0.1:5000/hwlab/hwlab-cloud-web:7de6edd", HWLAB_IMAGE_TAG: "7de6edd" } ); assert.equal(comparison.status, "match"); assert.equal(comparison.matchesDesired, true); assert.deepEqual(comparison.drift, []); }); test("runtime identity env comparator detects stale cloud-web env drift", () => { const comparison = compareRuntimeIdentityEnv( { HWLAB_COMMIT_ID: "c7de474", HWLAB_IMAGE: "127.0.0.1:5000/hwlab/hwlab-cloud-web:c7de474", HWLAB_IMAGE_TAG: "c7de474" }, { HWLAB_COMMIT_ID: "7de6edd", HWLAB_IMAGE: "127.0.0.1:5000/hwlab/hwlab-cloud-web:7de6edd", HWLAB_IMAGE_TAG: "7de6edd" } ); assert.equal(comparison.status, "drift"); assert.equal(comparison.matchesDesired, false); assert.deepEqual( comparison.drift.map((field) => field.name), ["HWLAB_COMMIT_ID", "HWLAB_IMAGE", "HWLAB_IMAGE_TAG"] ); }); test("Code Agent provider desired state preserves Secret ref and egress base URL", () => { const inspection = inspectCodeAgentProviderDesiredState(codeAgentDeploy(), codeAgentWorkloads()); assert.equal(inspection.ready, true); assert.equal(inspection.status, "pass"); assert.equal(inspection.provider, "codex-stdio"); assert.equal(inspection.runtimeProvider, "openai-responses"); assert.equal(inspection.backend, "hwlab-cloud-api/codex-mcp-stdio"); assert.equal(inspection.secretRef.present, true); assert.equal(inspection.secretMaterialRead, false); assert.equal(inspection.egress.deployMatchesDevProxy, true); assert.equal(inspection.egress.workloadMatchesDevProxy, true); }); test("Code Agent provider desired state blocks direct public OpenAI base URL", () => { const inspection = inspectCodeAgentProviderDesiredState( codeAgentDeploy({ HWLAB_CODE_AGENT_OPENAI_BASE_URL: "https://api.openai.com/v1/responses" }), codeAgentWorkloads([ { name: "HWLAB_CODE_AGENT_OPENAI_BASE_URL", value: "https://api.openai.com/v1/responses" } ]) ); assert.equal(inspection.ready, false); assert.equal(inspection.status, "blocked"); assert.equal(inspection.egress.directPublicOpenAi, true); assert.ok(inspection.missingEgressContract.some((entry) => entry.includes("api.openai.com"))); }); test("Code Agent live Deployment inspection reads only env metadata", () => { const deployment = codeAgentWorkloads().items[0]; const inspection = inspectCodeAgentProviderLiveDeployment(deployment); assert.equal(inspection.ready, true); assert.equal(inspection.secretRef.present, true); assert.equal(inspection.secretValuesRead, false); assert.equal(inspection.kubernetesSecretDataRead, false); assert.equal(inspection.validationScope, "deployment-env-and-secretKeyRef-metadata-only"); }); test("DEV kubeconfig selection prefers flag then HWLAB_DEV_KUBECONFIG then KUBECONFIG then D601 default", () => { assert.deepEqual( resolveDevKubeconfigSelection({ flagValue: "/tmp/from-flag", env: { HWLAB_DEV_KUBECONFIG: "/tmp/from-hwlab", KUBECONFIG: "/tmp/from-kubeconfig" } }), { kubeconfig: "/tmp/from-flag", source: "flag:--kubeconfig", paths: ["/tmp/from-flag"] } ); assert.deepEqual( resolveDevKubeconfigSelection({ env: { HWLAB_DEV_KUBECONFIG: "/tmp/from-hwlab", KUBECONFIG: "/tmp/from-kubeconfig" } }), { kubeconfig: "/tmp/from-hwlab", source: "env:HWLAB_DEV_KUBECONFIG", paths: ["/tmp/from-hwlab"] } ); assert.deepEqual( resolveDevKubeconfigSelection({ env: { KUBECONFIG: "/tmp/from-kubeconfig" } }), { kubeconfig: "/tmp/from-kubeconfig", source: "env:KUBECONFIG", paths: ["/tmp/from-kubeconfig"] } ); assert.deepEqual( resolveDevKubeconfigSelection({ env: {} }), { kubeconfig: "/etc/rancher/k3s/k3s.yaml", source: "default:d601-k3s", paths: ["/etc/rancher/k3s/k3s.yaml"] } ); }); test("DEV kubeconfig selection keeps multi-path KUBECONFIG readable checks explicit", () => { const delimiter = process.platform === "win32" ? ";" : ":"; const selection = resolveDevKubeconfigSelection({ env: { KUBECONFIG: [`/tmp/one`, `/tmp/two`].join(delimiter) } }); assert.equal(selection.kubeconfig, [`/tmp/one`, `/tmp/two`].join(delimiter)); assert.equal(selection.source, "env:KUBECONFIG"); assert.deepEqual(selection.paths, ["/tmp/one", "/tmp/two"]); }); test("kubectl command prefix quotes kubeconfig paths without exposing contents", () => { assert.equal( buildKubectlCommandPrefix("/var/lib/unidesk/code-queue/kubeconfig"), "KUBECONFIG=/var/lib/unidesk/code-queue/kubeconfig kubectl" ); assert.equal( buildKubectlCommandPrefix("/tmp/path with space/kubeconfig"), "KUBECONFIG='/tmp/path with space/kubeconfig' kubectl" ); }); test("kubeconfig access failure names source and path without secret material", () => { const message = formatKubeconfigAccessFailure( { source: "env:HWLAB_DEV_KUBECONFIG" }, "/missing/kubeconfig" ); assert.match(message, /env:HWLAB_DEV_KUBECONFIG/u); assert.match(message, /\/missing\/kubeconfig/u); assert.match(message, /Secret values are never printed/u); assert.equal(message.includes("client-key-data:"), false); }); test("parseArgs reports a missing kubeconfig path as a blocking error", () => { const args = parseArgs(["--apply", "--kubeconfig"]); assert.equal(args.apply, true); assert.equal(args.kubeconfigSpecified, true); assert.deepEqual(args.errors, ["--kubeconfig requires a non-empty path value"]); }); test("redaction removes common token and password material from kubectl output", () => { const output = redactSensitiveText( [ "Authorization: Bearer abc.def-123", "token: abcdef123456", "password=plain-secret", "client-key-data: LS0tPRIVATE", "postgresql://hwlab:db-secret@example.invalid:5432/hwlab" ].join("\n") ); assert.equal(output.includes("abc.def-123"), false); assert.equal(output.includes("plain-secret"), false); assert.equal(output.includes("LS0tPRIVATE"), false); assert.equal(output.includes("db-secret"), false); assert.match(output, /Bearer /u); assert.match(output, /password=/u); assert.match(output, /client-key-data: /u); assert.match(output, /postgresql:\/\/hwlab:@example.invalid/u); });