mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 10:02:05 +00:00
533 lines
14 KiB
TypeScript
533 lines
14 KiB
TypeScript
import type {
|
|
INodeExecutionData,
|
|
IPairedItemData,
|
|
IRunExecutionData,
|
|
ISourceData,
|
|
ITaskData,
|
|
} from 'n8n-workflow';
|
|
|
|
import { previousTaskData, findPairedItemThroughWorkflowData } from './workflow-backtracking';
|
|
|
|
describe('backtracking.ts', () => {
|
|
describe('previousTaskData', () => {
|
|
it('should return undefined when source is empty', () => {
|
|
const runData = {};
|
|
const currentRunData: ITaskData = {
|
|
source: [],
|
|
data: { main: [[]] },
|
|
executionTime: 0,
|
|
executionStatus: 'success',
|
|
executionIndex: 0,
|
|
startTime: 0,
|
|
};
|
|
|
|
const result = previousTaskData(runData, currentRunData);
|
|
|
|
expect(result).toBeUndefined();
|
|
});
|
|
|
|
it('should return undefined when source is undefined', () => {
|
|
const runData = {};
|
|
const currentRunData: ITaskData = {
|
|
data: { main: [[]] },
|
|
executionTime: 0,
|
|
executionStatus: 'success',
|
|
executionIndex: 0,
|
|
startTime: 0,
|
|
} as unknown as ITaskData; // Type assertion to match the expected type
|
|
|
|
const result = previousTaskData(runData, currentRunData);
|
|
|
|
expect(result).toBeUndefined();
|
|
});
|
|
|
|
it('should return undefined when previousNode is undefined', () => {
|
|
const runData = {};
|
|
const currentRunData: ITaskData = {
|
|
source: [{} as unknown as ISourceData],
|
|
data: { main: [[]] },
|
|
executionTime: 0,
|
|
executionStatus: 'success',
|
|
executionIndex: 0,
|
|
startTime: 0,
|
|
};
|
|
|
|
const result = previousTaskData(runData, currentRunData);
|
|
|
|
expect(result).toBeUndefined();
|
|
});
|
|
|
|
it('should return undefined when run data for previousNode does not exist', () => {
|
|
const runData = {};
|
|
const currentRunData: ITaskData = {
|
|
source: [{ previousNode: 'node1' }],
|
|
data: { main: [[]] },
|
|
executionTime: 0,
|
|
executionStatus: 'success',
|
|
executionIndex: 0,
|
|
startTime: 0,
|
|
};
|
|
|
|
const result = previousTaskData(runData, currentRunData);
|
|
|
|
expect(result).toBeUndefined();
|
|
});
|
|
|
|
it('should return undefined when run data for previousNode is empty', () => {
|
|
const runData = {
|
|
node1: [],
|
|
};
|
|
const currentRunData: ITaskData = {
|
|
source: [{ previousNode: 'node1' }],
|
|
data: { main: [[]] },
|
|
executionTime: 0,
|
|
executionStatus: 'success',
|
|
executionIndex: 0,
|
|
startTime: 0,
|
|
};
|
|
|
|
const result = previousTaskData(runData, currentRunData);
|
|
|
|
expect(result).toBeUndefined();
|
|
});
|
|
|
|
it('should return the correct task data from previousNode', () => {
|
|
const expectedTaskData: ITaskData = {
|
|
data: { main: [[{ json: { test: 'value' } }]] },
|
|
executionTime: 100,
|
|
executionStatus: 'success',
|
|
executionIndex: 0,
|
|
startTime: 1000,
|
|
} as unknown as ITaskData;
|
|
|
|
const runData = {
|
|
node1: [expectedTaskData],
|
|
};
|
|
const currentRunData: ITaskData = {
|
|
source: [{ previousNode: 'node1' }],
|
|
data: { main: [[]] },
|
|
executionTime: 0,
|
|
executionStatus: 'success',
|
|
executionIndex: 0,
|
|
startTime: 0,
|
|
};
|
|
|
|
const result = previousTaskData(runData, currentRunData);
|
|
|
|
expect(result).toBe(expectedTaskData);
|
|
});
|
|
|
|
it('should return correct task data using previousNodeRun index', () => {
|
|
const taskData1: ITaskData = {
|
|
data: { main: [[{ json: { run: 1 } }]] },
|
|
executionTime: 100,
|
|
executionStatus: 'success',
|
|
executionIndex: 0,
|
|
startTime: 1000,
|
|
} as unknown as ITaskData;
|
|
|
|
const taskData2: ITaskData = {
|
|
data: { main: [[{ json: { run: 2 } }]] },
|
|
executionTime: 200,
|
|
executionStatus: 'success',
|
|
executionIndex: 0,
|
|
startTime: 2000,
|
|
} as unknown as ITaskData;
|
|
|
|
const runData = {
|
|
node1: [taskData1, taskData2],
|
|
};
|
|
const currentRunData: ITaskData = {
|
|
source: [{ previousNode: 'node1', previousNodeRun: 1 }],
|
|
data: { main: [[]] },
|
|
executionTime: 0,
|
|
executionStatus: 'success',
|
|
executionIndex: 0,
|
|
startTime: 0,
|
|
};
|
|
|
|
const result = previousTaskData(runData, currentRunData);
|
|
|
|
expect(result).toBe(taskData2);
|
|
});
|
|
|
|
it('should default to index 0 when previousNodeRun is undefined', () => {
|
|
const taskData1: ITaskData = {
|
|
data: { main: [[{ json: { run: 1 } }]] },
|
|
executionTime: 100,
|
|
executionStatus: 'success',
|
|
executionIndex: 0,
|
|
startTime: 1000,
|
|
} as unknown as ITaskData;
|
|
|
|
const taskData2: ITaskData = {
|
|
data: { main: [[{ json: { run: 2 } }]] },
|
|
executionTime: 200,
|
|
executionStatus: 'success',
|
|
executionIndex: 0,
|
|
startTime: 2000,
|
|
} as unknown as ITaskData;
|
|
|
|
const runData = {
|
|
node1: [taskData1, taskData2],
|
|
};
|
|
const currentRunData: ITaskData = {
|
|
source: [{ previousNode: 'node1' }],
|
|
data: { main: [[]] },
|
|
executionTime: 0,
|
|
executionStatus: 'success',
|
|
executionIndex: 0,
|
|
startTime: 0,
|
|
};
|
|
|
|
const result = previousTaskData(runData, currentRunData);
|
|
|
|
expect(result).toBe(taskData1);
|
|
});
|
|
});
|
|
|
|
describe('findPairedItemThroughWorkflowData', () => {
|
|
it('should return undefined when lastNodeExecuted is undefined', () => {
|
|
const workflowRunData: IRunExecutionData = {
|
|
resultData: {
|
|
runData: {},
|
|
lastNodeExecuted: undefined,
|
|
},
|
|
};
|
|
const item: INodeExecutionData = {
|
|
json: { test: 'value' },
|
|
pairedItem: { item: 0 },
|
|
};
|
|
|
|
const result = findPairedItemThroughWorkflowData(workflowRunData, item, 0);
|
|
|
|
expect(result).toBeUndefined();
|
|
});
|
|
|
|
it('should return undefined when no run data exists for lastNodeExecuted', () => {
|
|
const workflowRunData: IRunExecutionData = {
|
|
resultData: {
|
|
runData: {},
|
|
lastNodeExecuted: 'node1',
|
|
},
|
|
};
|
|
const item: INodeExecutionData = {
|
|
json: { test: 'value' },
|
|
pairedItem: { item: 0 },
|
|
};
|
|
|
|
const result = findPairedItemThroughWorkflowData(workflowRunData, item, 0);
|
|
|
|
expect(result).toBeUndefined();
|
|
});
|
|
|
|
it('should return undefined when run data is empty', () => {
|
|
const workflowRunData: IRunExecutionData = {
|
|
resultData: {
|
|
runData: {
|
|
node1: [],
|
|
},
|
|
lastNodeExecuted: 'node1',
|
|
},
|
|
};
|
|
const item: INodeExecutionData = {
|
|
json: { test: 'value' },
|
|
pairedItem: { item: 0 },
|
|
};
|
|
|
|
const result = findPairedItemThroughWorkflowData(workflowRunData, item, 0);
|
|
|
|
expect(result).toBeUndefined();
|
|
});
|
|
|
|
it('should return undefined when task data is undefined', () => {
|
|
const workflowRunData: IRunExecutionData = {
|
|
resultData: {
|
|
runData: {
|
|
node1: [undefined as any],
|
|
},
|
|
lastNodeExecuted: 'node1',
|
|
},
|
|
};
|
|
const item: INodeExecutionData = {
|
|
json: { test: 'value' },
|
|
pairedItem: { item: 0 },
|
|
};
|
|
|
|
const result = findPairedItemThroughWorkflowData(workflowRunData, item, 0);
|
|
|
|
expect(result).toBeUndefined();
|
|
});
|
|
|
|
it('should return paired item when no previous task data exists', () => {
|
|
const expectedPairedItem: IPairedItemData = { item: 0 };
|
|
const workflowRunData: IRunExecutionData = {
|
|
resultData: {
|
|
runData: {
|
|
node1: [
|
|
{
|
|
data: { main: [[]] },
|
|
executionTime: 0,
|
|
executionStatus: 'success',
|
|
startTime: 0,
|
|
} as unknown as ITaskData,
|
|
],
|
|
},
|
|
lastNodeExecuted: 'node1',
|
|
},
|
|
};
|
|
const item: INodeExecutionData = {
|
|
json: { test: 'value' },
|
|
pairedItem: expectedPairedItem,
|
|
};
|
|
|
|
const result = findPairedItemThroughWorkflowData(workflowRunData, item, 0);
|
|
|
|
expect(result).toBe(expectedPairedItem);
|
|
});
|
|
|
|
it('should backtrack through workflow data with simple paired item', () => {
|
|
const finalPairedItem: IPairedItemData = { item: 5 };
|
|
const item: INodeExecutionData = {
|
|
json: { test: 'value' },
|
|
pairedItem: { item: 0 },
|
|
};
|
|
const workflowRunData: IRunExecutionData = {
|
|
resultData: {
|
|
runData: {
|
|
node1: [
|
|
{
|
|
source: [{ previousNode: 'node2' }],
|
|
data: { main: [[item]] },
|
|
executionTime: 100,
|
|
executionStatus: 'success',
|
|
executionIndex: 0,
|
|
startTime: 1000,
|
|
},
|
|
],
|
|
node2: [
|
|
{
|
|
data: { main: [[{ json: { value: 2 }, pairedItem: finalPairedItem }]] },
|
|
executionTime: 200,
|
|
executionStatus: 'success',
|
|
executionIndex: 0,
|
|
startTime: 2000,
|
|
} as unknown as ITaskData,
|
|
],
|
|
},
|
|
lastNodeExecuted: 'node1',
|
|
},
|
|
};
|
|
|
|
const result = findPairedItemThroughWorkflowData(workflowRunData, item, 0);
|
|
|
|
expect(result).toBe(finalPairedItem);
|
|
});
|
|
|
|
it('should backtrack through workflow data with object paired item', () => {
|
|
const finalPairedItem: IPairedItemData = { item: 3, input: 1 };
|
|
const item: INodeExecutionData = {
|
|
json: { test: 'value' },
|
|
pairedItem: { item: 0, input: 1 },
|
|
};
|
|
const workflowRunData: IRunExecutionData = {
|
|
resultData: {
|
|
runData: {
|
|
node1: [
|
|
{
|
|
source: [{ previousNode: 'node2' }],
|
|
data: { main: [[item]] },
|
|
executionTime: 100,
|
|
executionStatus: 'success',
|
|
startTime: 1000,
|
|
} as unknown as ITaskData,
|
|
],
|
|
node2: [
|
|
{
|
|
data: { main: [[], [{ json: { value: 2 }, pairedItem: finalPairedItem }]] },
|
|
executionTime: 200,
|
|
executionStatus: 'success',
|
|
startTime: 2000,
|
|
} as unknown as ITaskData,
|
|
],
|
|
},
|
|
lastNodeExecuted: 'node1',
|
|
},
|
|
};
|
|
|
|
const result = findPairedItemThroughWorkflowData(workflowRunData, item, 0);
|
|
|
|
expect(result).toBe(finalPairedItem);
|
|
});
|
|
|
|
it('should use itemIndex parameter when paired item is numeric', () => {
|
|
const finalPairedItem: IPairedItemData = { item: 7 };
|
|
|
|
const item: INodeExecutionData = {
|
|
json: { test: 'value' },
|
|
pairedItem: 2, // Numeric paired item
|
|
};
|
|
|
|
const workflowRunData: IRunExecutionData = {
|
|
resultData: {
|
|
runData: {
|
|
node1: [
|
|
{
|
|
source: [{ previousNode: 'node2' }],
|
|
data: { main: [[item]] },
|
|
executionTime: 100,
|
|
executionStatus: 'success',
|
|
startTime: 1000,
|
|
} as unknown as ITaskData,
|
|
],
|
|
node2: [
|
|
{
|
|
data: {
|
|
main: [
|
|
[
|
|
{ json: {} },
|
|
{ json: {} },
|
|
{ json: { value: 2 }, pairedItem: finalPairedItem },
|
|
],
|
|
],
|
|
},
|
|
executionTime: 200,
|
|
executionStatus: 'success',
|
|
startTime: 2000,
|
|
} as unknown as ITaskData,
|
|
],
|
|
},
|
|
lastNodeExecuted: 'node1',
|
|
},
|
|
};
|
|
|
|
const result = findPairedItemThroughWorkflowData(workflowRunData, item, 5);
|
|
|
|
expect(result).toBe(finalPairedItem);
|
|
});
|
|
|
|
it('should handle multiple levels of backtracking', () => {
|
|
const finalPairedItem: IPairedItemData = { item: 10 };
|
|
const workflowRunData: IRunExecutionData = {
|
|
resultData: {
|
|
runData: {
|
|
node1: [
|
|
{
|
|
source: [{ previousNode: 'node2' }],
|
|
data: { main: [[{ json: { value: 1 }, pairedItem: { item: 0 } }]] },
|
|
executionTime: 100,
|
|
executionStatus: 'success',
|
|
startTime: 1000,
|
|
} as unknown as ITaskData,
|
|
],
|
|
node2: [
|
|
{
|
|
source: [{ previousNode: 'node3' }],
|
|
data: { main: [[{ json: { value: 2 }, pairedItem: { item: 1 } }]] },
|
|
executionTime: 200,
|
|
executionStatus: 'success',
|
|
startTime: 2000,
|
|
} as unknown as ITaskData,
|
|
],
|
|
node3: [
|
|
{
|
|
data: { main: [[null, { json: { value: 3 }, pairedItem: finalPairedItem }]] },
|
|
executionTime: 300,
|
|
executionStatus: 'success',
|
|
startTime: 3000,
|
|
} as unknown as ITaskData,
|
|
],
|
|
},
|
|
lastNodeExecuted: 'node1',
|
|
},
|
|
};
|
|
const item: INodeExecutionData = {
|
|
json: { test: 'value' },
|
|
pairedItem: { item: 0 },
|
|
};
|
|
|
|
const result = findPairedItemThroughWorkflowData(workflowRunData, item, 0);
|
|
|
|
expect(result).toBe(finalPairedItem);
|
|
});
|
|
|
|
it('should use last run data when multiple runs exist', () => {
|
|
const finalPairedItem: IPairedItemData = { item: 15 };
|
|
const workflowRunData: IRunExecutionData = {
|
|
resultData: {
|
|
runData: {
|
|
node1: [
|
|
{
|
|
source: [{ previousNode: 'node2' }],
|
|
data: { main: [[{ json: { value: 1 }, pairedItem: { item: 0 } }]] },
|
|
executionTime: 100,
|
|
executionStatus: 'success',
|
|
startTime: 1000,
|
|
} as unknown as ITaskData,
|
|
{
|
|
source: [{ previousNode: 'node2' }],
|
|
data: { main: [[{ json: { value: 2 }, pairedItem: { item: 0 } }]] },
|
|
executionTime: 150,
|
|
executionStatus: 'success',
|
|
startTime: 1500,
|
|
} as unknown as ITaskData,
|
|
],
|
|
node2: [
|
|
{
|
|
data: { main: [[{ json: { value: 3 }, pairedItem: finalPairedItem }]] },
|
|
executionTime: 200,
|
|
executionStatus: 'success',
|
|
startTime: 2000,
|
|
} as unknown as ITaskData,
|
|
],
|
|
},
|
|
lastNodeExecuted: 'node1',
|
|
},
|
|
};
|
|
const item: INodeExecutionData = {
|
|
json: { test: 'value' },
|
|
pairedItem: { item: 0 },
|
|
};
|
|
|
|
const result = findPairedItemThroughWorkflowData(workflowRunData, item, 0);
|
|
|
|
expect(result).toBe(finalPairedItem);
|
|
});
|
|
|
|
it('should handle missing nodeInformationArray gracefully', () => {
|
|
const workflowRunData: IRunExecutionData = {
|
|
resultData: {
|
|
runData: {
|
|
node1: [
|
|
{
|
|
source: [{ previousNode: 'node2' }],
|
|
data: {},
|
|
executionTime: 100,
|
|
executionStatus: 'success',
|
|
startTime: 1000,
|
|
} as unknown as ITaskData,
|
|
],
|
|
node2: [
|
|
{
|
|
data: { main: [[]] },
|
|
executionTime: 200,
|
|
executionStatus: 'success',
|
|
startTime: 2000,
|
|
} as unknown as ITaskData,
|
|
],
|
|
},
|
|
lastNodeExecuted: 'node1',
|
|
},
|
|
};
|
|
const item: INodeExecutionData = {
|
|
json: { test: 'value' },
|
|
pairedItem: { item: 0 },
|
|
};
|
|
|
|
const result = findPairedItemThroughWorkflowData(workflowRunData, item, 0);
|
|
|
|
expect(result).toBeUndefined();
|
|
});
|
|
});
|
|
});
|