diff --git a/packages/cli/src/webhooks/__tests__/live-webhooks.test.ts b/packages/cli/src/webhooks/__tests__/live-webhooks.test.ts index ca63c0ffe1c..46e80bd1f1c 100644 --- a/packages/cli/src/webhooks/__tests__/live-webhooks.test.ts +++ b/packages/cli/src/webhooks/__tests__/live-webhooks.test.ts @@ -25,6 +25,10 @@ import type { WorkflowStaticDataService } from '@/workflows/workflow-static-data jest.mock('@/webhooks/webhook-helpers'); jest.mock('@/workflow-execute-additional-data'); +const WORKFLOW_ID = 'workflow-1'; +const NODE_NAME = 'Webhook'; +const WEBHOOK_PATH = 'test-webhook'; + describe('LiveWebhooks', () => { const workflowRepository = mock(); const webhookService = mock(); @@ -49,20 +53,83 @@ describe('LiveWebhooks', () => { ); }); + /** + * Wires up the standard `executeWebhook` mock setup (`findWebhook`, + * `getWebhookMethods`, `findOne`, `getByNameAndVersion`, `getNodeWebhooks`, + * and the `WebhookHelpers.executeWebhook` shim that drives the callback). + * + * Callers supply the `workflowEntity` for the case they're exercising and + * optionally an `onExecuteWebhook` hook to capture what the shim sees. + */ + function setupExecuteWebhookMocks( + workflowEntity: WorkflowEntity, + { + httpMethod = 'GET', + onExecuteWebhook, + }: { + httpMethod?: IHttpRequestMethods; + onExecuteWebhook?: (args: { + workflow: Workflow; + webhookData: IWebhookData; + workflowData: IWorkflowBase; + }) => void; + } = {}, + ): WebhookRequest { + const webhookEntity = mock({ + workflowId: WORKFLOW_ID, + node: NODE_NAME, + webhookPath: WEBHOOK_PATH, + method: httpMethod, + isDynamic: false, + }); + + const webhookNodeType = mock({ + description: { name: NODE_NAME, properties: [] }, + webhook: jest.fn(), + }); + + const webhookData = mock({ + httpMethod, + path: WEBHOOK_PATH, + node: NODE_NAME, + webhookDescription: { nodeType: undefined } as never, + workflowId: WORKFLOW_ID, + }); + + webhookService.findWebhook.mockResolvedValue(webhookEntity); + webhookService.getWebhookMethods.mockResolvedValue([httpMethod]); + workflowRepository.findOne.mockResolvedValue(workflowEntity); + nodeTypes.getByNameAndVersion.mockReturnValue(webhookNodeType); + webhookService.getNodeWebhooks.mockReturnValue([webhookData]); + + (WebhookHelpers.executeWebhook as jest.Mock).mockImplementation( + (workflow: Workflow, wd: IWebhookData, workflowData: IWorkflowBase, ...args: unknown[]) => { + onExecuteWebhook?.({ workflow, webhookData: wd, workflowData }); + const webhookCallback = args[args.length - 1] as ( + error: Error | null, + data: object, + ) => void; + void webhookCallback(null, {}); + }, + ); + + return mock({ + method: httpMethod, + params: { path: WEBHOOK_PATH }, + }); + } + describe('executeWebhook', () => { it('should use active version nodes when executing webhook', async () => { - const workflowId = 'workflow-1'; - const nodeName = 'Webhook'; - const webhookPath = 'test-webhook'; const httpMethod: IHttpRequestMethods = 'GET'; const createWebhookNode = (id: string, position: [number, number]): INode => ({ id, - name: nodeName, + name: NODE_NAME, type: 'n8n-nodes-base.webhook', typeVersion: 1, position, - parameters: { path: webhookPath, httpMethod }, + parameters: { path: WEBHOOK_PATH, httpMethod }, }); const draftNodes = [createWebhookNode('webhook-node-draft', [0, 0])]; @@ -70,7 +137,7 @@ describe('LiveWebhooks', () => { const activeVersion = mock({ versionId: 'v1', - workflowId, + workflowId: WORKFLOW_ID, nodes: activeNodes, connections: {}, authors: 'test-user', @@ -79,7 +146,7 @@ describe('LiveWebhooks', () => { }); const workflowEntity = mock({ - id: workflowId, + id: WORKFLOW_ID, name: 'Test Workflow', active: true, activeVersionId: activeVersion.versionId, @@ -89,46 +156,13 @@ describe('LiveWebhooks', () => { shared: [{ role: 'workflow:owner', project: { id: 'project-1', projectRelations: [] } }], }); - const webhookEntity = mock({ - workflowId, - node: nodeName, - webhookPath, - method: httpMethod, - isDynamic: false, - }); - - const webhookNodeType = mock({ - description: { name: nodeName, properties: [] }, - webhook: jest.fn(), - }); - - const webhookData = mock({ - httpMethod, - path: webhookPath, - node: nodeName, - webhookDescription: { nodeType: undefined } as never, - workflowId, - }); - - webhookService.findWebhook.mockResolvedValue(webhookEntity); - webhookService.getWebhookMethods.mockResolvedValue([httpMethod]); - workflowRepository.findOne.mockResolvedValue(workflowEntity); - nodeTypes.getByNameAndVersion.mockReturnValue(webhookNodeType); - webhookService.getNodeWebhooks.mockReturnValue([webhookData]); - let capturedNodes: INode[] = []; - (WebhookHelpers.executeWebhook as jest.Mock).mockImplementation( - (workflow: Workflow, ...args: unknown[]) => { + const request = setupExecuteWebhookMocks(workflowEntity, { + httpMethod, + onExecuteWebhook: ({ workflow }) => { capturedNodes = Object.values(workflow.nodes); - const webhookCallback = args[args.length - 1] as ( - error: Error | null, - data: object, - ) => void; - void webhookCallback(null, {}); }, - ); - - const request = mock({ method: httpMethod, params: { path: webhookPath } }); + }); await liveWebhooks.executeWebhook(request, mock()); @@ -136,9 +170,6 @@ describe('LiveWebhooks', () => { }); it('should pass workflowData with activeVersion nodes/connections to executeWebhook', async () => { - const workflowId = 'workflow-1'; - const nodeName = 'Webhook'; - const webhookPath = 'test-webhook'; const httpMethod: IHttpRequestMethods = 'POST'; const createWebhookNode = (id: string, name: string): INode => ({ @@ -147,7 +178,7 @@ describe('LiveWebhooks', () => { type: 'n8n-nodes-base.webhook', typeVersion: 1, position: [0, 0], - parameters: { path: webhookPath, httpMethod }, + parameters: { path: WEBHOOK_PATH, httpMethod }, }); const createSetNode = (id: string, name: string, value: string): INode => ({ @@ -183,7 +214,7 @@ describe('LiveWebhooks', () => { const activeVersion = mock({ versionId: 'v1', - workflowId, + workflowId: WORKFLOW_ID, nodes: activeNodes, connections: activeConnections, authors: 'test-user', @@ -192,7 +223,7 @@ describe('LiveWebhooks', () => { }); const workflowEntity = mock({ - id: workflowId, + id: WORKFLOW_ID, name: 'Test Workflow', active: true, activeVersionId: 'v1', @@ -202,51 +233,13 @@ describe('LiveWebhooks', () => { shared: [{ role: 'workflow:owner', project: { id: 'project-1', projectRelations: [] } }], }); - const webhookEntity = mock({ - workflowId, - node: nodeName, - webhookPath, - method: httpMethod, - isDynamic: false, - }); - - const webhookNodeType = mock({ - description: { name: nodeName, properties: [] }, - webhook: jest.fn(), - }); - - const webhookData = mock({ - httpMethod, - path: webhookPath, - node: nodeName, - webhookDescription: { nodeType: undefined } as never, - workflowId, - }); - - webhookService.findWebhook.mockResolvedValue(webhookEntity); - webhookService.getWebhookMethods.mockResolvedValue([httpMethod]); - workflowRepository.findOne.mockResolvedValue(workflowEntity as WorkflowEntity); - nodeTypes.getByNameAndVersion.mockReturnValue(webhookNodeType); - webhookService.getNodeWebhooks.mockReturnValue([webhookData]); - let capturedWorkflowData: IWorkflowBase | undefined; - (WebhookHelpers.executeWebhook as jest.Mock).mockImplementation( - ( - _workflow: Workflow, - _webhookData: IWebhookData, - workflowData: IWorkflowBase, - ...args: unknown[] - ) => { + const request = setupExecuteWebhookMocks(workflowEntity, { + httpMethod, + onExecuteWebhook: ({ workflowData }) => { capturedWorkflowData = workflowData; - const webhookCallback = args[args.length - 1] as ( - error: Error | null, - data: object, - ) => void; - void webhookCallback(null, {}); }, - ); - - const request = mock({ method: httpMethod, params: { path: webhookPath } }); + }); await liveWebhooks.executeWebhook(request, mock());