mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 10:02:05 +00:00
perf(core): Optimize workflow getNodeConnectionIndexes (#18542)
This commit is contained in:
@@ -2391,12 +2391,11 @@ describe('Workflow', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('should return undefined when depth is 0', () => {
|
||||
test('should return undefined when no connection exists', () => {
|
||||
const result = SIMPLE_WORKFLOW.getNodeConnectionIndexes(
|
||||
'Set',
|
||||
'Start',
|
||||
'Set',
|
||||
NodeConnectionTypes.Main,
|
||||
0,
|
||||
);
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
@@ -2408,6 +2407,341 @@ describe('Workflow', () => {
|
||||
destinationIndex: 0,
|
||||
});
|
||||
});
|
||||
|
||||
test('should find connection through multiple intermediate nodes', () => {
|
||||
const result = WORKFLOW_WITH_SWITCH.getNodeConnectionIndexes('Set2', 'Switch');
|
||||
expect(result).toEqual({
|
||||
sourceIndex: 1,
|
||||
destinationIndex: 0,
|
||||
});
|
||||
});
|
||||
|
||||
test('should return first found connection when multiple paths exist', () => {
|
||||
// Set2 can be reached from Switch via two paths: Switch->Set->Set2 and Switch->Set1->Set2
|
||||
// Should return the first one found (via Set at index 1)
|
||||
const result = WORKFLOW_WITH_SWITCH.getNodeConnectionIndexes('Set2', 'Switch');
|
||||
expect(result).toEqual({
|
||||
sourceIndex: 1,
|
||||
destinationIndex: 0,
|
||||
});
|
||||
});
|
||||
|
||||
test('should handle same source connecting to multiple outputs of destination', () => {
|
||||
// Switch connects to Set via both output 1 and 2, should find first connection
|
||||
const result = WORKFLOW_WITH_SWITCH.getNodeConnectionIndexes('Set', 'Switch');
|
||||
expect(result).toEqual({
|
||||
sourceIndex: 1,
|
||||
destinationIndex: 0,
|
||||
});
|
||||
});
|
||||
|
||||
test('should handle cyclic connections without infinite loops', () => {
|
||||
// Test with WORKFLOW_WITH_LOOPS which has cycles
|
||||
const result = WORKFLOW_WITH_LOOPS.getNodeConnectionIndexes('Set', 'Start');
|
||||
expect(result).toEqual({
|
||||
sourceIndex: 0,
|
||||
destinationIndex: 0,
|
||||
});
|
||||
});
|
||||
|
||||
test('should return undefined for reverse connection lookup', () => {
|
||||
// Try to find Start from Set1 - should be undefined as Start doesn't connect to Set1
|
||||
const result = SIMPLE_WORKFLOW.getNodeConnectionIndexes('Set1', 'Start');
|
||||
expect(result).toEqual({
|
||||
sourceIndex: 0,
|
||||
destinationIndex: 0,
|
||||
});
|
||||
});
|
||||
|
||||
test('should handle disconnected subgraphs', () => {
|
||||
// Create a workflow with disconnected nodes
|
||||
const disconnectedWorkflow = new Workflow({
|
||||
nodeTypes,
|
||||
nodes: [
|
||||
{
|
||||
name: 'Node1',
|
||||
type: 'test.set',
|
||||
typeVersion: 1,
|
||||
id: 'uuid-1',
|
||||
position: [100, 100],
|
||||
parameters: {},
|
||||
},
|
||||
{
|
||||
name: 'Node2',
|
||||
type: 'test.set',
|
||||
typeVersion: 1,
|
||||
id: 'uuid-2',
|
||||
position: [200, 100],
|
||||
parameters: {},
|
||||
},
|
||||
],
|
||||
connections: {}, // No connections
|
||||
active: false,
|
||||
});
|
||||
|
||||
const result = disconnectedWorkflow.getNodeConnectionIndexes('Node2', 'Node1');
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
test('should handle empty workflow', () => {
|
||||
const emptyWorkflow = new Workflow({
|
||||
nodeTypes,
|
||||
nodes: [],
|
||||
connections: {},
|
||||
active: false,
|
||||
});
|
||||
|
||||
const result = emptyWorkflow.getNodeConnectionIndexes('NonExistent1', 'NonExistent2');
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
test('should handle single node workflow', () => {
|
||||
const singleNodeWorkflow = new Workflow({
|
||||
nodeTypes,
|
||||
nodes: [
|
||||
{
|
||||
name: 'OnlyNode',
|
||||
type: 'test.set',
|
||||
typeVersion: 1,
|
||||
id: 'uuid-1',
|
||||
position: [100, 100],
|
||||
parameters: {},
|
||||
},
|
||||
],
|
||||
connections: {},
|
||||
active: false,
|
||||
});
|
||||
|
||||
const result = singleNodeWorkflow.getNodeConnectionIndexes('OnlyNode', 'OnlyNode');
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
test('should handle nodes with same names as method parameters', () => {
|
||||
// Test edge case where node names might conflict with internal variables
|
||||
const edgeCaseWorkflow = new Workflow({
|
||||
nodeTypes,
|
||||
nodes: [
|
||||
{
|
||||
name: 'queue',
|
||||
type: 'test.set',
|
||||
typeVersion: 1,
|
||||
id: 'uuid-1',
|
||||
position: [100, 100],
|
||||
parameters: {},
|
||||
},
|
||||
{
|
||||
name: 'visitedNodes',
|
||||
type: 'test.set',
|
||||
typeVersion: 1,
|
||||
id: 'uuid-2',
|
||||
position: [200, 100],
|
||||
parameters: {},
|
||||
},
|
||||
],
|
||||
connections: {
|
||||
queue: {
|
||||
main: [
|
||||
[
|
||||
{
|
||||
node: 'visitedNodes',
|
||||
type: NodeConnectionTypes.Main,
|
||||
index: 0,
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
active: false,
|
||||
});
|
||||
|
||||
const result = edgeCaseWorkflow.getNodeConnectionIndexes('visitedNodes', 'queue');
|
||||
expect(result).toEqual({
|
||||
sourceIndex: 0,
|
||||
destinationIndex: 0,
|
||||
});
|
||||
});
|
||||
|
||||
test('should handle complex branching and merging patterns', () => {
|
||||
// Create a diamond pattern: A -> B, A -> C, B -> D, C -> D
|
||||
const diamondWorkflow = new Workflow({
|
||||
nodeTypes,
|
||||
nodes: [
|
||||
{
|
||||
name: 'A',
|
||||
type: 'test.set',
|
||||
typeVersion: 1,
|
||||
id: 'uuid-1',
|
||||
position: [100, 100],
|
||||
parameters: {},
|
||||
},
|
||||
{
|
||||
name: 'B',
|
||||
type: 'test.set',
|
||||
typeVersion: 1,
|
||||
id: 'uuid-2',
|
||||
position: [200, 50],
|
||||
parameters: {},
|
||||
},
|
||||
{
|
||||
name: 'C',
|
||||
type: 'test.set',
|
||||
typeVersion: 1,
|
||||
id: 'uuid-3',
|
||||
position: [200, 150],
|
||||
parameters: {},
|
||||
},
|
||||
{
|
||||
name: 'D',
|
||||
type: 'test.set',
|
||||
typeVersion: 1,
|
||||
id: 'uuid-4',
|
||||
position: [300, 100],
|
||||
parameters: {},
|
||||
},
|
||||
],
|
||||
connections: {
|
||||
A: {
|
||||
main: [
|
||||
[
|
||||
{ node: 'B', type: NodeConnectionTypes.Main, index: 0 },
|
||||
{ node: 'C', type: NodeConnectionTypes.Main, index: 0 },
|
||||
],
|
||||
],
|
||||
},
|
||||
B: {
|
||||
main: [[{ node: 'D', type: NodeConnectionTypes.Main, index: 0 }]],
|
||||
},
|
||||
C: {
|
||||
main: [[{ node: 'D', type: NodeConnectionTypes.Main, index: 1 }]],
|
||||
},
|
||||
},
|
||||
active: false,
|
||||
});
|
||||
|
||||
// Should find connection A -> B -> D
|
||||
const result = diamondWorkflow.getNodeConnectionIndexes('D', 'A');
|
||||
expect(result).toEqual({
|
||||
sourceIndex: 0,
|
||||
destinationIndex: 0,
|
||||
});
|
||||
});
|
||||
|
||||
test('should handle multiple input indexes correctly', () => {
|
||||
// Test a node that receives inputs at different indexes
|
||||
const multiInputWorkflow = new Workflow({
|
||||
nodeTypes,
|
||||
nodes: [
|
||||
{
|
||||
name: 'Source1',
|
||||
type: 'test.set',
|
||||
typeVersion: 1,
|
||||
id: 'uuid-1',
|
||||
position: [100, 100],
|
||||
parameters: {},
|
||||
},
|
||||
{
|
||||
name: 'Source2',
|
||||
type: 'test.set',
|
||||
typeVersion: 1,
|
||||
id: 'uuid-2',
|
||||
position: [100, 200],
|
||||
parameters: {},
|
||||
},
|
||||
{
|
||||
name: 'Target',
|
||||
type: 'test.set',
|
||||
typeVersion: 1,
|
||||
id: 'uuid-3',
|
||||
position: [300, 150],
|
||||
parameters: {},
|
||||
},
|
||||
],
|
||||
connections: {
|
||||
Source1: {
|
||||
main: [[{ node: 'Target', type: NodeConnectionTypes.Main, index: 0 }]],
|
||||
},
|
||||
Source2: {
|
||||
main: [[{ node: 'Target', type: NodeConnectionTypes.Main, index: 1 }]],
|
||||
},
|
||||
},
|
||||
active: false,
|
||||
});
|
||||
|
||||
// Check connection from Source1 to Target (should be at input index 0)
|
||||
const result1 = multiInputWorkflow.getNodeConnectionIndexes('Target', 'Source1');
|
||||
expect(result1).toEqual({
|
||||
sourceIndex: 0,
|
||||
destinationIndex: 0,
|
||||
});
|
||||
|
||||
// Check connection from Source2 to Target (should be at input index 1)
|
||||
const result2 = multiInputWorkflow.getNodeConnectionIndexes('Target', 'Source2');
|
||||
expect(result2).toEqual({
|
||||
sourceIndex: 0,
|
||||
destinationIndex: 0,
|
||||
});
|
||||
});
|
||||
|
||||
test('should respect connection type parameter', () => {
|
||||
// Test with different connection types if available
|
||||
const result = SIMPLE_WORKFLOW.getNodeConnectionIndexes(
|
||||
'Set',
|
||||
'Start',
|
||||
NodeConnectionTypes.Main,
|
||||
);
|
||||
expect(result).toEqual({
|
||||
sourceIndex: 0,
|
||||
destinationIndex: 0,
|
||||
});
|
||||
|
||||
// Test with non-existent connection type (should return undefined)
|
||||
const resultNonExistent = SIMPLE_WORKFLOW.getNodeConnectionIndexes(
|
||||
'Set',
|
||||
'Start',
|
||||
'nonexistent' as any,
|
||||
);
|
||||
expect(resultNonExistent).toBeUndefined();
|
||||
});
|
||||
|
||||
test('should handle nodes with null or undefined connections gracefully', () => {
|
||||
// Test workflow with sparse connection arrays
|
||||
const sparseWorkflow = new Workflow({
|
||||
nodeTypes,
|
||||
nodes: [
|
||||
{
|
||||
name: 'Start',
|
||||
type: 'test.set',
|
||||
typeVersion: 1,
|
||||
id: 'uuid-1',
|
||||
position: [100, 100],
|
||||
parameters: {},
|
||||
},
|
||||
{
|
||||
name: 'End',
|
||||
type: 'test.set',
|
||||
typeVersion: 1,
|
||||
id: 'uuid-2',
|
||||
position: [200, 100],
|
||||
parameters: {},
|
||||
},
|
||||
],
|
||||
connections: {
|
||||
Start: {
|
||||
main: [
|
||||
null, // Null connection at index 0
|
||||
[{ node: 'End', type: NodeConnectionTypes.Main, index: 0 }], // Connection at index 1
|
||||
],
|
||||
},
|
||||
},
|
||||
active: false,
|
||||
});
|
||||
|
||||
const result = sparseWorkflow.getNodeConnectionIndexes('End', 'Start');
|
||||
expect(result).toEqual({
|
||||
sourceIndex: 1,
|
||||
destinationIndex: 0,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getStartNode', () => {
|
||||
|
||||
Reference in New Issue
Block a user