import assert from "node:assert/strict"; import { spawn } from "node:child_process"; import { createServer } from "node:http"; import test from "node:test"; const bunCommand = process.env.HWLAB_TEST_BUN_COMMAND || process.env.HWLAB_BUN_COMMAND || (process.versions.bun ? process.execPath : "bun"); test("device pod executor exposes cloud-api authority boundary and method guards", async () => { const service = await startDevicePod("device-pod-test"); try { const health = await fetchJson(`http://127.0.0.1:${service.port}/health/live`); assert.equal(health.serviceId, "hwlab-device-pod"); assert.equal(health.status, "live"); assert.equal(health.devicePodId, "device-pod-test"); assert.equal(health.contractVersion, "device-pod-executor-v1"); assert.equal(health.role, "device-pod-internal-executor"); assert.equal(health.authority, "hwlab-cloud-api"); assert.equal(health.fake, false); const list = await fetchJson(`http://127.0.0.1:${service.port}/v1/device-pods`); assert.equal(list.serviceId, "hwlab-device-pod"); assert.equal(list.selectedDevicePodId, "device-pod-test"); assert.equal(list.devicePods.length, 1); assert.equal(list.source.fake, false); const status = await fetchJson(`http://127.0.0.1:${service.port}/v1/device-pods/device-pod-test/status`); assert.equal(status.devicePodId, "device-pod-test"); assert.equal(status.status, "blocked"); assert.equal(status.summary.blocker.code, "gateway_dispatch_unavailable"); const events = await fetchJson(`http://127.0.0.1:${service.port}/v1/device-pods/device-pod-test/events?limit=1`); assert.equal(events.events.length, 1); assert.equal(events.truncation.limit, 1); assert.equal(events.truncation.truncated, false); assert.equal(events.events[0].blocker.code, "gateway_dispatch_unavailable"); const externalJob = await fetch(`http://127.0.0.1:${service.port}/v1/device-pods/device-pod-test/jobs`, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ intent: "workspace.ls" }) }); assert.equal(externalJob.status, 403); assert.equal((await externalJob.json()).error.code, "cloud_api_authority_required"); const internalJob = await fetch(`http://127.0.0.1:${service.port}/v1/device-pods/device-pod-test/jobs`, { method: "POST", headers: { "content-type": "application/json", "x-hwlab-internal-service": "hwlab-cloud-api" }, body: JSON.stringify({ intent: "workspace.ls", traceId: "trc_device_pod_test" }) }); assert.equal(internalJob.status, 409); assert.equal((await internalJob.json()).blocker.code, "gateway_dispatch_unavailable"); const missing = await fetch(`http://127.0.0.1:${service.port}/missing`); assert.equal(missing.status, 404); assert.deepEqual(await missing.json(), { error: "not_found", path: "/missing" }); const methodGuard = await fetch(`http://127.0.0.1:${service.port}/health`, { method: "POST" }); assert.equal(methodGuard.status, 405); assert.deepEqual(await methodGuard.json(), { error: "method_not_allowed", method: "POST", path: "/health" }); } finally { await service.stop(); } }); async function startDevicePod(devicePodId) { const port = await freePort(); const child = spawn(bunCommand, ["run", "cmd/hwlab-device-pod/main.ts"], { cwd: process.cwd(), env: { ...process.env, HWLAB_DEVICE_POD_PORT: String(port), HWLAB_DEVICE_POD_ID: devicePodId, HWLAB_ENVIRONMENT: "v02" }, stdio: ["ignore", "pipe", "pipe"] }); await waitForListening(child, "device pod"); return { port, stop: () => stopChild(child) }; } async function fetchJson(url) { const response = await fetch(url); assert.equal(response.ok, true, `${url} returned ${response.status}`); return response.json(); } async function freePort() { const server = createServer(); await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); const port = server.address().port; await new Promise((resolve) => server.close(resolve)); return port; } async function waitForListening(child, label) { let stderr = ""; child.stderr.on("data", (chunk) => { stderr += chunk; }); await new Promise((resolve, reject) => { const timer = setTimeout(() => reject(new Error(`${label} did not start: ${stderr}`)), 5000); child.stdout.on("data", (chunk) => { if (chunk.toString().includes("listening")) { clearTimeout(timer); resolve(); } }); child.on("exit", (code) => { clearTimeout(timer); reject(new Error(`${label} exited early code=${code}: ${stderr}`)); }); }); } async function stopChild(child) { if (child.exitCode !== null) return; child.kill("SIGTERM"); await new Promise((resolve) => child.once("exit", resolve)); }