test(core): Move unit tests closer to testable components (no-changelog) (#10287)
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
import type { INode } from 'n8n-workflow';
|
||||
import { mock } from 'jest-mock-extended';
|
||||
|
||||
import type { WorkflowEntity } from '@db/entities/WorkflowEntity';
|
||||
import type { IWorkflowDb } from '@/Interfaces';
|
||||
import { WorkflowExecutionService } from '@/workflows/workflowExecution.service';
|
||||
import type { WorkflowRunner } from '@/WorkflowRunner';
|
||||
|
||||
const webhookNode: INode = {
|
||||
name: 'Webhook',
|
||||
type: 'n8n-nodes-base.webhook',
|
||||
id: '111f1db0-e7be-44c5-9ce9-3e35362490f0',
|
||||
parameters: {},
|
||||
typeVersion: 1,
|
||||
position: [0, 0],
|
||||
webhookId: 'de0f8dcb-7b64-4f22-b66d-d8f74d6aefb7',
|
||||
};
|
||||
|
||||
const secondWebhookNode = {
|
||||
...webhookNode,
|
||||
name: 'Webhook 2',
|
||||
id: '222f1db0-e7be-44c5-9ce9-3e35362490f1',
|
||||
};
|
||||
|
||||
const executeWorkflowTriggerNode: INode = {
|
||||
name: 'Execute Workflow Trigger',
|
||||
type: 'n8n-nodes-base.executeWorkflowTrigger',
|
||||
id: '78d63bca-bb6c-4568-948f-8ed9aacb1fe9',
|
||||
parameters: {},
|
||||
typeVersion: 1,
|
||||
position: [0, 0],
|
||||
};
|
||||
|
||||
const respondToWebhookNode: INode = {
|
||||
name: 'Respond to Webhook',
|
||||
type: 'n8n-nodes-base.respondToWebhook',
|
||||
id: '66d63bca-bb6c-4568-948f-8ed9aacb1fe9',
|
||||
parameters: {},
|
||||
typeVersion: 1,
|
||||
position: [0, 0],
|
||||
};
|
||||
|
||||
const hackerNewsNode: INode = {
|
||||
name: 'Hacker News',
|
||||
type: 'n8n-nodes-base.hackerNews',
|
||||
id: '55d63bca-bb6c-4568-948f-8ed9aacb1fe9',
|
||||
parameters: {},
|
||||
typeVersion: 1,
|
||||
position: [0, 0],
|
||||
};
|
||||
|
||||
describe('WorkflowExecutionService', () => {
|
||||
const workflowRunner = mock<WorkflowRunner>();
|
||||
const workflowExecutionService = new WorkflowExecutionService(
|
||||
mock(),
|
||||
mock(),
|
||||
mock(),
|
||||
mock(),
|
||||
mock(),
|
||||
workflowRunner,
|
||||
mock(),
|
||||
mock(),
|
||||
);
|
||||
|
||||
describe('runWorkflow()', () => {
|
||||
test('should call `WorkflowRunner.run()`', async () => {
|
||||
const node = mock<INode>();
|
||||
const workflow = mock<WorkflowEntity>({ active: true, nodes: [node] });
|
||||
|
||||
workflowRunner.run.mockResolvedValue('fake-execution-id');
|
||||
|
||||
await workflowExecutionService.runWorkflow(workflow, node, [[]], mock(), 'trigger');
|
||||
|
||||
expect(workflowRunner.run).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('selectPinnedActivatorStarter()', () => {
|
||||
const workflow = mock<IWorkflowDb>({
|
||||
nodes: [],
|
||||
});
|
||||
|
||||
const pinData = {
|
||||
[webhookNode.name]: [{ json: { key: 'value' } }],
|
||||
[executeWorkflowTriggerNode.name]: [{ json: { key: 'value' } }],
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
workflow.nodes = [];
|
||||
});
|
||||
|
||||
it('should return `null` if no pindata', () => {
|
||||
const node = workflowExecutionService.selectPinnedActivatorStarter(workflow, []);
|
||||
|
||||
expect(node).toBeNull();
|
||||
});
|
||||
|
||||
it('should return `null` if no starter nodes', () => {
|
||||
const node = workflowExecutionService.selectPinnedActivatorStarter(workflow);
|
||||
|
||||
expect(node).toBeNull();
|
||||
});
|
||||
|
||||
it('should select webhook node if only choice', () => {
|
||||
workflow.nodes.push(webhookNode);
|
||||
|
||||
const node = workflowExecutionService.selectPinnedActivatorStarter(workflow, [], pinData);
|
||||
|
||||
expect(node).toEqual(webhookNode);
|
||||
});
|
||||
|
||||
it('should return `null` if no choice', () => {
|
||||
workflow.nodes.push(hackerNewsNode);
|
||||
|
||||
const node = workflowExecutionService.selectPinnedActivatorStarter(workflow, [], pinData);
|
||||
|
||||
expect(node).toBeNull();
|
||||
});
|
||||
|
||||
it('should return ignore Respond to Webhook', () => {
|
||||
workflow.nodes.push(respondToWebhookNode);
|
||||
|
||||
const node = workflowExecutionService.selectPinnedActivatorStarter(workflow, [], pinData);
|
||||
|
||||
expect(node).toBeNull();
|
||||
});
|
||||
|
||||
it('should select execute workflow trigger if only choice', () => {
|
||||
workflow.nodes.push(executeWorkflowTriggerNode);
|
||||
|
||||
const node = workflowExecutionService.selectPinnedActivatorStarter(workflow, [], pinData);
|
||||
|
||||
expect(node).toEqual(executeWorkflowTriggerNode);
|
||||
});
|
||||
|
||||
it('should favor webhook node over execute workflow trigger', () => {
|
||||
workflow.nodes.push(webhookNode, executeWorkflowTriggerNode);
|
||||
|
||||
const node = workflowExecutionService.selectPinnedActivatorStarter(workflow, [], pinData);
|
||||
|
||||
expect(node).toEqual(webhookNode);
|
||||
});
|
||||
|
||||
it('should favor first webhook node over second webhook node', () => {
|
||||
workflow.nodes.push(webhookNode, secondWebhookNode);
|
||||
|
||||
const node = workflowExecutionService.selectPinnedActivatorStarter(workflow, [], pinData);
|
||||
|
||||
expect(node).toEqual(webhookNode);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,114 @@
|
||||
import { mockClear } from 'jest-mock-extended';
|
||||
import { User } from '@db/entities/User';
|
||||
import { WorkflowHistoryRepository } from '@db/repositories/workflowHistory.repository';
|
||||
import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository';
|
||||
import { WorkflowHistoryService } from '@/workflows/workflowHistory/workflowHistory.service.ee';
|
||||
import { Logger } from '@/Logger';
|
||||
import { mockInstance } from '@test/mocking';
|
||||
import { getWorkflow } from '@test-integration/workflow';
|
||||
|
||||
const workflowHistoryRepository = mockInstance(WorkflowHistoryRepository);
|
||||
const logger = mockInstance(Logger);
|
||||
const sharedWorkflowRepository = mockInstance(SharedWorkflowRepository);
|
||||
const workflowHistoryService = new WorkflowHistoryService(
|
||||
logger,
|
||||
workflowHistoryRepository,
|
||||
sharedWorkflowRepository,
|
||||
);
|
||||
const testUser = Object.assign(new User(), {
|
||||
id: '1234',
|
||||
password: 'passwordHash',
|
||||
mfaEnabled: false,
|
||||
firstName: 'John',
|
||||
lastName: 'Doe',
|
||||
});
|
||||
let isWorkflowHistoryEnabled = true;
|
||||
|
||||
jest.mock('@/workflows/workflowHistory/workflowHistoryHelper.ee', () => {
|
||||
return {
|
||||
isWorkflowHistoryEnabled: jest.fn(() => isWorkflowHistoryEnabled),
|
||||
};
|
||||
});
|
||||
|
||||
describe('WorkflowHistoryService', () => {
|
||||
beforeEach(() => {
|
||||
mockClear(workflowHistoryRepository.insert);
|
||||
});
|
||||
|
||||
describe('saveVersion', () => {
|
||||
it('should save a new version when workflow history is enabled and nodes and connections are present', async () => {
|
||||
// Arrange
|
||||
isWorkflowHistoryEnabled = true;
|
||||
const workflow = getWorkflow({ addNodeWithoutCreds: true });
|
||||
const workflowId = '123';
|
||||
workflow.connections = {};
|
||||
workflow.id = workflowId;
|
||||
workflow.versionId = '456';
|
||||
|
||||
// Act
|
||||
await workflowHistoryService.saveVersion(testUser, workflow, workflowId);
|
||||
|
||||
// Assert
|
||||
expect(workflowHistoryRepository.insert).toHaveBeenCalledWith({
|
||||
authors: 'John Doe',
|
||||
connections: {},
|
||||
nodes: workflow.nodes,
|
||||
versionId: workflow.versionId,
|
||||
workflowId,
|
||||
});
|
||||
});
|
||||
|
||||
it('should not save a new version when workflow history is disabled', async () => {
|
||||
// Arrange
|
||||
isWorkflowHistoryEnabled = false;
|
||||
const workflow = getWorkflow({ addNodeWithoutCreds: true });
|
||||
const workflowId = '123';
|
||||
workflow.connections = {};
|
||||
workflow.id = workflowId;
|
||||
workflow.versionId = '456';
|
||||
|
||||
// Act
|
||||
await workflowHistoryService.saveVersion(testUser, workflow, workflowId);
|
||||
|
||||
// Assert
|
||||
expect(workflowHistoryRepository.insert).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not save a new version when nodes or connections are missing', async () => {
|
||||
// Arrange
|
||||
isWorkflowHistoryEnabled = true;
|
||||
const workflow = getWorkflow({ addNodeWithoutCreds: true });
|
||||
const workflowId = '123';
|
||||
workflow.id = workflowId;
|
||||
workflow.versionId = '456';
|
||||
// Nodes are set but connections is empty
|
||||
|
||||
// Act
|
||||
await workflowHistoryService.saveVersion(testUser, workflow, workflowId);
|
||||
|
||||
// Assert
|
||||
expect(workflowHistoryRepository.insert).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should log an error when failed to save workflow history version', async () => {
|
||||
// Arrange
|
||||
isWorkflowHistoryEnabled = true;
|
||||
const workflow = getWorkflow({ addNodeWithoutCreds: true });
|
||||
const workflowId = '123';
|
||||
workflow.connections = {};
|
||||
workflow.id = workflowId;
|
||||
workflow.versionId = '456';
|
||||
workflowHistoryRepository.insert.mockRejectedValueOnce(new Error('Test error'));
|
||||
|
||||
// Act
|
||||
await workflowHistoryService.saveVersion(testUser, workflow, workflowId);
|
||||
|
||||
// Assert
|
||||
expect(workflowHistoryRepository.insert).toHaveBeenCalled();
|
||||
expect(logger.error).toHaveBeenCalledWith(
|
||||
'Failed to save workflow history version for workflow 123',
|
||||
expect.any(Error),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,54 @@
|
||||
import { License } from '@/License';
|
||||
import config from '@/config';
|
||||
import { getWorkflowHistoryPruneTime } from '@/workflows/workflowHistory/workflowHistoryHelper.ee';
|
||||
import { mockInstance } from '@test/mocking';
|
||||
|
||||
let licensePruneTime = -1;
|
||||
|
||||
beforeAll(async () => {
|
||||
mockInstance(License, {
|
||||
getWorkflowHistoryPruneLimit() {
|
||||
return licensePruneTime;
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
licensePruneTime = -1;
|
||||
config.set('workflowHistory.pruneTime', -1);
|
||||
});
|
||||
|
||||
describe('getWorkflowHistoryPruneTime', () => {
|
||||
test('should return -1 (infinite) if config and license are -1', () => {
|
||||
licensePruneTime = -1;
|
||||
config.set('workflowHistory.pruneTime', -1);
|
||||
|
||||
expect(getWorkflowHistoryPruneTime()).toBe(-1);
|
||||
});
|
||||
|
||||
test('should return config time if license is infinite and config is not', () => {
|
||||
licensePruneTime = -1;
|
||||
config.set('workflowHistory.pruneTime', 24);
|
||||
|
||||
expect(getWorkflowHistoryPruneTime()).toBe(24);
|
||||
});
|
||||
|
||||
test('should return license time if config is infinite and license is not', () => {
|
||||
licensePruneTime = 25;
|
||||
config.set('workflowHistory.pruneTime', -1);
|
||||
|
||||
expect(getWorkflowHistoryPruneTime()).toBe(25);
|
||||
});
|
||||
|
||||
test('should return lowest of config and license time if both are not -1', () => {
|
||||
licensePruneTime = 26;
|
||||
config.set('workflowHistory.pruneTime', 100);
|
||||
|
||||
expect(getWorkflowHistoryPruneTime()).toBe(26);
|
||||
|
||||
licensePruneTime = 100;
|
||||
config.set('workflowHistory.pruneTime', 27);
|
||||
|
||||
expect(getWorkflowHistoryPruneTime()).toBe(27);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user