From 5959c9927ed24c9213da0db07770e18ac445867e Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sun, 12 Apr 2026 11:14:11 +0100 Subject: [PATCH] test(agents): share approval abort helper --- .../pi-tools.before-tool-call.e2e.test.ts | 73 ++++++------------- 1 file changed, 24 insertions(+), 49 deletions(-) diff --git a/src/agents/pi-tools.before-tool-call.e2e.test.ts b/src/agents/pi-tools.before-tool-call.e2e.test.ts index d87bbcc1d44..5c9125a7dfc 100644 --- a/src/agents/pi-tools.before-tool-call.e2e.test.ts +++ b/src/agents/pi-tools.before-tool-call.e2e.test.ts @@ -365,6 +365,28 @@ describe("before_tool_call requireApproval handling", () => { mockCallGateway.mockReset(); }); + async function runAbortDuringApprovalWait(options?: { onResolution?: ReturnType }) { + hookRunner.runBeforeToolCall.mockResolvedValue({ + requireApproval: { + title: "Abortable", + description: "Will be aborted", + onResolution: options?.onResolution, + }, + }); + + const controller = new AbortController(); + mockCallGateway.mockResolvedValueOnce({ id: "server-id-abort", status: "accepted" }); + mockCallGateway.mockImplementationOnce(() => new Promise(() => {})); + setTimeout(() => controller.abort(new Error("run cancelled")), 10); + + return await runBeforeToolCallHook({ + toolName: "bash", + params: {}, + ctx: { agentId: "main", sessionKey: "main" }, + signal: controller.signal, + }); + } + it("blocks without triggering approval when both block and requireApproval are set", async () => { hookRunner.runBeforeToolCall.mockResolvedValue({ block: true, @@ -574,31 +596,7 @@ describe("before_tool_call requireApproval handling", () => { }); it("unblocks immediately when abort signal fires during waitDecision", async () => { - hookRunner.runBeforeToolCall.mockResolvedValue({ - requireApproval: { - title: "Abortable", - description: "Will be aborted", - }, - }); - - const controller = new AbortController(); - - // First call: plugin.approval.request → accepted - mockCallGateway.mockResolvedValueOnce({ id: "server-id-abort", status: "accepted" }); - // Second call: plugin.approval.waitDecision → never resolves (simulates long wait) - mockCallGateway.mockImplementationOnce( - () => new Promise(() => {}), // hangs forever - ); - - // Abort after a short delay - setTimeout(() => controller.abort(new Error("run cancelled")), 10); - - const result = await runBeforeToolCallHook({ - toolName: "bash", - params: {}, - ctx: { agentId: "main", sessionKey: "main" }, - signal: controller.signal, - }); + const result = await runAbortDuringApprovalWait(); expect(result.blocked).toBe(true); expect(result).toHaveProperty("reason", "Approval cancelled (run aborted)"); @@ -767,30 +765,7 @@ describe("before_tool_call requireApproval handling", () => { it("calls onResolution with cancelled when abort signal fires", async () => { const onResolution = vi.fn(); - - hookRunner.runBeforeToolCall.mockResolvedValue({ - requireApproval: { - title: "Abortable with callback", - description: "Will be aborted", - onResolution, - }, - }); - - const controller = new AbortController(); - - mockCallGateway.mockResolvedValueOnce({ id: "server-id-r5", status: "accepted" }); - mockCallGateway.mockImplementationOnce( - () => new Promise(() => {}), // hangs forever - ); - - setTimeout(() => controller.abort(new Error("run cancelled")), 10); - - const result = await runBeforeToolCallHook({ - toolName: "bash", - params: {}, - ctx: { agentId: "main", sessionKey: "main" }, - signal: controller.signal, - }); + const result = await runAbortDuringApprovalWait({ onResolution }); expect(result.blocked).toBe(true); expect(result).toHaveProperty("reason", "Approval cancelled (run aborted)");