mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-10 22:54:05 -08:00
0511458d41
* refactor: Initial setup * Refactor for clarity * Comments to clarify * More replacements * Simplify with `fullPath` * Fix tests * Implement remaining methods * chore: Fix misresolved conflicts * Simplify syntax * Reduce diff * Minor cleanup * Fix lint * Inject dependency * Improve typings * Remove unused method * Restore method * Add comment * Rename in test * Restore comments * Clean up dynamic webhook handling * Clean up tests * Remove redundant `cache` prefix * fix: Correct `uniquePath` for dynamic webhooks
215 lines
6.9 KiB
TypeScript
215 lines
6.9 KiB
TypeScript
import { v4 as uuid } from 'uuid';
|
|
import config from '@/config';
|
|
import { mockInstance } from '../../integration/shared/utils/';
|
|
import { WebhookRepository } from '@/databases/repositories';
|
|
import { CacheService } from '@/services/cache.service';
|
|
import { WebhookService } from '@/services/webhook.service';
|
|
import { WebhookEntity } from '@/databases/entities/WebhookEntity';
|
|
|
|
const createWebhook = (method: string, path: string, webhookId?: string, pathSegments?: number) =>
|
|
Object.assign(new WebhookEntity(), {
|
|
method,
|
|
webhookPath: path,
|
|
webhookId,
|
|
pathSegments,
|
|
}) as WebhookEntity;
|
|
|
|
describe('WebhookService', () => {
|
|
const webhookRepository = mockInstance(WebhookRepository);
|
|
const cacheService = mockInstance(CacheService);
|
|
const webhookService = new WebhookService(webhookRepository, cacheService);
|
|
|
|
beforeEach(() => {
|
|
config.load(config.default);
|
|
jest.clearAllMocks();
|
|
});
|
|
|
|
[true, false].forEach((isCacheEnabled) => {
|
|
const tag = '[' + ['cache', isCacheEnabled ? 'enabled' : 'disabled'].join(' ') + ']';
|
|
|
|
describe(`findWebhook() - static case ${tag}`, () => {
|
|
test('should return the webhook if found', async () => {
|
|
const method = 'GET';
|
|
const path = 'user/profile';
|
|
const mockWebhook = createWebhook(method, path);
|
|
|
|
webhookRepository.findOneBy.mockResolvedValue(mockWebhook);
|
|
|
|
const returnedWebhook = await webhookService.findWebhook(method, path);
|
|
|
|
expect(returnedWebhook).toBe(mockWebhook);
|
|
});
|
|
|
|
test('should return null if not found', async () => {
|
|
webhookRepository.findOneBy.mockResolvedValue(null); // static
|
|
webhookRepository.findBy.mockResolvedValue([]);
|
|
|
|
const returnValue = await webhookService.findWebhook('GET', 'user/profile');
|
|
|
|
expect(returnValue).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe(`findWebhook() - dynamic case ${tag}`, () => {
|
|
test('should return the webhook if found', async () => {
|
|
const method = 'GET';
|
|
const webhookId = uuid();
|
|
const path = 'user/:id/posts';
|
|
const mockWebhook = createWebhook(method, path, webhookId, 3);
|
|
|
|
webhookRepository.findOneBy.mockResolvedValue(null); // static
|
|
webhookRepository.findBy.mockResolvedValue([mockWebhook]); // dynamic
|
|
|
|
const returnedWebhook = await webhookService.findWebhook(
|
|
method,
|
|
[webhookId, 'user/123/posts'].join('/'),
|
|
);
|
|
|
|
expect(returnedWebhook).toBe(mockWebhook);
|
|
});
|
|
|
|
test('should handle subset dynamic path case', async () => {
|
|
const method1 = 'GET';
|
|
const webhookId1 = uuid();
|
|
const path1 = 'user/:id/posts';
|
|
const mockWebhook1 = createWebhook(method1, path1, webhookId1, 3);
|
|
|
|
const method2 = 'GET';
|
|
const webhookId2 = uuid();
|
|
const path2 = 'user/:id/posts/:postId/comments';
|
|
const mockWebhook2 = createWebhook(method2, path2, webhookId2, 3);
|
|
|
|
webhookRepository.findOneBy.mockResolvedValue(null); // static
|
|
webhookRepository.findBy.mockResolvedValue([mockWebhook1, mockWebhook2]); // dynamic
|
|
|
|
const fullPath1 = [webhookId1, 'user/123/posts'].join('/');
|
|
const returnedWebhook1 = await webhookService.findWebhook(method1, fullPath1);
|
|
|
|
const fullPath2 = [webhookId1, 'user/123/posts/456/comments'].join('/');
|
|
const returnedWebhook2 = await webhookService.findWebhook(method2, fullPath2);
|
|
|
|
expect(returnedWebhook1).toBe(mockWebhook1);
|
|
expect(returnedWebhook2).toBe(mockWebhook2);
|
|
});
|
|
|
|
test('should handle single-segment dynamic path case', async () => {
|
|
const method1 = 'GET';
|
|
const webhookId1 = uuid();
|
|
const path1 = ':var';
|
|
const mockWebhook1 = createWebhook(method1, path1, webhookId1, 3);
|
|
|
|
const method2 = 'GET';
|
|
const webhookId2 = uuid();
|
|
const path2 = 'user/:id/posts/:postId/comments';
|
|
const mockWebhook2 = createWebhook(method2, path2, webhookId2, 3);
|
|
|
|
webhookRepository.findOneBy.mockResolvedValue(null); // static
|
|
webhookRepository.findBy.mockResolvedValue([mockWebhook1, mockWebhook2]); // dynamic
|
|
|
|
const fullPath = [webhookId1, 'user/123/posts/456'].join('/');
|
|
const returnedWebhook = await webhookService.findWebhook(method1, fullPath);
|
|
|
|
expect(returnedWebhook).toBe(mockWebhook1);
|
|
});
|
|
|
|
test('should return null if not found', async () => {
|
|
const fullPath = [uuid(), 'user/:id/posts'].join('/');
|
|
|
|
webhookRepository.findOneBy.mockResolvedValue(null); // static
|
|
webhookRepository.findBy.mockResolvedValue([]); // dynamic
|
|
|
|
const returnValue = await webhookService.findWebhook('GET', fullPath);
|
|
|
|
expect(returnValue).toBeNull();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('getWebhookMethods()', () => {
|
|
test('should return all methods for webhook', async () => {
|
|
const path = 'user/profile';
|
|
|
|
webhookRepository.find.mockResolvedValue([
|
|
createWebhook('GET', path),
|
|
createWebhook('POST', path),
|
|
createWebhook('PUT', path),
|
|
createWebhook('PATCH', path),
|
|
]);
|
|
|
|
const returnedMethods = await webhookService.getWebhookMethods(path);
|
|
|
|
expect(returnedMethods).toEqual(['GET', 'POST', 'PUT', 'PATCH']);
|
|
});
|
|
|
|
test('should return empty array if no webhooks found', async () => {
|
|
webhookRepository.find.mockResolvedValue([]);
|
|
|
|
const returnedMethods = await webhookService.getWebhookMethods('user/profile');
|
|
|
|
expect(returnedMethods).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe('deleteInstanceWebhooks()', () => {
|
|
test('should delete all webhooks of the instance', async () => {
|
|
const mockInstanceWebhooks = [
|
|
createWebhook('PUT', 'users'),
|
|
createWebhook('GET', 'user/:id'),
|
|
createWebhook('POST', ':var'),
|
|
];
|
|
|
|
webhookRepository.find.mockResolvedValue(mockInstanceWebhooks);
|
|
|
|
await webhookService.deleteInstanceWebhooks();
|
|
|
|
expect(webhookRepository.remove).toHaveBeenCalledWith(mockInstanceWebhooks);
|
|
});
|
|
|
|
test('should not delete any webhooks if none found', async () => {
|
|
webhookRepository.find.mockResolvedValue([]);
|
|
|
|
await webhookService.deleteInstanceWebhooks();
|
|
|
|
expect(webhookRepository.remove).toHaveBeenCalledWith([]);
|
|
});
|
|
});
|
|
|
|
describe('deleteWorkflowWebhooks()', () => {
|
|
test('should delete all webhooks of the workflow', async () => {
|
|
const mockWorkflowWebhooks = [
|
|
createWebhook('PUT', 'users'),
|
|
createWebhook('GET', 'user/:id'),
|
|
createWebhook('POST', ':var'),
|
|
];
|
|
|
|
webhookRepository.findBy.mockResolvedValue(mockWorkflowWebhooks);
|
|
|
|
const workflowId = uuid();
|
|
|
|
await webhookService.deleteWorkflowWebhooks(workflowId);
|
|
|
|
expect(webhookRepository.remove).toHaveBeenCalledWith(mockWorkflowWebhooks);
|
|
});
|
|
|
|
test('should not delete any webhooks if none found', async () => {
|
|
webhookRepository.findBy.mockResolvedValue([]);
|
|
|
|
const workflowId = uuid();
|
|
|
|
await webhookService.deleteWorkflowWebhooks(workflowId);
|
|
|
|
expect(webhookRepository.remove).toHaveBeenCalledWith([]);
|
|
});
|
|
});
|
|
|
|
describe('createWebhook()', () => {
|
|
test('should create the webhook', async () => {
|
|
const mockWebhook = createWebhook('GET', 'user/:id');
|
|
|
|
await webhookService.storeWebhook(mockWebhook);
|
|
|
|
expect(webhookRepository.insert).toHaveBeenCalledWith(mockWebhook);
|
|
});
|
|
});
|
|
});
|