mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 10:02:05 +00:00
fix: Run evaluations as full manual executions in queue mode (#16230)
This commit is contained in:
@@ -597,4 +597,36 @@ describe('ManualExecutionService', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should call workflowExecute.run for full execution when execution mode is evaluation', async () => {
|
||||||
|
const data = mock<IWorkflowExecutionDataProcess>({
|
||||||
|
executionMode: 'evaluation',
|
||||||
|
destinationNode: undefined,
|
||||||
|
pinData: {},
|
||||||
|
runData: {},
|
||||||
|
triggerToStartFrom: undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
const workflow = mock<Workflow>({
|
||||||
|
getNode: jest.fn().mockReturnValue(null),
|
||||||
|
getTriggerNodes: jest.fn().mockReturnValue([]),
|
||||||
|
});
|
||||||
|
|
||||||
|
const additionalData = mock<IWorkflowExecuteAdditionalData>();
|
||||||
|
const executionId = 'test-execution-id-evaluation';
|
||||||
|
|
||||||
|
const mockRun = jest.fn().mockReturnValue('mockRunReturnEvaluation');
|
||||||
|
require('n8n-core').WorkflowExecute.mockImplementationOnce(() => ({
|
||||||
|
run: mockRun,
|
||||||
|
processRunExecutionData: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
await manualExecutionService.runManually(data, workflow, additionalData, executionId);
|
||||||
|
|
||||||
|
expect(mockRun.mock.calls[0][0]).toBe(workflow);
|
||||||
|
expect(mockRun.mock.calls[0][1]).toBeUndefined(); // startNode
|
||||||
|
expect(mockRun.mock.calls[0][2]).toBeUndefined(); // destinationNode
|
||||||
|
expect(mockRun.mock.calls[0][3]).toBe(data.pinData); // pinData
|
||||||
|
expect(mockRun.mock.calls[0][4]).toBeUndefined(); // triggerToStartFrom
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import type { TestCaseExecutionRepository } from '@n8n/db';
|
|||||||
import type { TestRunRepository } from '@n8n/db';
|
import type { TestRunRepository } from '@n8n/db';
|
||||||
import type { WorkflowRepository } from '@n8n/db';
|
import type { WorkflowRepository } from '@n8n/db';
|
||||||
import { readFileSync } from 'fs';
|
import { readFileSync } from 'fs';
|
||||||
|
import type { Mock } from 'jest-mock';
|
||||||
import { mock } from 'jest-mock-extended';
|
import { mock } from 'jest-mock-extended';
|
||||||
import type { ErrorReporter } from 'n8n-core';
|
import type { ErrorReporter } from 'n8n-core';
|
||||||
import { EVALUATION_NODE_TYPE, EVALUATION_TRIGGER_NODE_TYPE } from 'n8n-workflow';
|
import { EVALUATION_NODE_TYPE, EVALUATION_TRIGGER_NODE_TYPE } from 'n8n-workflow';
|
||||||
@@ -11,6 +12,7 @@ import type { IRun, ExecutionError } from 'n8n-workflow';
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
import type { ActiveExecutions } from '@/active-executions';
|
import type { ActiveExecutions } from '@/active-executions';
|
||||||
|
import config from '@/config';
|
||||||
import { TestRunError } from '@/evaluation.ee/test-runner/errors.ee';
|
import { TestRunError } from '@/evaluation.ee/test-runner/errors.ee';
|
||||||
import { LoadNodesAndCredentials } from '@/load-nodes-and-credentials';
|
import { LoadNodesAndCredentials } from '@/load-nodes-and-credentials';
|
||||||
import type { Telemetry } from '@/telemetry';
|
import type { Telemetry } from '@/telemetry';
|
||||||
@@ -679,6 +681,92 @@ describe('TestRunnerService', () => {
|
|||||||
abortController.signal.addEventListener = originalAddEventListener;
|
abortController.signal.addEventListener = originalAddEventListener;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('runTestCase - Queue Mode', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
// Mock config to return 'queue' mode
|
||||||
|
jest.spyOn(config, 'getEnv').mockImplementation((key) => {
|
||||||
|
if (key === 'executions.mode') {
|
||||||
|
return 'queue';
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
(config.getEnv as unknown as Mock).mockRestore();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should call workflowRunner.run with correct data in queue mode', async () => {
|
||||||
|
// Setup test data
|
||||||
|
const triggerNodeName = 'TriggerNode';
|
||||||
|
const workflow = mock<IWorkflowBase>({
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
id: 'node1',
|
||||||
|
name: triggerNodeName,
|
||||||
|
type: EVALUATION_TRIGGER_NODE_TYPE,
|
||||||
|
typeVersion: 1,
|
||||||
|
position: [0, 0],
|
||||||
|
parameters: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
connections: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
const metadata = {
|
||||||
|
testRunId: 'test-run-id',
|
||||||
|
userId: 'user-id',
|
||||||
|
};
|
||||||
|
|
||||||
|
const testCase = { json: { id: 1, name: 'Test 1' } };
|
||||||
|
const abortController = new AbortController();
|
||||||
|
|
||||||
|
// Call the method
|
||||||
|
await (testRunnerService as any).runTestCase(
|
||||||
|
workflow,
|
||||||
|
metadata,
|
||||||
|
testCase,
|
||||||
|
abortController.signal,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Verify workflowRunner.run was called with the correct data
|
||||||
|
expect(workflowRunner.run).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
const runCallArg = workflowRunner.run.mock.calls[0][0];
|
||||||
|
|
||||||
|
// Verify the expected structure for queue mode
|
||||||
|
expect(runCallArg).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
executionMode: 'evaluation',
|
||||||
|
pinData: {
|
||||||
|
[triggerNodeName]: [testCase],
|
||||||
|
},
|
||||||
|
workflowData: workflow,
|
||||||
|
userId: metadata.userId,
|
||||||
|
partialExecutionVersion: 2,
|
||||||
|
triggerToStartFrom: {
|
||||||
|
name: triggerNodeName,
|
||||||
|
},
|
||||||
|
executionData: {
|
||||||
|
resultData: {
|
||||||
|
pinData: {
|
||||||
|
[triggerNodeName]: [testCase],
|
||||||
|
},
|
||||||
|
runData: {},
|
||||||
|
},
|
||||||
|
manualData: {
|
||||||
|
userId: metadata.userId,
|
||||||
|
partialExecutionVersion: 2,
|
||||||
|
triggerToStartFrom: {
|
||||||
|
name: triggerNodeName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('validateSetMetricsNodes', () => {
|
describe('validateSetMetricsNodes', () => {
|
||||||
|
|||||||
@@ -201,11 +201,8 @@ export class TestRunnerService {
|
|||||||
// the same way as it would be passed in manual mode
|
// the same way as it would be passed in manual mode
|
||||||
if (config.getEnv('executions.mode') === 'queue') {
|
if (config.getEnv('executions.mode') === 'queue') {
|
||||||
data.executionData = {
|
data.executionData = {
|
||||||
startData: {
|
|
||||||
// startNodes: startNodesData.startNodes,
|
|
||||||
},
|
|
||||||
resultData: {
|
resultData: {
|
||||||
// pinData,
|
pinData,
|
||||||
runData: {},
|
runData: {},
|
||||||
},
|
},
|
||||||
manualData: {
|
manualData: {
|
||||||
|
|||||||
@@ -113,7 +113,8 @@ export class ManualExecutionService {
|
|||||||
return workflowExecute.processRunExecutionData(workflow);
|
return workflowExecute.processRunExecutionData(workflow);
|
||||||
} else if (
|
} else if (
|
||||||
data.runData === undefined ||
|
data.runData === undefined ||
|
||||||
(data.partialExecutionVersion !== 2 && (!data.startNodes || data.startNodes.length === 0))
|
(data.partialExecutionVersion !== 2 && (!data.startNodes || data.startNodes.length === 0)) ||
|
||||||
|
data.executionMode === 'evaluation'
|
||||||
) {
|
) {
|
||||||
// Full Execution
|
// Full Execution
|
||||||
// TODO: When the old partial execution logic is removed this block can
|
// TODO: When the old partial execution logic is removed this block can
|
||||||
|
|||||||
Reference in New Issue
Block a user