refactor(core): Improve test-webhooks (no-changelog) (#8069)
Remove duplication, improve readability, and expand tests for `TestWebhooks.ts` - in anticipation for storing test webhooks in Redis. --------- Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ <aditya@netroy.in>
This commit is contained in:
154
packages/cli/test/unit/TestWebhooks.test.ts
Normal file
154
packages/cli/test/unit/TestWebhooks.test.ts
Normal file
@@ -0,0 +1,154 @@
|
||||
import { mockInstance } from '../shared/mocking';
|
||||
import { NodeTypes } from '@/NodeTypes';
|
||||
import { Push } from '@/push';
|
||||
import { TestWebhooks } from '@/TestWebhooks';
|
||||
import { WebhookNotFoundError } from '@/errors/response-errors/webhook-not-found.error';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { generateNanoId } from '@/databases/utils/generators';
|
||||
import { NotFoundError } from '@/errors/response-errors/not-found.error';
|
||||
import * as WebhookHelpers from '@/WebhookHelpers';
|
||||
|
||||
import type { IWorkflowDb, WebhookRequest } from '@/Interfaces';
|
||||
import type express from 'express';
|
||||
import type {
|
||||
IWebhookData,
|
||||
IWorkflowExecuteAdditionalData,
|
||||
Workflow,
|
||||
WorkflowActivateMode,
|
||||
WorkflowExecuteMode,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
describe('TestWebhooks', () => {
|
||||
jest.useFakeTimers();
|
||||
|
||||
const push = mockInstance(Push);
|
||||
const nodeTypes = mockInstance(NodeTypes);
|
||||
|
||||
const testWebhooks = new TestWebhooks(push, nodeTypes);
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('needsWebhook()', () => {
|
||||
const httpMethod = 'GET';
|
||||
const path = uuid();
|
||||
const workflowId = generateNanoId();
|
||||
|
||||
const webhook = {
|
||||
httpMethod,
|
||||
path,
|
||||
workflowId,
|
||||
webhookDescription: {},
|
||||
} as IWebhookData;
|
||||
|
||||
const keyPart = [httpMethod, path].join('|');
|
||||
|
||||
type NeedsWebhookArgs = [
|
||||
IWorkflowDb,
|
||||
Workflow,
|
||||
IWorkflowExecuteAdditionalData,
|
||||
WorkflowExecuteMode,
|
||||
WorkflowActivateMode,
|
||||
];
|
||||
|
||||
const workflow = {
|
||||
id: workflowId,
|
||||
createWebhookIfNotExists: () => {},
|
||||
deleteWebhook: () => {},
|
||||
} as unknown as Workflow;
|
||||
|
||||
const args: NeedsWebhookArgs = [
|
||||
{ id: workflowId } as unknown as IWorkflowDb,
|
||||
workflow,
|
||||
{} as unknown as IWorkflowExecuteAdditionalData,
|
||||
'manual',
|
||||
'manual',
|
||||
];
|
||||
|
||||
test('should register a webhook as active', async () => {
|
||||
jest.spyOn(WebhookHelpers, 'getWorkflowWebhooks').mockReturnValue([webhook]);
|
||||
jest.spyOn(testWebhooks, 'toWebhookKey').mockReturnValue(keyPart);
|
||||
const activateWebhookSpy = jest.spyOn(testWebhooks, 'activateWebhook');
|
||||
|
||||
const needsWebhook = await testWebhooks.needsWebhook(...args);
|
||||
|
||||
expect(needsWebhook).toBe(true);
|
||||
expect(activateWebhookSpy).toHaveBeenCalledWith(workflow, webhook, 'manual', 'manual');
|
||||
});
|
||||
|
||||
test('should remove from active webhooks on failure to add', async () => {
|
||||
const msg = 'Failed to add webhook to active webhooks';
|
||||
|
||||
jest.spyOn(WebhookHelpers, 'getWorkflowWebhooks').mockReturnValue([webhook]);
|
||||
jest.spyOn(testWebhooks, 'toWebhookKey').mockReturnValue(keyPart);
|
||||
jest.spyOn(testWebhooks, 'activateWebhook').mockRejectedValue(new Error(msg));
|
||||
const deactivateSpy = jest.spyOn(testWebhooks, 'deactivateWebhooksFor');
|
||||
|
||||
const needsWebhook = testWebhooks.needsWebhook(...args);
|
||||
|
||||
await expect(needsWebhook).rejects.toThrowError(msg);
|
||||
expect(deactivateSpy).toHaveBeenCalledWith(workflow);
|
||||
});
|
||||
|
||||
test('should return false if no webhook to start workflow', async () => {
|
||||
webhook.webhookDescription.restartWebhook = true;
|
||||
jest.spyOn(WebhookHelpers, 'getWorkflowWebhooks').mockReturnValue([webhook]);
|
||||
|
||||
const result = await testWebhooks.needsWebhook(...args);
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('executeWebhook()', () => {
|
||||
const httpMethod = 'GET';
|
||||
const path = uuid();
|
||||
const workflowId = generateNanoId();
|
||||
|
||||
const webhook = {
|
||||
httpMethod,
|
||||
path,
|
||||
workflowId,
|
||||
} as IWebhookData;
|
||||
|
||||
const keyPart = [httpMethod, path].join('|');
|
||||
|
||||
test('should throw if webhook is not registered', async () => {
|
||||
jest.spyOn(testWebhooks, 'getActiveWebhook').mockReturnValue(webhook);
|
||||
jest.spyOn(testWebhooks, 'getWebhookMethods').mockResolvedValue([]);
|
||||
jest.spyOn(testWebhooks, 'toWebhookKey').mockReturnValue(keyPart);
|
||||
|
||||
const request = { params: { path } } as WebhookRequest;
|
||||
const response = {} as express.Response;
|
||||
const promise = testWebhooks.executeWebhook(request, response);
|
||||
|
||||
await expect(promise).rejects.toThrowError(WebhookNotFoundError);
|
||||
});
|
||||
|
||||
test('should throw if webhook node is registered but missing from workflow', async () => {
|
||||
jest.spyOn(testWebhooks, 'getActiveWebhook').mockReturnValue(webhook);
|
||||
jest.spyOn(testWebhooks, 'getWebhookMethods').mockResolvedValue([]);
|
||||
jest.spyOn(testWebhooks, 'toWebhookKey').mockReturnValue(keyPart);
|
||||
|
||||
// @ts-expect-error Private property
|
||||
testWebhooks.registeredWebhooks[`${keyPart}|${workflowId}`] = {
|
||||
sessionId: 'some-session-id',
|
||||
timeout: setTimeout(() => {}, 0),
|
||||
workflowEntity: {} as IWorkflowDb,
|
||||
workflow: {
|
||||
getNode: () => null,
|
||||
} as unknown as Workflow,
|
||||
};
|
||||
|
||||
const request = { params: { path } } as WebhookRequest;
|
||||
const response = {} as express.Response;
|
||||
const promise = testWebhooks.executeWebhook(request, response);
|
||||
|
||||
await expect(promise).rejects.toThrowError(NotFoundError);
|
||||
|
||||
// @ts-expect-error Private property
|
||||
delete testWebhooks.registeredWebhooks[`${keyPart}|${workflowId}`];
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,32 +1,44 @@
|
||||
import { webhookNotFoundErrorMessage } from '@/utils';
|
||||
import { webhookNotFoundErrorMessage } from '@/errors/response-errors/webhook-not-found.error';
|
||||
|
||||
describe('utils test webhookNotFoundErrorMessage ', () => {
|
||||
it('should return a message with path and method', () => {
|
||||
const message = webhookNotFoundErrorMessage('webhook12345', 'GET');
|
||||
const message = webhookNotFoundErrorMessage({ path: 'webhook12345', httpMethod: 'GET' });
|
||||
|
||||
expect(message).toEqual('The requested webhook "GET webhook12345" is not registered.');
|
||||
});
|
||||
it('should return a message with path', () => {
|
||||
const message = webhookNotFoundErrorMessage('webhook12345');
|
||||
const message = webhookNotFoundErrorMessage({ path: 'webhook12345' });
|
||||
|
||||
expect(message).toEqual('The requested webhook "webhook12345" is not registered.');
|
||||
});
|
||||
it('should return a message with method with tip', () => {
|
||||
const message = webhookNotFoundErrorMessage('webhook12345', 'POST', ['GET', 'PUT']);
|
||||
const message = webhookNotFoundErrorMessage({
|
||||
path: 'webhook12345',
|
||||
httpMethod: 'POST',
|
||||
webhookMethods: ['GET', 'PUT'],
|
||||
});
|
||||
|
||||
expect(message).toEqual(
|
||||
'This webhook is not registered for POST requests. Did you mean to make a GET or PUT request?',
|
||||
);
|
||||
});
|
||||
it('should return a message with method with tip', () => {
|
||||
const message = webhookNotFoundErrorMessage('webhook12345', 'POST', ['PUT']);
|
||||
const message = webhookNotFoundErrorMessage({
|
||||
path: 'webhook12345',
|
||||
httpMethod: 'POST',
|
||||
webhookMethods: ['PUT'],
|
||||
});
|
||||
|
||||
expect(message).toEqual(
|
||||
'This webhook is not registered for POST requests. Did you mean to make a PUT request?',
|
||||
);
|
||||
});
|
||||
it('should return a message with method with tip', () => {
|
||||
const message = webhookNotFoundErrorMessage('webhook12345', 'POST', ['GET', 'PUT', 'DELETE']);
|
||||
const message = webhookNotFoundErrorMessage({
|
||||
path: 'webhook12345',
|
||||
httpMethod: 'POST',
|
||||
webhookMethods: ['GET', 'PUT', 'DELETE'],
|
||||
});
|
||||
|
||||
expect(message).toEqual(
|
||||
'This webhook is not registered for POST requests. Did you mean to make a GET, PUT or DELETE request?',
|
||||
|
||||
Reference in New Issue
Block a user