#!/usr/bin/env node import assert from "node:assert/strict"; import { readFile } from "node:fs/promises"; import path from "node:path"; import { fileURLToPath } from "node:url"; const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); const namespace = "hwlab-dev"; const m3EndpointMap = Object.freeze({ res_boxsimu_2: "http://hwlab-box-simu-2.hwlab-dev.svc.cluster.local:7201" }); const m3WiringConfig = Object.freeze({ wiringConfigId: "wir_m3_do1_di1", projectId: "prj_m3_hardware_loop", gatewaySessionId: "gws_gwsimu_1", name: "M3 simulated DO1 to DI1 patch wiring", status: "active", connections: [ { from: { resourceId: "res_boxsimu_1", port: "DO1" }, to: { resourceId: "res_boxsimu_2", port: "DI1" }, mode: "exclusive" } ], constraints: { propagation: "patch-panel-only", localLoopbackSubstituteAllowed: false }, createdAt: "2026-05-21T00:00:00.000Z", updatedAt: "2026-05-21T00:00:00.000Z" }); const expected = Object.freeze({ "hwlab-box-simu": { kind: "StatefulSet", replicas: 2, serviceName: "hwlab-box-simu", instanceNames: ["hwlab-box-simu-1", "hwlab-box-simu-2"], env: { HWLAB_BOX_ID: ["boxsimu_1", "boxsimu_2"], HWLAB_BOX_RESOURCE_ID: ["res_boxsimu_1", "res_boxsimu_2"], HWLAB_GATEWAY_SESSION_ID: ["gws_gwsimu_1", "gws_gwsimu_2"] } }, "hwlab-gateway-simu": { kind: "StatefulSet", replicas: 2, serviceName: "hwlab-gateway-simu", instanceNames: ["hwlab-gateway-simu-1", "hwlab-gateway-simu-2"], env: { HWLAB_GATEWAY_ID: ["gwsimu_1", "gwsimu_2"], HWLAB_BOX_IDS: ["boxsimu_1", "boxsimu_2"], HWLAB_BOX_RESOURCES: ["res_boxsimu_1", "res_boxsimu_2"], HWLAB_BOX_URLS: [ "http://hwlab-box-simu-1.hwlab-dev.svc.cluster.local:7201", "http://hwlab-box-simu-2.hwlab-dev.svc.cluster.local:7201" ] } }, "hwlab-patch-panel": { kind: "Deployment", replicas: 1, m3Route: { fromResourceId: "res_boxsimu_1", fromPort: "DO1", patchPanelServiceId: "hwlab-patch-panel", toResourceId: "res_boxsimu_2", toPort: "DI1" }, env: { HWLAB_PATCH_PANEL_ENDPOINT_MAP: m3EndpointMap, HWLAB_PATCH_PANEL_WIRING_CONFIG: m3WiringConfig } } }); const expectedInstanceServices = Object.freeze([ { serviceId: "hwlab-box-simu", name: "hwlab-box-simu-1", instanceId: "res_boxsimu_1", podName: "hwlab-box-simu-0", port: 7201 }, { serviceId: "hwlab-box-simu", name: "hwlab-box-simu-2", instanceId: "res_boxsimu_2", podName: "hwlab-box-simu-1", port: 7201 }, { serviceId: "hwlab-gateway-simu", name: "hwlab-gateway-simu-1", instanceId: "gwsimu_1", podName: "hwlab-gateway-simu-0", port: 7101 }, { serviceId: "hwlab-gateway-simu", name: "hwlab-gateway-simu-2", instanceId: "gwsimu_2", podName: "hwlab-gateway-simu-1", port: 7101 } ]); async function readJson(relativePath) { const raw = await readFile(path.join(repoRoot, relativePath), "utf8"); return JSON.parse(raw); } function listItems(document) { return document?.kind === "List" ? document.items ?? [] : [document]; } function envMap(env = []) { return Object.fromEntries(env.map((item) => [item.name, item.value ?? ""])); } function splitList(value) { return String(value ?? "") .split(",") .map((item) => item.trim()) .filter(Boolean); } function parseEnvValue(value) { const text = String(value ?? "").trim(); if (text.startsWith("{")) { return JSON.parse(text); } return text; } function assertSameMembers(actual, required, label) { assert.deepEqual([...actual].sort(), [...required].sort(), label); } function workloadByName(workloads) { return new Map( listItems(workloads) .filter((item) => item?.kind === "Deployment" || item?.kind === "StatefulSet") .map((item) => [item.metadata?.name, item]) ); } function servicesByName(services) { return new Map( listItems(services) .filter((item) => item?.kind === "Service") .map((item) => [item.metadata?.name, item]) ); } function assertDevK8sCardinality(workloads) { const byName = workloadByName(workloads); for (const [serviceId, requirement] of Object.entries(expected)) { const deployment = byName.get(serviceId); assert.ok(deployment, `deploy/k8s/base/workloads.yaml missing ${serviceId} workload`); assert.equal(deployment.kind, requirement.kind, `${serviceId} workload kind`); assert.equal(deployment.metadata?.namespace, namespace, `${serviceId} namespace`); assert.equal(deployment.spec?.replicas, requirement.replicas, `${serviceId} replicas`); if (requirement.serviceName) { assert.equal(deployment.spec?.serviceName, requirement.serviceName, `${serviceId} stateful serviceName`); } const labels = deployment.metadata?.labels ?? {}; assert.equal(labels["hwlab.pikastech.local/service-id"], serviceId, `${serviceId} service label`); const container = deployment.spec?.template?.spec?.containers?.find((item) => item.name === serviceId); assert.ok(container, `${serviceId} container`); const env = envMap(container.env); for (const [name, values] of Object.entries(requirement.env)) { if (Array.isArray(values)) { assertSameMembers(splitList(env[name]), values, `${serviceId} ${name}`); } else { assert.deepEqual(parseEnvValue(env[name]), values, `${serviceId} ${name}`); } } } } function assertDevK8sInstanceServices(services) { const byName = servicesByName(services); for (const expectedService of expectedInstanceServices) { const service = byName.get(expectedService.name); assert.ok(service, `deploy/k8s/base/services.yaml missing ${expectedService.name}`); assert.equal(service.metadata?.namespace, namespace, `${expectedService.name} namespace`); assert.equal( service.metadata?.labels?.["hwlab.pikastech.local/service-id"], expectedService.serviceId, `${expectedService.name} service-id label` ); assert.equal( service.metadata?.labels?.["hwlab.pikastech.local/instance-id"], expectedService.instanceId, `${expectedService.name} instance-id label` ); assert.equal( service.spec?.selector?.["statefulset.kubernetes.io/pod-name"], expectedService.podName, `${expectedService.name} pod selector` ); const port = service.spec?.ports?.find((entry) => entry.name === "http") ?? service.spec?.ports?.[0]; assert.equal(port?.port, expectedService.port, `${expectedService.name} port`); assert.equal(port?.targetPort, "http", `${expectedService.name} targetPort`); } } function assertDeployManifestCardinality(deploy) { assert.equal(deploy.environment, "dev", "deploy/deploy.json environment"); assert.equal(deploy.namespace, namespace, "deploy/deploy.json namespace"); assert.equal(deploy.profiles?.prod?.enabled, false, "deploy/deploy.json prod disabled"); const byId = new Map((deploy.services ?? []).map((service) => [service.serviceId, service])); for (const [serviceId, requirement] of Object.entries(expected)) { const service = byId.get(serviceId); assert.ok(service, `deploy/deploy.json missing ${serviceId}`); assert.equal(service.namespace, namespace, `${serviceId} deploy namespace`); assert.equal(service.profile, "dev", `${serviceId} deploy profile`); assert.equal(service.replicas, requirement.replicas, `${serviceId} deploy replicas`); if (requirement.instanceNames) { assertSameMembers(service.instanceNames ?? [], requirement.instanceNames, `${serviceId} deploy instanceNames`); assert.equal(service.identityStrategy, "indexed-workloads", `${serviceId} deploy identityStrategy`); } if (requirement.m3Route) { assert.deepEqual(service.m3Route, requirement.m3Route, `${serviceId} deploy first-class M3 route`); } for (const [name, values] of Object.entries(requirement.env)) { if (Array.isArray(values)) { assertSameMembers(splitList(service.env?.[name]), values, `${serviceId} deploy ${name}`); } else { assert.deepEqual(parseEnvValue(service.env?.[name]), values, `${serviceId} deploy ${name}`); } } } } function assertDeployManifestInstanceMappings(deploy) { const mappings = deploy.k3s?.serviceMappings ?? []; const byName = new Map(mappings.map((mapping) => [mapping.name, mapping])); for (const expectedService of expectedInstanceServices) { const mapping = byName.get(expectedService.name); assert.ok(mapping, `deploy/deploy.json missing k3s serviceMapping ${expectedService.name}`); assert.equal(mapping.serviceId, expectedService.serviceId, `${expectedService.name} serviceId`); assert.equal(mapping.namespace, namespace, `${expectedService.name} namespace`); assert.equal(mapping.port, expectedService.port, `${expectedService.name} port`); assert.equal(mapping.targetPort, "http", `${expectedService.name} targetPort`); } } function assertM3FixtureWiring(topology) { assert.deepEqual(topology.services?.patchPanel?.activeConnections, [ { fromResourceId: "res_boxsimu_1", fromPort: "DO1", toResourceId: "res_boxsimu_2", toPort: "DI1" } ], "M3 fixture active patch-panel wiring"); assert.deepEqual(topology.wiringConfig?.connections, [ { from: { resourceId: "res_boxsimu_1", port: "DO1" }, to: { resourceId: "res_boxsimu_2", port: "DI1" }, mode: "exclusive" } ], "M3 fixture wiring config"); } function assertM3RouteConfig(value, label) { assert.deepEqual(value, m3WiringConfig, label); assert.deepEqual(value.connections, [ { from: { resourceId: "res_boxsimu_1", port: "DO1" }, to: { resourceId: "res_boxsimu_2", port: "DI1" }, mode: "exclusive" } ], `${label} required route`); assert.equal(value.constraints.propagation, "patch-panel-only", `${label} propagation`); assert.equal(value.constraints.localLoopbackSubstituteAllowed, false, `${label} loopback substitute`); } const [deploy, workloads, services, topology] = await Promise.all([ readJson("deploy/deploy.json"), readJson("deploy/k8s/base/workloads.yaml"), readJson("deploy/k8s/base/services.yaml"), readJson("fixtures/mvp/m3-hardware-loop/topology.json") ]); const patchPanelWorkloadEnv = envMap( workloadByName(workloads) .get("hwlab-patch-panel") ?.spec?.template?.spec?.containers?.find((item) => item.name === "hwlab-patch-panel") ?.env ); assertDeployManifestCardinality(deploy); assertDeployManifestInstanceMappings(deploy); assertDevK8sCardinality(workloads); assertDevK8sInstanceServices(services); assertM3FixtureWiring(topology); assertM3RouteConfig( parseEnvValue(patchPanelWorkloadEnv.HWLAB_PATCH_PANEL_WIRING_CONFIG), "DEV workload first-class M3 patch-panel wiring config" ); assertM3RouteConfig( parseEnvValue(deploy.services.find((service) => service.serviceId === "hwlab-patch-panel")?.env?.HWLAB_PATCH_PANEL_WIRING_CONFIG), "deploy manifest first-class M3 patch-panel wiring config" ); console.log("validated DEV M3 manifest cardinality: 2 distinct box-simu, 2 distinct gateway-simu, 1 patch-panel, M3 DO1->DI1 wiring");