From 8fd0738191f87f5762f23c763f64e86fdd444830 Mon Sep 17 00:00:00 2001 From: jeanpaul Date: Mon, 19 May 2025 16:21:23 +0200 Subject: [PATCH] fix(Chat Trigger Node): Don't continue when action is load previous session and option is not set (#15438) --- .../trigger/ChatTrigger/ChatTrigger.node.ts | 2 +- .../__test__/ChatTrigger.node.test.ts | 133 ++++++++++++++++++ 2 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 packages/@n8n/nodes-langchain/nodes/trigger/ChatTrigger/__test__/ChatTrigger.node.test.ts diff --git a/packages/@n8n/nodes-langchain/nodes/trigger/ChatTrigger/ChatTrigger.node.ts b/packages/@n8n/nodes-langchain/nodes/trigger/ChatTrigger/ChatTrigger.node.ts index 3401bc0b18..f12f9b9764 100644 --- a/packages/@n8n/nodes-langchain/nodes/trigger/ChatTrigger/ChatTrigger.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/trigger/ChatTrigger/ChatTrigger.node.ts @@ -563,7 +563,7 @@ ${cssVariables} return { webhookResponse: { data: messages }, }; - } else if (options?.loadPreviousSession === 'notSupported') { + } else if (!options?.loadPreviousSession || options?.loadPreviousSession === 'notSupported') { // If messages of a previous session should not be loaded, simply return an empty array return { webhookResponse: { data: [] }, diff --git a/packages/@n8n/nodes-langchain/nodes/trigger/ChatTrigger/__test__/ChatTrigger.node.test.ts b/packages/@n8n/nodes-langchain/nodes/trigger/ChatTrigger/__test__/ChatTrigger.node.test.ts new file mode 100644 index 0000000000..2f5510b4ed --- /dev/null +++ b/packages/@n8n/nodes-langchain/nodes/trigger/ChatTrigger/__test__/ChatTrigger.node.test.ts @@ -0,0 +1,133 @@ +import { jest } from '@jest/globals'; +import type { Request, Response } from 'express'; +import { mock } from 'jest-mock-extended'; +import type { IWebhookFunctions } from 'n8n-workflow'; + +import { ChatTrigger } from '../ChatTrigger.node'; +import type { LoadPreviousSessionChatOption } from '../types'; + +jest.mock('../GenericFunctions', () => ({ + validateAuth: jest.fn(), +})); + +describe('ChatTrigger Node', () => { + const mockContext = mock(); + const mockRequest = mock(); + const mockResponse = mock(); + let chatTrigger: ChatTrigger; + + beforeEach(() => { + jest.clearAllMocks(); + + chatTrigger = new ChatTrigger(); + + mockContext.getRequestObject.mockReturnValue(mockRequest); + mockContext.getResponseObject.mockReturnValue(mockResponse); + mockContext.getNodeParameter.mockImplementation( + ( + paramName: string, + defaultValue?: boolean | string | object, + ): boolean | string | object | undefined => { + if (paramName === 'public') return true; + if (paramName === 'mode') return 'hostedChat'; + if (paramName === 'options') return {}; + return defaultValue; + }, + ); + mockContext.getBodyData.mockReturnValue({}); + }); + + describe('webhook method: loadPreviousSession action', () => { + beforeEach(() => { + mockContext.getBodyData.mockReturnValue({ action: 'loadPreviousSession' }); + }); + + it('should return empty array when loadPreviousSession is undefined', async () => { + // Mock options with undefined loadPreviousSession + mockContext.getNodeParameter.mockImplementation( + ( + paramName: string, + defaultValue?: boolean | string | object, + ): boolean | string | object | undefined => { + if (paramName === 'public') return true; + if (paramName === 'mode') return 'hostedChat'; + if (paramName === 'options') return { loadPreviousSession: undefined }; + return defaultValue; + }, + ); + + // Call the webhook method + const result = await chatTrigger.webhook(mockContext); + + // Verify the returned result contains empty data array + expect(result).toEqual({ + webhookResponse: { data: [] }, + }); + }); + + it('should return empty array when loadPreviousSession is "notSupported"', async () => { + // Mock options with notSupported loadPreviousSession + mockContext.getNodeParameter.mockImplementation( + ( + paramName: string, + defaultValue?: boolean | string | object, + ): boolean | string | object | undefined => { + if (paramName === 'public') return true; + if (paramName === 'mode') return 'hostedChat'; + if (paramName === 'options') return { loadPreviousSession: 'notSupported' }; + return defaultValue; + }, + ); + + // Call the webhook method + const result = await chatTrigger.webhook(mockContext); + + // Verify the returned result contains empty data array + expect(result).toEqual({ + webhookResponse: { data: [] }, + }); + }); + + it('should handle loadPreviousSession="memory" correctly', async () => { + // Mock chat history data + const mockMessages = [ + { toJSON: () => ({ content: 'Message 1' }) }, + { toJSON: () => ({ content: 'Message 2' }) }, + ]; + + // Mock memory with chat history + const mockMemory = { + chatHistory: { + getMessages: jest.fn().mockReturnValueOnce(mockMessages), + }, + }; + + // Mock options with memory loadPreviousSession + mockContext.getNodeParameter.mockImplementation( + ( + paramName: string, + defaultValue?: boolean | string | object, + ): boolean | string | object | undefined => { + if (paramName === 'public') return true; + if (paramName === 'mode') return 'hostedChat'; + if (paramName === 'options') + return { loadPreviousSession: 'memory' as LoadPreviousSessionChatOption }; + return defaultValue; + }, + ); + + // Mock getInputConnectionData to return memory + mockContext.getInputConnectionData.mockResolvedValue(mockMemory); + + // Call the webhook method + const result = await chatTrigger.webhook(mockContext); + + // Verify the returned result contains messages from memory + expect(result).toEqual({ + webhookResponse: { + data: [{ content: 'Message 1' }, { content: 'Message 2' }], + }, + }); + }); + }); +});