From e687ad15acc273dfb3822abaf887d6eda98343e9 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 15 Feb 2026 15:44:14 +0000 Subject: [PATCH] refactor(test): share server chat event harness --- src/gateway/server-chat.agent-events.test.ts | 178 +++++-------------- 1 file changed, 48 insertions(+), 130 deletions(-) diff --git a/src/gateway/server-chat.agent-events.test.ts b/src/gateway/server-chat.agent-events.test.ts index 9978416a4d0..56eb2464a73 100644 --- a/src/gateway/server-chat.agent-events.test.ts +++ b/src/gateway/server-chat.agent-events.test.ts @@ -7,15 +7,18 @@ import { } from "./server-chat.js"; describe("agent event handler", () => { - it("emits chat delta for assistant text-only events", () => { - const nowSpy = vi.spyOn(Date, "now").mockReturnValue(1_000); + function createHarness(params?: { + now?: number; + resolveSessionKeyForRun?: (runId: string) => string | undefined; + }) { + const nowSpy = + params?.now === undefined ? undefined : vi.spyOn(Date, "now").mockReturnValue(params.now); const broadcast = vi.fn(); const broadcastToConnIds = vi.fn(); const nodeSendToSession = vi.fn(); const agentRunSeq = new Map(); const chatRunState = createChatRunState(); const toolEventRecipients = createToolEventRecipientRegistry(); - chatRunState.registry.add("run-1", { sessionKey: "session-1", clientRunId: "client-1" }); const handler = createAgentEventHandler({ broadcast, @@ -23,11 +26,29 @@ describe("agent event handler", () => { nodeSendToSession, agentRunSeq, chatRunState, - resolveSessionKeyForRun: () => undefined, + resolveSessionKeyForRun: params?.resolveSessionKeyForRun ?? (() => undefined), clearAgentRunContext: vi.fn(), toolEventRecipients, }); + return { + nowSpy, + broadcast, + broadcastToConnIds, + nodeSendToSession, + agentRunSeq, + chatRunState, + toolEventRecipients, + handler, + }; + } + + it("emits chat delta for assistant text-only events", () => { + const { broadcast, nodeSendToSession, chatRunState, handler, nowSpy } = createHarness({ + now: 1_000, + }); + chatRunState.registry.add("run-1", { sessionKey: "session-1", clientRunId: "client-1" }); + handler({ runId: "run-1", seq: 1, @@ -46,29 +67,14 @@ describe("agent event handler", () => { expect(payload.message?.content?.[0]?.text).toBe("Hello world"); const sessionChatCalls = nodeSendToSession.mock.calls.filter(([, event]) => event === "chat"); expect(sessionChatCalls).toHaveLength(1); - nowSpy.mockRestore(); + nowSpy?.mockRestore(); }); it("does not emit chat delta for NO_REPLY streaming text", () => { - const nowSpy = vi.spyOn(Date, "now").mockReturnValue(1_000); - const broadcast = vi.fn(); - const broadcastToConnIds = vi.fn(); - const nodeSendToSession = vi.fn(); - const agentRunSeq = new Map(); - const chatRunState = createChatRunState(); - const toolEventRecipients = createToolEventRecipientRegistry(); - chatRunState.registry.add("run-1", { sessionKey: "session-1", clientRunId: "client-1" }); - - const handler = createAgentEventHandler({ - broadcast, - broadcastToConnIds, - nodeSendToSession, - agentRunSeq, - chatRunState, - resolveSessionKeyForRun: () => undefined, - clearAgentRunContext: vi.fn(), - toolEventRecipients, + const { broadcast, nodeSendToSession, chatRunState, handler, nowSpy } = createHarness({ + now: 1_000, }); + chatRunState.registry.add("run-1", { sessionKey: "session-1", clientRunId: "client-1" }); handler({ runId: "run-1", @@ -82,29 +88,14 @@ describe("agent event handler", () => { expect(chatCalls).toHaveLength(0); const sessionChatCalls = nodeSendToSession.mock.calls.filter(([, event]) => event === "chat"); expect(sessionChatCalls).toHaveLength(0); - nowSpy.mockRestore(); + nowSpy?.mockRestore(); }); it("does not include NO_REPLY text in chat final message", () => { - const nowSpy = vi.spyOn(Date, "now").mockReturnValue(2_000); - const broadcast = vi.fn(); - const broadcastToConnIds = vi.fn(); - const nodeSendToSession = vi.fn(); - const agentRunSeq = new Map(); - const chatRunState = createChatRunState(); - const toolEventRecipients = createToolEventRecipientRegistry(); - chatRunState.registry.add("run-2", { sessionKey: "session-2", clientRunId: "client-2" }); - - const handler = createAgentEventHandler({ - broadcast, - broadcastToConnIds, - nodeSendToSession, - agentRunSeq, - chatRunState, - resolveSessionKeyForRun: () => undefined, - clearAgentRunContext: vi.fn(), - toolEventRecipients, + const { broadcast, nodeSendToSession, chatRunState, handler, nowSpy } = createHarness({ + now: 2_000, }); + chatRunState.registry.add("run-2", { sessionKey: "session-2", clientRunId: "client-2" }); handler({ runId: "run-2", @@ -128,33 +119,16 @@ describe("agent event handler", () => { expect(payload.message).toBeUndefined(); const sessionChatCalls = nodeSendToSession.mock.calls.filter(([, event]) => event === "chat"); expect(sessionChatCalls).toHaveLength(1); - nowSpy.mockRestore(); + nowSpy?.mockRestore(); }); it("cleans up agent run sequence tracking when lifecycle completes", () => { - const nowSpy = vi.spyOn(Date, "now").mockReturnValue(2_500); - const broadcast = vi.fn(); - const broadcastToConnIds = vi.fn(); - const nodeSendToSession = vi.fn(); - const agentRunSeq = new Map(); - const chatRunState = createChatRunState(); - const toolEventRecipients = createToolEventRecipientRegistry(); + const { agentRunSeq, chatRunState, handler, nowSpy } = createHarness({ now: 2_500 }); chatRunState.registry.add("run-cleanup", { sessionKey: "session-cleanup", clientRunId: "client-cleanup", }); - const handler = createAgentEventHandler({ - broadcast, - broadcastToConnIds, - nodeSendToSession, - agentRunSeq, - chatRunState, - resolveSessionKeyForRun: () => undefined, - clearAgentRunContext: vi.fn(), - toolEventRecipients, - }); - handler({ runId: "run-cleanup", seq: 1, @@ -174,31 +148,17 @@ describe("agent event handler", () => { expect(agentRunSeq.has("run-cleanup")).toBe(false); expect(agentRunSeq.has("client-cleanup")).toBe(false); - nowSpy.mockRestore(); + nowSpy?.mockRestore(); }); it("routes tool events only to registered recipients when verbose is enabled", () => { - const broadcast = vi.fn(); - const broadcastToConnIds = vi.fn(); - const nodeSendToSession = vi.fn(); - const agentRunSeq = new Map(); - const chatRunState = createChatRunState(); - const toolEventRecipients = createToolEventRecipientRegistry(); + const { broadcast, broadcastToConnIds, toolEventRecipients, handler } = createHarness({ + resolveSessionKeyForRun: () => "session-1", + }); registerAgentRunContext("run-tool", { sessionKey: "session-1", verboseLevel: "on" }); toolEventRecipients.add("run-tool", "conn-1"); - const handler = createAgentEventHandler({ - broadcast, - broadcastToConnIds, - nodeSendToSession, - agentRunSeq, - chatRunState, - resolveSessionKeyForRun: () => "session-1", - clearAgentRunContext: vi.fn(), - toolEventRecipients, - }); - handler({ runId: "run-tool", seq: 1, @@ -213,27 +173,13 @@ describe("agent event handler", () => { }); it("broadcasts tool events to WS recipients even when verbose is off, but skips node send", () => { - const broadcast = vi.fn(); - const broadcastToConnIds = vi.fn(); - const nodeSendToSession = vi.fn(); - const agentRunSeq = new Map(); - const chatRunState = createChatRunState(); - const toolEventRecipients = createToolEventRecipientRegistry(); + const { broadcastToConnIds, nodeSendToSession, toolEventRecipients, handler } = createHarness({ + resolveSessionKeyForRun: () => "session-1", + }); registerAgentRunContext("run-tool-off", { sessionKey: "session-1", verboseLevel: "off" }); toolEventRecipients.add("run-tool-off", "conn-1"); - const handler = createAgentEventHandler({ - broadcast, - broadcastToConnIds, - nodeSendToSession, - agentRunSeq, - chatRunState, - resolveSessionKeyForRun: () => "session-1", - clearAgentRunContext: vi.fn(), - toolEventRecipients, - }); - handler({ runId: "run-tool-off", seq: 1, @@ -251,27 +197,13 @@ describe("agent event handler", () => { }); it("strips tool output when verbose is on", () => { - const broadcast = vi.fn(); - const broadcastToConnIds = vi.fn(); - const nodeSendToSession = vi.fn(); - const agentRunSeq = new Map(); - const chatRunState = createChatRunState(); - const toolEventRecipients = createToolEventRecipientRegistry(); + const { broadcastToConnIds, toolEventRecipients, handler } = createHarness({ + resolveSessionKeyForRun: () => "session-1", + }); registerAgentRunContext("run-tool-on", { sessionKey: "session-1", verboseLevel: "on" }); toolEventRecipients.add("run-tool-on", "conn-1"); - const handler = createAgentEventHandler({ - broadcast, - broadcastToConnIds, - nodeSendToSession, - agentRunSeq, - chatRunState, - resolveSessionKeyForRun: () => "session-1", - clearAgentRunContext: vi.fn(), - toolEventRecipients, - }); - handler({ runId: "run-tool-on", seq: 1, @@ -294,27 +226,13 @@ describe("agent event handler", () => { }); it("keeps tool output when verbose is full", () => { - const broadcast = vi.fn(); - const broadcastToConnIds = vi.fn(); - const nodeSendToSession = vi.fn(); - const agentRunSeq = new Map(); - const chatRunState = createChatRunState(); - const toolEventRecipients = createToolEventRecipientRegistry(); + const { broadcastToConnIds, toolEventRecipients, handler } = createHarness({ + resolveSessionKeyForRun: () => "session-1", + }); registerAgentRunContext("run-tool-full", { sessionKey: "session-1", verboseLevel: "full" }); toolEventRecipients.add("run-tool-full", "conn-1"); - const handler = createAgentEventHandler({ - broadcast, - broadcastToConnIds, - nodeSendToSession, - agentRunSeq, - chatRunState, - resolveSessionKeyForRun: () => "session-1", - clearAgentRunContext: vi.fn(), - toolEventRecipients, - }); - const result = { content: [{ type: "text", text: "secret" }] }; handler({ runId: "run-tool-full",