import assert from "node:assert/strict"; import { test } from "bun:test"; import { createCloudApiServer } from "./server.ts"; import { createCloudRuntimeStore } from "../db/runtime-store.ts"; import { createGreenM3StatusRuntimeStore, m3ReadinessRequestJson } from "./server-test-helpers.ts"; test("cloud api /v1 exposes M3 IO control contract without generic frontend hardware RPC", async () => { const server = createCloudApiServer({ env: { HWLAB_M3_IO_CONTROL_ENABLED: "true", HWLAB_M3_GATEWAY_SIMU_1_URL: "http://gateway-1", HWLAB_M3_GATEWAY_SIMU_2_URL: "http://gateway-2", HWLAB_M3_PATCH_PANEL_URL: "http://patch-panel" }, m3IoRequestJson: m3ReadinessRequestJson }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const index = await fetch(`http://127.0.0.1:${port}/v1`); assert.equal(index.status, 200); const indexPayload = await index.json(); assert.equal(indexPayload.m3IoControl.route, "/v1/m3/io"); assert.equal(indexPayload.m3IoControl.boundaries.directFrontendGatewayOrBoxAccess, false); assert.ok(indexPayload.methods.includes("m3.io.do.write")); assert.ok(indexPayload.methods.includes("m3.io.di.read")); const contract = await fetch(`http://127.0.0.1:${port}/v1/m3/io`); assert.equal(contract.status, 200); const payload = await contract.json(); assert.equal(payload.status, "available"); assert.equal(payload.controlReady, true); assert.equal(payload.readiness.status, "ready"); assert.equal(payload.chain.sourceResourceId, "res_boxsimu_1"); assert.equal(payload.chain.targetResourceId, "res_boxsimu_2"); assert.equal(payload.boundaries.frontendCallsOnly, "/v1/m3/io"); assert.equal(payload.boundaries.patchPanelOwnsPropagation, true); assert.equal(payload.boundaries.genericHardwareRpcExposedToFrontend, false); const statusResponse = await fetch(`http://127.0.0.1:${port}/v1/m3/status`); assert.equal(statusResponse.status, 200); const statusPayload = await statusResponse.json(); assert.equal(statusPayload.route, "/v1/m3/status"); assert.equal(statusPayload.chain.sourceResourceId, "res_boxsimu_1"); assert.equal(statusPayload.chain.targetResourceId, "res_boxsimu_2"); assert.equal(statusPayload.gateways.length, 2); assert.equal(statusPayload.gateways.every((gateway) => gateway.online === true), true); assert.equal(statusPayload.boxes.find((box) => box.resourceId === "res_boxsimu_1").ports.DO1.value, false); assert.equal(statusPayload.boxes.find((box) => box.resourceId === "res_boxsimu_2").ports.DI1.value, false); assert.equal(statusPayload.patchPanel.connectionActive, true); assert.equal(statusPayload.io.status, "live"); assert.equal(statusPayload.io.do1.resourceId, "res_boxsimu_1"); assert.equal(statusPayload.io.do1.port, "DO1"); assert.equal(statusPayload.io.do1.value, false); assert.equal(statusPayload.io.do1.sourceKind, "DEV-LIVE"); assert.equal(statusPayload.io.di1.resourceId, "res_boxsimu_2"); assert.equal(statusPayload.io.di1.port, "DI1"); assert.equal(statusPayload.io.di1.value, false); assert.equal(statusPayload.io.di1.sourceKind, "DEV-LIVE"); assert.equal(statusPayload.io.patchPanel.serviceId, "hwlab-patch-panel"); assert.equal(statusPayload.io.patchPanel.connectionActive, true); assert.equal(statusPayload.operation.status, "blocked"); assert.equal(statusPayload.operation.operationId, null); assert.equal(statusPayload.operation.blocker, "runtime_durable_adapter_missing"); assert.equal(statusPayload.trace.status, "blocked"); assert.equal(statusPayload.trace.traceId, null); assert.equal(statusPayload.trace.blocker, "runtime_durable_adapter_missing"); assert.equal(statusPayload.audit.status, "blocked"); assert.equal(statusPayload.audit.auditId, null); assert.equal(statusPayload.audit.blocker, "runtime_durable_adapter_missing"); assert.equal(statusPayload.runtimeDurable.status, "blocked"); assert.equal(statusPayload.runtimeDurable.green, false); assert.equal(statusPayload.runtimeDurable.blocker, "runtime_durable_adapter_missing"); assert.equal(statusPayload.runtimeDurable.readStatus.audit, "read"); assert.equal(statusPayload.runtimeDurable.readStatus.evidence, "read"); assert.equal(statusPayload.runtimeDurable.auditReady, true); assert.equal(statusPayload.runtimeDurable.evidenceReady, true); assert.equal(statusPayload.trust.durableStatus, "blocked"); assert.notEqual(statusPayload.trust.durableStatus, "green"); assert.equal(statusPayload.trust.runtime.blocker, "runtime_durable_adapter_missing"); assert.equal(statusPayload.boundaries.frontendCallsOnly, "/v1/m3/status"); assert.equal(statusPayload.boundaries.directFrontendGatewayOrBoxAccess, false); } finally { await new Promise((resolve, reject) => { server.close((error) => (error ? reject(error) : resolve())); }); } }); test("cloud api /v1/m3/status clears nested runtime blocker when trusted durable evidence is green", async () => { const server = createCloudApiServer({ runtimeStore: createGreenM3StatusRuntimeStore({ blocker: "runtime_durable_not_green" }), env: { HWLAB_M3_IO_CONTROL_ENABLED: "true", HWLAB_M3_GATEWAY_SIMU_1_URL: "http://gateway-1", HWLAB_M3_GATEWAY_SIMU_2_URL: "http://gateway-2", HWLAB_M3_PATCH_PANEL_URL: "http://patch-panel" }, m3IoRequestJson: m3ReadinessRequestJson }); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); try { const { port } = server.address(); const statusResponse = await fetch(`http://127.0.0.1:${port}/v1/m3/status`); assert.equal(statusResponse.status, 200); const statusPayload = await statusResponse.json(); assert.equal(statusPayload.contractVersion, "m3-status-v1"); assert.equal(statusPayload.status, "live"); assert.equal(statusPayload.sourceKind, "DEV-LIVE"); assert.equal(statusPayload.patchPanel.connectionActive, true); assert.equal(statusPayload.trust.durableStatus, "green"); assert.equal(statusPayload.trust.blocker, null); assert.equal(statusPayload.trust.runtime.status, "ready"); assert.equal(statusPayload.trust.runtime.blocker, null); assert.equal(statusPayload.trust.operationId, "op_m3_di_read_75fc664f-e94f-41e8-816e-aff9fdb3666f"); assert.equal(statusPayload.trust.traceId, "trc_hotfix_307_verify_1779527747043_read_false"); assert.equal(statusPayload.trust.auditId, "aud_m3_di_read_d5afd84b-3a9d-475f-894f-dff8ea5f49ff_succeeded"); assert.equal(statusPayload.trust.evidenceId, "evd_m3_di_read_75fc664f-e94f-41e8-816e-aff9fdb3666f_succeeded"); assert.equal(statusPayload.operation.status, "read"); assert.equal(statusPayload.operation.operationId, "op_m3_di_read_75fc664f-e94f-41e8-816e-aff9fdb3666f"); assert.equal(statusPayload.operation.blocker, null); assert.equal(statusPayload.trace.status, "read"); assert.equal(statusPayload.trace.traceId, "trc_hotfix_307_verify_1779527747043_read_false"); assert.equal(statusPayload.trace.blocker, null); assert.equal(statusPayload.audit.status, "read"); assert.equal(statusPayload.audit.auditId, "aud_m3_di_read_d5afd84b-3a9d-475f-894f-dff8ea5f49ff_succeeded"); assert.equal(statusPayload.audit.blocker, null); assert.equal(statusPayload.runtimeDurable.status, "green"); assert.equal(statusPayload.runtimeDurable.green, true); assert.equal(statusPayload.runtimeDurable.blocker, null); assert.equal(statusPayload.runtimeDurable.auditReady, true); assert.equal(statusPayload.runtimeDurable.evidenceReady, true); } finally { await new Promise((resolve, reject) => { server.close((error) => (error ? reject(error) : resolve())); }); } }); test("cloud api /v1/diagnostics/gate exposes Chinese single-table rows from live backend aggregation", async () => { const runtimeStore = createCloudRuntimeStore({ now: () => "2026-05-23T00:00:00.000Z" }); const server = createCloudApiServer({ accessController: { required: false, async authenticate() { return { ok: true, actor: { id: "usr_m3_diagnostics", role: "admin" }, session: { id: "uss_m3_diagnostics" } }; } }, runtimeStore, env: { HWLAB_M3_GATEWAY_SIMU_1_URL: "http://gateway-1", HWLAB_M3_GATEWAY_SIMU_2_URL: "http://gateway-2", HWLAB_M3_PATCH_PANEL_URL: "http://patch-panel", HWLAB_COMMIT_ID: "abc123456789", HWLAB_IMAGE_TAG: "abc1234" }, m3IoRequestJson: async () => ({ ok: false, status: 503, body: { error: { message: "gateway 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/diagnostics/gate`); assert.equal(response.status, 200); const payload = await response.json(); assert.equal(payload.route, "/v1/diagnostics/gate"); assert.equal(payload.contractVersion, "gate-diagnostics-table-v1"); assert.equal(payload.sourceKind, "LIVE-BACKEND"); assert.equal(payload.liveBackend, true); assert.deepEqual(payload.columns, ["类别", "检查项", "状态", "归因对象", "关键细节", "证据/trace", "更新时间", "下一步"]); assert.equal(payload.safety.frontendHardcodedRows, false); assert.equal(payload.safety.sourceFixturePromoted, false); assert.equal(payload.safety.blockedIsGreen, false); assert.equal(payload.safety.hardwareControlSurface, false); assert.equal(payload.safety.codeAgentConversationSurface, false); assert.ok(payload.rows.length >= 6); assert.ok(payload.rows.every((row) => row.sourceKind === "LIVE-BACKEND")); assert.ok(payload.rows.every((row) => ["通过", "阻塞", "失败", "待验证", "信息"].includes(row.status))); assert.ok(payload.rows.some((row) => row.category === "M3" && row.status === "阻塞")); assert.ok(payload.rows.some((row) => row.check === "/health/live readiness")); assert.ok(payload.rows.some((row) => row.check === "audit.event.query")); assert.ok(payload.rows.some((row) => row.check === "evidence.record.query")); assert.ok(payload.rows.some((row) => row.check === "Cloud API 实况身份" && row.status === "通过")); assert.equal( payload.rows.some((row) => row.status === "阻塞" && row.statusKey === "pass"), false, "blocked rows must not be mapped to pass" ); } finally { await new Promise((resolve, reject) => { server.close((error) => (error ? reject(error) : resolve())); }); } }); test("cloud api /v1/m3/io returns structured blocked payload when gateway is unavailable", async () => { const runtimeStore = createCloudRuntimeStore({ now: () => "2026-05-23T00:00:00.000Z" }); const server = createCloudApiServer({ runtimeStore, env: { HWLAB_M3_IO_CONTROL_ENABLED: "true", HWLAB_M3_GATEWAY_SIMU_1_URL: "http://gateway-1", HWLAB_M3_GATEWAY_SIMU_2_URL: "http://gateway-2", HWLAB_M3_PATCH_PANEL_URL: "http://patch-panel" }, m3IoRequestJson: async () => ({ ok: false, status: 503, body: { error: { message: "gateway 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/m3/io`, { method: "POST", headers: { "content-type": "application/json", "x-trace-id": "trc_http_m3_gateway_missing", "x-actor-id": "usr_http_m3" }, body: JSON.stringify({ action: "do.write", value: true }) }); assert.equal(response.status, 200); const payload = await response.json(); assert.equal(payload.status, "blocked"); assert.equal(payload.accepted, false); assert.equal(payload.route, "/v1/m3/io"); assert.equal(payload.method, "POST"); assert.equal(payload.traceId, "trc_http_m3_gateway_missing"); assert.equal(payload.operationId, null); assert.equal(payload.auditId, null); assert.equal(payload.evidenceId, null); assert.equal(payload.audit.status, "not_written"); assert.equal(payload.evidence.status, "blocked"); assert.equal(payload.readback, null); assert.match(payload.blocker.zh, /gateway 未注册\/不可用/u); assert.equal(payload.controlPath.cloudApi, true); assert.equal(payload.controlPath.frontendBypass, false); assert.equal(payload.evidenceState.sourceKind, "BLOCKED"); } finally { await new Promise((resolve, reject) => { server.close((error) => (error ? reject(error) : resolve())); }); } });