From 6b6f75ab2fac290759cf5223755504bd96ba3fdc Mon Sep 17 00:00:00 2001 From: AgAngle <1323481023@qq.com> Date: Tue, 11 Jun 2024 18:15:36 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E6=8E=A5=E5=8F=A3=E6=B5=8B=E8=AF=95):=20?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E8=AE=A1=E5=88=92=E6=8E=A5=E5=8F=A3=E5=92=8C?= =?UTF-8?q?=E5=9C=BA=E6=99=AF=E6=89=B9=E9=87=8F=E6=89=A7=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ApiTestCaseBatchRunRequest.java | 5 - .../api/service/ApiBatchRunBaseService.java | 9 + .../ApiTestCaseBatchRunService.java | 12 +- .../scenario/ApiScenarioBatchRunService.java | 118 ++++---- .../ApiScenarioControllerTests.java | 3 +- .../controller/TestPlanApiCaseController.java | 11 + .../TestPlanApiScenarioController.java | 31 +- .../TestPlanApiCaseBatchRunRequest.java | 16 + .../TestPlanApiScenarioBatchRunRequest.java | 20 ++ .../plan/mapper/ExtTestPlanApiCaseMapper.java | 2 + .../plan/mapper/ExtTestPlanApiCaseMapper.xml | 9 + .../mapper/ExtTestPlanApiScenarioMapper.java | 2 + .../mapper/ExtTestPlanApiScenarioMapper.xml | 9 + .../TestPlanApiCaseBatchRunService.java | 271 +++++++++++++++++ .../TestPlanApiScenarioBatchRunService.java | 279 ++++++++++++++++++ .../TestPlanApiCaseControllerTests.java | 32 ++ .../TestPlanApiScenarioControllerTests.java | 49 ++- 17 files changed, 795 insertions(+), 83 deletions(-) create mode 100644 backend/services/test-plan/src/main/java/io/metersphere/plan/dto/request/TestPlanApiCaseBatchRunRequest.java create mode 100644 backend/services/test-plan/src/main/java/io/metersphere/plan/dto/request/TestPlanApiScenarioBatchRunRequest.java create mode 100644 backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiCaseBatchRunService.java create mode 100644 backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiScenarioBatchRunService.java diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/dto/definition/ApiTestCaseBatchRunRequest.java b/backend/services/api-test/src/main/java/io/metersphere/api/dto/definition/ApiTestCaseBatchRunRequest.java index 022d4332af..14cdcbe05d 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/dto/definition/ApiTestCaseBatchRunRequest.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/dto/definition/ApiTestCaseBatchRunRequest.java @@ -3,7 +3,6 @@ package io.metersphere.api.dto.definition; import io.metersphere.api.dto.ApiRunModeRequest; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; -import jakarta.validation.constraints.Size; import lombok.Data; import lombok.EqualsAndHashCode; @@ -15,10 +14,6 @@ public class ApiTestCaseBatchRunRequest extends ApiTestCaseBatchRequest implemen private static final long serialVersionUID = 1L; - @Schema(description = "接口pk") - @Size(max = 50, message = "{api_definition.id.length_range}") - private String apiDefinitionId; - @Valid @Schema(description = "运行模式配置") private ApiRunModeRequest runModeConfig; diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiBatchRunBaseService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiBatchRunBaseService.java index 9d04034e54..46ffb0059e 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiBatchRunBaseService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/ApiBatchRunBaseService.java @@ -3,6 +3,7 @@ package io.metersphere.api.service; import io.metersphere.api.domain.ApiScenarioReport; import io.metersphere.api.service.queue.ApiExecutionQueueService; import io.metersphere.sdk.dto.api.task.ApiRunModeConfigDTO; +import io.metersphere.sdk.dto.api.task.TaskInfo; import io.metersphere.sdk.dto.queue.ExecutionQueue; import io.metersphere.sdk.dto.queue.ExecutionQueueDetail; import io.metersphere.sdk.util.LogUtils; @@ -92,4 +93,12 @@ public class ApiBatchRunBaseService { private static String formatRate(double rate) { return String.format("%.2f", rate * 100); } + + public TaskInfo setBatchRunTaskInfoParam(ApiRunModeConfigDTO runModeConfig, TaskInfo taskInfo) { + taskInfo.setSaveResult(true); + taskInfo.setRealTime(false); + taskInfo.setNeedParseScript(true); + taskInfo.setRunModeConfig(runModeConfig); + return taskInfo; + } } diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiTestCaseBatchRunService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiTestCaseBatchRunService.java index e8357a8e63..4ed91bb0ae 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiTestCaseBatchRunService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/definition/ApiTestCaseBatchRunService.java @@ -311,16 +311,11 @@ public class ApiTestCaseBatchRunService { return taskRequest; } - private TaskInfo getTaskInfo(String projectId, ApiRunModeConfigDTO runModeConfig) { + public TaskInfo getTaskInfo(String projectId, ApiRunModeConfigDTO runModeConfig) { TaskInfo taskInfo = apiTestCaseService.getTaskInfo(projectId, ApiExecuteRunMode.RUN.name()); - taskInfo.setSaveResult(true); - taskInfo.setRealTime(false); - taskInfo.setNeedParseScript(true); - taskInfo.setRunModeConfig(runModeConfig); - return taskInfo; + return apiBatchRunBaseService.setBatchRunTaskInfoParam(runModeConfig, taskInfo); } - /** * 预生成用例的执行报告 * @@ -346,8 +341,7 @@ public class ApiTestCaseBatchRunService { return apiTestCaseRecords; } - - private ApiReport getApiReport(ApiRunModeConfigDTO runModeConfig, ApiTestCase apiTestCase, String userId) { + public ApiReport getApiReport(ApiRunModeConfigDTO runModeConfig, ApiTestCase apiTestCase, String userId) { ApiReport apiReport = getApiReport(runModeConfig, userId); apiReport.setEnvironmentId(apiTestCaseService.getEnvId(runModeConfig, apiTestCase)); apiReport.setName(apiTestCase.getName() + "_" + DateUtils.getTimeString(System.currentTimeMillis())); diff --git a/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioBatchRunService.java b/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioBatchRunService.java index bca8edd9eb..1e92123c54 100644 --- a/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioBatchRunService.java +++ b/backend/services/api-test/src/main/java/io/metersphere/api/service/scenario/ApiScenarioBatchRunService.java @@ -24,6 +24,7 @@ import io.metersphere.sdk.util.SubListUtils; import io.metersphere.system.uid.IDGenerator; import jakarta.annotation.Resource; import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -33,6 +34,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; import java.util.function.Function; import java.util.stream.Collectors; @@ -78,7 +80,7 @@ public class ApiScenarioBatchRunService { */ private void batchRun(ApiScenarioBatchRunRequest request, String userId) { try { - if (StringUtils.equals(request.getRunModeConfig().getRunMode(), ApiBatchRunMode.PARALLEL.name())) { + if (isParallel(request.getRunModeConfig().getRunMode())) { parallelExecute(request, userId); } else { serialExecute(request, userId); @@ -88,6 +90,10 @@ public class ApiScenarioBatchRunService { } } + private boolean isParallel(String runMode) { + return StringUtils.equals(runMode, ApiBatchRunMode.PARALLEL.name()); + } + /** * 串行批量执行 * @@ -149,42 +155,32 @@ public class ApiScenarioBatchRunService { private Map initReport(List ids, ApiRunModeConfigDTO runModeConfig, String userId) { - Map scenarioReportMap = new HashMap<>(); - Boolean isIntegratedReport = runModeConfig.isIntegratedReport(); - AtomicInteger sort = new AtomicInteger(1); - - List apiScenarioReportSteps = new ArrayList<>(ids.size()); - List apiScenarios = new ArrayList<>(ids.size()); // 分批查询 - SubListUtils.dealForSubList(ids, 100, subIds -> apiScenarios.addAll(extApiScenarioMapper.getScenarioExecuteInfoByIds(subIds))); + List finalApiScenarios = apiScenarios; + SubListUtils.dealForSubList(ids, 100, subIds -> finalApiScenarios.addAll(extApiScenarioMapper.getScenarioExecuteInfoByIds(subIds))); Map apiScenarioMap = apiScenarios.stream() .collect(Collectors.toMap(ApiScenario::getId, Function.identity())); + apiScenarios = new ArrayList<>(ids.size()); - // 这里ID顺序和队列的ID顺序保持一致 for (String id : ids) { + // 按照ID顺序排序 ApiScenario apiScenario = apiScenarioMap.get(id); if (apiScenario == null) { break; } - - if (runModeConfig.isIntegratedReport()) { - // 集合报告初始化一级步骤 - ApiScenarioReportStep apiScenarioReportStep = getApiScenarioReportStep(apiScenario, runModeConfig.getCollectionReport().getReportId(), sort.getAndIncrement()); - apiScenarioReportSteps.add(apiScenarioReportStep); - } else { - // 非集合报告,初始化独立报告,执行时初始化步骤 - String reportId = initScenarioReport(runModeConfig, apiScenario, userId).getApiScenarioReportId(); - scenarioReportMap.put(id, reportId); - } + apiScenarios.add(apiScenario); } - if (CollectionUtils.isNotEmpty(apiScenarioReportSteps)) { - apiScenarioReportService.insertApiScenarioReportStep(apiScenarioReportSteps); + if (runModeConfig.isIntegratedReport()) { + // 集合报告初始化一级步骤 + initApiScenarioReportStep(apiScenarios, runModeConfig.getCollectionReport().getReportId()); + return null; + } else { + // 非集合报告,初始化独立报告,执行时初始化步骤 + return initScenarioReport(runModeConfig, apiScenarios, userId); } - - return isIntegratedReport ? null : scenarioReportMap; } @@ -199,14 +195,21 @@ public class ApiScenarioBatchRunService { } } - private ApiScenarioReportStep getApiScenarioReportStep(ApiScenario apiScenario, String reportId, long sort) { - ApiScenarioReportStep apiReportStep = new ApiScenarioReportStep(); - apiReportStep.setReportId(reportId); - apiReportStep.setStepId(apiScenario.getId()); - apiReportStep.setSort(sort); - apiReportStep.setName(apiScenario.getName()); - apiReportStep.setStepType(ApiExecuteResourceType.API_SCENARIO.name()); - return apiReportStep; + public void initApiScenarioReportStep(List apiScenarios, String reportId) { + AtomicLong sort = new AtomicLong(1); + List apiScenarioReportSteps = new ArrayList<>(apiScenarios.size()); + for (ApiScenario apiScenario : apiScenarios) { + ApiScenarioReportStep apiReportStep = new ApiScenarioReportStep(); + apiReportStep.setReportId(reportId); + apiReportStep.setStepId(apiScenario.getId()); + apiReportStep.setSort(sort.getAndIncrement()); + apiReportStep.setName(apiScenario.getName()); + apiReportStep.setStepType(ApiExecuteResourceType.API_SCENARIO.name()); + apiScenarioReportSteps.add(apiReportStep); + } + if (CollectionUtils.isNotEmpty(apiScenarioReportSteps)) { + apiScenarioReportService.insertApiScenarioReportStep(apiScenarioReportSteps); + } } private ApiRunModeConfigDTO getRunModeConfig(ApiScenarioBatchRunRequest request) { @@ -289,13 +292,25 @@ public class ApiScenarioBatchRunService { return taskRequest; } - private TaskInfo getTaskInfo(String projectId, ApiRunModeConfigDTO runModeConfig) { + public TaskInfo getTaskInfo(String projectId, ApiRunModeConfigDTO runModeConfig) { TaskInfo taskInfo = apiScenarioRunService.getTaskInfo(projectId, ApiExecuteRunMode.RUN.name()); - taskInfo.setSaveResult(true); - taskInfo.setRealTime(false); - taskInfo.setNeedParseScript(true); - taskInfo.setRunModeConfig(runModeConfig); - return taskInfo; + return apiBatchRunBaseService.setBatchRunTaskInfoParam(runModeConfig, taskInfo); + } + + public Map initScenarioReport(ApiRunModeConfigDTO runModeConfig, List apiScenarios, String userId) { + List apiScenarioReports = new ArrayList<>(apiScenarios.size()); + List apiScenarioRecords = new ArrayList<>(apiScenarios.size()); + for (ApiScenario apiScenario : apiScenarios) { + // 初始化报告 + ApiScenarioReport apiScenarioReport = getScenarioReport(runModeConfig, apiScenario, userId); + apiScenarioReport.setId(IDGenerator.nextStr()); + apiScenarioReports.add(apiScenarioReport); + // 创建报告和用例的关联关系 + ApiScenarioRecord apiScenarioRecord = apiScenarioRunService.getApiScenarioRecord(apiScenario, apiScenarioReport); + apiScenarioRecords.add(apiScenarioRecord); + } + apiScenarioReportService.insertApiScenarioReport(apiScenarioReports, apiScenarioRecords); + return apiScenarioRecords.stream().collect(Collectors.toMap(ApiScenarioRecord::getApiScenarioId, ApiScenarioRecord::getApiScenarioReportId)); } /** @@ -335,9 +350,11 @@ public class ApiScenarioBatchRunService { } - public void updateStopOnFailureReport(ExecutionQueue queue) { ApiRunModeConfigDTO runModeConfig = queue.getRunModeConfig(); + if (BooleanUtils.isFalse(runModeConfig.isIntegratedReport())) { + return; + } try { ExecutionQueueDetail queueDetail = apiExecutionQueueService.getNextDetail(queue.getQueueId()); if (queueDetail == null) { @@ -361,19 +378,18 @@ public class ApiScenarioBatchRunService { queueDetail = apiExecutionQueueService.getNextDetail(queue.getQueueId()); } - if (runModeConfig.isIntegratedReport()) { - // 获取未执行的请求数,更新统计指标 - String reportId = runModeConfig.getCollectionReport().getReportId(); - ApiScenarioReport report = apiScenarioReportMapper.selectByPrimaryKey(reportId); - Long pendingCount = requestCount + report.getPendingCount(); - report.setPendingCount(pendingCount); - // 计算各种通过率 - long total = apiScenarioReportService.getRequestTotal(report); - report = apiBatchRunBaseService.computeRequestRate(report, total); - report.setStatus(ReportStatus.ERROR.name()); - report.setExecStatus(ExecStatus.COMPLETED.name()); - apiScenarioReportMapper.updateByPrimaryKeySelective(report); - } + + // 获取未执行的请求数,更新统计指标 + String reportId = runModeConfig.getCollectionReport().getReportId(); + ApiScenarioReport report = apiScenarioReportMapper.selectByPrimaryKey(reportId); + Long pendingCount = requestCount + report.getPendingCount(); + report.setPendingCount(pendingCount); + // 计算各种通过率 + long total = apiScenarioReportService.getRequestTotal(report); + report = apiBatchRunBaseService.computeRequestRate(report, total); + report.setStatus(ReportStatus.ERROR.name()); + report.setExecStatus(ExecStatus.COMPLETED.name()); + apiScenarioReportMapper.updateByPrimaryKeySelective(report); } catch (Exception e) { LogUtils.error("失败停止,补充报告步骤失败:", e); } diff --git a/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiScenarioControllerTests.java b/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiScenarioControllerTests.java index 179dc8c460..bf4f667745 100644 --- a/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiScenarioControllerTests.java +++ b/backend/services/api-test/src/test/java/io/metersphere/api/controller/ApiScenarioControllerTests.java @@ -1197,9 +1197,10 @@ public class ApiScenarioControllerTests extends BaseTest { requestGetPermissionTest(PermissionConstants.PROJECT_API_SCENARIO_EXECUTE, RUN_REAL_TIME, addApiScenario.getId(), "reportId"); } + @Test @Order(6) public void batchRun() throws Exception { - mockPost("/api/run", ""); + mockPost("/api/batch/run", ""); ApiScenarioBatchRunRequest request = new ApiScenarioBatchRunRequest(); List ids = new ArrayList<>(); diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/controller/TestPlanApiCaseController.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/controller/TestPlanApiCaseController.java index c15c75ae08..d0fb4339ad 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/controller/TestPlanApiCaseController.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/controller/TestPlanApiCaseController.java @@ -6,6 +6,7 @@ import io.metersphere.plan.constants.TestPlanResourceConfig; import io.metersphere.plan.dto.request.*; import io.metersphere.plan.dto.response.TestPlanApiCasePageResponse; import io.metersphere.plan.dto.response.TestPlanAssociationResponse; +import io.metersphere.plan.service.TestPlanApiCaseBatchRunService; import io.metersphere.plan.dto.response.TestPlanOperationResponse; import io.metersphere.plan.service.TestPlanApiCaseLogService; import io.metersphere.plan.service.TestPlanApiCaseService; @@ -43,6 +44,8 @@ public class TestPlanApiCaseController { @Resource private TestPlanApiCaseService testPlanApiCaseService; @Resource + private TestPlanApiCaseBatchRunService testPlanApiCaseBatchRunService; + @Resource private TestPlanManagementService testPlanManagementService; @Resource private TestPlanService testPlanService; @@ -128,5 +131,13 @@ public class TestPlanApiCaseController { } + @PostMapping("/batch/run") + @Operation(summary = "批量执行") + @RequiresPermissions(PermissionConstants.TEST_PLAN_READ_EXECUTE) +// @CheckOwner(resourceId = "#request.getId()", resourceType = "test_plan_api_case") todo + public void batchRun(@Validated @RequestBody TestPlanApiCaseBatchRunRequest request) { + testPlanApiCaseBatchRunService.asyncBatchRun(request, SessionUtils.getUserId()); + } + //TODO 批量移动 (计划集内) } diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/controller/TestPlanApiScenarioController.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/controller/TestPlanApiScenarioController.java index 9323c4d3c3..62a28ca1bd 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/controller/TestPlanApiScenarioController.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/controller/TestPlanApiScenarioController.java @@ -7,6 +7,10 @@ import io.metersphere.plan.dto.request.TestPlanApiScenarioModuleRequest; import io.metersphere.plan.dto.request.TestPlanApiScenarioRequest; import io.metersphere.plan.dto.request.TestPlanApiScenarioTreeRequest; import io.metersphere.plan.dto.response.TestPlanApiScenarioPageResponse; +import io.metersphere.plan.dto.request.TestPlanApiCaseBatchRunRequest; +import io.metersphere.plan.dto.request.TestPlanApiScenarioBatchRunRequest; +import io.metersphere.plan.service.TestPlanApiCaseBatchRunService; +import io.metersphere.plan.service.TestPlanApiScenarioBatchRunService; import io.metersphere.plan.service.TestPlanApiScenarioService; import io.metersphere.sdk.constants.PermissionConstants; import io.metersphere.sdk.dto.api.task.TaskRequestDTO; @@ -33,15 +37,8 @@ public class TestPlanApiScenarioController { @Resource private TestPlanApiScenarioService testPlanApiScenarioService; - - @GetMapping("/run/{id}") - @Operation(summary = "接口测试-接口场景管理-场景执行") - @RequiresPermissions(PermissionConstants.TEST_PLAN_READ_EXECUTE) -// @CheckOwner(resourceId = "#id", resourceType = "test_plan_api_scenario") - public TaskRequestDTO run(@PathVariable String id, @RequestParam(required = false) String reportId) { - return testPlanApiScenarioService.run(id, reportId, SessionUtils.getUserId()); - } - + @Resource + private TestPlanApiScenarioBatchRunService testPlanApiScenarioBatchRunService; @PostMapping("/page") @Operation(summary = "测试计划-已关联场景用例列表分页查询") @@ -68,4 +65,20 @@ public class TestPlanApiScenarioController { public List getTree(@Validated @RequestBody TestPlanApiScenarioTreeRequest request) { return testPlanApiScenarioService.getTree(request); } + + @GetMapping("/run/{id}") + @Operation(summary = "接口测试-接口场景管理-场景执行") + @RequiresPermissions(PermissionConstants.TEST_PLAN_READ_EXECUTE) +// @CheckOwner(resourceId = "#id", resourceType = "test_plan_api_scenario") + public TaskRequestDTO run(@PathVariable String id, @RequestParam(required = false) String reportId) { + return testPlanApiScenarioService.run(id, reportId, SessionUtils.getUserId()); + } + + @PostMapping("/batch/run") + @Operation(summary = "批量执行") + @RequiresPermissions(PermissionConstants.TEST_PLAN_READ_EXECUTE) +// @CheckOwner(resourceId = "#request.getId()", resourceType = "test_plan_api_case") todo + public void batchRun(@Validated @RequestBody TestPlanApiScenarioBatchRunRequest request) { + testPlanApiScenarioBatchRunService.asyncBatchRun(request, SessionUtils.getUserId()); + } } diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/request/TestPlanApiCaseBatchRunRequest.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/request/TestPlanApiCaseBatchRunRequest.java new file mode 100644 index 0000000000..48aca6798d --- /dev/null +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/request/TestPlanApiCaseBatchRunRequest.java @@ -0,0 +1,16 @@ +package io.metersphere.plan.dto.request; + +import io.metersphere.api.dto.ApiRunModeRequest; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; + +@Data +@EqualsAndHashCode(callSuper = false) +public class TestPlanApiCaseBatchRunRequest extends TestPlanApiCaseBatchRequest implements Serializable { + + private static final long serialVersionUID = 1L; + + private ApiRunModeRequest runModeConfig; +} diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/request/TestPlanApiScenarioBatchRunRequest.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/request/TestPlanApiScenarioBatchRunRequest.java new file mode 100644 index 0000000000..260361c112 --- /dev/null +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/dto/request/TestPlanApiScenarioBatchRunRequest.java @@ -0,0 +1,20 @@ +package io.metersphere.plan.dto.request; + +import io.metersphere.api.dto.ApiRunModeRequest; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; + +@Data +@EqualsAndHashCode(callSuper = false) +public class TestPlanApiScenarioBatchRunRequest extends BasePlanCaseBatchRequest implements Serializable { + + private static final long serialVersionUID = 1L; + + @Valid + @Schema(description = "运行模式配置") + private ApiRunModeRequest runModeConfig; +} diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiCaseMapper.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiCaseMapper.java index 5e4a43abbc..580310beab 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiCaseMapper.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiCaseMapper.java @@ -65,4 +65,6 @@ public interface ExtTestPlanApiCaseMapper { * @return 计划功能用例集合 */ List getPlanApiCaseByIds(@Param("planIds") List planIds); + + List getApiCaseExecuteInfoByIds(@Param("ids") List ids); } diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiCaseMapper.xml b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiCaseMapper.xml index a860bd2d65..0f784ee459 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiCaseMapper.xml +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiCaseMapper.xml @@ -670,4 +670,13 @@ + + \ No newline at end of file diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiScenarioMapper.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiScenarioMapper.java index 3efb29d8cc..8bf175b37a 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiScenarioMapper.java +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiScenarioMapper.java @@ -55,4 +55,6 @@ public interface ExtTestPlanApiScenarioMapper { * @return 计划功能用例集合 */ List getPlanApiScenarioByIds(@Param("planIds") List planIds); + + List getScenarioExecuteInfoByIds(@Param("ids") List ids); } diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiScenarioMapper.xml b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiScenarioMapper.xml index 7515320578..7503055d69 100644 --- a/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiScenarioMapper.xml +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/mapper/ExtTestPlanApiScenarioMapper.xml @@ -386,4 +386,13 @@ + + \ No newline at end of file diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiCaseBatchRunService.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiCaseBatchRunService.java new file mode 100644 index 0000000000..2fd51976f5 --- /dev/null +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiCaseBatchRunService.java @@ -0,0 +1,271 @@ +package io.metersphere.plan.service; + +import io.metersphere.api.domain.ApiReport; +import io.metersphere.api.domain.ApiReportStep; +import io.metersphere.api.domain.ApiTestCase; +import io.metersphere.api.domain.ApiTestCaseRecord; +import io.metersphere.api.mapper.ApiTestCaseMapper; +import io.metersphere.api.mapper.ExtApiTestCaseMapper; +import io.metersphere.api.service.ApiBatchRunBaseService; +import io.metersphere.api.service.ApiExecuteService; +import io.metersphere.api.service.definition.ApiReportService; +import io.metersphere.api.service.definition.ApiTestCaseBatchRunService; +import io.metersphere.api.service.definition.ApiTestCaseService; +import io.metersphere.api.service.queue.ApiExecutionQueueService; +import io.metersphere.plan.domain.TestPlan; +import io.metersphere.plan.domain.TestPlanApiCase; +import io.metersphere.plan.dto.request.TestPlanApiCaseBatchRunRequest; +import io.metersphere.plan.mapper.ExtTestPlanApiCaseMapper; +import io.metersphere.plan.mapper.TestPlanApiCaseMapper; +import io.metersphere.plan.mapper.TestPlanMapper; +import io.metersphere.sdk.constants.ApiBatchRunMode; +import io.metersphere.sdk.constants.ApiExecuteResourceType; +import io.metersphere.sdk.dto.api.task.*; +import io.metersphere.sdk.dto.queue.ExecutionQueue; +import io.metersphere.sdk.dto.queue.ExecutionQueueDetail; +import io.metersphere.sdk.util.BeanUtils; +import io.metersphere.sdk.util.LogUtils; +import io.metersphere.sdk.util.SubListUtils; +import jakarta.annotation.Resource; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +@Service +@Transactional(rollbackFor = Exception.class) +public class TestPlanApiCaseBatchRunService { + @Resource + private ApiTestCaseMapper apiTestCaseMapper; + @Resource + private TestPlanApiCaseMapper testPlanApiCaseMapper; + @Resource + private ExtApiTestCaseMapper extApiTestCaseMapper; + @Resource + private ExtTestPlanApiCaseMapper extTestPlanApiCaseMapper; + @Resource + private TestPlanApiCaseService testPlanApiCaseService; + @Resource + private ApiExecuteService apiExecuteService; + @Resource + private ApiExecutionQueueService apiExecutionQueueService; + @Resource + private ApiReportService apiReportService; + @Resource + private ApiTestCaseBatchRunService apiTestCaseBatchRunService; + @Resource + private ApiBatchRunBaseService apiBatchRunBaseService; + @Resource + private ApiTestCaseService apiTestCaseService; + @Resource + private TestPlanMapper testPlanMapper; + + /** + * 异步批量执行 + * + * @param request + * @param userId + */ + public void asyncBatchRun(TestPlanApiCaseBatchRunRequest request, String userId) { + Thread.startVirtualThread(() -> batchRun(request, userId)); + } + + /** + * 批量执行 + * + * @param request + * @param userId + */ + private void batchRun(TestPlanApiCaseBatchRunRequest request, String userId) { + try { + if (StringUtils.equals(request.getRunModeConfig().getRunMode(), ApiBatchRunMode.PARALLEL.name())) { + parallelExecute(request, userId); + } else { + serialExecute(request, userId); + } + } catch (Exception e) { + LogUtils.error("批量执行用例失败: ", e); + } + } + + /** + * 串行批量执行 + * + * @param request + */ + public void serialExecute(TestPlanApiCaseBatchRunRequest request, String userId) throws Exception { + List ids = testPlanApiCaseService.doSelectIds(request); + // todo 查询测试规划 + ApiRunModeConfigDTO runModeConfig = getRunModeConfig(request); + + // 先初始化集成报告,设置好报告ID,再初始化执行队列 + ExecutionQueue queue = apiBatchRunBaseService.initExecutionqueue(ids, runModeConfig, ApiExecuteResourceType.API_CASE.name(), userId); + + // 执行第一个任务 + ExecutionQueueDetail nextDetail = apiExecutionQueueService.getNextDetail(queue.getQueueId()); + + executeNextTask(queue, nextDetail); + } + + /** + * 并行批量执行 + * + * @param request + */ + public void parallelExecute(TestPlanApiCaseBatchRunRequest request, String userId) { + List ids = testPlanApiCaseService.doSelectIds(request); + + // todo 查询测试规划 + ApiRunModeConfigDTO runModeConfig = getRunModeConfig(request); + + List testPlanApiCases = new ArrayList<>(ids.size()); + List apiTestCases = new ArrayList<>(ids.size()); + + // 分批查询 + SubListUtils.dealForSubList(ids, 100, subIds -> testPlanApiCases.addAll(extTestPlanApiCaseMapper.getApiCaseExecuteInfoByIds(subIds))); + + List caseIds = testPlanApiCases.stream() + .map(TestPlanApiCase::getApiCaseId).collect(Collectors.toList()); + + // 分批查询 + SubListUtils.dealForSubList(caseIds, 100, subIds -> apiTestCases.addAll(extApiTestCaseMapper.getApiCaseExecuteInfoByIds(subIds))); + + Map apiCaseMap = apiTestCases.stream() + .collect(Collectors.toMap(ApiTestCase::getId, Function.identity())); + + // 初始化报告,返回用例和报告的 map + Map caseReportMap = initParallelReport(runModeConfig, testPlanApiCases, apiCaseMap, userId); + + List taskItems = new ArrayList<>(ids.size()); + + // 这里ID顺序和队列的ID顺序保持一致 + for (String id : ids) { + String reportId = caseReportMap.get(id); + TaskItem taskItem = apiExecuteService.getTaskItem(reportId, id); + taskItem.setRequestCount(1L); + taskItems.add(taskItem); + } + + TestPlan testPlan = testPlanMapper.selectByPrimaryKey(request.getTestPlanId()); + + TaskBatchRequestDTO taskRequest = getTaskBatchRequestDTO(testPlan.getProjectId(), runModeConfig); + taskRequest.setTaskItems(taskItems); + apiExecuteService.batchExecute(taskRequest); + } + + private Map initParallelReport(ApiRunModeConfigDTO runModeConfig, List testPlanApiCases, Map caseMap, String userId) { + // 初始化非集成报告 + List apiTestCaseRecords = initApiReport(runModeConfig, testPlanApiCases, caseMap, userId); + return apiTestCaseRecords.stream() + .collect(Collectors.toMap(ApiTestCaseRecord::getApiTestCaseId, ApiTestCaseRecord::getApiReportId)); + } + + private ApiReportStep getApiReportStep(ApiTestCase apiTestCase, String reportId, long sort) { + ApiReportStep apiReportStep = new ApiReportStep(); + apiReportStep.setReportId(reportId); + apiReportStep.setStepId(apiTestCase.getId()); + apiReportStep.setSort(sort); + apiReportStep.setName(apiTestCase.getName()); + apiReportStep.setStepType(ApiExecuteResourceType.API_CASE.name()); + return apiReportStep; + } + + private ApiRunModeConfigDTO getRunModeConfig(TestPlanApiCaseBatchRunRequest request) { + ApiRunModeConfigDTO runModeConfig = BeanUtils.copyBean(new ApiRunModeConfigDTO(), request.getRunModeConfig()); + return runModeConfig; + } + + /** + * 执行串行的下一个任务 + * + * @param queue + * @param queueDetail + */ + public void executeNextTask(ExecutionQueue queue, ExecutionQueueDetail queueDetail) { + ApiRunModeConfigDTO runModeConfig = queue.getRunModeConfig(); + String resourceId = queueDetail.getResourceId(); + + TestPlanApiCase testPlanApiCase = testPlanApiCaseMapper.selectByPrimaryKey(resourceId); + + if (testPlanApiCase == null) { + LogUtils.info("当前执行任务的用例已删除 {}", resourceId); + return; + } + + ApiTestCase apiTestCase = apiTestCaseMapper.selectByPrimaryKey(testPlanApiCase.getApiCaseId()); + + // 独立报告,执行到当前任务时初始化报告 + String reportId = initApiReport(runModeConfig, List.of(testPlanApiCase), Map.of(apiTestCase.getId(), apiTestCase), queue.getUserId()).get(0).getApiReportId(); + TaskRequestDTO taskRequest = getTaskRequestDTO(reportId, apiTestCase, runModeConfig); + taskRequest.getTaskInfo().setQueueId(queue.getQueueId()); + taskRequest.getTaskItem().setRequestCount(1L); + + apiExecuteService.execute(taskRequest); + } + + private TaskRequestDTO getTaskRequestDTO(String reportId, ApiTestCase apiTestCase, ApiRunModeConfigDTO runModeConfig) { + TaskRequestDTO taskRequest = new TaskRequestDTO(); + TaskItem taskItem = apiExecuteService.getTaskItem(reportId, apiTestCase.getId()); + TaskInfo taskInfo = getTaskInfo(apiTestCase.getProjectId(), runModeConfig); + taskRequest.setTaskInfo(taskInfo); + taskRequest.setTaskItem(taskItem); + return taskRequest; + } + + private TaskBatchRequestDTO getTaskBatchRequestDTO(String projectId, ApiRunModeConfigDTO runModeConfig) { + TaskBatchRequestDTO taskRequest = new TaskBatchRequestDTO(); + TaskInfo taskInfo = getTaskInfo(projectId, runModeConfig); + taskRequest.setTaskInfo(taskInfo); + return taskRequest; + } + + private TaskInfo getTaskInfo(String projectId, ApiRunModeConfigDTO runModeConfig) { + TaskInfo taskInfo = apiTestCaseBatchRunService.getTaskInfo(projectId, runModeConfig); + taskInfo.setResourceType(ApiExecuteResourceType.TEST_PLAN_API_CASE.name()); + return taskInfo; + } + + /** + * 预生成用例的执行报告 + * + * @param runModeConfig + * @return + */ + public List initApiReport(ApiRunModeConfigDTO runModeConfig, List testPlanApiCases, + Map caseMap, String userId) { + List apiReports = new ArrayList<>(); + List apiTestCaseRecords = new ArrayList<>(); + List apiReportSteps = new ArrayList<>(); + + for (TestPlanApiCase testPlanApiCase : testPlanApiCases) { + ApiTestCase apiTestCase = caseMap.get(testPlanApiCase.getApiCaseId()); + // 初始化报告 + ApiReport apiReport = getApiReport(runModeConfig, testPlanApiCase, apiTestCase, userId); + apiReports.add(apiReport); + // 创建报告和用例的关联关系 + ApiTestCaseRecord apiTestCaseRecord = apiTestCaseService.getApiTestCaseRecord(apiTestCase, apiReport); + apiTestCaseRecords.add(apiTestCaseRecord); + apiReportSteps.add(getApiReportStep(apiTestCase, apiReport.getId(), 1)); + } + apiReportService.insertApiReport(apiReports, apiTestCaseRecords); + apiReportService.insertApiReportStep(apiReportSteps); + return apiTestCaseRecords; + } + + + private ApiReport getApiReport(ApiRunModeConfigDTO runModeConfig, TestPlanApiCase testPlanApiCase, ApiTestCase apiTestCase, String userId) { + ApiReport apiReport = apiTestCaseBatchRunService.getApiReport(runModeConfig, apiTestCase, userId); + apiReport.setEnvironmentId(getEnvId(runModeConfig, testPlanApiCase)); + apiReport.setTestPlanCaseId(testPlanApiCase.getId()); + return apiReport; + } + + public String getEnvId(ApiRunModeConfigDTO runModeConfig, TestPlanApiCase testPlanApiCase) { + return StringUtils.isBlank(runModeConfig.getEnvironmentId()) ? testPlanApiCase.getEnvironmentId() : runModeConfig.getEnvironmentId(); + } +} diff --git a/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiScenarioBatchRunService.java b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiScenarioBatchRunService.java new file mode 100644 index 0000000000..bce037c6e0 --- /dev/null +++ b/backend/services/test-plan/src/main/java/io/metersphere/plan/service/TestPlanApiScenarioBatchRunService.java @@ -0,0 +1,279 @@ +package io.metersphere.plan.service; + +import io.metersphere.api.domain.ApiScenario; +import io.metersphere.api.domain.ApiScenarioRecord; +import io.metersphere.api.domain.ApiScenarioReport; +import io.metersphere.api.mapper.ApiScenarioMapper; +import io.metersphere.api.mapper.ExtApiScenarioMapper; +import io.metersphere.api.service.ApiBatchRunBaseService; +import io.metersphere.api.service.ApiExecuteService; +import io.metersphere.api.service.queue.ApiExecutionQueueService; +import io.metersphere.api.service.scenario.ApiScenarioBatchRunService; +import io.metersphere.api.service.scenario.ApiScenarioReportService; +import io.metersphere.api.service.scenario.ApiScenarioRunService; +import io.metersphere.plan.domain.TestPlan; +import io.metersphere.plan.domain.TestPlanApiScenario; +import io.metersphere.plan.dto.request.TestPlanApiScenarioBatchRunRequest; +import io.metersphere.plan.mapper.ExtTestPlanApiScenarioMapper; +import io.metersphere.plan.mapper.TestPlanApiScenarioMapper; +import io.metersphere.plan.mapper.TestPlanMapper; +import io.metersphere.sdk.constants.ApiBatchRunMode; +import io.metersphere.sdk.constants.ApiExecuteResourceType; +import io.metersphere.sdk.constants.TaskTriggerMode; +import io.metersphere.sdk.dto.api.task.*; +import io.metersphere.sdk.dto.queue.ExecutionQueue; +import io.metersphere.sdk.dto.queue.ExecutionQueueDetail; +import io.metersphere.sdk.util.BeanUtils; +import io.metersphere.sdk.util.DateUtils; +import io.metersphere.sdk.util.LogUtils; +import io.metersphere.sdk.util.SubListUtils; +import io.metersphere.system.uid.IDGenerator; +import jakarta.annotation.Resource; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + + +@Service +@Transactional(rollbackFor = Exception.class) +public class TestPlanApiScenarioBatchRunService { + @Resource + private TestPlanApiScenarioService testPlanApiScenarioService; + @Resource + private ApiExecuteService apiExecuteService; + @Resource + private ApiExecutionQueueService apiExecutionQueueService; + @Resource + private ApiScenarioReportService apiScenarioReportService; + @Resource + private ApiScenarioBatchRunService apiScenarioBatchRunService; + @Resource + private ApiBatchRunBaseService apiBatchRunBaseService; + @Resource + private ExtApiScenarioMapper extApiScenarioMapper; + @Resource + private ExtTestPlanApiScenarioMapper extTestPlanApiScenarioMapper; + @Resource + private ApiScenarioMapper apiScenarioMapper; + @Resource + private TestPlanApiScenarioMapper testPlanApiScenarioMapper; + @Resource + private ApiScenarioRunService apiScenarioRunService; + @Resource + private TestPlanMapper testPlanMapper; + + /** + * 异步批量执行 + * + * @param request + * @param userId + */ + public void asyncBatchRun(TestPlanApiScenarioBatchRunRequest request, String userId) { + Thread.startVirtualThread(() -> batchRun(request, userId)); + } + + /** + * 批量执行 + * + * @param request + * @param userId + */ + private void batchRun(TestPlanApiScenarioBatchRunRequest request, String userId) { + try { + if (StringUtils.equals(request.getRunModeConfig().getRunMode(), ApiBatchRunMode.PARALLEL.name())) { + parallelExecute(request, userId); + } else { + serialExecute(request, userId); + } + } catch (Exception e) { + LogUtils.error("批量执行用例失败: ", e); + } + } + + /** + * 串行批量执行 + * + * @param request + */ + public void serialExecute(TestPlanApiScenarioBatchRunRequest request, String userId) throws Exception { +// List ids = testPlanApiScenarioService.doSelectIds(request, false); //todo + List ids = request.getSelectIds(); + // todo 查询测试规划 + ApiRunModeConfigDTO runModeConfig = getRunModeConfig(request); + + // 先初始化集成报告,设置好报告ID,再初始化执行队列 + ExecutionQueue queue = apiBatchRunBaseService.initExecutionqueue(ids, runModeConfig, ApiExecuteResourceType.TEST_PLAN_API_SCENARIO.name(), userId); + // 执行第一个任务 + ExecutionQueueDetail nextDetail = apiExecutionQueueService.getNextDetail(queue.getQueueId()); + executeNextTask(queue, nextDetail); + } + + /** + * 并行批量执行 + * + * @param request + */ + public void parallelExecute(TestPlanApiScenarioBatchRunRequest request, String userId) { +// List ids = testPlanApiScenarioService.doSelectIds(request, false); //todo + List ids = request.getSelectIds(); + + // todo 查询测试规划 + ApiRunModeConfigDTO runModeConfig = getRunModeConfig(request); + + Map scenarioReportMap = initReport(ids, runModeConfig, userId); + + List taskItems = ids.stream() + .map(id -> apiExecuteService.getTaskItem(scenarioReportMap.get(id), id)).toList(); + + TestPlan testPlan = testPlanMapper.selectByPrimaryKey(request.getTestPlanId()); + + TaskBatchRequestDTO taskRequest = getTaskBatchRequestDTO(testPlan.getProjectId(), runModeConfig); + taskRequest.setTaskItems(taskItems); + + apiExecuteService.batchExecute(taskRequest); + } + + private ApiRunModeConfigDTO getRunModeConfig(TestPlanApiScenarioBatchRunRequest request) { + ApiRunModeConfigDTO runModeConfig = BeanUtils.copyBean(new ApiRunModeConfigDTO(), request.getRunModeConfig()); + return runModeConfig; + } + + private Map initReport(List ids, ApiRunModeConfigDTO runModeConfig, String userId) { + List testPlanApiScenarios = new ArrayList<>(ids.size()); + + List apiScenarios = new ArrayList<>(ids.size()); + // 分批查询 + List finalTestPlanApiScenarios = testPlanApiScenarios; + SubListUtils.dealForSubList(ids, 100, subIds -> finalTestPlanApiScenarios.addAll(extTestPlanApiScenarioMapper.getScenarioExecuteInfoByIds(subIds))); + + List caseIds = testPlanApiScenarios.stream().map(TestPlanApiScenario::getApiScenarioId).toList(); + List finalApiScenarios = apiScenarios; + SubListUtils.dealForSubList(caseIds, 100, subIds -> finalApiScenarios.addAll(extApiScenarioMapper.getScenarioExecuteInfoByIds(subIds))); + + Map apiScenarioMap = apiScenarios.stream() + .collect(Collectors.toMap(ApiScenario::getId, Function.identity())); + + Map testPlanApiScenarioMap = testPlanApiScenarios.stream() + .collect(Collectors.toMap(TestPlanApiScenario::getId, Function.identity())); + + testPlanApiScenarios = new ArrayList<>(ids.size()); + for (String id : ids) { + // 按照ID顺序排序 + TestPlanApiScenario testPlanApiScenario = testPlanApiScenarioMap.get(id); + if (testPlanApiScenario == null) { + break; + } + testPlanApiScenarios.add(testPlanApiScenario); + } + // 初始化独立报告,执行时初始化步骤 + return initScenarioReport(runModeConfig, testPlanApiScenarios, apiScenarioMap, userId); + } + + public Map initScenarioReport(ApiRunModeConfigDTO runModeConfig, List testPlanApiScenarios, + Map apiScenarioMap, String userId) { + List apiScenarioReports = new ArrayList<>(testPlanApiScenarios.size()); + List apiScenarioRecords = new ArrayList<>(testPlanApiScenarios.size()); + for (TestPlanApiScenario testPlanApiScenario : testPlanApiScenarios) { + ApiScenario apiScenario = apiScenarioMap.get(testPlanApiScenario.getApiScenarioId()); + // 初始化报告 + ApiScenarioReport apiScenarioReport = getScenarioReport(runModeConfig, testPlanApiScenario, apiScenario, userId); + apiScenarioReport.setId(IDGenerator.nextStr()); + apiScenarioReports.add(apiScenarioReport); + // 创建报告和用例的关联关系 + ApiScenarioRecord apiScenarioRecord = apiScenarioRunService.getApiScenarioRecord(apiScenario, apiScenarioReport); + apiScenarioRecords.add(apiScenarioRecord); + } + apiScenarioReportService.insertApiScenarioReport(apiScenarioReports, apiScenarioRecords); + return apiScenarioRecords.stream().collect(Collectors.toMap(ApiScenarioRecord::getApiScenarioId, ApiScenarioRecord::getApiScenarioReportId)); + } + + /** + * 执行串行的下一个任务 + * + * @param queue + * @param queueDetail + */ + public void executeNextTask(ExecutionQueue queue, ExecutionQueueDetail queueDetail) { + ApiRunModeConfigDTO runModeConfig = queue.getRunModeConfig(); + TestPlanApiScenario testPlanApiScenario = testPlanApiScenarioMapper.selectByPrimaryKey(queueDetail.getResourceId()); + ApiScenario apiScenario = apiScenarioMapper.selectByPrimaryKey(testPlanApiScenario.getApiScenarioId()); + + // 独立报告,执行到当前任务时初始化报告 + String reportId = initScenarioReport(runModeConfig, testPlanApiScenario, apiScenario, queue.getUserId()).getApiScenarioReportId(); + + TaskRequestDTO taskRequest = getTaskRequestDTO(apiScenario.getProjectId(), queue.getRunModeConfig()); + TaskItem taskItem = apiExecuteService.getTaskItem(reportId, queueDetail.getResourceId()); + taskRequest.setTaskItem(taskItem); + taskRequest.getTaskInfo().setQueueId(queue.getQueueId()); + + apiExecuteService.execute(taskRequest); + } + + private TaskRequestDTO getTaskRequestDTO(String projectId, ApiRunModeConfigDTO runModeConfig) { + TaskRequestDTO taskRequest = new TaskRequestDTO(); + TaskInfo taskInfo = getTaskInfo(projectId, runModeConfig); + taskRequest.setTaskInfo(taskInfo); + return taskRequest; + } + + private TaskBatchRequestDTO getTaskBatchRequestDTO(String projectId, ApiRunModeConfigDTO runModeConfig) { + TaskBatchRequestDTO taskRequest = new TaskBatchRequestDTO(); + TaskInfo taskInfo = getTaskInfo(projectId, runModeConfig); + taskRequest.setTaskInfo(taskInfo); + return taskRequest; + } + + private TaskInfo getTaskInfo(String projectId, ApiRunModeConfigDTO runModeConfig) { + TaskInfo taskInfo = apiScenarioBatchRunService.getTaskInfo(projectId, runModeConfig); + taskInfo.setResourceType(ApiExecuteResourceType.TEST_PLAN_API_SCENARIO.name()); + return taskInfo; + } + + /** + * 预生成用例的执行报告 + * + * @param runModeConfig + * @param apiScenario + * @return + */ + public ApiScenarioRecord initScenarioReport(ApiRunModeConfigDTO runModeConfig, TestPlanApiScenario testPlanApiScenario, + ApiScenario apiScenario, String userId) { + // 初始化报告 + ApiScenarioReport apiScenarioReport = getScenarioReport(runModeConfig, testPlanApiScenario, apiScenario, userId); + apiScenarioReport.setId(IDGenerator.nextStr()); + // 创建报告和用例的关联关系 + ApiScenarioRecord apiScenarioRecord = apiScenarioRunService.getApiScenarioRecord(apiScenario, apiScenarioReport); + apiScenarioReportService.insertApiScenarioReport(List.of(apiScenarioReport), List.of(apiScenarioRecord)); + return apiScenarioRecord; + } + + public String getEnvId(ApiRunModeConfigDTO runModeConfig, TestPlanApiScenario testPlanApiScenario) { + return StringUtils.isBlank(runModeConfig.getEnvironmentId()) ? testPlanApiScenario.getEnvironmentId() : runModeConfig.getEnvironmentId(); + } + + private ApiScenarioReport getScenarioReport(ApiRunModeConfigDTO runModeConfig, TestPlanApiScenario testPlanApiScenario, ApiScenario apiScenario, String userId) { + ApiScenarioReport apiScenarioReport = getScenarioReport(runModeConfig, userId); + apiScenarioReport.setEnvironmentId(apiScenarioRunService.getEnvId(runModeConfig, apiScenario)); + apiScenarioReport.setName(apiScenario.getName() + "_" + DateUtils.getTimeString(System.currentTimeMillis())); + apiScenarioReport.setProjectId(apiScenario.getProjectId()); + apiScenarioReport.setTriggerMode(TaskTriggerMode.BATCH.name()); + apiScenarioReport.setTestPlanScenarioId(testPlanApiScenario.getId()); + apiScenarioReport.setEnvironmentId(getEnvId(runModeConfig, testPlanApiScenario)); + return apiScenarioReport; + } + + public ApiScenarioReport getScenarioReport(ApiRunModeConfigDTO runModeConfig, String userId) { + ApiScenarioReport apiScenarioReport = apiScenarioRunService.getScenarioReport(userId); + apiScenarioReport.setEnvironmentId(runModeConfig.getEnvironmentId()); + apiScenarioReport.setRunMode(runModeConfig.getRunMode()); + apiScenarioReport.setPoolId(runModeConfig.getPoolId()); + apiScenarioReport.setTriggerMode(TaskTriggerMode.BATCH.name()); + return apiScenarioReport; + } +} diff --git a/backend/services/test-plan/src/test/java/io/metersphere/plan/controller/TestPlanApiCaseControllerTests.java b/backend/services/test-plan/src/test/java/io/metersphere/plan/controller/TestPlanApiCaseControllerTests.java index 99168be068..72e7bef329 100644 --- a/backend/services/test-plan/src/test/java/io/metersphere/plan/controller/TestPlanApiCaseControllerTests.java +++ b/backend/services/test-plan/src/test/java/io/metersphere/plan/controller/TestPlanApiCaseControllerTests.java @@ -5,6 +5,7 @@ import io.metersphere.api.constants.ApiDefinitionStatus; import io.metersphere.api.controller.result.ApiResultCode; import io.metersphere.api.domain.ApiDefinition; import io.metersphere.api.domain.ApiTestCase; +import io.metersphere.api.dto.ApiRunModeRequest; import io.metersphere.api.dto.definition.ApiDefinitionAddRequest; import io.metersphere.api.dto.definition.ApiTestCaseAddRequest; import io.metersphere.api.dto.request.http.MsHTTPElement; @@ -21,6 +22,7 @@ import io.metersphere.plan.dto.response.TestPlanOperationResponse; import io.metersphere.plan.mapper.TestPlanApiCaseMapper; import io.metersphere.plan.service.TestPlanApiCaseService; import io.metersphere.project.mapper.ExtBaseProjectVersionMapper; +import io.metersphere.sdk.constants.ApiBatchRunMode; import io.metersphere.sdk.constants.PermissionConstants; import io.metersphere.sdk.dto.api.task.GetRunScriptRequest; import io.metersphere.sdk.dto.api.task.TaskItem; @@ -56,6 +58,7 @@ public class TestPlanApiCaseControllerTests extends BaseTest { public static final String API_CASE_BATCH_UPDATE_EXECUTOR_URL = "batch/update/executor"; private static final String URL_POST_RESOURCE_API_CASE_SORT = "/sort"; public static final String RUN = "run/{0}"; + public static final String BATCH_RUN = "batch/run"; public static final String RUN_WITH_REPORT_ID = "run/{0}?reportId={1}"; @Resource @@ -274,6 +277,35 @@ public class TestPlanApiCaseControllerTests extends BaseTest { requestGetPermissionTest(PermissionConstants.TEST_PLAN_READ_EXECUTE, RUN, testPlanApiCase.getId()); } + @Test + @Order(7) + public void batchRun() throws Exception { + TestPlanApiCaseBatchRunRequest request = new TestPlanApiCaseBatchRunRequest(); + request.setSelectIds(List.of(testPlanApiCase.getId())); + request.setTestPlanId(testPlanApiCase.getTestPlanId()); + ApiRunModeRequest apiRunModeRequest = new ApiRunModeRequest(); + apiRunModeRequest.setRunMode(ApiBatchRunMode.PARALLEL.name()); + apiRunModeRequest.setStopOnFailure(false); + apiRunModeRequest.setPoolId("poolId"); + request.setRunModeConfig(apiRunModeRequest); + this.requestPostWithOk(BATCH_RUN, request); + request.setProtocols(List.of("HTTP")); + this.requestPostWithOk(BATCH_RUN, request); + + apiRunModeRequest.setIntegratedReport(false); + apiRunModeRequest.setStopOnFailure(true); + this.requestPostWithOk(BATCH_RUN, request); + + apiRunModeRequest.setRunMode(ApiBatchRunMode.SERIAL.name()); + this.requestPostWithOk(BATCH_RUN, request); + + apiRunModeRequest.setIntegratedReport(true); + this.requestPostWithOk(BATCH_RUN, request); + + // @@校验权限 + requestPostPermissionTest(PermissionConstants.TEST_PLAN_READ_EXECUTE, BATCH_RUN, request); + } + public ApiTestCase initApiData() { ApiDefinitionAddRequest apiDefinitionAddRequest = createApiDefinitionAddRequest(); MsHTTPElement msHttpElement = new MsHTTPElement(); diff --git a/backend/services/test-plan/src/test/java/io/metersphere/plan/controller/TestPlanApiScenarioControllerTests.java b/backend/services/test-plan/src/test/java/io/metersphere/plan/controller/TestPlanApiScenarioControllerTests.java index 3deaf09249..75d41b369f 100644 --- a/backend/services/test-plan/src/test/java/io/metersphere/plan/controller/TestPlanApiScenarioControllerTests.java +++ b/backend/services/test-plan/src/test/java/io/metersphere/plan/controller/TestPlanApiScenarioControllerTests.java @@ -5,17 +5,16 @@ import io.metersphere.api.constants.ApiScenarioStepRefType; import io.metersphere.api.constants.ApiScenarioStepType; import io.metersphere.api.controller.result.ApiResultCode; import io.metersphere.api.domain.ApiScenario; +import io.metersphere.api.dto.ApiRunModeRequest; import io.metersphere.api.dto.assertion.MsAssertionConfig; import io.metersphere.api.dto.request.http.MsHTTPElement; import io.metersphere.api.dto.request.http.body.Body; import io.metersphere.api.dto.request.http.body.RawBody; -import io.metersphere.api.dto.scenario.ApiScenarioAddRequest; -import io.metersphere.api.dto.scenario.ApiScenarioStepRequest; -import io.metersphere.api.dto.scenario.ScenarioConfig; -import io.metersphere.api.dto.scenario.ScenarioOtherConfig; +import io.metersphere.api.dto.scenario.*; import io.metersphere.api.service.scenario.ApiScenarioService; import io.metersphere.api.utils.ApiDataUtils; import io.metersphere.plan.domain.TestPlanApiScenario; +import io.metersphere.plan.dto.request.TestPlanApiScenarioBatchRunRequest; import io.metersphere.plan.dto.request.TestPlanApiScenarioModuleRequest; import io.metersphere.plan.dto.request.TestPlanApiScenarioRequest; import io.metersphere.plan.dto.request.TestPlanApiScenarioTreeRequest; @@ -24,7 +23,9 @@ import io.metersphere.plan.service.TestPlanApiScenarioService; import io.metersphere.project.api.assertion.MsResponseCodeAssertion; import io.metersphere.project.api.assertion.MsScriptAssertion; import io.metersphere.project.mapper.ExtBaseProjectVersionMapper; +import io.metersphere.sdk.constants.ApiBatchRunMode; import io.metersphere.sdk.constants.MsAssertionCondition; +import io.metersphere.sdk.constants.PermissionConstants; import io.metersphere.sdk.dto.api.task.GetRunScriptRequest; import io.metersphere.sdk.dto.api.task.TaskItem; import io.metersphere.sdk.util.JSON; @@ -40,10 +41,7 @@ import org.springframework.test.context.jdbc.SqlConfig; import org.springframework.test.web.servlet.MvcResult; import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; +import java.util.*; import static io.metersphere.system.controller.handler.result.MsHttpResultCode.NOT_FOUND; @@ -58,6 +56,7 @@ public class TestPlanApiScenarioControllerTests extends BaseTest { public static final String API_SCENARIO_PAGE = "page"; public static final String API_SCENARIO_TREE_COUNT = "module/count"; public static final String API_SCENARIO_TREE = "tree"; + public static final String BATCH_RUN = "batch/run"; @Resource private TestPlanApiScenarioService testPlanApiScenarioService; @@ -105,6 +104,40 @@ public class TestPlanApiScenarioControllerTests extends BaseTest { taskItem.setReportId("reportId"); request.setTaskItem(taskItem); testPlanApiScenarioService.getRunScript(request); + + // @@校验权限 + requestGetPermissionTest(PermissionConstants.TEST_PLAN_READ_EXECUTE, RUN, testPlanApiScenario.getId()); + } + + @Test + @Order(2) + public void batchRun() throws Exception { + mockPost("/api/batch/run", ""); + + TestPlanApiScenarioBatchRunRequest request = new TestPlanApiScenarioBatchRunRequest(); + request.setSelectIds(List.of(testPlanApiScenario.getId())); + request.setTestPlanId(testPlanApiScenario.getTestPlanId()); + ApiRunModeRequest apiRunModeRequest = new ApiRunModeRequest(); + apiRunModeRequest.setRunMode(ApiBatchRunMode.PARALLEL.name()); + apiRunModeRequest.setIntegratedReport(true); + apiRunModeRequest.setStopOnFailure(false); + apiRunModeRequest.setIntegratedReportName("aaaa"); + apiRunModeRequest.setPoolId("poolId"); + request.setRunModeConfig(apiRunModeRequest); + this.requestPostWithOk(BATCH_RUN, request); + + apiRunModeRequest.setIntegratedReport(false); + apiRunModeRequest.setStopOnFailure(true); + this.requestPostWithOk(BATCH_RUN, request); + + apiRunModeRequest.setRunMode(ApiBatchRunMode.SERIAL.name()); + this.requestPostWithOk(BATCH_RUN, request); + + apiRunModeRequest.setIntegratedReport(true); + this.requestPostWithOk(BATCH_RUN, request); + + // @@校验权限 + requestPostPermissionTest(PermissionConstants.TEST_PLAN_READ_EXECUTE, BATCH_RUN, request); } public ApiScenario initApiData() {