mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 18:12:04 +00:00
refactor(core): Simplify state in test webhooks (no-changelog) (#8155)
This PR simplifies state in test webhooks so that it can be cached easily. Caching this state will allow us to start using Redis for manual webhooks, to support manual webhooks to work in multi-main setup. - [x] Convert `workflowWebhooks` to a getter - no need to optimize for deactivation - [x] Remove array from value in `TestWebhooks.webhookUrls` - [x] Consolidate `webhookUrls` and `registeredWebhooks`
This commit is contained in:
@@ -1,6 +1,4 @@
|
||||
import { mockInstance } from '../shared/mocking';
|
||||
import { NodeTypes } from '@/NodeTypes';
|
||||
import { Push } from '@/push';
|
||||
import { mock } from 'jest-mock-extended';
|
||||
import { TestWebhooks } from '@/TestWebhooks';
|
||||
import { WebhookNotFoundError } from '@/errors/response-errors/webhook-not-found.error';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
@@ -8,8 +6,7 @@ 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 { IWorkflowDb, WebhookRegistration, WebhookRequest } from '@/Interfaces';
|
||||
import type {
|
||||
IWebhookData,
|
||||
IWorkflowExecuteAdditionalData,
|
||||
@@ -19,31 +16,27 @@ import type {
|
||||
} from 'n8n-workflow';
|
||||
|
||||
describe('TestWebhooks', () => {
|
||||
jest.useFakeTimers();
|
||||
const testWebhooks = new TestWebhooks(mock(), mock());
|
||||
|
||||
const push = mockInstance(Push);
|
||||
const nodeTypes = mockInstance(NodeTypes);
|
||||
|
||||
const testWebhooks = new TestWebhooks(push, nodeTypes);
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
testWebhooks.clearRegistrations();
|
||||
});
|
||||
|
||||
const httpMethod = 'GET';
|
||||
const path = uuid();
|
||||
const workflowId = generateNanoId();
|
||||
|
||||
const webhook = mock<IWebhookData>({
|
||||
httpMethod,
|
||||
path,
|
||||
workflowId,
|
||||
});
|
||||
|
||||
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,
|
||||
@@ -52,23 +45,18 @@ describe('TestWebhooks', () => {
|
||||
WorkflowActivateMode,
|
||||
];
|
||||
|
||||
const workflow = {
|
||||
id: workflowId,
|
||||
createWebhookIfNotExists: () => {},
|
||||
deleteWebhook: () => {},
|
||||
} as unknown as Workflow;
|
||||
const workflow = mock<Workflow>({ id: workflowId });
|
||||
|
||||
const args: NeedsWebhookArgs = [
|
||||
{ id: workflowId } as unknown as IWorkflowDb,
|
||||
mock<IWorkflowDb>({ id: workflowId }),
|
||||
workflow,
|
||||
{} as unknown as IWorkflowExecuteAdditionalData,
|
||||
mock<IWorkflowExecuteAdditionalData>(),
|
||||
'manual',
|
||||
'manual',
|
||||
];
|
||||
|
||||
test('should register a webhook as active', async () => {
|
||||
test('should return true and activate webhook if needed', 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);
|
||||
@@ -77,18 +65,17 @@ describe('TestWebhooks', () => {
|
||||
expect(activateWebhookSpy).toHaveBeenCalledWith(workflow, webhook, 'manual', 'manual');
|
||||
});
|
||||
|
||||
test('should remove from active webhooks on failure to add', async () => {
|
||||
test('should deactivate webhooks on failure to activate', 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 deactivateWebhooksSpy = jest.spyOn(testWebhooks, 'deactivateWebhooks');
|
||||
|
||||
const needsWebhook = testWebhooks.needsWebhook(...args);
|
||||
|
||||
await expect(needsWebhook).rejects.toThrowError(msg);
|
||||
expect(deactivateSpy).toHaveBeenCalledWith(workflow);
|
||||
expect(deactivateWebhooksSpy).toHaveBeenCalledWith(workflow);
|
||||
});
|
||||
|
||||
test('should return false if no webhook to start workflow', async () => {
|
||||
@@ -102,26 +89,14 @@ describe('TestWebhooks', () => {
|
||||
});
|
||||
|
||||
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);
|
||||
const promise = testWebhooks.executeWebhook(
|
||||
mock<WebhookRequest>({ params: { path } }),
|
||||
mock(),
|
||||
);
|
||||
|
||||
await expect(promise).rejects.toThrowError(WebhookNotFoundError);
|
||||
});
|
||||
@@ -129,26 +104,22 @@ describe('TestWebhooks', () => {
|
||||
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}`] = {
|
||||
const registration = mock<WebhookRegistration>({
|
||||
sessionId: 'some-session-id',
|
||||
timeout: setTimeout(() => {}, 0),
|
||||
workflowEntity: {} as IWorkflowDb,
|
||||
workflow: {
|
||||
getNode: () => null,
|
||||
} as unknown as Workflow,
|
||||
};
|
||||
timeout: mock<NodeJS.Timeout>(),
|
||||
workflowEntity: mock<IWorkflowDb>({}),
|
||||
workflow: mock<Workflow>(),
|
||||
});
|
||||
|
||||
const request = { params: { path } } as WebhookRequest;
|
||||
const response = {} as express.Response;
|
||||
const promise = testWebhooks.executeWebhook(request, response);
|
||||
testWebhooks.setRegistration(registration);
|
||||
|
||||
const promise = testWebhooks.executeWebhook(
|
||||
mock<WebhookRequest>({ params: { path } }),
|
||||
mock(),
|
||||
);
|
||||
|
||||
await expect(promise).rejects.toThrowError(NotFoundError);
|
||||
|
||||
// @ts-expect-error Private property
|
||||
delete testWebhooks.registeredWebhooks[`${keyPart}|${workflowId}`];
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user