cc-libs/tools/trapos-server/test/connection.test.ts

162 lines
6.7 KiB
TypeScript

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<string, unknown> = {};
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);
});