diff --git a/src/gateway/server.health.e2e.test.ts b/src/gateway/server.health.e2e.test.ts index e4c54aa32..ba46d9c06 100644 --- a/src/gateway/server.health.e2e.test.ts +++ b/src/gateway/server.health.e2e.test.ts @@ -7,6 +7,10 @@ import { startGatewayServerHarness, type GatewayServerHarness } from "./server.e import { installGatewayTestHooks, onceMessage } from "./test-helpers.js"; installGatewayTestHooks({ scope: "suite" }); +const HEALTH_E2E_TIMEOUT_MS = 30_000; +const PRESENCE_EVENT_TIMEOUT_MS = 6_000; +const SHUTDOWN_EVENT_TIMEOUT_MS = 3_000; +const FINGERPRINT_TIMEOUT_MS = 3_000; let harness: GatewayServerHarness; @@ -29,39 +33,43 @@ afterAll(async () => { }); describe("gateway server health/presence", () => { - test("connect + health + presence + status succeed", { timeout: 60_000 }, async () => { - const { ws } = await harness.openClient(); + test( + "connect + health + presence + status succeed", + { timeout: HEALTH_E2E_TIMEOUT_MS }, + async () => { + const { ws } = await harness.openClient(); - const healthP = onceMessage(ws, (o) => o.type === "res" && o.id === "health1"); - const statusP = onceMessage(ws, (o) => o.type === "res" && o.id === "status1"); - const presenceP = onceMessage( - ws, - (o) => o.type === "res" && o.id === "presence1", - ); - const channelsP = onceMessage( - ws, - (o) => o.type === "res" && o.id === "channels1", - ); + const healthP = onceMessage(ws, (o) => o.type === "res" && o.id === "health1"); + const statusP = onceMessage(ws, (o) => o.type === "res" && o.id === "status1"); + const presenceP = onceMessage( + ws, + (o) => o.type === "res" && o.id === "presence1", + ); + const channelsP = onceMessage( + ws, + (o) => o.type === "res" && o.id === "channels1", + ); - const sendReq = (id: string, method: string) => - ws.send(JSON.stringify({ type: "req", id, method })); - sendReq("health1", "health"); - sendReq("status1", "status"); - sendReq("presence1", "system-presence"); - sendReq("channels1", "channels.status"); + const sendReq = (id: string, method: string) => + ws.send(JSON.stringify({ type: "req", id, method })); + sendReq("health1", "health"); + sendReq("status1", "status"); + sendReq("presence1", "system-presence"); + sendReq("channels1", "channels.status"); - const health = await healthP; - const status = await statusP; - const presence = await presenceP; - const channels = await channelsP; - expect(health.ok).toBe(true); - expect(status.ok).toBe(true); - expect(presence.ok).toBe(true); - expect(channels.ok).toBe(true); - expect(Array.isArray(presence.payload)).toBe(true); + const health = await healthP; + const status = await statusP; + const presence = await presenceP; + const channels = await channelsP; + expect(health.ok).toBe(true); + expect(status.ok).toBe(true); + expect(presence.ok).toBe(true); + expect(channels.ok).toBe(true); + expect(Array.isArray(presence.payload)).toBe(true); - ws.close(); - }); + ws.close(); + }, + ); test("broadcasts heartbeat events and serves last-heartbeat", async () => { type HeartbeatPayload = { @@ -121,32 +129,36 @@ describe("gateway server health/presence", () => { ws.close(); }); - test("presence events carry seq + stateVersion", { timeout: 8000 }, async () => { - const { ws } = await harness.openClient(); + test( + "presence events carry seq + stateVersion", + { timeout: PRESENCE_EVENT_TIMEOUT_MS }, + async () => { + const { ws } = await harness.openClient(); - const presenceEventP = onceMessage( - ws, - (o) => o.type === "event" && o.event === "presence", - ); - ws.send( - JSON.stringify({ - type: "req", - id: "evt-1", - method: "system-event", - params: { text: "note from test" }, - }), - ); + const presenceEventP = onceMessage( + ws, + (o) => o.type === "event" && o.event === "presence", + ); + ws.send( + JSON.stringify({ + type: "req", + id: "evt-1", + method: "system-event", + params: { text: "note from test" }, + }), + ); - const evt = await presenceEventP; - expect(typeof evt.seq).toBe("number"); - expect(evt.stateVersion?.presence).toBeGreaterThan(0); - const evtPayload = evt.payload as { presence?: unknown } | undefined; - expect(Array.isArray(evtPayload?.presence)).toBe(true); + const evt = await presenceEventP; + expect(typeof evt.seq).toBe("number"); + expect(evt.stateVersion?.presence).toBeGreaterThan(0); + const evtPayload = evt.payload as { presence?: unknown } | undefined; + expect(Array.isArray(evtPayload?.presence)).toBe(true); - ws.close(); - }); + ws.close(); + }, + ); - test("agent events stream with seq", { timeout: 8000 }, async () => { + test("agent events stream with seq", { timeout: PRESENCE_EVENT_TIMEOUT_MS }, async () => { const { ws } = await harness.openClient(); const runId = randomUUID(); @@ -169,13 +181,13 @@ describe("gateway server health/presence", () => { ws.close(); }); - test("shutdown event is broadcast on close", { timeout: 8000 }, async () => { + test("shutdown event is broadcast on close", { timeout: PRESENCE_EVENT_TIMEOUT_MS }, async () => { const localHarness = await startGatewayServerHarness(); const { ws } = await localHarness.openClient(); const shutdownP = onceMessage( ws, (o) => o.type === "event" && o.event === "shutdown", - 5000, + SHUTDOWN_EVENT_TIMEOUT_MS, ); await localHarness.close(); const evt = await shutdownP; @@ -183,33 +195,37 @@ describe("gateway server health/presence", () => { expect(evtPayload?.reason).toBeDefined(); }); - test("presence broadcast reaches multiple clients", { timeout: 8000 }, async () => { - const clients = await Promise.all([ - harness.openClient(), - harness.openClient(), - harness.openClient(), - ]); - const waits = clients.map(({ ws }) => - onceMessage(ws, (o) => o.type === "event" && o.event === "presence"), - ); - clients[0].ws.send( - JSON.stringify({ - type: "req", - id: "broadcast", - method: "system-event", - params: { text: "fanout" }, - }), - ); - const events = await Promise.all(waits); - for (const evt of events) { - const evtPayload = evt.payload as { presence?: unknown[] } | undefined; - expect(evtPayload?.presence?.length).toBeGreaterThan(0); - expect(typeof evt.seq).toBe("number"); - } - for (const { ws } of clients) { - ws.close(); - } - }); + test( + "presence broadcast reaches multiple clients", + { timeout: PRESENCE_EVENT_TIMEOUT_MS }, + async () => { + const clients = await Promise.all([ + harness.openClient(), + harness.openClient(), + harness.openClient(), + ]); + const waits = clients.map(({ ws }) => + onceMessage(ws, (o) => o.type === "event" && o.event === "presence"), + ); + clients[0].ws.send( + JSON.stringify({ + type: "req", + id: "broadcast", + method: "system-event", + params: { text: "fanout" }, + }), + ); + const events = await Promise.all(waits); + for (const evt of events) { + const evtPayload = evt.payload as { presence?: unknown[] } | undefined; + expect(evtPayload?.presence?.length).toBeGreaterThan(0); + expect(typeof evt.seq).toBe("number"); + } + for (const { ws } of clients) { + ws.close(); + } + }, + ); test("presence includes client fingerprint", async () => { const role = "operator"; @@ -231,7 +247,7 @@ describe("gateway server health/presence", () => { const presenceP = onceMessage( ws, (o) => o.type === "res" && o.id === "fingerprint", - 4000, + FINGERPRINT_TIMEOUT_MS, ); ws.send( JSON.stringify({