flowengine-n8n-workflow-builder
Version:
Build n8n workflows from text using AI. Connect to Claude, Cursor, or any LLM to generate and validate n8n workflows with expert knowledge and intelligent auto-fixing. Built by FlowEngine. Now with real node parameter schemas from n8n packages!
101 lines • 3.73 kB
JavaScript
/**
* Workflow Tester - Dry-run and Simulation
*
* Test workflows without executing them, with mock data injection
* and validation of execution paths.
*/
/**
* Perform dry-run test of workflow
*/
export function testWorkflow(workflow, mockData) {
const errors = [];
const warnings = [];
const executionPath = [];
// Find trigger node
const trigger = workflow.nodes.find(n => n.type.includes('Trigger') || n.type.includes('trigger'));
if (!trigger) {
errors.push('No trigger node found - workflow cannot start');
return { success: false, errors, warnings, executionPath };
}
executionPath.push(trigger.name);
// Trace execution path
let currentNode = trigger.name;
const visited = new Set();
let iterations = 0;
const maxIterations = 100; // Prevent infinite loops
while (currentNode && iterations < maxIterations) {
if (visited.has(currentNode)) {
warnings.push(`Potential infinite loop detected at ${currentNode}`);
break;
}
visited.add(currentNode);
iterations++;
// Find next nodes
const connections = workflow.connections[currentNode];
if (!connections || !connections.main || connections.main.length === 0) {
break; // End of workflow
}
// Take first path (main output 0)
const nextConnections = connections.main[0];
if (!nextConnections || nextConnections.length === 0) {
break;
}
currentNode = nextConnections[0].node;
executionPath.push(currentNode);
}
// Check for orphaned nodes (not in execution path)
const orphaned = workflow.nodes.filter(n => !n.type.includes('Trigger') &&
!n.type.includes('trigger') &&
!executionPath.includes(n.name));
if (orphaned.length > 0) {
warnings.push(`${orphaned.length} node(s) not in execution path: ${orphaned.map(n => n.name).join(', ')}`);
}
// Validate required credentials
workflow.nodes.forEach(node => {
if (node.credentials && Object.keys(node.credentials).length > 0) {
Object.keys(node.credentials).forEach(credType => {
const cred = node.credentials?.[credType];
if (cred?.id?.includes('placeholder')) {
errors.push(`Node "${node.name}" requires ${credType} credentials`);
}
});
}
});
// Estimate duration (rough calculation)
const estimatedDuration = executionPath.length * 1000; // 1 second per node
return {
success: errors.length === 0,
errors,
warnings,
executionPath,
estimatedDuration,
};
}
/**
* Validate workflow can execute end-to-end
*/
export function validateExecutionPath(workflow) {
const issues = [];
// Check all connections point to valid nodes
Object.entries(workflow.connections).forEach(([sourceName, connections]) => {
const sourceNode = workflow.nodes.find(n => n.name === sourceName);
if (!sourceNode) {
issues.push(`Connection source "${sourceName}" not found in nodes`);
}
Object.values(connections).forEach((connArray) => {
connArray?.forEach?.((conns) => {
conns?.forEach?.((conn) => {
const targetNode = workflow.nodes.find(n => n.name === conn?.node);
if (!targetNode && conn?.node) {
issues.push(`Connection target "${conn.node}" not found in nodes`);
}
});
});
});
});
return {
valid: issues.length === 0,
issues,
};
}
//# sourceMappingURL=tester.js.map