From 0522d9352c7185562bf0bd31a90aa461d08dff0c Mon Sep 17 00:00:00 2001 From: Anand Reddy Jonnalagadda Date: Sun, 12 Apr 2026 09:33:43 +0200 Subject: [PATCH] fix(nodes-langchain): clear timer on PendingCallsManager.remove() and expand test coverage Co-Authored-By: Claude Sonnet 4.6 --- .../execution/PendingCallsManager.ts | 4 +++ .../__tests__/PendingCallsManager.test.ts | 26 +++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/execution/PendingCallsManager.ts b/packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/execution/PendingCallsManager.ts index bb946a15ddd..cfbba710728 100644 --- a/packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/execution/PendingCallsManager.ts +++ b/packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/execution/PendingCallsManager.ts @@ -63,6 +63,10 @@ export class PendingCallsManager { } remove(callId: string): void { + const pending = this.pendingCalls[callId]; + if (pending) { + clearTimeout(pending.timer); + } delete this.pendingCalls[callId]; } diff --git a/packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/execution/__tests__/PendingCallsManager.test.ts b/packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/execution/__tests__/PendingCallsManager.test.ts index 6c995272de7..65d993afe38 100644 --- a/packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/execution/__tests__/PendingCallsManager.test.ts +++ b/packages/@n8n/nodes-langchain/nodes/mcp/McpTrigger/execution/__tests__/PendingCallsManager.test.ts @@ -247,5 +247,31 @@ describe('PendingCallsManager', () => { it('should handle removing non-existent call', () => { expect(() => manager.remove('non-existent')).not.toThrow(); }); + + it('should clear the timer when removing a pending call', async () => { + const clearTimeoutSpy = jest.spyOn(global, 'clearTimeout'); + + const promise = manager.waitForResult('call-1', 'tool', {}, 1000); + manager.remove('call-1'); + + expect(clearTimeoutSpy).toHaveBeenCalled(); + + clearTimeoutSpy.mockRestore(); + // Drain the hanging promise to avoid open handles + await Promise.race([promise.catch(() => {}), Promise.resolve()]); + }); + + it('should not reject the promise when the timeout fires after removal', async () => { + const settled = { value: false }; + const promise = manager.waitForResult('call-1', 'tool', {}, 500); + promise.then(() => (settled.value = true)).catch(() => (settled.value = true)); + + manager.remove('call-1'); + jest.advanceTimersByTime(600); + + await Promise.resolve(); + + expect(settled.value).toBe(false); + }); }); });