import assert from "node:assert/strict"; import { spawnSync } from "node:child_process"; import { mkdtemp, readFile, rm } 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; } 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 render = spawnSync(process.execPath, [ "scripts/g14-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); 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\.spec\?\.template\?\.metadata\?\.labels\?\.\[\\"hwlab\.pikastech\.local\/source-commit\\"\]/u); const removedServiceIds = [ "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-agent-mgr", "hwlab-agent-worker", "hwlab-device-pod", "hwlab-gateway", "hwlab-edge-proxy", "hwlab-cli", "hwlab-agent-skills" ]; for (let index = 0; index < retainedServiceIds.length; index += 1) { const serviceId = retainedServiceIds[index]; const task = taskByName(pipelineJson, `build-${serviceId}`); const expectedRunAfter = index === 0 ? ["plan-artifacts"] : [`build-${retainedServiceIds[index - 1]}`]; assert.deepEqual(task.runAfter, expectedRunAfter); } const workloads = await readFile(path.join(outDir, "runtime-v02", "workloads.yaml"), "utf8"); const services = await readFile(path.join(outDir, "runtime-v02", "services.yaml"), "utf8"); for (const serviceId of removedServiceIds) { assert.doesNotMatch(workloads, new RegExp(serviceId, "u")); assert.doesNotMatch(services, new RegExp(serviceId, "u")); } assert.doesNotMatch(pipeline, /hwlab-gateway-simu-status/u); assert.match(pipeline, /hwlab-cloud-api-status/u); const application = await readFile(path.join(outDir, "argocd", "application-v02.yaml"), "utf8"); assert.match(application, /"prune": true/u); for (const generatedFile of ["deepseek-proxy.yaml", "postgres.yaml", "g14-frpc.yaml"]) { const content = await readFile(path.join(outDir, "runtime-v02", generatedFile), "utf8"); assert.match(content, /"hwlab\.pikastech\.local\/source-commit"/u); } const reconciler = await readFile(path.join(outDir, "tekton-v02", "control-plane-reconciler.yaml"), "utf8"); assert.match(reconciler, /cross-namespace-rbac-bootstrap-managed/u); assert.match(reconciler, /HWLAB_RECONCILE_CROSS_NAMESPACE_RBAC/u); } finally { await rm(outDir, { recursive: true, force: true }); } });