UNPKG

n8n

Version:

n8n Workflow Automation Tool

183 lines 9.75 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.TestRunnerService = void 0; const di_1 = require("@n8n/di"); const flatted_1 = require("flatted"); const n8n_core_1 = require("n8n-core"); const n8n_workflow_1 = require("n8n-workflow"); const node_assert_1 = __importDefault(require("node:assert")); const active_executions_1 = require("../../active-executions"); const execution_repository_1 = require("../../databases/repositories/execution.repository"); const test_metric_repository_ee_1 = require("../../databases/repositories/test-metric.repository.ee"); const test_run_repository_ee_1 = require("../../databases/repositories/test-run.repository.ee"); const workflow_repository_1 = require("../../databases/repositories/workflow.repository"); const node_types_1 = require("../../node-types"); const workflow_execute_additional_data_1 = require("../../workflow-execute-additional-data"); const workflow_runner_1 = require("../../workflow-runner"); const evaluation_metrics_ee_1 = require("./evaluation-metrics.ee"); const utils_ee_1 = require("./utils.ee"); let TestRunnerService = class TestRunnerService { constructor(workflowRepository, workflowRunner, executionRepository, activeExecutions, testRunRepository, testMetricRepository, nodeTypes, errorReporter) { this.workflowRepository = workflowRepository; this.workflowRunner = workflowRunner; this.executionRepository = executionRepository; this.activeExecutions = activeExecutions; this.testRunRepository = testRunRepository; this.testMetricRepository = testMetricRepository; this.nodeTypes = nodeTypes; this.errorReporter = errorReporter; } getStartNodesData(workflow, pastExecutionData) { const workflowInstance = new n8n_workflow_1.Workflow({ nodes: workflow.nodes, connections: workflow.connections, active: false, nodeTypes: this.nodeTypes, }); const pastExecutionTriggerNode = (0, utils_ee_1.getPastExecutionTriggerNode)(pastExecutionData); (0, node_assert_1.default)(pastExecutionTriggerNode, 'Could not find the trigger node of the past execution'); const triggerNodeData = pastExecutionData.resultData.runData[pastExecutionTriggerNode][0]; (0, node_assert_1.default)(triggerNodeData, 'Trigger node data not found'); const triggerToStartFrom = { name: pastExecutionTriggerNode, data: triggerNodeData, }; const startNodes = workflowInstance .getChildNodes(pastExecutionTriggerNode, "main", 1) .map((nodeName) => ({ name: nodeName, sourceData: { previousNode: pastExecutionTriggerNode }, })); return { startNodes, triggerToStartFrom, }; } async runTestCase(workflow, pastExecutionData, pastExecutionWorkflowData, mockedNodes, userId) { const pinData = (0, utils_ee_1.createPinData)(workflow, mockedNodes, pastExecutionData, pastExecutionWorkflowData); const data = { ...this.getStartNodesData(workflow, pastExecutionData), executionMode: 'evaluation', runData: {}, pinData, workflowData: workflow, userId, }; const executionId = await this.workflowRunner.run(data); (0, node_assert_1.default)(executionId); const executePromise = this.activeExecutions.getPostExecutePromise(executionId); return await executePromise; } async runTestCaseEvaluation(evaluationWorkflow, expectedData, actualData, testRunId) { const evaluationInputData = { json: { originalExecution: expectedData, newExecution: actualData, }, }; const data = await (0, workflow_execute_additional_data_1.getRunData)(evaluationWorkflow, [evaluationInputData]); if (testRunId && data.executionData) { data.executionData.resultData.metadata = { testRunId, }; } data.executionMode = 'evaluation'; const executionId = await this.workflowRunner.run(data); (0, node_assert_1.default)(executionId); const executePromise = this.activeExecutions.getPostExecutePromise(executionId); return await executePromise; } extractEvaluationResult(execution) { const lastNodeExecuted = execution.data.resultData.lastNodeExecuted; (0, node_assert_1.default)(lastNodeExecuted, 'Could not find the last node executed in evaluation workflow'); const lastNodeTaskData = execution.data.resultData.runData[lastNodeExecuted]?.[0]; const mainConnectionData = lastNodeTaskData?.data?.main?.[0]; return mainConnectionData?.[0]?.json ?? {}; } async getTestMetricNames(testDefinitionId) { const metrics = await this.testMetricRepository.find({ where: { testDefinition: { id: testDefinitionId, }, }, }); return new Set(metrics.map((m) => m.name)); } async runTest(user, test) { const workflow = await this.workflowRepository.findById(test.workflowId); (0, node_assert_1.default)(workflow, 'Workflow not found'); const evaluationWorkflow = await this.workflowRepository.findById(test.evaluationWorkflowId); (0, node_assert_1.default)(evaluationWorkflow, 'Evaluation workflow not found'); const testRun = await this.testRunRepository.createTestRun(test.id); (0, node_assert_1.default)(testRun, 'Unable to create a test run'); const pastExecutions = await this.executionRepository .createQueryBuilder('execution') .select('execution.id') .leftJoin('execution.annotation', 'annotation') .leftJoin('annotation.tags', 'annotationTag') .where('annotationTag.id = :tagId', { tagId: test.annotationTagId }) .andWhere('execution.workflowId = :workflowId', { workflowId: test.workflowId }) .getMany(); const testMetricNames = await this.getTestMetricNames(test.id); await this.testRunRepository.markAsRunning(testRun.id, pastExecutions.length); const metrics = new evaluation_metrics_ee_1.EvaluationMetrics(testMetricNames); for (const { id: pastExecutionId } of pastExecutions) { try { const pastExecution = await this.executionRepository.findOne({ where: { id: pastExecutionId }, relations: ['executionData', 'metadata'], }); (0, node_assert_1.default)(pastExecution, 'Execution not found'); const executionData = (0, flatted_1.parse)(pastExecution.executionData.data); const testCaseExecution = await this.runTestCase(workflow, executionData, pastExecution.executionData.workflowData, test.mockedNodes, user.id); if (!testCaseExecution) { await this.testRunRepository.incrementFailed(testRun.id); continue; } const testCaseRunData = testCaseExecution.data.resultData.runData; const originalRunData = executionData.resultData.runData; const evalExecution = await this.runTestCaseEvaluation(evaluationWorkflow, originalRunData, testCaseRunData, testRun.id); (0, node_assert_1.default)(evalExecution); metrics.addResults(this.extractEvaluationResult(evalExecution)); if (evalExecution.data.resultData.error) { await this.testRunRepository.incrementFailed(testRun.id); } else { await this.testRunRepository.incrementPassed(testRun.id); } } catch (e) { await this.testRunRepository.incrementFailed(testRun.id); this.errorReporter.error(e); } } const aggregatedMetrics = metrics.getAggregatedMetrics(); await this.testRunRepository.markAsCompleted(testRun.id, aggregatedMetrics); } }; exports.TestRunnerService = TestRunnerService; exports.TestRunnerService = TestRunnerService = __decorate([ (0, di_1.Service)(), __metadata("design:paramtypes", [workflow_repository_1.WorkflowRepository, workflow_runner_1.WorkflowRunner, execution_repository_1.ExecutionRepository, active_executions_1.ActiveExecutions, test_run_repository_ee_1.TestRunRepository, test_metric_repository_ee_1.TestMetricRepository, node_types_1.NodeTypes, n8n_core_1.ErrorReporter]) ], TestRunnerService); //# sourceMappingURL=test-runner.service.ee.js.map