mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-27 13:39:44 -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', () => {
|
||||
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.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>({
|
||||
workflowId: 'workflow-under-test-id',
|
||||
evaluationWorkflowId: 'evaluation-workflow-id',
|
||||
mockedNodes: [],
|
||||
}),
|
||||
);
|
||||
|
||||
|
@ -219,6 +220,7 @@ describe('TestRunnerService', () => {
|
|||
mock<TestDefinition>({
|
||||
workflowId: 'workflow-under-test-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 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 { WorkflowEntity } from '@/databases/entities/workflow-entity';
|
||||
import { ExecutionRepository } from '@/databases/repositories/execution.repository';
|
||||
|
@ -30,9 +30,7 @@ import { createPinData, getPastExecutionTriggerNode } from './utils.ee';
|
|||
* past executions, creates pin data from them,
|
||||
* and runs the workflow-under-test with the pin data.
|
||||
* After the workflow-under-test finishes, it runs the evaluation workflow
|
||||
* with the original and new run data.
|
||||
* TODO: Node pinning
|
||||
* TODO: Collect metrics
|
||||
* with the original and new run data, and collects the metrics.
|
||||
*/
|
||||
@Service()
|
||||
export class TestRunnerService {
|
||||
|
@ -52,10 +50,11 @@ export class TestRunnerService {
|
|||
private async runTestCase(
|
||||
workflow: WorkflowEntity,
|
||||
pastExecutionData: IRunExecutionData,
|
||||
mockedNodes: MockedNodeItem[],
|
||||
userId: string,
|
||||
): Promise<IRun | undefined> {
|
||||
// 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
|
||||
const pastExecutionStartNode = getPastExecutionTriggerNode(pastExecutionData);
|
||||
|
@ -196,7 +195,12 @@ export class TestRunnerService {
|
|||
const executionData = parse(pastExecution.executionData.data) as IRunExecutionData;
|
||||
|
||||
// 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.
|
||||
// Skip them and continue with the next test case
|
||||
|
|
|
@ -1,21 +1,29 @@
|
|||
import type { IRunExecutionData, IPinData } from 'n8n-workflow';
|
||||
|
||||
import type { MockedNodeItem } from '@/databases/entities/test-definition.ee';
|
||||
import type { WorkflowEntity } from '@/databases/entities/workflow-entity';
|
||||
|
||||
/**
|
||||
* Extracts the execution data from the past execution
|
||||
* 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) {
|
||||
const triggerNodes = workflow.nodes.filter((node) => /trigger$/i.test(node.type));
|
||||
|
||||
export function createPinData(
|
||||
workflow: WorkflowEntity,
|
||||
mockedNodes: MockedNodeItem[],
|
||||
executionData: IRunExecutionData,
|
||||
) {
|
||||
const pinData = {} as IPinData;
|
||||
|
||||
for (const triggerNode of triggerNodes) {
|
||||
const triggerData = executionData.resultData.runData[triggerNode.name];
|
||||
if (triggerData?.[0]?.data?.main?.[0]) {
|
||||
pinData[triggerNode.name] = triggerData[0]?.data?.main?.[0];
|
||||
const workflowNodeNames = new Set(workflow.nodes.map((node) => node.name));
|
||||
|
||||
for (const mockedNode of mockedNodes) {
|
||||
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