import assert from "node:assert/strict"; import { mkdtemp, rm, writeFile } from "node:fs/promises"; import { createServer, request as httpRequest } from "node:http"; import os from "node:os"; import path from "node:path"; import test from "node:test"; import { gunzipSync } from "node:zlib"; import { createCloudWebServer } from "./cloud-web-runtime.mjs"; test("cloud web auth requests are proxied once", async () => { let upstreamRequests = 0; const upstream = createServer((request, response) => { upstreamRequests += 1; request.resume(); response.writeHead(200, { "content-type": "application/json" }); response.end(JSON.stringify({ authenticated: true, path: request.url })); }); await listen(upstream); const cloudWeb = createCloudWebServer({ serviceId: "hwlab-cloud-web", cloudApiBaseUrl: serverUrl(upstream), cloudApiProxyTimeoutMs: 1000, healthPayload: () => ({ status: "ok" }), sendJson(response, statusCode, body) { const payload = JSON.stringify(body); response.writeHead(statusCode, { "content-type": "application/json", "content-length": Buffer.byteLength(payload) }); response.end(payload); } }); await listen(cloudWeb); try { const response = await fetch(`${serverUrl(cloudWeb)}/auth/session`, { headers: { cookie: "hwlab_session=token" } }); assert.equal(response.status, 200); assert.deepEqual(await response.json(), { authenticated: true, path: "/auth/session" }); assert.equal(upstreamRequests, 1); } finally { await close(cloudWeb); await close(upstream); } }); test("cloud web auth bootstrap keeps an existing session without auto-login", async () => { const upstreamRequests = []; const upstream = createServer((request, response) => { upstreamRequests.push({ method: request.method, url: request.url, cookie: request.headers.cookie }); request.resume(); response.writeHead(200, { "content-type": "application/json" }); response.end(JSON.stringify({ authenticated: true, actor: { username: "alice" }, path: request.url })); }); await listen(upstream); const cloudWeb = createCloudWebServer({ serviceId: "hwlab-cloud-web", cloudApiBaseUrl: serverUrl(upstream), cloudApiProxyTimeoutMs: 1000, healthPayload: () => ({ status: "ok" }), sendJson(response, statusCode, body) { const payload = JSON.stringify(body); response.writeHead(statusCode, { "content-type": "application/json", "content-length": Buffer.byteLength(payload) }); response.end(payload); } }); await listen(cloudWeb); try { const response = await fetch(`${serverUrl(cloudWeb)}/auth/bootstrap`, { headers: { cookie: "hwlab_session=alice" } }); assert.equal(response.status, 200); assert.deepEqual(await response.json(), { authenticated: true, actor: { username: "alice" }, path: "/auth/session" }); assert.deepEqual(upstreamRequests, [{ method: "GET", url: "/auth/session", cookie: "hwlab_session=alice" }]); } finally { await close(cloudWeb); await close(upstream); } }); test("cloud web auth bootstrap auto-logins only after an unauthenticated session", async () => { const upstreamRequests = []; const upstream = createServer(async (request, response) => { let body = ""; for await (const chunk of request) body += chunk; upstreamRequests.push({ method: request.method, url: request.url, body }); response.writeHead(200, { "content-type": "application/json", ...(request.url === "/auth/login" ? { "set-cookie": "hwlab_session=session-auto; Path=/; HttpOnly" } : {}) }); response.end(JSON.stringify(request.url === "/auth/login" ? { authenticated: true, actor: { username: "admin" } } : { authenticated: false })); }); await listen(upstream); const cloudWeb = createCloudWebServer({ serviceId: "hwlab-cloud-web", cloudApiBaseUrl: serverUrl(upstream), cloudApiProxyTimeoutMs: 1000, healthPayload: () => ({ status: "ok" }), sendJson(response, statusCode, body) { const payload = JSON.stringify(body); response.writeHead(statusCode, { "content-type": "application/json", "content-length": Buffer.byteLength(payload) }); response.end(payload); } }); await listen(cloudWeb); try { const response = await fetch(`${serverUrl(cloudWeb)}/auth/bootstrap`); assert.equal(response.status, 200); assert.equal(response.headers.get("set-cookie"), "hwlab_session=session-auto; Path=/; HttpOnly"); assert.deepEqual(await response.json(), { authenticated: true, actor: { username: "admin" } }); assert.deepEqual(upstreamRequests, [ { method: "GET", url: "/auth/session", body: "" }, { method: "POST", url: "/auth/login", body: JSON.stringify({ username: "admin", password: "hwlab2026" }) } ]); } finally { await close(cloudWeb); await close(upstream); } }); test("cloud web proxies Admin Access write routes", async () => { const upstreamRequests = []; const upstream = createServer(async (request, response) => { let body = ""; for await (const chunk of request) body += chunk; upstreamRequests.push({ method: request.method, url: request.url, authorization: request.headers.authorization, cookie: request.headers.cookie, body }); response.writeHead(200, { "content-type": "application/json" }); response.end(JSON.stringify({ ok: true, path: request.url })); }); await listen(upstream); const cloudWeb = createCloudWebServer({ serviceId: "hwlab-cloud-web", cloudApiBaseUrl: serverUrl(upstream), cloudApiProxyTimeoutMs: 1000, healthPayload: () => ({ status: "ok" }), roots: [], sendJson(response, statusCode, body) { const payload = JSON.stringify(body); response.writeHead(statusCode, { "content-type": "application/json", "content-length": Buffer.byteLength(payload) }); response.end(payload); } }); await listen(cloudWeb); try { const checkBody = JSON.stringify({ user: "usr_alice", relation: "viewer", object: "device_pod:D601-71-FREQ" }); const checkResponse = await fetch(`${serverUrl(cloudWeb)}/v1/admin/access/check`, { method: "POST", headers: { "content-type": "application/json", authorization: "Bearer hwl_live_admin", cookie: "hwlab_session=session-a" }, body: checkBody }); assert.equal(checkResponse.status, 200); assert.deepEqual(await checkResponse.json(), { ok: true, path: "/v1/admin/access/check" }); const grantResponse = await fetch(`${serverUrl(cloudWeb)}/v1/admin/access/users/usr_alice/device-pods/D601-71-FREQ/viewer`, { method: "PUT", headers: { "content-type": "application/json", authorization: "Bearer hwl_live_admin" }, body: "{}" }); assert.equal(grantResponse.status, 200); assert.equal(upstreamRequests.length, 2); assert.deepEqual(upstreamRequests[0], { method: "POST", url: "/v1/admin/access/check", authorization: "Bearer hwl_live_admin", cookie: "hwlab_session=session-a", body: checkBody }); assert.deepEqual(upstreamRequests[1], { method: "PUT", url: "/v1/admin/access/users/usr_alice/device-pods/D601-71-FREQ/viewer", authorization: "Bearer hwl_live_admin", cookie: undefined, body: "{}" }); } finally { await close(cloudWeb); await close(upstream); } }); test("cloud web forwards hwpod node-ops plans to cloud-api", async () => { const upstreamRequests = []; const upstream = createServer(async (request, response) => { let body = ""; for await (const chunk of request) body += chunk; upstreamRequests.push({ method: request.method, url: request.url, authorization: request.headers.authorization, body }); response.writeHead(200, { "content-type": "application/json" }); response.end(JSON.stringify({ ok: false, status: "blocked", path: request.url, blocker: { code: "hwpod_node_unavailable" } })); }); await listen(upstream); const cloudWeb = createCloudWebServer({ serviceId: "hwlab-cloud-web", cloudApiBaseUrl: serverUrl(upstream), cloudApiProxyTimeoutMs: 1000, healthPayload: () => ({ status: "ok" }), roots: [], sendJson(response, statusCode, body) { const payload = JSON.stringify(body); response.writeHead(statusCode, { "content-type": "application/json", "content-length": Buffer.byteLength(payload) }); response.end(payload); } }); await listen(cloudWeb); try { const plan = { contractVersion: "hwpod-node-ops-v1", planId: "hwpod_plan_proxy_test", hwpodId: "hwpod-test", nodeId: "node-test", ops: [{ opId: "op_01", op: "node.health", args: {} }] }; const response = await fetch(`${serverUrl(cloudWeb)}/v1/hwpod-node-ops`, { method: "POST", headers: { "content-type": "application/json", authorization: "Bearer hwl_live_operator" }, body: JSON.stringify(plan) }); assert.equal(response.status, 200); assert.deepEqual(await response.json(), { ok: false, status: "blocked", path: "/v1/hwpod-node-ops", blocker: { code: "hwpod_node_unavailable" } }); assert.equal(upstreamRequests.length, 1); assert.deepEqual(upstreamRequests[0], { method: "POST", url: "/v1/hwpod-node-ops", authorization: "Bearer hwl_live_operator", body: JSON.stringify(plan) }); } finally { await close(cloudWeb); await close(upstream); } }); test("cloud web serves access deep link through the React shell", async () => { const root = await mkdtemp(path.join(os.tmpdir(), "hwlab-cloud-web-runtime-")); await writeFile(path.join(root, "index.html"), "
\n", "utf8"); const cloudWeb = createCloudWebServer({ serviceId: "hwlab-cloud-web", roots: [root], cloudApiBaseUrl: "http://127.0.0.1:1", healthPayload: () => ({ status: "ok" }), sendJson(response, statusCode, body) { const payload = JSON.stringify(body); response.writeHead(statusCode, { "content-type": "application/json", "content-length": Buffer.byteLength(payload) }); response.end(payload); } }); await listen(cloudWeb); try { const response = await fetch(`${serverUrl(cloudWeb)}/access`); assert.equal(response.status, 200); assert.equal(response.headers.get("content-type"), "text/html; charset=utf-8"); assert.equal(await response.text(), "
\n"); } finally { await close(cloudWeb); await rm(root, { recursive: true, force: true }); } }); test("cloud web serves compressible static assets with gzip", async () => { const root = await mkdtemp(path.join(os.tmpdir(), "hwlab-cloud-web-runtime-")); const appJs = `console.log(${JSON.stringify("fixture-static-asset ".repeat(512))});\n`; await writeFile(path.join(root, "app.js"), appJs, "utf8"); const cloudWeb = createCloudWebServer({ serviceId: "hwlab-cloud-web", roots: [root], cloudApiBaseUrl: "http://127.0.0.1:1", healthPayload: () => ({ status: "ok" }), sendJson(response, statusCode, body) { const payload = JSON.stringify(body); response.writeHead(statusCode, { "content-type": "application/json", "content-length": Buffer.byteLength(payload) }); response.end(payload); } }); await listen(cloudWeb); try { const compressed = await requestRaw(`${serverUrl(cloudWeb)}/app.js`, { "accept-encoding": "gzip" }); assert.equal(compressed.statusCode, 200); assert.equal(compressed.headers["content-type"], "text/javascript; charset=utf-8"); assert.equal(compressed.headers["content-encoding"], "gzip"); assert.equal(compressed.headers.vary, "Accept-Encoding"); assert.equal(gunzipSync(compressed.body).toString("utf8"), appJs); assert.ok(Number(compressed.headers["content-length"]) < Buffer.byteLength(appJs), "gzip payload should be smaller than raw app.js"); const identity = await requestRaw(`${serverUrl(cloudWeb)}/app.js`, { "accept-encoding": "identity" }); assert.equal(identity.statusCode, 200); assert.equal(identity.headers["content-encoding"], undefined); assert.equal(identity.headers.vary, "Accept-Encoding"); assert.equal(identity.body.toString("utf8"), appJs); } finally { await close(cloudWeb); await rm(root, { recursive: true, force: true }); } }); function listen(server) { return new Promise((resolve, reject) => { server.once("error", reject); server.listen(0, "127.0.0.1", resolve); }); } function close(server) { return new Promise((resolve, reject) => { server.close((error) => (error ? reject(error) : resolve())); }); } function serverUrl(server) { const address = server.address(); return `http://127.0.0.1:${address.port}`; } function requestRaw(url, headers = {}) { return new Promise((resolve, reject) => { const request = httpRequest(url, { method: "GET", headers }, (response) => { const chunks = []; response.on("data", (chunk) => chunks.push(chunk)); response.on("end", () => resolve({ statusCode: response.statusCode, headers: response.headers, body: Buffer.concat(chunks) })); }); request.once("error", reject); request.end(); }); }