import assert from "node:assert/strict"; import { mkdtemp, mkdir, rm, writeFile } from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { test } from "bun:test"; import { createCloudApiServer } from "./server.ts"; test("cloud api aggregates live HWLAB build times from health and controlled deploy/catalog metadata", async () => { const healthByPath = new Map([ ["/hwlab-cloud-web/health/live", { serviceId: "hwlab-cloud-web", status: "ok", revision: "webabcdef123456", commit: { id: "webabcdef123456", source: "test" }, image: { reference: "127.0.0.1:5000/hwlab/hwlab-cloud-web:webabcd", tag: "webabcd", digest: "sha256:" + "1".repeat(64) }, build: { createdAt: "2026-05-23T02:10:00.000Z", metadataSource: "runtime-env:HWLAB_BUILD_CREATED_AT" } }], ["/hwlab-gateway/health/live", { serviceId: "hwlab-gateway", status: "ok", revision: "48dfbf9", commit: { id: "48dfbf9", source: "test" }, image: { reference: "127.0.0.1:5000/hwlab/hwlab-gateway:48dfbf9", tag: "48dfbf9", digest: "sha256:" + "2".repeat(64) } }], ["/hwlab-agent-skills/health/live", { serviceId: "hwlab-agent-skills", status: "ok", revision: "skillsabcdef123456", commit: { id: "skillsabcdef123456", source: "test" }, image: { reference: "127.0.0.1:5000/hwlab/hwlab-agent-skills:skillsabcd", tag: "skillsabcd", digest: "sha256:" + "5".repeat(64) } }], ["/external/health", { serviceId: "postgres", status: "ok", image: { reference: "postgres:16", tag: "16" } }] ]); const liveBuildMetadata = { deployManifest: { services: [ { serviceId: "hwlab-cloud-web", image: "127.0.0.1:5000/hwlab/hwlab-cloud-web:webcat", namespace: "hwlab-dev", profile: "dev", healthPath: "/health/live", replicas: 1 }, { serviceId: "hwlab-gateway", image: "127.0.0.1:5000/hwlab/hwlab-gateway:3df89fe", namespace: "hwlab-dev", profile: "dev", healthPath: "/health/live", replicas: 1 }, { serviceId: "hwlab-agent-skills", image: "127.0.0.1:5000/hwlab/hwlab-agent-skills:skillsabcd", namespace: "hwlab-dev", profile: "dev", healthPath: "/health/live", replicas: 1, env: { HWLAB_COMMIT_ID: "skillsabcdef123456", HWLAB_IMAGE: "127.0.0.1:5000/hwlab/hwlab-agent-skills:skillsabcd", HWLAB_IMAGE_TAG: "skillsabcd", HWLAB_BUILD_CREATED_AT: "2026-05-23T05:30:00.000Z", HWLAB_BUILD_SOURCE: "deploy-env-test" } }, { serviceId: "hwlab-edge-proxy", image: "127.0.0.1:5000/hwlab/hwlab-edge-proxy:edgecat", namespace: "hwlab-dev", profile: "dev", healthPath: "/health", replicas: 1 }, { serviceId: "hwlab-extra-lab", image: "127.0.0.1:5000/hwlab/hwlab-extra-lab:extracat", namespace: "hwlab-dev", profile: "dev", healthPath: "/health/live", replicas: 1 } ] }, artifactCatalog: { services: [ { serviceId: "hwlab-gateway", commitId: "3df89fe", image: "127.0.0.1:5000/hwlab/hwlab-gateway:3df89fe", imageTag: "3df89fe", digest: "sha256:" + "3".repeat(64), namespace: "hwlab-dev", profile: "dev", healthPath: "/health/live", buildCreatedAt: "2026-05-23T04:20:00.000Z", buildSource: "artifact-catalog-test" }, { serviceId: "hwlab-agent-skills", commitId: "skillsabcdef123456", image: "127.0.0.1:5000/hwlab/hwlab-agent-skills:skillsabcd", imageTag: "skillsabcd", digest: "sha256:" + "5".repeat(64), namespace: "hwlab-dev", profile: "dev", healthPath: "/health/live", buildCreatedAt: null, buildSource: null }, { serviceId: "hwlab-extra-lab", commitId: "extracatabcdef123456", image: "127.0.0.1:5000/hwlab/hwlab-extra-lab:extracat", imageTag: "extracat", digest: "sha256:" + "4".repeat(64), namespace: "hwlab-dev", profile: "dev", healthPath: "/health/live", buildCreatedAt: "2026-05-23T03:00:00.000Z", buildSource: "artifact-catalog-test" } ] } }; const server = createCloudApiServer({ env: { PATH: "", HWLAB_COMMIT_ID: "apiabcdef123456", HWLAB_IMAGE: "127.0.0.1:5000/hwlab/hwlab-cloud-api:apiabcd", HWLAB_IMAGE_TAG: "apiabcd", HWLAB_BUILD_CREATED_AT: "2026-05-23T01:00:00.000Z", HOSTNAME: "hwlab-cloud-api-test-pod", POD_NAMESPACE: "hwlab-dev", HWLAB_RUNTIME_CONTAINER: "hwlab-cloud-api", HWLAB_CLOUD_WEB_SERVICE_URL: "http://live.test/hwlab-cloud-web", HWLAB_GATEWAY_URL: "http://live.test/hwlab-gateway", HWLAB_GATEWAY_SIMU_URL: "http://live.test/missing-gateway-simu", HWLAB_BOX_SIMU_URL: "http://live.test/missing-box-simu", HWLAB_PATCH_PANEL_URL: "http://live.test/missing-patch-panel", HWLAB_ROUTER_URL: "http://live.test/missing-router", HWLAB_TUNNEL_CLIENT_URL: "http://live.test/missing-tunnel-client", HWLAB_EDGE_PROXY_URL: "http://live.test/external", HWLAB_CLI_URL: "http://live.test/missing-cli", HWLAB_AGENT_SKILLS_URL: "http://live.test/hwlab-agent-skills", HWLAB_EXTRA_LAB_URL: "http://live.test/missing-extra-lab" }, liveBuildMetadata, fetchImpl: async (url) => { const path = new URL(url).pathname; const payload = healthByPath.get(path); if (!payload) { return { ok: false, status: 503, async text() { return JSON.stringify({ error: "offline" }); } }; } return { ok: true, status: 200, async text() { return JSON.stringify(payload); } }; } }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const response = await fetch(`http://127.0.0.1:${port}/v1/live-builds`); assert.equal(response.status, 200); const payload = await response.json(); assert.equal(payload.contractVersion, "live-builds-v1"); assert.equal(payload.latest.serviceId, "hwlab-agent-skills"); assert.equal(payload.latest.build.createdAt, "2026-05-23T05:30:00.000Z"); assert.equal(payload.latest.image.tag, "skillsabcd"); assert.equal(payload.latest.commit.id, "skillsabcdef123456"); assert.equal(payload.latest.build.metadataSource, "deploy-env:HWLAB_BUILD_CREATED_AT"); assert.equal(payload.latest.build.liveMetadataMatch.metadata.imageTag, "skillsabcd"); assert.match(payload.latest.build.liveHealthMissingReason, /匹配的 deploy metadata/u); const api = payload.services.find((service) => service.serviceId === "hwlab-cloud-api"); assert.equal(api.runtime.pod.name, "hwlab-cloud-api-test-pod"); assert.equal(api.runtime.unideskRoute, "G14:k3s:hwlab-dev:pod:hwlab-cloud-api-test-pod:hwlab-cloud-api"); assert.equal(payload.counts.total, payload.services.length); assert.equal(payload.counts.external, 2); assert.equal(payload.counts.withBuildTime, 3); const gateway = payload.services.find((service) => service.serviceId === "hwlab-gateway"); assert.equal(gateway.build.createdAt, null); assert.equal(gateway.image.tag, "48dfbf9"); assert.equal(gateway.revision, "48dfbf9"); assert.equal(gateway.build.metadataSource, "live-health:controlled-metadata-mismatch"); assert.equal(gateway.build.unavailableReason, "构建时间不可用:live tag 与 catalog metadata 不匹配"); assert.match(gateway.build.unavailableDetail, /live tag 48dfbf9/u); assert.match(gateway.build.unavailableDetail, /metadata tag 3df89fe/u); const extra = payload.services.find((service) => service.serviceId === "hwlab-extra-lab"); assert.equal(extra.build.createdAt, null); assert.match(extra.build.unavailableReason, /当前 live health 不可用/u); assert.ok(payload.services.some((service) => service.kind === "external" && service.serviceId === "hwlab-edge-proxy" && service.healthUrl.endsWith("/health"))); assert.ok(payload.services.some((service) => service.kind === "external" && service.serviceId === "hwlab-frpc" && service.image.tag === "v0.68.1")); assert.ok(payload.services.some((service) => service.serviceId === "hwlab-gateway")); } finally { await new Promise((resolve, reject) => { server.close((error) => (error ? reject(error) : resolve())); }); } }); test("cloud api ignores old repository reports and keeps missing buildCreatedAt unavailable", async () => { const tempRoot = await mkdtemp(path.join(os.tmpdir(), "hwlab-live-builds-")); const deployDir = path.join(tempRoot, "deploy"); const oldReportDir = path.join(tempRoot, "reports", "dev-gate"); await mkdir(deployDir, { recursive: true }); await mkdir(oldReportDir, { recursive: true }); await writeFile(path.join(deployDir, "deploy.yaml"), `${JSON.stringify({ services: [ { serviceId: "hwlab-gateway", image: "127.0.0.1:5000/hwlab/hwlab-gateway:48dfbf9", namespace: "hwlab-dev", profile: "dev", healthPath: "/health/live", replicas: 1 } ] }, null, 2)}\n`); await writeFile(path.join(deployDir, "artifact-catalog.dev.json"), `${JSON.stringify({ services: [ { serviceId: "hwlab-gateway", commitId: "48dfbf9abcdef", image: "127.0.0.1:5000/hwlab/hwlab-gateway:48dfbf9", imageTag: "48dfbf9", digest: "sha256:" + "6".repeat(64), namespace: "hwlab-dev", profile: "dev", healthPath: "/health/live", buildCreatedAt: null, buildSource: null } ] }, null, 2)}\n`); await writeFile(path.join(oldReportDir, "dev-artifacts.json"), `${JSON.stringify({ artifactPublish: { services: [ { serviceId: "hwlab-gateway", sourceCommitId: "48dfbf9abcdef", image: "127.0.0.1:5000/hwlab/hwlab-gateway:48dfbf9", imageTag: "48dfbf9", digest: "sha256:" + "6".repeat(64), buildCreatedAt: "2099-01-01T00:00:00.000Z", buildSource: "old-report-must-not-be-read" } ] } }, null, 2)}\n`); const observe = async () => { const server = createCloudApiServer({ env: { PATH: "", HWLAB_GATEWAY_URL: "http://live.test/hwlab-gateway" }, repoRoot: tempRoot, fetchImpl: async (url) => { if (new URL(url).pathname === "/hwlab-gateway/health/live") { return { ok: true, status: 200, async text() { return JSON.stringify({ serviceId: "hwlab-gateway", status: "ok", revision: "48dfbf9abcdef", commit: { id: "48dfbf9abcdef", source: "test" }, image: { reference: "127.0.0.1:5000/hwlab/hwlab-gateway:48dfbf9", tag: "48dfbf9", digest: "sha256:" + "6".repeat(64) } }); } }; } return { ok: false, status: 503, async text() { return JSON.stringify({ error: "offline" }); } }; } }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const response = await fetch(`http://127.0.0.1:${port}/v1/live-builds`); assert.equal(response.status, 200); return response.json(); } finally { await new Promise((resolve, reject) => { server.close((error) => (error ? reject(error) : resolve())); }); } }; try { const withOldReport = await observe(); await rm(path.join(tempRoot, "reports"), { recursive: true, force: true }); const withoutOldReport = await observe(); const summarize = (payload) => { const manager = payload.services.find((service) => service.serviceId === "hwlab-gateway"); return { latest: payload.latest, source: payload.source, createdAt: manager.build.createdAt, metadataSource: manager.build.metadataSource, unavailableReason: manager.build.unavailableReason, unavailableDetail: manager.build.unavailableDetail, liveMetadataMatch: manager.build.liveMetadataMatch, counts: payload.counts }; }; assert.deepEqual(summarize(withOldReport), summarize(withoutOldReport)); const manager = withOldReport.services.find((service) => service.serviceId === "hwlab-gateway"); assert.equal(withOldReport.latest, null); assert.equal(manager.build.createdAt, null); assert.equal(Object.hasOwn(withOldReport.source, "artifactReportSource"), false); assert.equal(JSON.stringify(withOldReport).includes("2099-01-01T00:00:00.000Z"), false); assert.equal(JSON.stringify(withOldReport).includes("artifact-report"), false); } finally { await rm(tempRoot, { recursive: true, force: true }); } });