mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
chore: Add node mocking logic to the Test Runner (no-changelog) (#12009)
This commit is contained in:
parent
c3b968acf5
commit
70b0d81604
|
@ -13,7 +13,9 @@ const executionDataJson = JSON.parse(
|
||||||
|
|
||||||
describe('createPinData', () => {
|
describe('createPinData', () => {
|
||||||
test('should create pin data from past execution data', () => {
|
test('should create pin data from past execution data', () => {
|
||||||
const pinData = createPinData(wfUnderTestJson, executionDataJson);
|
const mockedNodes = ['When clicking ‘Test workflow’'].map((name) => ({ name }));
|
||||||
|
|
||||||
|
const pinData = createPinData(wfUnderTestJson, mockedNodes, executionDataJson);
|
||||||
|
|
||||||
expect(pinData).toEqual(
|
expect(pinData).toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
|
@ -21,4 +23,34 @@ describe('createPinData', () => {
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should not create pin data for non-existing mocked nodes', () => {
|
||||||
|
const mockedNodes = ['Non-existing node'].map((name) => ({ name }));
|
||||||
|
|
||||||
|
const pinData = createPinData(wfUnderTestJson, mockedNodes, executionDataJson);
|
||||||
|
|
||||||
|
expect(pinData).toEqual({});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should create pin data for all mocked nodes', () => {
|
||||||
|
const mockedNodes = ['When clicking ‘Test workflow’', 'Edit Fields', 'Code'].map((name) => ({
|
||||||
|
name,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const pinData = createPinData(wfUnderTestJson, mockedNodes, executionDataJson);
|
||||||
|
|
||||||
|
expect(pinData).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
'When clicking ‘Test workflow’': expect.anything(),
|
||||||
|
'Edit Fields': expect.anything(),
|
||||||
|
Code: expect.anything(),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should return empty object if no mocked nodes are provided', () => {
|
||||||
|
const pinData = createPinData(wfUnderTestJson, [], executionDataJson);
|
||||||
|
|
||||||
|
expect(pinData).toEqual({});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -163,6 +163,7 @@ describe('TestRunnerService', () => {
|
||||||
mock<TestDefinition>({
|
mock<TestDefinition>({
|
||||||
workflowId: 'workflow-under-test-id',
|
workflowId: 'workflow-under-test-id',
|
||||||
evaluationWorkflowId: 'evaluation-workflow-id',
|
evaluationWorkflowId: 'evaluation-workflow-id',
|
||||||
|
mockedNodes: [],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -219,6 +220,7 @@ describe('TestRunnerService', () => {
|
||||||
mock<TestDefinition>({
|
mock<TestDefinition>({
|
||||||
workflowId: 'workflow-under-test-id',
|
workflowId: 'workflow-under-test-id',
|
||||||
evaluationWorkflowId: 'evaluation-workflow-id',
|
evaluationWorkflowId: 'evaluation-workflow-id',
|
||||||
|
mockedNodes: [{ name: 'When clicking ‘Test workflow’' }],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ import { Service } from 'typedi';
|
||||||
|
|
||||||
import { ActiveExecutions } from '@/active-executions';
|
import { ActiveExecutions } from '@/active-executions';
|
||||||
import type { ExecutionEntity } from '@/databases/entities/execution-entity';
|
import type { ExecutionEntity } from '@/databases/entities/execution-entity';
|
||||||
import type { TestDefinition } from '@/databases/entities/test-definition.ee';
|
import type { MockedNodeItem, TestDefinition } from '@/databases/entities/test-definition.ee';
|
||||||
import type { User } from '@/databases/entities/user';
|
import type { User } from '@/databases/entities/user';
|
||||||
import type { WorkflowEntity } from '@/databases/entities/workflow-entity';
|
import type { WorkflowEntity } from '@/databases/entities/workflow-entity';
|
||||||
import { ExecutionRepository } from '@/databases/repositories/execution.repository';
|
import { ExecutionRepository } from '@/databases/repositories/execution.repository';
|
||||||
|
@ -30,9 +30,7 @@ import { createPinData, getPastExecutionTriggerNode } from './utils.ee';
|
||||||
* past executions, creates pin data from them,
|
* past executions, creates pin data from them,
|
||||||
* and runs the workflow-under-test with the pin data.
|
* and runs the workflow-under-test with the pin data.
|
||||||
* After the workflow-under-test finishes, it runs the evaluation workflow
|
* After the workflow-under-test finishes, it runs the evaluation workflow
|
||||||
* with the original and new run data.
|
* with the original and new run data, and collects the metrics.
|
||||||
* TODO: Node pinning
|
|
||||||
* TODO: Collect metrics
|
|
||||||
*/
|
*/
|
||||||
@Service()
|
@Service()
|
||||||
export class TestRunnerService {
|
export class TestRunnerService {
|
||||||
|
@ -52,10 +50,11 @@ export class TestRunnerService {
|
||||||
private async runTestCase(
|
private async runTestCase(
|
||||||
workflow: WorkflowEntity,
|
workflow: WorkflowEntity,
|
||||||
pastExecutionData: IRunExecutionData,
|
pastExecutionData: IRunExecutionData,
|
||||||
|
mockedNodes: MockedNodeItem[],
|
||||||
userId: string,
|
userId: string,
|
||||||
): Promise<IRun | undefined> {
|
): Promise<IRun | undefined> {
|
||||||
// Create pin data from the past execution data
|
// Create pin data from the past execution data
|
||||||
const pinData = createPinData(workflow, pastExecutionData);
|
const pinData = createPinData(workflow, mockedNodes, pastExecutionData);
|
||||||
|
|
||||||
// Determine the start node of the past execution
|
// Determine the start node of the past execution
|
||||||
const pastExecutionStartNode = getPastExecutionTriggerNode(pastExecutionData);
|
const pastExecutionStartNode = getPastExecutionTriggerNode(pastExecutionData);
|
||||||
|
@ -196,7 +195,12 @@ export class TestRunnerService {
|
||||||
const executionData = parse(pastExecution.executionData.data) as IRunExecutionData;
|
const executionData = parse(pastExecution.executionData.data) as IRunExecutionData;
|
||||||
|
|
||||||
// Run the test case and wait for it to finish
|
// Run the test case and wait for it to finish
|
||||||
const testCaseExecution = await this.runTestCase(workflow, executionData, user.id);
|
const testCaseExecution = await this.runTestCase(
|
||||||
|
workflow,
|
||||||
|
executionData,
|
||||||
|
test.mockedNodes,
|
||||||
|
user.id,
|
||||||
|
);
|
||||||
|
|
||||||
// In case of a permission check issue, the test case execution will be undefined.
|
// In case of a permission check issue, the test case execution will be undefined.
|
||||||
// Skip them and continue with the next test case
|
// Skip them and continue with the next test case
|
||||||
|
|
|
@ -1,21 +1,29 @@
|
||||||
import type { IRunExecutionData, IPinData } from 'n8n-workflow';
|
import type { IRunExecutionData, IPinData } from 'n8n-workflow';
|
||||||
|
|
||||||
|
import type { MockedNodeItem } from '@/databases/entities/test-definition.ee';
|
||||||
import type { WorkflowEntity } from '@/databases/entities/workflow-entity';
|
import type { WorkflowEntity } from '@/databases/entities/workflow-entity';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extracts the execution data from the past execution
|
* Extracts the execution data from the past execution
|
||||||
* and creates a pin data object from it for the given workflow.
|
* and creates a pin data object from it for the given workflow.
|
||||||
* For now, it only pins trigger nodes.
|
* It uses a list of mocked nodes defined in a test definition
|
||||||
|
* to decide which nodes to pin.
|
||||||
*/
|
*/
|
||||||
export function createPinData(workflow: WorkflowEntity, executionData: IRunExecutionData) {
|
export function createPinData(
|
||||||
const triggerNodes = workflow.nodes.filter((node) => /trigger$/i.test(node.type));
|
workflow: WorkflowEntity,
|
||||||
|
mockedNodes: MockedNodeItem[],
|
||||||
|
executionData: IRunExecutionData,
|
||||||
|
) {
|
||||||
const pinData = {} as IPinData;
|
const pinData = {} as IPinData;
|
||||||
|
|
||||||
for (const triggerNode of triggerNodes) {
|
const workflowNodeNames = new Set(workflow.nodes.map((node) => node.name));
|
||||||
const triggerData = executionData.resultData.runData[triggerNode.name];
|
|
||||||
if (triggerData?.[0]?.data?.main?.[0]) {
|
for (const mockedNode of mockedNodes) {
|
||||||
pinData[triggerNode.name] = triggerData[0]?.data?.main?.[0];
|
if (workflowNodeNames.has(mockedNode.name)) {
|
||||||
|
const nodeData = executionData.resultData.runData[mockedNode.name];
|
||||||
|
if (nodeData?.[0]?.data?.main?.[0]) {
|
||||||
|
pinData[mockedNode.name] = nodeData[0]?.data?.main?.[0];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue