import assert from "node:assert/strict"; import { test } from "node:test"; import { createConnection } from "../src/modules/trapos-cloud-gateway/connection.js"; import { GatewayRegistry } from "../src/modules/trapos-cloud-gateway/registry.js"; import type { OpencodeProbe, ProbeResult } from "../src/opencode.js"; import { FakeSocket, silentLog } from "./helpers.js"; function fakeProbe(result: ProbeResult): OpencodeProbe { return { probe: () => Promise.resolve(result) }; } function setup(opts?: { secret?: string; probe?: ProbeResult }) { const socket = new FakeSocket(); const registry = new GatewayRegistry(silentLog); const expectedSecret = opts?.secret; const connection = createConnection({ socket, registry, probe: fakeProbe(opts?.probe ?? { ok: true }), resolveNetwork: (secret) => (expectedSecret === undefined || secret === expectedSecret ? "global" : null), log: silentLog, }); return { socket, registry, connection }; } function helloFrame(traposId: string | undefined, secret?: string): string { const payload: Record = {}; if (traposId !== undefined) payload.traposId = traposId; if (secret !== undefined) payload.secret = secret; return JSON.stringify({ event: "computer_request", traposSenderId: traposId ?? "?", messageId: "h1", type: "hello", payload }); } function requestFrame(type: string, messageId = "r1"): string { return JSON.stringify({ event: "computer_request", traposSenderId: "7:", messageId, type, payload: {} }); } test("pre-hello non-hello request -> not_authenticated, no close, not registered", async () => { const { socket, registry, connection } = setup(); await connection.onMessage(requestFrame("probe_server")); const frame = socket.lastFrame(); assert.equal(frame.event, "server_response"); assert.equal(frame.ok, false); assert.deepEqual(frame.error, { code: "not_authenticated", message: "hello required before any other request" }); assert.equal(socket.closed, false); assert.equal(registry.count(), 0); }); test("pre-hello wrong-direction frame is dropped silently", async () => { const { socket, connection } = setup(); await connection.onMessage( JSON.stringify({ event: "computer_response", traposSenderId: "7:", messageId: "x", type: "ping", ok: true, payload: {} }), ); assert.equal(socket.sent.length, 0); }); test("hello missing traposId -> not_authenticated, no close, not registered", async () => { const { socket, registry, connection } = setup(); await connection.onMessage(helloFrame(undefined)); const frame = socket.lastFrame(); assert.equal(frame.ok, false); assert.deepEqual(frame.error, { code: "not_authenticated", message: "missing traposId" }); assert.equal(socket.closed, false); assert.equal(registry.count(), 0); }); test("hello bad secret -> unauthorized and close", async () => { const { socket, registry, connection } = setup({ secret: "right" }); await connection.onMessage(helloFrame("7:", "wrong")); const frame = socket.lastFrame(); assert.equal(frame.ok, false); assert.deepEqual(frame.error, { code: "unauthorized", message: "invalid secret" }); assert.equal(socket.closed, true); assert.equal(registry.count(), 0); }); test("hello ok -> registers and replies with networkId", async () => { const { socket, registry, connection } = setup(); await connection.onMessage(helloFrame("7:", "anything")); const frame = socket.lastFrame(); assert.equal(frame.event, "server_response"); assert.equal(frame.type, "hello"); assert.equal(frame.ok, true); assert.deepEqual(frame.payload, { networkId: "global" }); assert.equal(registry.has("global", "7:"), true); assert.equal(registry.count(), 1); }); test("post-hello probe_server (ok) -> serverOk + opencodeOk, no detail", async () => { const { socket, connection } = setup({ probe: { ok: true } }); await connection.onMessage(helloFrame("7:")); await connection.onMessage(requestFrame("probe_server")); const frame = socket.lastFrame(); assert.equal(frame.ok, true); assert.deepEqual(frame.payload, { serverOk: true, opencodeOk: true }); }); test("post-hello probe_server (fail) -> opencodeOk:false + opencodeDetail", async () => { const { socket, connection } = setup({ probe: { ok: false, detail: "http://x -> 401" } }); await connection.onMessage(helloFrame("7:")); await connection.onMessage(requestFrame("probe_server")); const frame = socket.lastFrame(); assert.deepEqual(frame.payload, { serverOk: true, opencodeOk: false, opencodeDetail: "http://x -> 401" }); }); test("post-hello unknown type -> unknown_type error", async () => { const { socket, connection } = setup(); await connection.onMessage(helloFrame("7:")); await connection.onMessage(requestFrame("does_not_exist")); const frame = socket.lastFrame(); assert.equal(frame.ok, false); assert.equal((frame.error as { code: string }).code, "unknown_type"); }); test("post-hello duplicate hello is ignored", async () => { const { socket, registry, connection } = setup(); await connection.onMessage(helloFrame("7:")); const afterFirst = socket.sent.length; await connection.onMessage(helloFrame("9:")); assert.equal(socket.sent.length, afterFirst); // no reply assert.equal(registry.has("global", "7:"), true); assert.equal(registry.has("global", "9:"), false); }); test("post-hello wrong-direction frame is dropped", async () => { const { socket, connection } = setup(); await connection.onMessage(helloFrame("7:")); const afterHello = socket.sent.length; await connection.onMessage( JSON.stringify({ event: "server_request", traposReceiverId: "7:", messageId: "g1", type: "ping", payload: {} }), ); assert.equal(socket.sent.length, afterHello); }); test("reverse direction: gateway request resolved by computer_response", async () => { const { socket, registry, connection } = setup(); await connection.onMessage(helloFrame("7:")); const promise = registry.request("global", "7:", "ping", {}); const sent = socket.lastFrame(); assert.equal(sent.event, "server_request"); assert.equal(sent.type, "ping"); await connection.onMessage( JSON.stringify({ event: "computer_response", traposSenderId: "7:", messageId: sent.messageId, type: "ping", ok: true, payload: { traposId: "7:" } }), ); assert.deepEqual(await promise, { traposId: "7:" }); }); test("onClose unregisters the connection", async () => { const { registry, connection } = setup(); await connection.onMessage(helloFrame("7:")); assert.equal(registry.count(), 1); connection.onClose(); assert.equal(registry.count(), 0); }); test("malformed frame is dropped silently", async () => { const { socket, connection } = setup(); await connection.onMessage("not json at all"); assert.equal(socket.sent.length, 0); });