mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 01:56:46 +00:00
refactor(core): Add test for workflow issue check and extract logic (#18845)
This commit is contained in:
@@ -46,38 +46,6 @@ describe('processRunExecutionData', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('throws if workflow contains nodes with missing required properties', () => {
|
|
||||||
// ARRANGE
|
|
||||||
const node = createNodeData({ name: 'node', type: types.testNodeWithRequiredProperty });
|
|
||||||
const workflow = new DirectedGraph()
|
|
||||||
.addNodes(node)
|
|
||||||
.toWorkflow({ name: '', active: false, nodeTypes, settings: { executionOrder: 'v1' } });
|
|
||||||
|
|
||||||
const taskDataConnection = { main: [[{ json: { foo: 1 } }]] };
|
|
||||||
const executionData: IRunExecutionData = {
|
|
||||||
startData: { startNodes: [{ name: node.name, sourceData: null }] },
|
|
||||||
resultData: { runData: {} },
|
|
||||||
executionData: {
|
|
||||||
contextData: {},
|
|
||||||
nodeExecutionStack: [{ data: taskDataConnection, node, source: null }],
|
|
||||||
metadata: {},
|
|
||||||
waitingExecution: {},
|
|
||||||
waitingExecutionSource: {},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const workflowExecute = new WorkflowExecute(additionalData, executionMode, executionData);
|
|
||||||
|
|
||||||
// ACT & ASSERT
|
|
||||||
// The function returns a Promise, but throws synchronously, so we can't await it.
|
|
||||||
// eslint-disable-next-line @typescript-eslint/promise-function-async
|
|
||||||
expect(() => workflowExecute.processRunExecutionData(workflow)).toThrowError(
|
|
||||||
new ApplicationError(
|
|
||||||
'The workflow has issues and cannot be executed for that reason. Please fix them first.',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('returns input data verbatim', async () => {
|
test('returns input data verbatim', async () => {
|
||||||
// ARRANGE
|
// ARRANGE
|
||||||
const node = createNodeData({ name: 'node', type: types.passThrough });
|
const node = createNodeData({ name: 'node', type: types.passThrough });
|
||||||
@@ -183,4 +151,71 @@ describe('processRunExecutionData', () => {
|
|||||||
expect(result.data.resultData.runData.waitingNode[0].executionStatus).toEqual('success');
|
expect(result.data.resultData.runData.waitingNode[0].executionStatus).toEqual('success');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('workflow issues', () => {
|
||||||
|
test('throws if workflow contains nodes with missing required properties', () => {
|
||||||
|
// ARRANGE
|
||||||
|
const node = createNodeData({ name: 'node', type: types.testNodeWithRequiredProperty });
|
||||||
|
const workflow = new DirectedGraph()
|
||||||
|
.addNodes(node)
|
||||||
|
.toWorkflow({ name: '', active: false, nodeTypes, settings: { executionOrder: 'v1' } });
|
||||||
|
|
||||||
|
const taskDataConnection = { main: [[{ json: { foo: 1 } }]] };
|
||||||
|
const executionData: IRunExecutionData = {
|
||||||
|
startData: { startNodes: [{ name: node.name, sourceData: null }] },
|
||||||
|
resultData: { runData: {} },
|
||||||
|
executionData: {
|
||||||
|
contextData: {},
|
||||||
|
nodeExecutionStack: [{ data: taskDataConnection, node, source: null }],
|
||||||
|
metadata: {},
|
||||||
|
waitingExecution: {},
|
||||||
|
waitingExecutionSource: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const workflowExecute = new WorkflowExecute(additionalData, executionMode, executionData);
|
||||||
|
|
||||||
|
// ACT & ASSERT
|
||||||
|
// The function returns a Promise, but throws synchronously, so we can't await it.
|
||||||
|
// eslint-disable-next-line @typescript-eslint/promise-function-async
|
||||||
|
expect(() => workflowExecute.processRunExecutionData(workflow)).toThrowError(
|
||||||
|
new ApplicationError(
|
||||||
|
'The workflow has issues and cannot be executed for that reason. Please fix them first.',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('does not complain about nodes with issue past the destination node', async () => {
|
||||||
|
// ARRANGE
|
||||||
|
const node1 = createNodeData({ name: 'node1', type: types.passThrough });
|
||||||
|
const node2 = createNodeData({ name: 'node2', type: types.testNodeWithRequiredProperty });
|
||||||
|
const workflow = new DirectedGraph()
|
||||||
|
.addNodes(node1, node2)
|
||||||
|
.addConnection({ from: node1, to: node2 })
|
||||||
|
.toWorkflow({ name: '', active: false, nodeTypes, settings: { executionOrder: 'v1' } });
|
||||||
|
|
||||||
|
const taskDataConnection = { main: [[{ json: { foo: 1 } }]] };
|
||||||
|
const executionData: IRunExecutionData = {
|
||||||
|
startData: {
|
||||||
|
startNodes: [{ name: node1.name, sourceData: null }],
|
||||||
|
destinationNode: node1.name,
|
||||||
|
},
|
||||||
|
resultData: { runData: {} },
|
||||||
|
executionData: {
|
||||||
|
contextData: {},
|
||||||
|
nodeExecutionStack: [{ data: taskDataConnection, node: node1, source: null }],
|
||||||
|
metadata: {},
|
||||||
|
waitingExecution: {},
|
||||||
|
waitingExecutionSource: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const workflowExecute = new WorkflowExecute(additionalData, executionMode, executionData);
|
||||||
|
|
||||||
|
// ACT & ASSERT
|
||||||
|
// The function returns a Promise, but throws synchronously, so we can't await it.
|
||||||
|
// eslint-disable-next-line @typescript-eslint/promise-function-async
|
||||||
|
expect(() => workflowExecute.processRunExecutionData(workflow)).not.toThrowError();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1513,6 +1513,27 @@ export class WorkflowExecute {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private checkForWorkflowIssues(workflow: Workflow): void {
|
||||||
|
this.assertExecutionDataExists(this.runExecutionData.executionData, workflow);
|
||||||
|
// Node execution stack will be empty for an execution containing only Chat
|
||||||
|
// Trigger.
|
||||||
|
const startNode = this.runExecutionData.executionData.nodeExecutionStack.at(0)?.node.name;
|
||||||
|
|
||||||
|
let destinationNode: string | undefined;
|
||||||
|
if (this.runExecutionData.startData && this.runExecutionData.startData.destinationNode) {
|
||||||
|
destinationNode = this.runExecutionData.startData.destinationNode;
|
||||||
|
}
|
||||||
|
const pinDataNodeNames = Object.keys(this.runExecutionData.resultData.pinData ?? {});
|
||||||
|
const workflowIssues = this.checkReadyForExecution(workflow, {
|
||||||
|
startNode,
|
||||||
|
destinationNode,
|
||||||
|
pinDataNodeNames,
|
||||||
|
});
|
||||||
|
if (workflowIssues !== null) {
|
||||||
|
throw new WorkflowHasIssuesError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs the given execution data.
|
* Runs the given execution data.
|
||||||
*
|
*
|
||||||
@@ -1528,38 +1549,9 @@ export class WorkflowExecute {
|
|||||||
|
|
||||||
this.status = 'running';
|
this.status = 'running';
|
||||||
|
|
||||||
const { hooks, executionId } = this.additionalData;
|
const { hooks } = this.additionalData;
|
||||||
assert.ok(hooks, 'Failed to run workflow due to missing execution lifecycle hooks');
|
assert.ok(hooks, 'Failed to run workflow due to missing execution lifecycle hooks');
|
||||||
|
|
||||||
if (!this.runExecutionData.executionData) {
|
|
||||||
throw new ApplicationError('Failed to run workflow due to missing execution data', {
|
|
||||||
extra: {
|
|
||||||
workflowId: workflow.id,
|
|
||||||
executionId,
|
|
||||||
mode: this.mode,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Node execution stack will be empty for an execution containing only Chat Trigger. */
|
|
||||||
const startNode = this.runExecutionData.executionData.nodeExecutionStack.at(0)?.node.name;
|
|
||||||
|
|
||||||
let destinationNode: string | undefined;
|
|
||||||
if (this.runExecutionData.startData && this.runExecutionData.startData.destinationNode) {
|
|
||||||
destinationNode = this.runExecutionData.startData.destinationNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
const pinDataNodeNames = Object.keys(this.runExecutionData.resultData.pinData ?? {});
|
|
||||||
|
|
||||||
const workflowIssues = this.checkReadyForExecution(workflow, {
|
|
||||||
startNode,
|
|
||||||
destinationNode,
|
|
||||||
pinDataNodeNames,
|
|
||||||
});
|
|
||||||
if (workflowIssues !== null) {
|
|
||||||
throw new WorkflowHasIssuesError();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Variables which hold temporary data for each node-execution
|
// Variables which hold temporary data for each node-execution
|
||||||
let executionData: IExecuteData;
|
let executionData: IExecuteData;
|
||||||
let executionError: ExecutionBaseError | undefined;
|
let executionError: ExecutionBaseError | undefined;
|
||||||
@@ -1570,6 +1562,7 @@ export class WorkflowExecute {
|
|||||||
this.runExecutionData.startData = {};
|
this.runExecutionData.startData = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.checkForWorkflowIssues(workflow);
|
||||||
this.handleWaitingState(workflow);
|
this.handleWaitingState(workflow);
|
||||||
|
|
||||||
let currentExecutionTry = '';
|
let currentExecutionTry = '';
|
||||||
|
|||||||
Reference in New Issue
Block a user