import assert from "node:assert/strict";
import { spawnSync } from "node:child_process";
import { mkdir, mkdtemp, readFile, readdir, rm, writeFile } from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import test from "node:test";

function taskByName(pipeline, name) {
  const task = pipeline.spec.tasks.find((item) => item.name === name);
  assert.ok(task, `missing task ${name}`);
  return task;
}

async function collectGeneratedFiles(root) {
  const files = [];
  async function walk(dir) {
    const entries = await readdir(dir, { withFileTypes: true });
    for (const entry of entries) {
      const fullPath = path.join(dir, entry.name);
      if (entry.isDirectory()) await walk(fullPath);
      else if (entry.isFile()) files.push(fullPath);
    }
  }
  await walk(root);
  return files.sort();
}

function collectContainersFromItem(item) {
  const podSpec = item?.spec?.template?.spec ?? item?.spec?.jobTemplate?.spec?.template?.spec ?? item?.spec?.template?.spec;
  return [
    ...(podSpec?.initContainers ?? []),
    ...(podSpec?.containers ?? [])
  ];
}

function extractRuntimeNoopDecisionScript(gitopsPromoteScript) {
  const marker = `runtime_noop_decision="$(node - "$runtime_path" "$old_runtime_snapshot" /workspace/source/affected-services.json <<'NODE'\n`;
  const start = gitopsPromoteScript.indexOf(marker);
  assert.notEqual(start, -1, "missing runtime no-op decision script");
  const rest = gitopsPromoteScript.slice(start + marker.length);
  const end = rest.indexOf("\nNODE\n)");
  assert.notEqual(end, -1, "missing runtime no-op decision terminator");
  return rest.slice(0, end);
}

function runtimeWorkloadsFixture({ runtimeCommit, agentRunBundleCommit }) {
  return {
    items: [{
      kind: "Deployment",
      metadata: {
        name: "hwlab-cloud-api",
        labels: { "hwlab.pikastech.local/source-commit": runtimeCommit },
        annotations: { "hwlab.pikastech.local/boot-commit": runtimeCommit }
      },
      spec: {
        template: {
          metadata: {
            labels: {
              "hwlab.pikastech.local/source-commit": runtimeCommit,
              "hwlab.pikastech.local/artifact-source-commit": runtimeCommit
            },
            annotations: { "hwlab.pikastech.local/boot-commit": runtimeCommit }
          },
          spec: {
            containers: [{
              name: "hwlab-cloud-api",
              env: [
                { name: "HWLAB_COMMIT_ID", value: runtimeCommit },
                { name: "HWLAB_GITOPS_SOURCE_COMMIT", value: runtimeCommit },
                { name: "HWLAB_CODE_AGENT_AGENTRUN_SOURCE_COMMIT", value: agentRunBundleCommit }
              ]
            }]
          }
        }
      }
    }]
  };
}

async function runtimeNoopDecision(gitopsPromoteScript, { oldRuntime, newRuntime }) {
  const script = extractRuntimeNoopDecisionScript(gitopsPromoteScript);
  const root = await mkdtemp(path.join(os.tmpdir(), "hwlab-v02-runtime-noop-"));
  try {
    const oldDir = path.join(root, "old-runtime");
    const newDir = path.join(root, "new-runtime");
    const planPath = path.join(root, "affected-services.json");
    await mkdir(oldDir, { recursive: true });
    await mkdir(newDir, { recursive: true });
    await writeFile(path.join(oldDir, "workloads.json"), JSON.stringify(runtimeWorkloadsFixture(oldRuntime), null, 2));
    await writeFile(path.join(newDir, "workloads.json"), JSON.stringify(runtimeWorkloadsFixture(newRuntime), null, 2));
    await writeFile(planPath, JSON.stringify({ buildServices: [], rolloutServices: [] }));
    const result = spawnSync(process.execPath, ["-", newDir, oldDir, planPath], { input: script, encoding: "utf8" });
    assert.equal(result.status, 0, result.stderr || result.stdout);
    return result.stdout.trim();
  } finally {
    await rm(root, { recursive: true, force: true });
  }
}

