#!/usr/bin/env bun import assert from "node:assert/strict"; import { createServer } from "node:net"; import { createCloudApiServer } from "../internal/cloud/server.ts"; import { ENVIRONMENT_DEV } from "../internal/protocol/index.mjs"; const previousEnv = { HWLAB_CLOUD_DB_URL: process.env.HWLAB_CLOUD_DB_URL, HWLAB_CLOUD_DB_SSL_MODE: process.env.HWLAB_CLOUD_DB_SSL_MODE }; function logOk(name) { process.stdout.write(`[cloud-api-runtime-smoke] ok ${name}\n`); } async function freePort() { const server = createServer(); await new Promise((resolve, reject) => { server.once("error", reject); server.listen(0, "127.0.0.1", resolve); }); const address = server.address(); const port = typeof address === "object" && address ? address.port : null; await new Promise((resolve, reject) => { server.close((error) => (error ? reject(error) : resolve())); }); return port; } async function requestJson(url, options = {}) { const response = await fetch(url, { ...options, headers: { "content-type": "application/json", ...(options.headers ?? {}) } }); const text = await response.text(); return { response, body: text ? JSON.parse(text) : null }; } function rpcBody(id, method, params = {}) { return JSON.stringify({ jsonrpc: "2.0", id, method, params, meta: { traceId: `trc_${id}`, actorId: "usr_cloud_api_runtime_smoke", serviceId: "hwlab-cloud-api", environment: ENVIRONMENT_DEV } }); } async function rpc(baseUrl, id, method, params = {}) { const { response, body } = await requestJson(`${baseUrl}/rpc`, { method: "POST", body: rpcBody(id, method, params) }); assert.equal(response.status, 200, `${method} http status`); assert.equal(body.jsonrpc, "2.0", `${method} JSON-RPC version`); assert.equal(Object.hasOwn(body, "error"), false, body.error?.message); return body.result; } async function run() { delete process.env.HWLAB_CLOUD_DB_URL; delete process.env.HWLAB_CLOUD_DB_SSL_MODE; const port = await freePort(); const server = createCloudApiServer(); await new Promise((resolve) => server.listen(port, "127.0.0.1", resolve)); const baseUrl = `http://127.0.0.1:${port}`; try { const health = await requestJson(`${baseUrl}/health`); assert.equal(health.response.status, 200); assert.equal(health.body.serviceId, "hwlab-cloud-api"); assert.equal(health.body.environment, ENVIRONMENT_DEV); assert.equal(health.body.status, "degraded"); assert.equal(health.body.db.status, "blocked"); assert.equal(health.body.db.connected, false); assert.equal(health.body.db.liveConnected, false); assert.equal(health.body.db.liveDbEvidence, false); assert.equal(health.body.db.connectionAttempted, false); assert.equal(health.body.db.connectionResult, "not_attempted_missing_env"); assert.equal(health.body.db.endpointSource, "secret-url-host"); assert.equal(health.body.db.optionalPublicDnsAlias.source, "optional-public-dns-alias"); assert.equal(health.body.db.optionalPublicDnsAlias.requiredForReadiness, false); assert.equal(health.body.db.optionalPublicDnsAlias.usedForProbe, false); assert.equal(health.body.db.ready, false); assert.deepEqual(health.body.db.missingEnv, ["HWLAB_CLOUD_DB_URL", "HWLAB_CLOUD_DB_SSL_MODE"]); assert.equal(health.body.runtime.adapter, "memory"); assert.equal(health.body.runtime.durable, false); assert.equal(health.body.runtime.status, "degraded"); assert.match(health.body.runtime.reason, /process-local/u); assert.equal(health.body.readiness.components.db, "blocked"); assert.equal(health.body.readiness.components.runtime, "waiting_for_db"); assert.ok(health.body.blockerCodes.includes("db_runtime_config_blocked")); assert.ok(health.body.blockerCodes.includes("runtime_durable_adapter_missing")); logOk("health degraded DB contract"); const v1 = await requestJson(`${baseUrl}/v1`); assert.equal(v1.response.status, 200); assert.equal(v1.body.status, "degraded"); for (const method of [ "gateway.session.register", "box.resource.register", "box.capability.report", "hardware.operation.request", "hardware.invoke.shell", "audit.event.query", "evidence.record.query" ]) { assert.ok(v1.body.methods.includes(method), `missing ${method}`); } logOk("REST adapter method contract"); const projectId = "prj_cloud-api-runtime-smoke"; const gatewaySessionId = "gws_cloud-api-runtime-smoke"; const resourceId = "res_cloud-api-runtime-smoke"; const capabilityId = "cap_cloud-api-runtime-smoke"; const gateway = await rpc(baseUrl, "req_cloud_api_runtime_gateway", "gateway.session.register", { projectId, gatewaySessionId, gatewayId: "gtw_cloud-api-runtime-smoke", serviceId: "hwlab-gateway-simu", endpoint: "http://127.0.0.1:7101" }); assert.equal(gateway.registered, true); assert.equal(gateway.gatewaySession.gatewaySessionId, gatewaySessionId); assert.equal(gateway.auditEvent.action, "gateway.session.register"); const resource = await rpc(baseUrl, "req_cloud_api_runtime_box", "box.resource.register", { projectId, gatewaySessionId, resourceId, boxId: "box_cloud-api-runtime-smoke", resourceType: "board", state: "available", metadata: { ports: ["shell0"] } }); assert.equal(resource.registered, true); assert.equal(resource.resource.resourceId, resourceId); const capability = await rpc(baseUrl, "req_cloud_api_runtime_capability", "box.capability.report", { capabilityId, resourceId, projectId, name: "shell.exec", direction: "bidirectional", valueType: "object", mutatesState: true }); assert.equal(capability.reported, true); assert.equal(capability.capabilities[0].capabilityId, capabilityId); logOk("gateway/box/capability write path"); const invoke = await rpc(baseUrl, "req_cloud_api_runtime_shell", "hardware.invoke.shell", { projectId, gatewaySessionId, resourceId, capabilityId, input: { command: "echo hwlab" } }); assert.equal(invoke.accepted, true); assert.equal(invoke.operation.status, "accepted"); assert.equal(invoke.dispatch.shellExecuted, false); assert.equal(invoke.dispatch.dispatchStatus, "not_connected"); assert.equal(invoke.evidenceRecord.kind, "trace"); assert.equal(invoke.evidenceRecord.serviceId, "hwlab-cloud-api"); assert.match(invoke.evidenceRecord.sha256, /^[a-f0-9]{64}$/); logOk("hardware invoke shell degraded dispatch with evidence"); const audit = await rpc(baseUrl, "req_cloud_api_runtime_audit_query", "audit.event.query", { projectId }); assert.ok(audit.count >= 5); assert.ok(audit.events.every((event) => event.serviceId === "hwlab-cloud-api")); assert.ok(audit.events.every((event) => event.environment === ENVIRONMENT_DEV)); assert.ok(audit.events.some((event) => event.action === "hardware.operation.request")); assert.ok(audit.events.some((event) => event.action === "hardware.invoke.shell")); const evidence = await rpc(baseUrl, "req_cloud_api_runtime_evidence_query", "evidence.record.query", { projectId }); assert.equal(evidence.count, 1); assert.equal(evidence.records[0].operationId, invoke.operationId); assert.equal(evidence.records[0].metadata.dispatchStatus, "not_connected"); logOk("audit/evidence query shape"); process.stdout.write("[cloud-api-runtime-smoke] passed\n"); } finally { await new Promise((resolve, reject) => { server.close((error) => (error ? reject(error) : resolve())); }); restoreEnv(); } } function restoreEnv() { for (const [name, value] of Object.entries(previousEnv)) { if (value === undefined) { delete process.env[name]; } else { process.env[name] = value; } } } await run();