fix(e2e): require gateway network health payload

This commit is contained in:
Vincent Koc
2026-06-07 07:16:19 +02:00
parent ed7f259ce7
commit 690a04f81e
2 changed files with 57 additions and 1 deletions

View File

@@ -17,6 +17,28 @@ async function openSocket(url, timeoutMs = 10_000) {
return ws;
}
function isRecord(value) {
return value !== null && typeof value === "object" && !Array.isArray(value);
}
export function hasGatewayHealthSummaryPayload(response) {
if (!isRecord(response) || !isRecord(response.payload)) {
return false;
}
const { payload } = response;
return (
payload.ok === true &&
typeof payload.ts === "number" &&
typeof payload.durationMs === "number" &&
typeof payload.defaultAgentId === "string" &&
payload.defaultAgentId.trim() !== "" &&
Array.isArray(payload.agents) &&
isRecord(payload.channels) &&
Array.isArray(payload.channelOrder) &&
isRecord(payload.sessions)
);
}
export function responseError(method, response) {
const message = response.error?.message ?? "unknown";
return new Error(`${method} failed: ${message}`);
@@ -92,6 +114,9 @@ export async function runGatewayNetworkClient(
(frame) => frame?.type === "res" && frame?.id === "h1",
);
if (healthRes.ok) {
if (!hasGatewayHealthSummaryPayload(healthRes)) {
throw new Error("health failed: missing health summary payload");
}
stdout("ok");
return;
}

View File

@@ -6,6 +6,22 @@ import { readGatewayNetworkClientConnectTimeoutMs } from "../../scripts/e2e/lib/
import { onceFrame } from "../../scripts/e2e/lib/gateway-network/ws-frames.mjs";
describe("gateway network WebSocket open guard", () => {
function healthResponse() {
return {
ok: true,
payload: {
agents: [],
channelOrder: [],
channels: {},
defaultAgentId: "codex",
durationMs: 3,
ok: true,
sessions: { count: 0, path: "/state/sessions", recent: [] },
ts: Date.now(),
},
};
}
it("rejects loose client timeout env values instead of parsing prefixes", () => {
expect(() =>
readGatewayNetworkClientConnectTimeoutMs({
@@ -129,7 +145,7 @@ describe("gateway network WebSocket open guard", () => {
}
it("proves health after the authenticated connect handshake", async () => {
const harness = createNetworkClientHarness([{ ok: true }, { ok: true }]);
const harness = createNetworkClientHarness([{ ok: true }, healthResponse()]);
await runGatewayNetworkClient(
{ token: "secret-token", url: "ws://127.0.0.1:12345", timeoutMs: 1000 },
@@ -141,6 +157,21 @@ describe("gateway network WebSocket open guard", () => {
expect(harness.closeCount).toBe(1);
});
it("fails a connected socket whose health success lacks summary evidence", async () => {
const harness = createNetworkClientHarness([{ ok: true }, { ok: true }]);
await expect(
runGatewayNetworkClient(
{ token: "secret-token", url: "ws://127.0.0.1:12345", timeoutMs: 1000 },
harness.deps,
),
).rejects.toThrow("health failed: missing health summary payload");
expect(harness.sentMethods).toEqual(["connect", "health"]);
expect(harness.stdout).toEqual([]);
expect(harness.closeCount).toBe(1);
});
it("fails a connected socket whose health probe fails", async () => {
const harness = createNetworkClientHarness([
{ ok: true },