test("v02 render follows TypeScript runtime checks and does not self-patch bootstrap RBAC", async () => {
  const outDir = await mkdtemp(path.join(os.tmpdir(), "hwlab-v02-render-test-"));
  try {
    const head = spawnSync("git", ["rev-parse", "HEAD"], { cwd: process.cwd(), encoding: "utf8" });
    assert.equal(head.status, 0, head.stderr || head.stdout);
    const sourceRevision = head.stdout.trim();

    const staleRuntimeFile = path.join(outDir, "runtime-v02", "stale-gateway-simu.yaml");
    const staleTektonFile = path.join(outDir, "tekton-v02", "stale-router.yaml");
    await mkdir(path.dirname(staleRuntimeFile), { recursive: true });
    await mkdir(path.dirname(staleTektonFile), { recursive: true });
    await writeFile(staleRuntimeFile, "kind: Service\nmetadata:\n  name: hwlab-gateway-simu\n");
    await writeFile(staleTektonFile, "hwlab-router\n");

    const render = spawnSync(process.execPath, [
      "scripts/run-bun.mjs",
      "scripts/gitops-render.mjs",
      "--lane", "v02",
      "--catalog-path", "deploy/artifact-catalog.v02.json",
      "--image-tag-mode", "full",
      "--source-branch", "v0.2",
      "--gitops-branch", "v0.2-gitops",
      "--out", outDir,
      "--source-revision", "HEAD"
    ], { cwd: process.cwd(), encoding: "utf8" });
    assert.equal(render.status, 0, render.stderr || render.stdout);
    await assert.rejects(() => readFile(staleRuntimeFile, "utf8"), { code: "ENOENT" });
    await assert.rejects(() => readFile(staleTektonFile, "utf8"), { code: "ENOENT" });

    const pipeline = await readFile(path.join(outDir, "tekton-v02", "pipeline.yaml"), "utf8");
    const pipelineJson = JSON.parse(pipeline);
    assert.match(pipeline, /127\.0\.0\.1:5000\/hwlab\/hwlab-ci-node-tools:node22-alpine-bun-v1/u);
    assert.match(pipeline, /tar -C \/workspace\/source\/repo -cf - \. \| tar -C \/workspace\/service-work\/repo -xo -f -/u);
    assert.match(pipeline, /node scripts\/run-bun\.mjs test cmd\/hwlab-codex-api-responses-forwarder\/main\.test\.ts/u);
    assert.doesNotMatch(pipeline, /cmd\/hwlab-codex-api-responses-forwarder\/main\.mjs/u);
    assert.match(pipeline, /process\.exitCode = 1/u);
    assert.match(pipeline, /item\.metadata\?\.labels\?\.\[\\"hwlab\.pikastech\.local\/source-commit\\"\]/u);

    const runtimeReady = taskByName(pipelineJson, "runtime-ready");
    assert.deepEqual(runtimeReady.when, [{ input: "$(tasks.gitops-promote.results.runtime-ready-required)", operator: "in", values: ["true"] }]);
    const runtimeReadyScript = runtimeReady.taskSpec.steps[0].script;
    assert.match(runtimeReadyScript, /const pollMs = 1000;/u);
    assert.match(runtimeReadyScript, /readObservedServiceIds/u);
    assert.match(runtimeReadyScript, /observedRuntimeItems/u);
    assert.match(runtimeReadyScript, /observedCount/u);
    assert.match(runtimeReadyScript, /readinessMode = observedServiceIds \? "service-rollout" : "infra-runtime-change"/u);
    assert.match(runtimeReadyScript, /waitForArgoSyncedHealthy/u);
    assert.match(runtimeReadyScript, /stage: "runtime-ready", status: "started"/u);
    assert.match(runtimeReadyScript, /status: "progress"/u);
    assert.match(runtimeReadyScript, /stage: "argo-sync-health"/u);
    assert.match(runtimeReadyScript, /status: "timeout", reason: "source-commit-refresh-timeout"/u);
    assert.match(runtimeReadyScript, /if \(!observedServiceIds\) \{/u);
    assert.match(runtimeReadyScript, /const refresh = await waitForArgoRefresh\(\);\n  if \(!refresh\.observed\) return;/u);
    assert.ok(runtimeReady.taskSpec.params.some((param) => param.name === "argo-application"));
    assert.ok(runtimeReady.params.some((param) => param.name === "argo-application" && param.value === "hwlab-node-v02"));
    assert.ok(runtimeReady.workspaces?.some((workspace) => workspace.name === "source" && workspace.workspace === "source"));
    assert.ok(runtimeReady.taskSpec.workspaces?.some((workspace) => workspace.name === "source"));

    const gitopsPromote = taskByName(pipelineJson, "gitops-promote");
    assert.ok(gitopsPromote.taskSpec.results.some((result) => result.name === "runtime-ready-required"));
    const gitopsPromoteScript = gitopsPromote.taskSpec.steps[0].script;
    assert.match(gitopsPromoteScript, /printf 'true' > \/tekton\/results\/runtime-ready-required/u);
    assert.match(gitopsPromoteScript, /runtime-identity-only/u);
    assert.match(gitopsPromoteScript, /skipped-runtime-unchanged/u);
    assert.match(gitopsPromoteScript, /printf 'false' > \/tekton\/results\/runtime-ready-required/u);
    assert.equal(await runtimeNoopDecision(gitopsPromoteScript, {
      oldRuntime: {
        runtimeCommit: "1111111111111111111111111111111111111111",
        agentRunBundleCommit: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      },
      newRuntime: {
        runtimeCommit: "2222222222222222222222222222222222222222",
        agentRunBundleCommit: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
      }
    }), "runtime-identity-only");
    assert.match(gitopsPromoteScript, /argo_hard_refresh_runtime_lane\(\) \{/u);
    assert.match(gitopsPromoteScript, /ARGO_APPLICATION="hwlab-node-\$lane" ARGO_FIELD_MANAGER="hwlab-\$lane-gitops-promote"/u);
    assert.match(gitopsPromoteScript, /argocd\.argoproj\.io\/refresh": "hard"/u);
    assert.match(gitopsPromoteScript, /applications\/" \+ encodeURIComponent\(application\) \+ "\?fieldManager=" \+ encodeURIComponent\(fieldManager\)/u);
    assert.match(gitopsPromoteScript, /BatchMode=yes/u);
    assert.match(gitopsPromoteScript, /ConnectTimeout=10/u);
    assert.match(gitopsPromoteScript, /git_timed "source-head-\$phase" 45 git ls-remote/u);
    assert.match(gitopsPromoteScript, /"event":"git-ssh-setup","phase":"gitops-promote","status":"skipped"/u);
    assert.match(gitopsPromoteScript, /source_head_url="\$\(params\.git-read-url\)"/u);
    assert.match(gitopsPromoteScript, /gitops_write_url="\$\(params\.git-write-url\)"/u);
    assert.match(gitopsPromoteScript, /git clone --no-checkout "\$gitops_read_url"/u);
    assert.match(gitopsPromoteScript, /git_timed gitops-push 120 git push origin/u);
    assert.doesNotMatch(gitopsPromoteScript, /gitops-promote-proxy-preflight/u);
    assert.doesNotMatch(gitopsPromoteScript, /for tool in node npm git ssh curl timeout/u);

    const prepareSource = taskByName(pipelineJson, "prepare-source");
    assert.ok(prepareSource.taskSpec.params.some((param) => param.name === "git-read-url"));
    assert.ok(prepareSource.params.some((param) => param.name === "git-read-url" && param.value === "$(params.git-read-url)"));
    const prepareSourceScript = prepareSource.taskSpec.steps[0].script;
    assert.match(prepareSourceScript, /git_read_url="\$\(params\.git-read-url\)"/u);
    assert.match(prepareSourceScript, /"event":"git-ssh-setup","phase":"prepare-source","status":"skipped"/u);
    assert.match(prepareSourceScript, /git_timed source-clone 180 git clone --branch "\$\(params\.source-branch\)" "\$git_read_url"/u);
    assert.match(prepareSourceScript, /git_timed catalog-ls-remote 45 git ls-remote --exit-code --heads "\$git_read_url"/u);
    assert.match(prepareSourceScript, /git remote set-url gitops-catalog "\$git_read_url"/u);
    assert.match(prepareSourceScript, /prepare-source-dependencies/u);
    assert.match(prepareSourceScript, /require\.resolve\("yaml"\)/u);
    assert.match(prepareSourceScript, /npm ci --ignore-scripts --no-audit --prefer-offline/u);
    assert.match(prepareSourceScript, /bun install --frozen-lockfile --ignore-scripts/u);
    assert.doesNotMatch(prepareSourceScript, /ci-tools-image-provides-required-node-runtime/u);
    assert.doesNotMatch(prepareSourceScript, /prepare-source-pre-npm/u);
    assert.doesNotMatch(prepareSourceScript, /prepare-source-proxy-preflight/u);
    assert.doesNotMatch(prepareSourceScript, /dependency-curl-probe-start/u);

    const planArtifacts = taskByName(pipelineJson, "plan-artifacts");
    assert.doesNotMatch(planArtifacts.taskSpec.steps[0].script, /plan-artifacts-proxy-preflight/u);
    const collectArtifacts = taskByName(pipelineJson, "collect-artifacts");
    assert.doesNotMatch(collectArtifacts.taskSpec.steps[0].script, /collect-artifacts-proxy-preflight/u);

    const pipelineGitReadParam = pipelineJson.spec.params.find((param) => param.name === "git-read-url");
    assert.equal(pipelineGitReadParam?.default, "http://git-mirror-http.devops-infra.svc.cluster.local/pikasTech/HWLAB.git");
    const pipelineGitWriteParam = pipelineJson.spec.params.find((param) => param.name === "git-write-url");
    assert.equal(pipelineGitWriteParam?.default, "http://git-mirror-write.devops-infra.svc.cluster.local/pikasTech/HWLAB.git");

    const sourceDescriptor = JSON.parse(await readFile(path.join(outDir, "source.json"), "utf8"));
    assert.equal(sourceDescriptor.gitReadUrl, "http://git-mirror-http.devops-infra.svc.cluster.local/pikasTech/HWLAB.git");
    assert.equal(sourceDescriptor.gitWriteUrl, "http://git-mirror-write.devops-infra.svc.cluster.local/pikasTech/HWLAB.git");

    const gitMirror = await readFile(path.join(outDir, "devops-infra", "git-mirror.yaml"), "utf8");
    assert.match(gitMirror, /"name": "devops-infra"/u);
    assert.match(gitMirror, /"name": "git-mirror-http"/u);
    assert.match(gitMirror, /"name": "git-mirror-write"/u);
    assert.match(gitMirror, /"name": "git-mirror-sync-script"/u);
    assert.match(gitMirror, /git-receive-pack/u);
    assert.match(gitMirror, /last-flush\.json/u);
    assert.match(gitMirror, /last-write\.json/u);
    assert.match(gitMirror, /ssh:\/\/git@ssh\.github\.com:443\/pikasTech\/HWLAB\.git/u);
    assert.doesNotMatch(gitMirror, /"kind": "CronJob"/u);
    assert.doesNotMatch(gitMirror, /"name": "git-mirror-hwlab-sync"/u);
    const gitMirrorJson = JSON.parse(gitMirror);
    const gitMirrorConfigMap = gitMirrorJson.items.find((item) => item.kind === "ConfigMap" && item.metadata?.name === "git-mirror-sync-script");
    const gitMirrorScripts = gitMirrorConfigMap?.data ?? {};
    const gitMirrorScript = gitMirrorScripts["sync.sh"] ?? "";
    assert.match(gitMirrorScript, /uploadpack\.allowReachableSHA1InWant true/u);
    assert.match(gitMirrorScript, /uploadpack\.allowAnySHA1InWant true/u);
    assert.match(gitMirrorScript, /refs\/heads\/v0\.2:refs\/mirror-stage\/heads\/v0\.2/u);
    assert.match(gitMirrorScript, /refs\/heads\/v0\.2-gitops:refs\/mirror-stage\/heads\/v0\.2-gitops/u);
    assert.match(gitMirrorScript, /refs\/heads\/v0\.3:refs\/mirror-stage\/heads\/v0\.3/u);
    assert.match(gitMirrorScript, /refs\/heads\/v0\.3-gitops:refs\/mirror-stage\/heads\/v0\.3-gitops/u);
    assert.doesNotMatch(gitMirrorScript, /refs\/heads\/G14:refs\/mirror-stage\/heads\/G14/u);
    assert.doesNotMatch(gitMirrorScript, /refs\/heads\/G14-gitops:refs\/mirror-stage\/heads\/G14-gitops/u);
    assert.doesNotMatch(gitMirrorScript, /refs\/heads\/\*:refs\/mirror-stage\/heads\/\*/u);
    assert.match(gitMirrorScript, /git -C "\$repo_path" update-ref "refs\/heads\/\$name" "\$sha"/u);
    assert.match(gitMirrorScript, /git-mirror-sync-timing/u);
    assert.match(gitMirrorScript, /emit_timing fetch succeeded/u);
    assert.match(gitMirrorScript, /emit_timing total succeeded/u);
    assert.match(gitMirrorScript, /sourceInSync/u);
    assert.match(gitMirrorScript, /refs\/mirror-stage\/heads\/v0\.2/u);
    assert.match(gitMirrorScript, /refs\/mirror-stage\/heads\/v0\.3/u);
    assert.match(gitMirrorScript, /for gitops_branch in 'v0\.2-gitops' 'v0\.3-gitops'/u);
    assert.match(gitMirrorScript, /keeps local \$name ahead of GitHub/u);
    assert.doesNotMatch(gitMirror, /not allowlisted/u);
    assert.doesNotMatch(gitMirror, /path allowlist/u);
    assert.doesNotMatch(gitMirror, /outside .* GitOps outputs/u);
    assert.ok((gitMirrorJson.items || []).every((item) => item.kind !== "Secret"));
    assert.ok((gitMirrorJson.items || []).every((item) => item.kind !== "CronJob"));
    assert.ok((gitMirrorJson.items || []).every((item) => !item.data?.["ssh-privatekey"]));

    const scriptCheckDir = path.join(outDir, "script-check");
    await mkdir(scriptCheckDir, { recursive: true });
    for (const scriptName of ["http.mjs", "sync.sh", "install-hooks.sh", "flush.sh"]) {
      assert.ok(gitMirrorScripts[scriptName], `missing git mirror script ${scriptName}`);
      await writeFile(path.join(scriptCheckDir, scriptName), gitMirrorScripts[scriptName]);
    }
    const httpCheck = spawnSync("node", ["--check", path.join(scriptCheckDir, "http.mjs")], { encoding: "utf8" });
    assert.equal(httpCheck.status, 0, httpCheck.stderr || httpCheck.stdout);
    for (const scriptName of ["sync.sh", "install-hooks.sh", "flush.sh"]) {
      const shellCheck = spawnSync("sh", ["-n", path.join(scriptCheckDir, scriptName)], { encoding: "utf8" });
      assert.equal(shellCheck.status, 0, `${scriptName}: ${shellCheck.stderr || shellCheck.stdout}`);
    }

    const removedServiceIds = [
      "hwlab-agent-mgr",
      "hwlab-agent-worker",
      "hwlab-agent-worker-template",
      "hwlab-code-agent-workspace",
      "hwlab-router",
      "hwlab-tunnel-client",
      "hwlab-gateway-simu",
      "hwlab-box-simu",
      "hwlab-patch-panel"
    ];
    for (const serviceId of removedServiceIds) {
      assert.doesNotMatch(pipeline, new RegExp(`build-${serviceId}`, "u"));
    }

    const retainedServiceIds = [
      "hwlab-cloud-api",
      "hwlab-cloud-web",
      "hwlab-gateway",
      "hwlab-edge-proxy",
      "hwlab-agent-skills"
    ];
    for (let index = 0; index < retainedServiceIds.length; index += 1) {
      const serviceId = retainedServiceIds[index];
      const task = taskByName(pipelineJson, `build-${serviceId}`);
      assert.deepEqual(task.runAfter, ["plan-artifacts"]);
      assert.deepEqual(task.when, [{ input: `$(tasks.plan-artifacts.results.affected-${serviceId})`, operator: "in", values: ["true"] }]);
    }

    const collectScript = collectArtifacts.taskSpec.steps[0].script;
    assert.match(collectScript, /--external-service-report-dir \/workspace\/source\/service-results/u);
    const cloudWebBuildScript = taskByName(pipelineJson, "build-hwlab-cloud-web").taskSpec.steps.find((step) => step.name === "publish")?.script ?? "";
    assert.match(cloudWebBuildScript, /node scripts\/artifact-publish\.mjs --publish/u);

    const artifactPublishSource = await readFile("scripts/artifact-publish.mjs", "utf8");
    assert.match(artifactPublishSource, /\["run", "--cwd", "web\/hwlab-cloud-web", "check"\]/u);
    assert.match(artifactPublishSource, /cloud web source check failed before BuildKit image build/u);
    assert.match(artifactPublishSource, /\/usr\/local\/bin\/hwpod/u);
    assert.doesNotMatch(artifactPublishSource, /Run bun run --cwd web\/hwlab-cloud-web build, fix the static asset build/u);

    const workloads = await readFile(path.join(outDir, "runtime-v02", "workloads.yaml"), "utf8");
    const workloadsJson = JSON.parse(workloads);
    const services = await readFile(path.join(outDir, "runtime-v02", "services.yaml"), "utf8");
    const servicesJson = JSON.parse(services);
    const deepseekProxy = JSON.parse(await readFile(path.join(outDir, "runtime-v02", "deepseek-proxy.yaml"), "utf8"));
    const observability = await readFile(path.join(outDir, "runtime-v02", "observability.yaml"), "utf8");
    const observabilityJson = JSON.parse(observability);
    for (const serviceId of removedServiceIds) {
      assert.doesNotMatch(workloads, new RegExp(serviceId, "u"));
      assert.doesNotMatch(services, new RegExp(serviceId, "u"));
    }
    const observableRuntimeServiceIds = [
      "hwlab-agent-skills",
      "hwlab-cloud-api",
      "hwlab-cloud-web",
      "hwlab-edge-proxy"
    ];
    for (const serviceId of observableRuntimeServiceIds) {
      const service = servicesJson.items.find((item) => item.kind === "Service" && item.metadata?.name === serviceId);
      assert.ok(service, `missing observable service ${serviceId}`);
      assert.equal(service.metadata.labels["hwlab.pikastech.local/monitoring"], "enabled");
      assert.ok(service.spec.ports.some((port) => port.name === "metrics" && port.port === 9100 && port.targetPort === "metrics"), `missing metrics port for ${serviceId}`);
      const workload = workloadsJson.items.find((item) => item.kind === "Deployment" && item.metadata?.name === serviceId);
      assert.ok(workload, `missing observable workload ${serviceId}`);
      assert.equal(workload.metadata.labels["hwlab.pikastech.local/monitoring"], "enabled");
      const sidecar = workload.spec.template.spec.containers.find((container) => container.name === "hwlab-metrics");
      assert.ok(sidecar, `missing metrics sidecar for ${serviceId}`);
      assert.match(sidecar.image, /hwlab-ci-node-tools:node22-alpine-bun-v1/u);
      assert.deepEqual(sidecar.command, ["node", "/metrics/metrics-sidecar.mjs"]);
      assert.match(workload.spec.template.metadata.annotations["hwlab.pikastech.local/metrics-sidecar-sha256"], /^[a-f0-9]{64}$/u);
      assert.ok(sidecar.env.some((entry) => entry.name === "HWLAB_METRICS_SERVICE_ID" && entry.value === serviceId));
      assert.ok(sidecar.ports.some((port) => port.name === "metrics" && port.containerPort === 9100));
      if (serviceId === "hwlab-cloud-api") {
        assert.ok(sidecar.env.some((entry) => entry.name === "HWLAB_METRICS_EXTRA_URL" && /\/v1\/web-performance\/metrics$/u.test(entry.value)), "cloud-api sidecar should scrape WebUI performance metrics from loopback");
      } else {
        assert.equal(sidecar.env.some((entry) => entry.name === "HWLAB_METRICS_EXTRA_URL"), false, `unexpected extra metrics target for ${serviceId}`);
      }
      if (serviceId === "hwlab-edge-proxy") {
        const container = workload.spec.template.spec.containers.find((entry) => entry.name === "hwlab-edge-proxy");
        assert.equal(container.readinessProbe.httpGet.path, "/health");
        assert.equal(container.livenessProbe.httpGet.path, "/health");
      }
    }
    const deepseekService = deepseekProxy.items.find((item) => item.kind === "Service" && item.metadata?.name === "hwlab-deepseek-proxy");
    assert.equal(deepseekService.metadata.labels["hwlab.pikastech.local/monitoring"], "enabled");
    assert.ok(deepseekService.spec.ports.some((port) => port.name === "metrics" && port.port === 9100));
    const deepseekDeployment = deepseekProxy.items.find((item) => item.kind === "Deployment" && item.metadata?.name === "hwlab-deepseek-proxy");
    assert.ok(deepseekDeployment.spec.template.spec.containers.some((container) => container.name === "hwlab-metrics"));
    assert.equal(
      deepseekDeployment.spec.template.metadata.annotations["hwlab.pikastech.local/metrics-sidecar-sha256"],
      workloadsJson.items.find((item) => item.kind === "Deployment" && item.metadata?.name === "hwlab-cloud-api").spec.template.metadata.annotations["hwlab.pikastech.local/metrics-sidecar-sha256"]
    );
    const observabilityItems = observabilityJson.items ?? [];
    const metricsConfig = observabilityItems.find((item) => item.kind === "ConfigMap" && item.metadata?.name === "hwlab-v02-metrics-sidecar");
    assert.ok(metricsConfig, "missing metrics sidecar ConfigMap");
    assert.match(metricsConfig.data["metrics-sidecar.mjs"], /hwlab_service_up/u);
    const serviceMonitors = observabilityItems.filter((item) => item.kind === "ServiceMonitor");
    assert.deepEqual(serviceMonitors.map((item) => item.metadata.name).sort(), [
      "hwlab-v02-hwlab-agent-skills",
      "hwlab-v02-hwlab-cloud-api",
      "hwlab-v02-hwlab-cloud-web",
      "hwlab-v02-hwlab-deepseek-proxy",
      "hwlab-v02-hwlab-edge-proxy"
    ]);
    for (const monitor of serviceMonitors) {
      assert.equal(monitor.metadata.namespace, "hwlab-v02");
      assert.equal(monitor.metadata.labels["hwlab.pikastech.local/monitoring"], "enabled");
      assert.deepEqual(monitor.spec.namespaceSelector.matchNames, ["hwlab-v02"]);
      assert.equal(monitor.spec.selector.matchLabels["hwlab.pikastech.local/gitops-target"], "v02");
      assert.equal(monitor.spec.selector.matchLabels["hwlab.pikastech.local/monitoring"], "enabled");
      assert.deepEqual(monitor.spec.endpoints, [{ port: "metrics", path: "/metrics", interval: "30s", scrapeTimeout: "10s" }]);
    }
    assert.ok(observabilityItems.some((item) => item.kind === "PrometheusRule" && item.metadata?.name === "hwlab-v02-observability"));
    assert.match(workloads, /"name": "HWLAB_ACCESS_CONTROL_REQUIRED"[\s\S]{0,80}"value": "1"/u);
    assert.match(workloads, /"name": "HWLAB_BOOTSTRAP_ADMIN_PASSWORD_HASH"/u);
    assert.match(workloads, /"name": "hwlab-v02-bootstrap-admin"/u);
    const cloudApi = workloadsJson.items.find((item) => item.metadata?.name === "hwlab-cloud-api");
    assert.ok(cloudApi, "expected v02 hwlab-cloud-api workload");
    const cloudApiContainer = cloudApi.spec.template.spec.containers.find((container) => container.name === "hwlab-cloud-api");
    const codexForwarder = cloudApi.spec.template.spec.containers.find((container) => container.name === "hwlab-codex-api-forwarder");
    assert.ok(cloudApiContainer, "expected cloud-api app container");
    assert.ok(codexForwarder, "expected codex API forwarder sidecar");
    const cloudApiEnv = cloudApiContainer.env;
    const cloudApiCommit = cloudApiEnv.find((entry) => entry.name === "HWLAB_COMMIT_ID")?.value;
    const cloudApiBootEnv = new Map(cloudApiEnv.map((entry) => [entry.name, entry.value]));
    assert.equal(cloudApiBootEnv.get("HWLAB_RUNTIME_MODE"), "env-reuse-git-mirror-checkout");
    assert.equal(cloudApiBootEnv.get("HWLAB_BOOT_SH"), "deploy/runtime/boot/hwlab-cloud-api.sh");
    const codexForwarderEnv = new Map(codexForwarder.env.map((entry) => [entry.name, entry.value]));
    assert.equal(codexForwarder.command, undefined);
    assert.equal(codexForwarderEnv.get("HWLAB_RUNTIME_MODE"), "env-reuse-git-mirror-checkout");
    assert.equal(codexForwarderEnv.get("HWLAB_BOOT_SH"), "deploy/runtime/boot/hwlab-codex-api-responses-forwarder.sh");
    assert.equal(codexForwarderEnv.get("HWLAB_BOOT_COMMIT"), cloudApiCommit);
    assert.equal(codexForwarderEnv.get("HWLAB_BOOT_READ_URL"), "http://git-mirror-http.devops-infra.svc.cluster.local/pikasTech/HWLAB.git");
    assert.ok(cloudApiEnv.some((entry) => entry.name === "HWLAB_CODE_AGENT_AGENTRUN_SOURCE_COMMIT" && entry.value === cloudApiCommit));
    assert.ok(cloudApiEnv.some((entry) => entry.name === "HWLAB_CODE_AGENT_ADAPTER" && entry.value === "agentrun-v01"));
    assert.equal(cloudApi.spec.template.spec.initContainers?.some((entry) => entry.name === "hwlab-code-agent-workspace-init"), false);
    assert.equal(cloudApi.spec.template.spec.volumes?.some((entry) => /code-agent-codex|code-agent-workspace/u.test(entry.name)), false);
    assert.equal(cloudApiContainer.volumeMounts?.some((entry) => /code-agent-codex|code-agent-workspace/u.test(entry.name)), false);
    assert.equal(cloudApiEnv.some((entry) => entry.name === "HWLAB_CODE_AGENT_PROVIDER" && entry.value === "codex-stdio"), false);
    assert.equal(cloudApiEnv.some((entry) => entry.name === "HWLAB_CODE_AGENT_CODEX_STDIO_SUPERVISOR"), false);
    assert.ok(cloudApiEnv.some((entry) => entry.name === "UNIDESK_MAIN_SERVER_IP" && entry.value === "http://74.48.78.17:18081"));
    assert.ok(cloudApiEnv.some((entry) => entry.name === "HWLAB_KEYCLOAK_ISSUER" && entry.value === "https://auth.74-48-78-17.nip.io/realms/hwlab"));
    assert.ok(cloudApiEnv.some((entry) => entry.name === "HWLAB_KEYCLOAK_CLIENT_ID" && entry.value === "hwlab-cloud-web"));
    assert.ok(cloudApiEnv.some((entry) => entry.name === "HWLAB_KEYCLOAK_CLIENT_SECRET" && entry.valueFrom?.secretKeyRef?.name === "hwlab-cloud-web-client" && entry.valueFrom?.secretKeyRef?.key === "client-secret"));
    assert.ok(cloudApiEnv.some((entry) => entry.name === "HWLAB_OPENFGA_MODE" && entry.value === "enforce"));
    assert.ok(cloudApiEnv.some((entry) => entry.name === "HWLAB_OPENFGA_API_URL" && entry.value === "http://hwlab-openfga.hwlab-v02.svc.cluster.local:8080"));
    assert.ok(cloudApiEnv.some((entry) => entry.name === "HWLAB_OPENFGA_AUTHN_TOKEN" && entry.valueFrom?.secretKeyRef?.name === "hwlab-v02-openfga" && entry.valueFrom?.secretKeyRef?.key === "authn-preshared-key"));
    const deepseekResponsesBridge = deepseekDeployment.spec.template.spec.containers.find((container) => container.name === "responses-bridge");
    assert.ok(deepseekResponsesBridge, "expected DeepSeek responses bridge container");
    const deepseekBridgeEnv = new Map(deepseekResponsesBridge.env.map((entry) => [entry.name, entry.value]));
    assert.equal(deepseekResponsesBridge.command, undefined);
    assert.equal(deepseekBridgeEnv.get("HWLAB_RUNTIME_MODE"), "env-reuse-git-mirror-checkout");
    assert.equal(deepseekBridgeEnv.get("HWLAB_BOOT_SH"), "deploy/runtime/boot/hwlab-deepseek-responses-bridge.sh");
    assert.equal(deepseekBridgeEnv.get("HWLAB_BOOT_COMMIT"), cloudApiCommit);
    assert.equal(deepseekBridgeEnv.get("HWLAB_BOOT_READ_URL"), "http://git-mirror-http.devops-infra.svc.cluster.local/pikasTech/HWLAB.git");
    assert.equal(workloadsJson.items.some((item) => item.metadata?.labels?.["hwlab.pikastech.local/service-id"] === "hwlab-agent-worker"), false);
    const kustomization = await readFile(path.join(outDir, "runtime-v02", "kustomization.yaml"), "utf8");
    assert.doesNotMatch(kustomization, /code-agent-codex-config/u);
    assert.match(kustomization, /openfga\.yaml/u);
    const workloadTemplates = workloadsJson.items.filter((item) => item.spec?.template?.metadata?.labels?.["hwlab.pikastech.local/source-commit"]);
    assert.ok(workloadTemplates.length > 0, "expected rendered pod-template source labels");
    assert.ok(workloadTemplates.some((item) => item.spec.template.metadata.labels["hwlab.pikastech.local/artifact-source-commit"]), "expected artifact source labels on pod templates");
    assert.doesNotMatch(workloads, /HWLAB_GATEWAY_SIMU_URL/u);
    assert.doesNotMatch(workloads, /HWLAB_BOX_SIMU_URL/u);
    assert.doesNotMatch(workloads, /HWLAB_PATCH_PANEL_URL/u);
    assert.doesNotMatch(workloads, /HWLAB_ROUTER_URL/u);
    assert.doesNotMatch(workloads, /HWLAB_TUNNEL_CLIENT_URL/u);

    assert.doesNotMatch(pipeline, /hwlab-gateway-simu-status/u);
    assert.doesNotMatch(pipeline, /tasks\.build-hwlab-cloud-api\.results/u);
    assert.match(pipeline, /--external-service-report-dir \/workspace\/source\/service-results/u);

    const application = await readFile(path.join(outDir, "argocd", "application-v02.yaml"), "utf8");
    assert.match(application, /"prune": true/u);
    assert.match(application, /git-mirror-http\.devops-infra\.svc\.cluster\.local/u);
    assert.doesNotMatch(application, /git@github\.com:pikasTech\/HWLAB\.git/u);

    for (const generatedFile of ["deepseek-proxy.yaml", "postgres.yaml", "openfga.yaml", "node-frpc.yaml"]) {
      const content = await readFile(path.join(outDir, "runtime-v02", generatedFile), "utf8");
      assert.match(content, /"hwlab\.pikastech\.local\/source-commit"/u);
    }
    const frpc = JSON.parse(await readFile(path.join(outDir, "runtime-v02", "node-frpc.yaml"), "utf8"));
    const frpcDeployment = frpc.items.find((item) => item.kind === "Deployment");
    const frpcConfigLabel = frpcDeployment.spec.template.metadata.labels["hwlab.pikastech.local/config-sha256"];
    const frpcConfigAnnotation = frpcDeployment.spec.template.metadata.annotations["hwlab.pikastech.local/config-sha256"];
    assert.equal(frpcDeployment.spec.template.spec.hostNetwork, true);
    assert.equal(frpcDeployment.spec.template.spec.dnsPolicy, "ClusterFirstWithHostNet");
    assert.ok(frpcConfigLabel);
    assert.equal(frpcConfigLabel.length <= 63, true);
    assert.match(frpcConfigAnnotation, /^[a-f0-9]{64}$/u);
    assert.equal(frpcConfigAnnotation.startsWith(frpcConfigLabel), true);
    assert.equal(frpcDeployment.spec.template.metadata.labels["hwlab.pikastech.local/source-commit"], undefined);

    const postgres = JSON.parse(await readFile(path.join(outDir, "runtime-v02", "postgres.yaml"), "utf8"));
    const postgresStatefulSet = postgres.items.find((item) => item.kind === "StatefulSet");
    const postgresMigrationLabel = postgresStatefulSet.spec.template.metadata.labels["hwlab.pikastech.local/migration-sha256"];
    const postgresMigrationAnnotation = postgresStatefulSet.spec.template.metadata.annotations["hwlab.pikastech.local/migration-sha256"];
    assert.ok(postgresMigrationLabel);
    assert.equal(postgresMigrationLabel.length <= 63, true);
    assert.match(postgresMigrationAnnotation, /^[a-f0-9]{64}$/u);
    assert.equal(postgresMigrationAnnotation.startsWith(postgresMigrationLabel), true);
    assert.equal(postgresStatefulSet.spec.template.metadata.labels["hwlab.pikastech.local/source-commit"], undefined);

    const openfga = JSON.parse(await readFile(path.join(outDir, "runtime-v02", "openfga.yaml"), "utf8"));
    const openfgaDeployment = openfga.items.find((item) => item.kind === "Deployment" && item.metadata?.name === "hwlab-openfga");
    const openfgaService = openfga.items.find((item) => item.kind === "Service" && item.metadata?.name === "hwlab-openfga");
    const openfgaMigrate = openfga.items.find((item) => item.kind === "Job" && item.metadata?.name === "hwlab-openfga-migrate");
    assert.ok(openfgaDeployment, "expected v02 OpenFGA deployment");
    assert.ok(openfgaService, "expected v02 OpenFGA ClusterIP service");
    assert.ok(openfgaMigrate, "expected v02 OpenFGA migration job");
    assert.equal(openfgaService.spec.type, "ClusterIP");
    assert.equal(openfga.items.some((item) => item.kind === "Ingress"), false);
    assert.equal(openfgaDeployment.spec.template.spec.containers[0].image, "127.0.0.1:5000/hwlab/openfga:v1.17.0");
    assert.deepEqual(openfgaDeployment.spec.template.spec.containers[0].args, ["run"]);
    assert.deepEqual(openfgaMigrate.spec.template.spec.containers[0].args, ["migrate"]);
    const openfgaEnv = openfgaDeployment.spec.template.spec.containers[0].env;
    assert.ok(openfgaEnv.some((entry) => entry.name === "OPENFGA_DATASTORE_ENGINE" && entry.value === "postgres"));
    assert.ok(openfgaEnv.some((entry) => entry.name === "OPENFGA_DATASTORE_URI" && entry.valueFrom?.secretKeyRef?.name === "hwlab-v02-openfga" && entry.valueFrom?.secretKeyRef?.key === "datastore-uri"));
    assert.ok(openfgaEnv.some((entry) => entry.name === "OPENFGA_AUTHN_METHOD" && entry.value === "preshared"));
    assert.ok(openfgaEnv.some((entry) => entry.name === "OPENFGA_AUTHN_PRESHARED_KEYS" && entry.valueFrom?.secretKeyRef?.name === "hwlab-v02-openfga" && entry.valueFrom?.secretKeyRef?.key === "authn-preshared-key"));
    assert.ok(openfgaEnv.some((entry) => entry.name === "OPENFGA_PLAYGROUND_ENABLED" && entry.value === "false"));
    assert.equal(openfgaDeployment.spec.template.spec.containers[0].readinessProbe.httpGet.path, "/healthz");
    assert.equal(openfgaDeployment.spec.template.metadata.labels["hwlab.pikastech.local/source-commit"], sourceRevision);

    await assert.rejects(() => readFile(path.join(outDir, "tekton-v02", "control-plane-reconciler.yaml"), "utf8"), { code: "ENOENT" });
    await assert.rejects(() => readFile(path.join(outDir, "tekton-v02", "poller.yaml"), "utf8"), { code: "ENOENT" });

    const pipelineRunSample = await readFile(path.join(outDir, "tekton-v02", "pipelinerun.sample.yaml"), "utf8");
    assert.match(pipelineRunSample, /"name": "git-read-url"[\s\S]{0,140}git-mirror-http\.devops-infra\.svc\.cluster\.local/u);
    assert.match(pipelineRunSample, /"name": "git-write-url"[\s\S]{0,140}git-mirror-write\.devops-infra\.svc\.cluster\.local/u);
    assert.match(pipelineRunSample, /"name": "revision"[\s\S]{0,80}"value": "[0-9a-f]{40}"/u);
    assert.match(pipelineRunSample, /"name": "source-branch"[\s\S]{0,80}"value": "v0\.2"/u);
  } finally {
    await rm(outDir, { recursive: true, force: true });
  }
});

test("v03 render keeps node identity as data instead of generated structure", async () => {
  const outDir = await mkdtemp(path.join(os.tmpdir(), "hwlab-v03-render-test-"));
  try {
    const render = spawnSync(process.execPath, [
      "scripts/run-bun.mjs",
      "scripts/gitops-render.mjs",
      "--lane", "v03",
      "--node", "G14",
      "--catalog-path", "deploy/artifact-catalog.v03.json",
      "--image-tag-mode", "full",
      "--source-branch", "v0.3",
      "--gitops-branch", "v0.3-gitops",
      "--out", outDir,
      "--source-revision", "HEAD"
    ], { cwd: process.cwd(), encoding: "utf8" });
    assert.equal(render.status, 0, render.stderr || render.stdout);

    const generatedFiles = await collectGeneratedFiles(outDir);
    const generatedPaths = generatedFiles.map((filePath) => path.relative(outDir, filePath));
    assert.ok(generatedPaths.includes("runtime-v03/node-frpc.yaml"));
    assert.ok(generatedPaths.includes("argocd/application-v03.yaml"));
    assert.ok(generatedPaths.includes("tekton-v03/pipeline.yaml"));
    assert.ok(generatedPaths.includes("runtime-v03/external-postgres.yaml"));
    assert.ok(!generatedPaths.includes("runtime-v03/postgres.yaml"));
    for (const relativePath of generatedPaths) {
      assert.doesNotMatch(relativePath, /g14/i, `legacy node token leaked into generated path ${relativePath}`);
    }

    const workloads = await readFile(path.join(outDir, "runtime-v03", "workloads.yaml"), "utf8");
    const workloadsJson = JSON.parse(workloads);
    const externalPostgres = JSON.parse(await readFile(path.join(outDir, "runtime-v03", "external-postgres.yaml"), "utf8"));
    assert.deepEqual((externalPostgres.items ?? []).map((item) => item.kind), ["Service", "EndpointSlice"]);
    const externalPostgresSlice = (externalPostgres.items ?? []).find((item) => item.kind === "EndpointSlice");
    assert.equal(externalPostgresSlice?.metadata?.name, "g14-platform-postgres-host");
    assert.equal(externalPostgresSlice?.metadata?.labels?.["kubernetes.io/service-name"], "g14-platform-postgres");
    const deepseekProxy = await readFile(path.join(outDir, "runtime-v03", "deepseek-proxy.yaml"), "utf8");
    const deepseekProxyJson = JSON.parse(deepseekProxy);
    const metricsSidecarConfigMaps = [...(workloadsJson.items ?? []), ...(deepseekProxyJson.items ?? [])]
      .filter((item) => item.kind === "Deployment")
      .flatMap((item) => item.spec?.template?.spec?.volumes ?? [])
      .filter((volume) => volume.name === "hwlab-metrics-sidecar")
      .map((volume) => volume.configMap?.name);
    assert.ok(metricsSidecarConfigMaps.length >= 5, "expected v03 observable workloads to mount metrics sidecar config");
    assert.deepEqual([...new Set(metricsSidecarConfigMaps)], ["hwlab-v03-metrics-sidecar"]);
    assert.doesNotMatch(`${workloads}\n${deepseekProxy}`, /hwlab-v02-metrics-sidecar/u);
    const agentRunNodeEnv = (workloadsJson.items ?? [])
      .flatMap((item) => collectContainersFromItem(item))
      .flatMap((container) => container.env ?? [])
      .filter((entry) => entry.name === "HWLAB_CODE_AGENT_AGENTRUN_PROVIDER_ID");
    assert.ok(agentRunNodeEnv.length > 0, "expected AgentRun provider id env to carry configured node id");
    assert.deepEqual([...new Set(agentRunNodeEnv.map((entry) => entry.value))], ["G14"]);

  } finally {
    await rm(outDir, { recursive: true, force: true });
  }
});
