mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-14 00:24:07 -08:00
wip: test runner draft
This commit is contained in:
parent
3a4fd24d19
commit
028a449289
|
@ -163,7 +163,13 @@ export class ExecutionRepository extends Repository<ExecutionEntity> {
|
||||||
if (!queryParams.relations) {
|
if (!queryParams.relations) {
|
||||||
queryParams.relations = [];
|
queryParams.relations = [];
|
||||||
}
|
}
|
||||||
(queryParams.relations as string[]).push('executionData', 'metadata');
|
|
||||||
|
if (Array.isArray(queryParams.relations)) {
|
||||||
|
queryParams.relations.push('executionData', 'metadata');
|
||||||
|
} else {
|
||||||
|
queryParams.relations.executionData = true;
|
||||||
|
queryParams.relations.metadata = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const executions = await this.find(queryParams);
|
const executions = await this.find(queryParams);
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {
|
||||||
testDefinitionCreateRequestBodySchema,
|
testDefinitionCreateRequestBodySchema,
|
||||||
testDefinitionPatchRequestBodySchema,
|
testDefinitionPatchRequestBodySchema,
|
||||||
} from '@/evaluation/test-definition.schema';
|
} from '@/evaluation/test-definition.schema';
|
||||||
|
import { TestRunnerService } from '@/evaluation/test-runner/test-runner.service.ee';
|
||||||
import { listQueryMiddleware } from '@/middlewares';
|
import { listQueryMiddleware } from '@/middlewares';
|
||||||
import { getSharedWorkflowIds } from '@/public-api/v1/handlers/workflows/workflows.service';
|
import { getSharedWorkflowIds } from '@/public-api/v1/handlers/workflows/workflows.service';
|
||||||
import { isPositiveInteger } from '@/utils';
|
import { isPositiveInteger } from '@/utils';
|
||||||
|
@ -26,7 +27,10 @@ export class TestDefinitionsController {
|
||||||
return Number(id);
|
return Number(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(private readonly testDefinitionService: TestDefinitionService) {}
|
constructor(
|
||||||
|
private readonly testDefinitionService: TestDefinitionService,
|
||||||
|
private readonly testRunnerService: TestRunnerService,
|
||||||
|
) {}
|
||||||
|
|
||||||
@Get('/', { middlewares: listQueryMiddleware })
|
@Get('/', { middlewares: listQueryMiddleware })
|
||||||
async getMany(req: TestDefinitionsRequest.GetMany) {
|
async getMany(req: TestDefinitionsRequest.GetMany) {
|
||||||
|
@ -135,4 +139,16 @@ export class TestDefinitionsController {
|
||||||
|
|
||||||
return testDefinition;
|
return testDefinition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Post('/:id/run')
|
||||||
|
async runTest(req: TestDefinitionsRequest.Run) {
|
||||||
|
if (!isPositiveInteger(req.params.id)) {
|
||||||
|
throw new BadRequestError('Test ID is not a number');
|
||||||
|
}
|
||||||
|
|
||||||
|
const workflowIds = await getSharedWorkflowIds(req.user, ['workflow:read']);
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||||
|
return await this.testRunnerService.runTest(req.user, Number(req.params.id), workflowIds);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,4 +30,6 @@ export declare namespace TestDefinitionsRequest {
|
||||||
>;
|
>;
|
||||||
|
|
||||||
type Delete = AuthenticatedRequest<RouteParams.TestId>;
|
type Delete = AuthenticatedRequest<RouteParams.TestId>;
|
||||||
|
|
||||||
|
type Run = AuthenticatedRequest<RouteParams.TestId>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
import { parse } from 'flatted';
|
||||||
|
import type { IPinData, IWorkflowExecutionDataProcess } from 'n8n-workflow';
|
||||||
|
import assert from 'node:assert';
|
||||||
|
import { Container, Service } from 'typedi';
|
||||||
|
|
||||||
|
import { ActiveExecutions } from '@/active-executions';
|
||||||
|
import type { User } from '@/databases/entities/user';
|
||||||
|
import { ExecutionRepository } from '@/databases/repositories/execution.repository';
|
||||||
|
import { WorkflowRepository } from '@/databases/repositories/workflow.repository';
|
||||||
|
import { NotFoundError } from '@/errors/response-errors/not-found.error';
|
||||||
|
import { TestDefinitionService } from '@/evaluation/test-definition.service.ee';
|
||||||
|
import type { IExecutionDb, IExecutionResponse } from '@/interfaces';
|
||||||
|
import { WorkflowRunner } from '@/workflow-runner';
|
||||||
|
// import { WorkflowExecutionService } from '@/workflows/workflow-execution.service';
|
||||||
|
|
||||||
|
@Service()
|
||||||
|
export class TestRunnerService {
|
||||||
|
constructor(
|
||||||
|
private readonly testDefinitionsService: TestDefinitionService,
|
||||||
|
private readonly workflowRepository: WorkflowRepository,
|
||||||
|
// private readonly workflowExecutionService: WorkflowExecutionService,
|
||||||
|
private readonly workflowRunner: WorkflowRunner,
|
||||||
|
private readonly executionRepository: ExecutionRepository,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public async runTest(user: User, testId: number, accessibleWorkflowIds: string[]): Promise<any> {
|
||||||
|
const test = await this.testDefinitionsService.findOne(testId, accessibleWorkflowIds);
|
||||||
|
|
||||||
|
console.log({ test });
|
||||||
|
|
||||||
|
if (!test) {
|
||||||
|
throw new NotFoundError('Test definition not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
const workflow = await this.workflowRepository.findById(test.workflowId);
|
||||||
|
assert(workflow, 'Workflow not found');
|
||||||
|
|
||||||
|
// Make a list of test cases
|
||||||
|
// const executions = await this.executionRepository.findManyByRangeQuery({
|
||||||
|
// kind: 'range',
|
||||||
|
// range: {
|
||||||
|
// limit: 99,
|
||||||
|
// },
|
||||||
|
// annotationTags: [test.annotationTagId],
|
||||||
|
// accessibleWorkflowIds,
|
||||||
|
// });
|
||||||
|
const executions = await this.executionRepository
|
||||||
|
.createQueryBuilder('execution')
|
||||||
|
.leftJoin('execution.annotation', 'annotation')
|
||||||
|
.leftJoin('annotation.tags', 'annotationTag')
|
||||||
|
.leftJoinAndSelect('execution.executionData', 'executionData')
|
||||||
|
.leftJoinAndSelect('execution.metadata', 'metadata')
|
||||||
|
.where('annotationTag.id = :tagId', { tagId: test.annotationTagId })
|
||||||
|
.andWhere('execution.workflowId = :workflowId', { workflowId: test.workflowId })
|
||||||
|
.getMany();
|
||||||
|
|
||||||
|
const testCases = executions.map((execution) => {
|
||||||
|
const executionData = parse(execution.executionData.data) as IExecutionResponse['data'];
|
||||||
|
|
||||||
|
return {
|
||||||
|
pinData: {
|
||||||
|
'When clicking ‘Test workflow’':
|
||||||
|
executionData.resultData.runData['When clicking ‘Test workflow’'][0]?.data?.main?.[0],
|
||||||
|
} as IPinData,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log({ testCases });
|
||||||
|
|
||||||
|
for (const testCase of testCases) {
|
||||||
|
// Start the workflow
|
||||||
|
const data: IWorkflowExecutionDataProcess = {
|
||||||
|
executionMode: 'evaluation',
|
||||||
|
runData: {},
|
||||||
|
pinData: testCase.pinData,
|
||||||
|
workflowData: workflow,
|
||||||
|
userId: user.id,
|
||||||
|
partialExecutionVersion: '-1',
|
||||||
|
};
|
||||||
|
|
||||||
|
// if (pinnedTrigger && !hasRunData(pinnedTrigger)) {
|
||||||
|
// data.startNodes = [{ name: pinnedTrigger.name, sourceData: null }];
|
||||||
|
// }
|
||||||
|
|
||||||
|
const executionId = await this.workflowRunner.run(data);
|
||||||
|
|
||||||
|
assert(executionId);
|
||||||
|
|
||||||
|
const executePromise = Container.get(ActiveExecutions).getPostExecutePromise(
|
||||||
|
executionId,
|
||||||
|
) as Promise<IExecutionDb | undefined>;
|
||||||
|
|
||||||
|
const execution = await executePromise;
|
||||||
|
console.log({ execution });
|
||||||
|
console.log(execution?.data.resultData.runData.Code?.[0].data?.main[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { success: true };
|
||||||
|
}
|
||||||
|
}
|
|
@ -2339,7 +2339,8 @@ export type WorkflowExecuteMode =
|
||||||
| 'manual'
|
| 'manual'
|
||||||
| 'retry'
|
| 'retry'
|
||||||
| 'trigger'
|
| 'trigger'
|
||||||
| 'webhook';
|
| 'webhook'
|
||||||
|
| 'evaluation';
|
||||||
|
|
||||||
export type WorkflowActivateMode =
|
export type WorkflowActivateMode =
|
||||||
| 'init'
|
| 'init'
|
||||||
|
|
Loading…
Reference in a new issue