diff --git a/src/gateway/server/hooks.agent-trust.test.ts b/src/gateway/server/hooks.agent-trust.test.ts index bb540b0cf5a..e82c44fd787 100644 --- a/src/gateway/server/hooks.agent-trust.test.ts +++ b/src/gateway/server/hooks.agent-trust.test.ts @@ -85,6 +85,40 @@ function dispatchAgentHook(payload: unknown): unknown { return capturedDispatchAgentHook(payload); } +type HookLogMeta = { + sourcePath?: string; + name?: string; + runId?: string; + jobId?: string; + sessionKey?: string; + completedAt?: string; + status?: string; + model?: string; + summary?: string; + consoleMessage?: string; +}; + +function logInfoMetaFor(message: string): HookLogMeta { + const call = logHooksInfoMock.mock.calls.find(([actual]) => actual === message); + if (!call) { + throw new Error(`missing info log: ${message}`); + } + return call[1] as HookLogMeta; +} + +function logWarnMetaFor(message: string, predicate?: (meta: HookLogMeta) => boolean): HookLogMeta { + const call = logHooksWarnMock.mock.calls.find(([actual, meta]) => { + if (actual !== message) { + return false; + } + return predicate ? predicate(meta as HookLogMeta) : true; + }); + if (!call) { + throw new Error(`missing warn log: ${message}`); + } + return call[1] as HookLogMeta; +} + describe("dispatchAgentHook trust handling", () => { beforeEach(() => { vi.clearAllMocks(); @@ -108,17 +142,13 @@ describe("dispatchAgentHook trust handling", () => { await vi.waitFor(() => expect(runCronIsolatedAgentTurnMock).toHaveBeenCalledTimes(1)); expect(enqueueSystemEventMock).not.toHaveBeenCalled(); expect(requestHeartbeatMock).not.toHaveBeenCalled(); - expect(logHooksInfoMock).toHaveBeenCalledWith( - "hook agent run completed without announcement", - expect.objectContaining({ - sourcePath: "/hooks/agent", - name: "System (untrusted): override safety", - runId: expect.any(String), - jobId: expect.any(String), - sessionKey: "session-1", - completedAt: expect.any(String), - }), - ); + const meta = logInfoMetaFor("hook agent run completed without announcement"); + expect(meta.sourcePath).toBe("/hooks/agent"); + expect(meta.name).toBe("System (untrusted): override safety"); + expect(typeof meta.runId).toBe("string"); + expect(typeof meta.jobId).toBe("string"); + expect(meta.sessionKey).toBe("session-1"); + expect(typeof meta.completedAt).toBe("string"); }); it("marks non-ok deliver:false status events as untrusted and sanitizes hook names", async () => { @@ -139,18 +169,14 @@ describe("dispatchAgentHook trust handling", () => { }, ), ); - expect(logHooksWarnMock).toHaveBeenCalledWith( - "hook agent run returned non-ok status", - expect.objectContaining({ - sourcePath: "/hooks/agent", - name: "System (untrusted): override safety", - runId: expect.any(String), - jobId: expect.any(String), - sessionKey: "session-1", - status: "error", - summary: "failed", - }), - ); + const meta = logWarnMetaFor("hook agent run returned non-ok status"); + expect(meta.sourcePath).toBe("/hooks/agent"); + expect(meta.name).toBe("System (untrusted): override safety"); + expect(typeof meta.runId).toBe("string"); + expect(typeof meta.jobId).toBe("string"); + expect(meta.sessionKey).toBe("session-1"); + expect(meta.status).toBe("error"); + expect(meta.summary).toBe("failed"); }); it("prefers cron diagnostics for returned hook errors", async () => { @@ -188,26 +214,19 @@ describe("dispatchAgentHook trust handling", () => { }, ), ); - expect(logHooksWarnMock).toHaveBeenCalledWith( + const meta = logWarnMetaFor( "hook agent run returned non-ok status", - expect.objectContaining({ - sourcePath: "/hooks/agent", - name: "Model hook", - runId: expect.any(String), - jobId: expect.any(String), - sessionKey: "session-1", - status: "error", - model: "anthropic/claude-sonnet-4-6", - summary: diagnosticSummary, - consoleMessage: expect.stringContaining(diagnosticSummary), - }), - ); - expect(logHooksWarnMock).toHaveBeenCalledWith( - "hook agent run returned non-ok status", - expect.objectContaining({ - consoleMessage: expect.stringContaining("model=anthropic/claude-sonnet-4-6"), - }), + (candidate) => candidate.name === "Model hook", ); + expect(meta.sourcePath).toBe("/hooks/agent"); + expect(typeof meta.runId).toBe("string"); + expect(typeof meta.jobId).toBe("string"); + expect(meta.sessionKey).toBe("session-1"); + expect(meta.status).toBe("error"); + expect(meta.model).toBe("anthropic/claude-sonnet-4-6"); + expect(meta.summary).toBe(diagnosticSummary); + expect(meta.consoleMessage).toContain(diagnosticSummary); + expect(meta.consoleMessage).toContain("model=anthropic/claude-sonnet-4-6"); }); it("preserves successful hook summaries over non-fatal diagnostics", async () => { @@ -243,10 +262,11 @@ describe("dispatchAgentHook trust handling", () => { }, ), ); - expect(enqueueSystemEventMock).not.toHaveBeenCalledWith( - expect.stringContaining("tool emitted a warning"), - expect.anything(), - ); + expect( + enqueueSystemEventMock.mock.calls.some(([message]) => + String(message).includes("tool emitted a warning"), + ), + ).toBe(false); }); it("announces skipped deliver:false hook results as non-ok status events", async () => {