Files
n8n-enterprise-unlocked/packages/cli/src/evaluation.ee/test-runner/utils.ee.ts
Eugene 8152f8c6a7 feat(core): Update data model for Evaluations (no-changelog) (#15520)
Co-authored-by: Yiorgis Gozadinos <yiorgis@n8n.io>
Co-authored-by: JP van Oosten <jp@n8n.io>
2025-05-22 12:55:31 +02:00

68 lines
2.2 KiB
TypeScript

import assert from 'assert';
import type { IRunExecutionData, IPinData, IWorkflowBase } from 'n8n-workflow';
import { TestCaseExecutionError } from '@/evaluation.ee/test-runner/errors.ee';
// Entity representing a node in a workflow under test, for which data should be mocked during test execution
export type MockedNodeItem = {
name?: string;
id: string;
};
/**
* Extracts the execution data from the past execution
* and creates a pin data object from it for the given workflow.
* It uses a list of mocked nodes defined in a test definition
* to decide which nodes to pin.
*/
export function createPinData(
workflow: IWorkflowBase,
mockedNodes: MockedNodeItem[],
executionData: IRunExecutionData,
pastWorkflowData?: IWorkflowBase,
) {
const pinData = {} as IPinData;
const workflowNodeIds = new Map(workflow.nodes.map((node) => [node.id, node.name]));
// If the past workflow data is provided, use it to create a map between node IDs and node names
const pastWorkflowNodeIds = new Map<string, string>();
if (pastWorkflowData) {
for (const node of pastWorkflowData.nodes) {
pastWorkflowNodeIds.set(node.id, node.name);
}
}
for (const mockedNode of mockedNodes) {
assert(mockedNode.id, 'Mocked node ID is missing');
const nodeName = workflowNodeIds.get(mockedNode.id);
// If mocked node is still present in the workflow
if (nodeName) {
// Try to restore node name from past execution data (it might have been renamed between past execution and up-to-date workflow)
const pastNodeName = pastWorkflowNodeIds.get(mockedNode.id) ?? nodeName;
const nodeData = executionData.resultData.runData[pastNodeName];
if (nodeData?.[0]?.data?.main?.[0]) {
pinData[nodeName] = nodeData[0]?.data?.main?.[0];
} else {
throw new TestCaseExecutionError('MOCKED_NODE_DOES_NOT_EXIST');
}
}
}
return pinData;
}
/**
* Returns the trigger node of the past execution.
* The trigger node is the node that has no source and has run data.
*/
export function getPastExecutionTriggerNode(executionData: IRunExecutionData) {
return Object.keys(executionData.resultData.runData).find((nodeName) => {
const data = executionData.resultData.runData[nodeName];
return !data[0].source || data[0].source.length === 0 || data[0].source[0] === null;
});
}