diff --git a/backend/src/main/java/io/metersphere/api/dto/ApiCaseRunRequest.java b/backend/src/main/java/io/metersphere/api/dto/ApiCaseRunRequest.java index 38822cb5e1..59cb4ef544 100644 --- a/backend/src/main/java/io/metersphere/api/dto/ApiCaseRunRequest.java +++ b/backend/src/main/java/io/metersphere/api/dto/ApiCaseRunRequest.java @@ -1,12 +1,14 @@ package io.metersphere.api.dto; import io.metersphere.api.dto.definition.ApiTestCaseRequest; +import io.metersphere.base.domain.ApiDefinitionExecResultWithBLOBs; import io.metersphere.controller.request.OrderRequest; import io.metersphere.dto.RunModeConfigDTO; import lombok.Getter; import lombok.Setter; import java.util.List; +import java.util.Map; @Getter @Setter @@ -20,4 +22,7 @@ public class ApiCaseRunRequest { private String environmentId; private RunModeConfigDTO config; private ApiTestCaseRequest condition; + // 失败重跑 + private boolean isRerun; + private Map executeQueue; } diff --git a/backend/src/main/java/io/metersphere/api/dto/ApiScenarioReportDTO.java b/backend/src/main/java/io/metersphere/api/dto/ApiScenarioReportDTO.java index 18688114a2..89189e9561 100644 --- a/backend/src/main/java/io/metersphere/api/dto/ApiScenarioReportDTO.java +++ b/backend/src/main/java/io/metersphere/api/dto/ApiScenarioReportDTO.java @@ -32,4 +32,9 @@ public class ApiScenarioReportDTO { private long totalAssertions = 0; private long passAssertions = 0; private long errorCodeAssertions = 0; + + private String actuator; + private String name; + private String envConfig; + } diff --git a/backend/src/main/java/io/metersphere/api/dto/StepTreeDTO.java b/backend/src/main/java/io/metersphere/api/dto/StepTreeDTO.java index ded076f423..50fbdd08eb 100644 --- a/backend/src/main/java/io/metersphere/api/dto/StepTreeDTO.java +++ b/backend/src/main/java/io/metersphere/api/dto/StepTreeDTO.java @@ -18,6 +18,8 @@ public class StepTreeDTO { private RequestResult value; private String allIndex; private String stepId; + // 某个场景未执行总量 + private int unExecuteTotal; //误报库编码 private String errorCode; diff --git a/backend/src/main/java/io/metersphere/api/dto/automation/APIScenarioReportResult.java b/backend/src/main/java/io/metersphere/api/dto/automation/APIScenarioReportResult.java index 0f2b531f08..8a83eb40ba 100644 --- a/backend/src/main/java/io/metersphere/api/dto/automation/APIScenarioReportResult.java +++ b/backend/src/main/java/io/metersphere/api/dto/automation/APIScenarioReportResult.java @@ -1,6 +1,6 @@ package io.metersphere.api.dto.automation; -import io.metersphere.base.domain.ApiScenarioReport; +import io.metersphere.base.domain.ApiScenarioReportWithBLOBs; import lombok.Getter; import lombok.Setter; @@ -8,7 +8,7 @@ import java.util.List; @Setter @Getter -public class APIScenarioReportResult extends ApiScenarioReport { +public class APIScenarioReportResult extends ApiScenarioReportWithBLOBs { private String testName; diff --git a/backend/src/main/java/io/metersphere/api/dto/automation/RunScenarioRequest.java b/backend/src/main/java/io/metersphere/api/dto/automation/RunScenarioRequest.java index 62f555d156..ef859f097b 100644 --- a/backend/src/main/java/io/metersphere/api/dto/automation/RunScenarioRequest.java +++ b/backend/src/main/java/io/metersphere/api/dto/automation/RunScenarioRequest.java @@ -47,4 +47,10 @@ public class RunScenarioRequest { private String testPlanReportId; private String requestOriginator; + + // 失败重跑 + private boolean isRerun; + private String serialReportId; + private Map reportMap; + } diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/BatchRunDefinitionRequest.java b/backend/src/main/java/io/metersphere/api/dto/definition/BatchRunDefinitionRequest.java index 769f29aecc..3f0d209b45 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/BatchRunDefinitionRequest.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/BatchRunDefinitionRequest.java @@ -1,10 +1,12 @@ package io.metersphere.api.dto.definition; +import io.metersphere.base.domain.ApiDefinitionExecResultWithBLOBs; import io.metersphere.dto.RunModeConfigDTO; import lombok.Getter; import lombok.Setter; import java.util.List; +import java.util.Map; @Setter @Getter @@ -21,5 +23,8 @@ public class BatchRunDefinitionRequest { //测试计划报告ID。 测试计划执行时使用 private String planReportId; + // 失败重跑 + private boolean rerun; + private Map executeQueue; } diff --git a/backend/src/main/java/io/metersphere/api/dto/definition/request/ElementUtil.java b/backend/src/main/java/io/metersphere/api/dto/definition/request/ElementUtil.java index 756a1f4030..ce98a361fb 100644 --- a/backend/src/main/java/io/metersphere/api/dto/definition/request/ElementUtil.java +++ b/backend/src/main/java/io/metersphere/api/dto/definition/request/ElementUtil.java @@ -579,6 +579,10 @@ public class ElementUtil { if (StringUtils.isNotEmpty(config.getScenarioId()) && StringUtils.equals(config.getReportType(), RunModeConstants.SET_REPORT.toString())) { resourceId = config.getScenarioId() + "=" + resourceId; } + // 跳过失败重试层 + if (parent != null && StringUtils.equalsAnyIgnoreCase("RetryLoopController", parent.getType())) { + parent = parent.getParent(); + } return resourceId + "_" + ElementUtil.getFullIndexPath(parent, indexPath); } diff --git a/backend/src/main/java/io/metersphere/api/exec/api/ApiCaseExecuteService.java b/backend/src/main/java/io/metersphere/api/exec/api/ApiCaseExecuteService.java index 2782831335..c0c322c357 100644 --- a/backend/src/main/java/io/metersphere/api/exec/api/ApiCaseExecuteService.java +++ b/backend/src/main/java/io/metersphere/api/exec/api/ApiCaseExecuteService.java @@ -15,6 +15,7 @@ import io.metersphere.api.service.ApiExecutionQueueService; import io.metersphere.api.service.ApiScenarioReportStructureService; import io.metersphere.api.service.ApiTestEnvironmentService; import io.metersphere.base.domain.*; +import io.metersphere.base.mapper.ApiDefinitionExecResultMapper; import io.metersphere.base.mapper.ApiTestCaseMapper; import io.metersphere.base.mapper.TestPlanApiCaseMapper; import io.metersphere.base.mapper.TestPlanMapper; @@ -63,6 +64,8 @@ public class ApiCaseExecuteService { private ApiCaseResultService apiCaseResultService; @Resource private ApiScenarioReportStructureService apiScenarioReportStructureService; + @Resource + private ApiDefinitionExecResultMapper apiDefinitionExecResultMapper; /** * 测试计划case执行 @@ -91,7 +94,7 @@ public class ApiCaseExecuteService { } LoggerUtil.debug("查询到测试计划用例 " + planApiCases.size()); - Map executeQueue = new LinkedHashMap<>(); + Map executeQueue = request.isRerun() ? request.getExecuteQueue() : new LinkedHashMap<>(); List responseDTOS = new LinkedList<>(); String status = request.getConfig().getMode().equals(RunModeConstants.SERIAL.toString()) ? APITestStatus.Waiting.name() : APITestStatus.Running.name(); @@ -106,26 +109,26 @@ public class ApiCaseExecuteService { if (request.getConfig() != null && GenerateHashTreeUtil.isResourcePool(request.getConfig().getResourcePoolId()).isPool()) { resourcePoolId = request.getConfig().getResourcePoolId(); } - - Map planProjects = new HashMap<>(); - for (TestPlanApiCase testPlanApiCase : planApiCases) { - ApiDefinitionExecResult report = ApiDefinitionExecResultUtil.addResult(request, testPlanApiCase, status, caseMap, resourcePoolId); - if (planProjects.containsKey(testPlanApiCase.getTestPlanId())) { - report.setProjectId(planProjects.get(testPlanApiCase.getTestPlanId())); - } else { - TestPlan plan = CommonBeanFactory.getBean(TestPlanMapper.class).selectByPrimaryKey(testPlanApiCase.getTestPlanId()); - if (plan != null) { - planProjects.put(plan.getId(), plan.getProjectId()); - report.setProjectId(plan.getProjectId()); + if (!request.isRerun()) { + Map planProjects = new HashMap<>(); + for (TestPlanApiCase testPlanApiCase : planApiCases) { + ApiDefinitionExecResultWithBLOBs report = ApiDefinitionExecResultUtil.addResult(request, testPlanApiCase, status, caseMap, resourcePoolId); + if (planProjects.containsKey(testPlanApiCase.getTestPlanId())) { + report.setProjectId(planProjects.get(testPlanApiCase.getTestPlanId())); + } else { + TestPlan plan = CommonBeanFactory.getBean(TestPlanMapper.class).selectByPrimaryKey(testPlanApiCase.getTestPlanId()); + if (plan != null) { + planProjects.put(plan.getId(), plan.getProjectId()); + report.setProjectId(plan.getProjectId()); + } } + executeQueue.put(testPlanApiCase.getId(), report); + responseDTOS.add(new MsExecResponseDTO(testPlanApiCase.getId(), report.getId(), request.getTriggerMode())); + LoggerUtil.debug("预生成测试用例结果报告:" + report.getName() + ", ID " + report.getId()); } - executeQueue.put(testPlanApiCase.getId(), report); - responseDTOS.add(new MsExecResponseDTO(testPlanApiCase.getId(), report.getId(), request.getTriggerMode())); - LoggerUtil.debug("预生成测试用例结果报告:" + report.getName() + ", ID " + report.getId()); + apiCaseResultService.batchSave(executeQueue); } - apiCaseResultService.batchSave(executeQueue); - LoggerUtil.debug("开始生成测试计划队列"); String reportType = request.getConfig().getReportType(); String poolId = request.getConfig().getResourcePoolId(); @@ -227,22 +230,30 @@ public class ApiCaseExecuteService { this.checkEnv(caseList); } // 集合报告设置 - String serialReportId = null; - if (StringUtils.equals(request.getConfig().getReportType(), RunModeConstants.SET_REPORT.toString()) + String serialReportId = request.isRerun() ? request.getReportId() : null; + if (!request.isRerun() && StringUtils.equals(request.getConfig().getReportType(), RunModeConstants.SET_REPORT.toString()) && StringUtils.isNotEmpty(request.getConfig().getReportName())) { serialReportId = UUID.randomUUID().toString(); - ApiDefinitionExecResult report = ApiDefinitionExecResultUtil.initBase(null, APITestStatus.Running.name(), serialReportId, request.getConfig()); + ApiDefinitionExecResultWithBLOBs report = ApiDefinitionExecResultUtil.initBase(null, APITestStatus.Running.name(), serialReportId, request.getConfig()); report.setName(request.getConfig().getReportName()); report.setProjectId(request.getProjectId()); report.setReportType(ReportTypeConstants.API_INTEGRATED.name()); report.setVersionId(caseList.get(0).getVersionId()); - Map executeQueue = new LinkedHashMap<>(); + + Map executeQueue = new LinkedHashMap<>(); executeQueue.put(serialReportId, report); apiScenarioReportStructureService.save(serialReportId, new ArrayList<>()); apiCaseResultService.batchSave(executeQueue); responseDTOS.add(new MsExecResponseDTO(JSON.toJSONString(request.getIds()), report.getId(), request.getTriggerMode())); } + // 失败重跑报告状态更新 + if (request.isRerun()) { + ApiDefinitionExecResultWithBLOBs result = new ApiDefinitionExecResultWithBLOBs(); + result.setId(serialReportId); + result.setStatus(APITestStatus.Rerunning.name()); + apiDefinitionExecResultMapper.updateByPrimaryKeySelective(result); + } if (request.getConfig() != null && request.getConfig().getMode().equals(RunModeConstants.SERIAL.toString())) { if (request.getCondition() == null || !request.getCondition().isSelectAll()) { @@ -258,28 +269,30 @@ public class ApiCaseExecuteService { request.setTriggerMode(ApiRunMode.DEFINITION.name()); } - Map executeQueue = new LinkedHashMap<>(); + Map executeQueue = request.isRerun() ? request.getExecuteQueue() : new LinkedHashMap<>(); + String status = request.getConfig().getMode().equals(RunModeConstants.SERIAL.toString()) ? APITestStatus.Waiting.name() : APITestStatus.Running.name(); - - for (int i = 0; i < caseList.size(); i++) { - ApiTestCaseWithBLOBs caseWithBLOBs = caseList.get(i); - ApiDefinitionExecResult report = ApiDefinitionExecResultUtil.initBase(caseWithBLOBs.getId(), APITestStatus.Running.name(), null, request.getConfig()); - report.setStatus(status); - report.setName(caseWithBLOBs.getName()); - report.setProjectId(caseWithBLOBs.getProjectId()); - report.setVersionId(caseWithBLOBs.getVersionId()); - report.setCreateTime(System.currentTimeMillis() + i); - if (StringUtils.isNotEmpty(serialReportId)) { - report.setIntegratedReportId(serialReportId); - } - executeQueue.put(caseWithBLOBs.getId(), report); - if (!StringUtils.equals(request.getConfig().getReportType(), RunModeConstants.SET_REPORT.toString())) { - responseDTOS.add(new MsExecResponseDTO(caseWithBLOBs.getId(), report.getId(), request.getTriggerMode())); + // 第一次执行非重跑的报告处理 + if (!request.isRerun()) { + for (int i = 0; i < caseList.size(); i++) { + ApiTestCaseWithBLOBs caseWithBLOBs = caseList.get(i); + ApiDefinitionExecResultWithBLOBs report = ApiDefinitionExecResultUtil.initBase(caseWithBLOBs.getId(), APITestStatus.Running.name(), null, request.getConfig()); + report.setStatus(status); + report.setName(caseWithBLOBs.getName()); + report.setProjectId(caseWithBLOBs.getProjectId()); + report.setVersionId(caseWithBLOBs.getVersionId()); + report.setCreateTime(System.currentTimeMillis() + i); + if (StringUtils.isNotEmpty(serialReportId)) { + report.setIntegratedReportId(serialReportId); + } + executeQueue.put(caseWithBLOBs.getId(), report); + if (!StringUtils.equals(request.getConfig().getReportType(), RunModeConstants.SET_REPORT.toString())) { + responseDTOS.add(new MsExecResponseDTO(caseWithBLOBs.getId(), report.getId(), request.getTriggerMode())); + } } + apiCaseResultService.batchSave(executeQueue); } - apiCaseResultService.batchSave(executeQueue); - String reportType = request.getConfig().getReportType(); String poolId = request.getConfig().getResourcePoolId(); DBTestQueue deQueue = apiExecutionQueueService.add(executeQueue, poolId, ApiRunMode.DEFINITION.name(), serialReportId, reportType, ApiRunMode.DEFINITION.name(), request.getConfig()); diff --git a/backend/src/main/java/io/metersphere/api/exec/api/ApiCaseParallelExecuteService.java b/backend/src/main/java/io/metersphere/api/exec/api/ApiCaseParallelExecuteService.java index 9f5e58d4b5..903ba5ac3b 100644 --- a/backend/src/main/java/io/metersphere/api/exec/api/ApiCaseParallelExecuteService.java +++ b/backend/src/main/java/io/metersphere/api/exec/api/ApiCaseParallelExecuteService.java @@ -5,7 +5,7 @@ import io.metersphere.api.exec.scenario.ApiScenarioSerialService; import io.metersphere.api.exec.utils.GenerateHashTreeUtil; import io.metersphere.api.jmeter.JMeterService; import io.metersphere.api.jmeter.utils.SmoothWeighted; -import io.metersphere.base.domain.ApiDefinitionExecResult; +import io.metersphere.base.domain.ApiDefinitionExecResultWithBLOBs; import io.metersphere.commons.utils.CommonBeanFactory; import io.metersphere.constants.RunModeConstants; import io.metersphere.dto.BaseSystemConfigDTO; @@ -32,7 +32,7 @@ public class ApiCaseParallelExecuteService { @Resource private RedisTemplate redisTemplate; - public void parallel(Map executeQueue, RunModeConfigDTO config, DBTestQueue executionQueue, String runMode) { + public void parallel(Map executeQueue, RunModeConfigDTO config, DBTestQueue executionQueue, String runMode) { BooleanPool pool = GenerateHashTreeUtil.isResourcePool(config.getResourcePoolId()); // 初始化分配策略 if (pool.isPool()) { @@ -44,7 +44,7 @@ public class ApiCaseParallelExecuteService { if (Thread.currentThread().isInterrupted()) { break; } - ApiDefinitionExecResult result = executeQueue.get(testId); + ApiDefinitionExecResultWithBLOBs result = executeQueue.get(testId); String reportId = result.getId(); JmeterRunRequestDTO runRequest = new JmeterRunRequestDTO(testId, reportId, runMode, null); runRequest.setPool(pool); @@ -53,6 +53,10 @@ public class ApiCaseParallelExecuteService { runRequest.setReportType(executionQueue.getReportType()); runRequest.setRunType(RunModeConstants.PARALLEL.toString()); runRequest.setQueueId(executionQueue.getId()); + + runRequest.setRetryNum(config.getRetryNum()); + runRequest.setRetryEnable(config.isRetryEnable()); + Map extendedParameters = new HashMap<>(); extendedParameters.put("userId", result.getUserId()); runRequest.setExtendedParameters(extendedParameters); diff --git a/backend/src/main/java/io/metersphere/api/exec/api/ApiExecuteService.java b/backend/src/main/java/io/metersphere/api/exec/api/ApiExecuteService.java index 92959bf891..4bf3da7904 100644 --- a/backend/src/main/java/io/metersphere/api/exec/api/ApiExecuteService.java +++ b/backend/src/main/java/io/metersphere/api/exec/api/ApiExecuteService.java @@ -82,7 +82,7 @@ public class ApiExecuteService { request.setEnvironmentId(extApiTestCaseMapper.getApiCaseEnvironment(request.getCaseId())); } //提前生成报告 - ApiDefinitionExecResult report = ApiDefinitionExecResultUtil.add(caseWithBLOBs.getId(), APITestStatus.Running.name(), request.getReportId(),Objects.requireNonNull(SessionUtils.getUser()).getId()); + ApiDefinitionExecResultWithBLOBs report = ApiDefinitionExecResultUtil.add(caseWithBLOBs.getId(), APITestStatus.Running.name(), request.getReportId(),Objects.requireNonNull(SessionUtils.getUser()).getId()); report.setName(caseWithBLOBs.getName()); report.setTriggerMode(ApiRunMode.JENKINS.name()); report.setType(ApiRunMode.JENKINS.name()); diff --git a/backend/src/main/java/io/metersphere/api/exec/api/ApiRetryOnFailureService.java b/backend/src/main/java/io/metersphere/api/exec/api/ApiRetryOnFailureService.java new file mode 100644 index 0000000000..e1709b7cee --- /dev/null +++ b/backend/src/main/java/io/metersphere/api/exec/api/ApiRetryOnFailureService.java @@ -0,0 +1,5 @@ +package io.metersphere.api.exec.api; + +public interface ApiRetryOnFailureService { + public String retry(String data, long retryNum); +} diff --git a/backend/src/main/java/io/metersphere/api/exec/scenario/ApiScenarioExecuteService.java b/backend/src/main/java/io/metersphere/api/exec/scenario/ApiScenarioExecuteService.java index 54f25563f4..749354ad84 100644 --- a/backend/src/main/java/io/metersphere/api/exec/scenario/ApiScenarioExecuteService.java +++ b/backend/src/main/java/io/metersphere/api/exec/scenario/ApiScenarioExecuteService.java @@ -15,9 +15,7 @@ import io.metersphere.api.service.ApiExecutionQueueService; import io.metersphere.api.service.ApiScenarioReportService; import io.metersphere.api.service.ApiScenarioReportStructureService; import io.metersphere.api.service.TcpApiParamService; -import io.metersphere.base.domain.ApiScenarioExample; -import io.metersphere.base.domain.ApiScenarioWithBLOBs; -import io.metersphere.base.domain.TestPlanApiScenario; +import io.metersphere.base.domain.*; import io.metersphere.base.mapper.ApiScenarioMapper; import io.metersphere.base.mapper.ApiScenarioReportMapper; import io.metersphere.base.mapper.TestPlanApiScenarioMapper; @@ -103,7 +101,7 @@ public class ApiScenarioExecuteService { } // 生成集成报告 - String serialReportId = null; + String serialReportId = request.isRerun() && GenerateHashTreeUtil.isSetReport(request.getConfig()) ? request.getSerialReportId() : null; LoggerUtil.info("Scenario run-执行脚本装载-根据条件查询所有场景 "); List apiScenarios = this.get(request); // 只有一个场景且没有测试步骤,则提示 @@ -114,7 +112,7 @@ public class ApiScenarioExecuteService { LoggerUtil.info("Scenario run-执行脚本装载-开始针对所有执行场景进行环境检查"); apiScenarioEnvService.checkEnv(request, apiScenarios); // 集合报告设置 - if (StringUtils.equals(request.getConfig().getReportType(), RunModeConstants.SET_REPORT.toString()) + if (!request.isRerun() && StringUtils.equals(request.getConfig().getReportType(), RunModeConstants.SET_REPORT.toString()) && StringUtils.isNotEmpty(request.getConfig().getReportName())) { if (request.getConfig().getMode().equals(RunModeConstants.SERIAL.toString())) { request.setExecuteType(ExecuteType.Completed.name()); @@ -144,25 +142,37 @@ public class ApiScenarioExecuteService { return responseDTOS; } if (GenerateHashTreeUtil.isSetReport(request.getConfig())) { - LoggerUtil.info("Scenario run-执行脚本装载-初始化集成报告:" + serialReportId); - request.getConfig().setReportId(UUID.randomUUID().toString()); + // 失败重跑更新报告状态 + if (request.isRerun()) { + ApiScenarioReportWithBLOBs report = new ApiScenarioReportWithBLOBs(); + report.setId(serialReportId); + report.setStatus(APITestStatus.Rerunning.name()); + apiScenarioReportMapper.updateByPrimaryKeySelective(report); + } else { + LoggerUtil.info("Scenario run-执行脚本装载-初始化集成报告:" + serialReportId); + request.getConfig().setReportId(UUID.randomUUID().toString()); - String reportScenarioIds = JSON.toJSONString(CollectionUtils.isNotEmpty(scenarioIds) && scenarioIds.size() > 50 ? scenarioIds.subList(0, 50) : scenarioIds); - APIScenarioReportResult report = apiScenarioReportService.init(request.getConfig().getReportId(), reportScenarioIds, - scenarioNames.toString(), request.getTriggerMode(), ExecuteType.Saved.name(), request.getProjectId(), - request.getReportUserID(), request.getConfig()); + String reportScenarioIds = JSON.toJSONString(CollectionUtils.isNotEmpty(scenarioIds) && scenarioIds.size() > 50 ? scenarioIds.subList(0, 50) : scenarioIds); + APIScenarioReportResult report = apiScenarioReportService.init(request.getConfig().getReportId(), reportScenarioIds, + scenarioNames.toString(), request.getTriggerMode(), ExecuteType.Saved.name(), request.getProjectId(), + request.getReportUserID(), request.getConfig()); - report.setVersionId(apiScenarios.get(0).getVersionId()); - report.setName(request.getConfig().getReportName()); - report.setId(serialReportId); - report.setReportType(ReportTypeConstants.SCENARIO_INTEGRATED.name()); - request.getConfig().setAmassReport(serialReportId); - report.setStatus(APITestStatus.Running.name()); - apiScenarioReportMapper.insert(report); + report.setVersionId(apiScenarios.get(0).getVersionId()); + report.setName(request.getConfig().getReportName()); + report.setId(serialReportId); + report.setReportType(ReportTypeConstants.SCENARIO_INTEGRATED.name()); + request.getConfig().setAmassReport(serialReportId); + if (request.getConfig() != null) { + report.setEnvConfig(JSON.toJSONString(request.getConfig())); + } + report.setStatus(APITestStatus.Running.name()); + apiScenarioReportMapper.insert(report); - responseDTOS.add(new MsExecResponseDTO(JSON.toJSONString(scenarioIds), serialReportId, request.getRunMode())); - reportStructureService.save(apiScenarios, serialReportId, request.getConfig().getReportType()); + responseDTOS.add(new MsExecResponseDTO(JSON.toJSONString(scenarioIds), serialReportId, request.getRunMode())); + reportStructureService.save(apiScenarios, serialReportId, request.getConfig().getReportType()); + } } + String reportType = request.getConfig().getReportType(); String planReportId = StringUtils.isNotEmpty(request.getTestPlanReportId()) ? request.getTestPlanReportId() : serialReportId; // 生成执行队列 @@ -170,8 +180,9 @@ public class ApiScenarioExecuteService { , ApiRunMode.SCENARIO.name(), planReportId, reportType, request.getRunMode(), request.getConfig()); // 预生成报告 - apiScenarioReportService.batchSave(executeQueue, serialReportId, request.getRunMode(), responseDTOS); - + if (!request.isRerun() && !GenerateHashTreeUtil.isSetReport(request.getConfig())) { + apiScenarioReportService.batchSave(executeQueue, serialReportId, request.getRunMode(), responseDTOS); + } // 开始执行 String finalSerialReportId = serialReportId; Thread thread = new Thread(new Runnable() { @@ -190,7 +201,7 @@ public class ApiScenarioExecuteService { return responseDTOS; } - private List get(RunScenarioRequest request) { + public List get(RunScenarioRequest request) { ServiceUtils.getSelectAllIds(request, request.getCondition(), (query) -> extApiScenarioMapper.selectIdsByQuery(query)); List ids = request.getIds(); @@ -248,9 +259,15 @@ public class ApiScenarioExecuteService { projectId = scenario.getProjectId(); } - APIScenarioReportResult report = apiScenarioReportService.init(reportId, testPlanScenarioId, scenario.getName(), request.getTriggerMode(), - request.getExecuteType(), projectId, request.getReportUserID(), request.getConfig()); - + APIScenarioReportResult report = request.isRerun() ? request.getReportMap().get(scenarioId) : + apiScenarioReportService.init(reportId, testPlanScenarioId, scenario.getName(), request.getTriggerMode(), + request.getExecuteType(), projectId, request.getReportUserID(), request.getConfig()); + if (report == null) { + report = request.getReportMap().get(testPlanScenarioId); + } + if (report == null) { + return; + } report.setVersionId(scenario.getVersionId()); scenarioIds.add(scenario.getId()); RunModeDataDTO runModeDataDTO = new RunModeDataDTO(); @@ -261,6 +278,9 @@ public class ApiScenarioExecuteService { runModeDataDTO.setScenario(scenario); executeQueue.put(report.getId(), runModeDataDTO); scenarioNames.append(scenario.getName()).append(","); + if (request.getConfig() != null) { + report.setEnvConfig(JSON.toJSONString(request.getConfig())); + } // 生成文档结构 if (!StringUtils.equals(request.getConfig().getReportType(), RunModeConstants.SET_REPORT.toString())) { reportStructureService.save(scenario, report.getId(), request.getConfig() != null ? request.getConfig().getReportType() : null); diff --git a/backend/src/main/java/io/metersphere/api/exec/scenario/ApiScenarioParallelService.java b/backend/src/main/java/io/metersphere/api/exec/scenario/ApiScenarioParallelService.java index e7024df16d..995e914822 100644 --- a/backend/src/main/java/io/metersphere/api/exec/scenario/ApiScenarioParallelService.java +++ b/backend/src/main/java/io/metersphere/api/exec/scenario/ApiScenarioParallelService.java @@ -50,6 +50,8 @@ public class ApiScenarioParallelService { runRequest.setTestPlanReportId(request.getTestPlanReportId()); runRequest.setPlatformUrl(GenerateHashTreeUtil.getPlatformUrl(baseInfo, runRequest, executionQueue.getDetailMap().get(reportId))); runRequest.setRunType(RunModeConstants.PARALLEL.toString()); + runRequest.setRetryNum(request.getConfig().getRetryNum()); + runRequest.setRetryEnable(request.getConfig().isRetryEnable()); // 本地执行生成hashTree if (!pool.isPool()) { runRequest.setHashTree(GenerateHashTreeUtil.generateHashTree(dataDTO.getScenario(), dataDTO.getPlanEnvMap(), runRequest)); diff --git a/backend/src/main/java/io/metersphere/api/exec/scenario/ApiScenarioSerialService.java b/backend/src/main/java/io/metersphere/api/exec/scenario/ApiScenarioSerialService.java index 9683bc8912..86deac34ed 100644 --- a/backend/src/main/java/io/metersphere/api/exec/scenario/ApiScenarioSerialService.java +++ b/backend/src/main/java/io/metersphere/api/exec/scenario/ApiScenarioSerialService.java @@ -13,6 +13,7 @@ import io.metersphere.api.dto.definition.request.sampler.MsDubboSampler; import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy; import io.metersphere.api.dto.definition.request.sampler.MsJDBCSampler; import io.metersphere.api.dto.definition.request.sampler.MsTCPSampler; +import io.metersphere.api.exec.api.ApiRetryOnFailureService; import io.metersphere.api.exec.utils.GenerateHashTreeUtil; import io.metersphere.api.jmeter.JMeterService; import io.metersphere.api.jmeter.utils.SmoothWeighted; @@ -23,7 +24,10 @@ import io.metersphere.base.domain.*; import io.metersphere.base.mapper.*; import io.metersphere.commons.constants.APITestStatus; import io.metersphere.commons.constants.ApiRunMode; -import io.metersphere.commons.utils.*; +import io.metersphere.commons.utils.BeanUtils; +import io.metersphere.commons.utils.CommonBeanFactory; +import io.metersphere.commons.utils.HashTreeUtil; +import io.metersphere.commons.utils.LogUtil; import io.metersphere.constants.RunModeConstants; import io.metersphere.dto.BaseSystemConfigDTO; import io.metersphere.dto.JmeterRunRequestDTO; @@ -37,7 +41,10 @@ import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import javax.annotation.Resource; -import java.util.*; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.Map; @Service public class ApiScenarioSerialService { @@ -70,7 +77,8 @@ public class ApiScenarioSerialService { JmeterRunRequestDTO runRequest = new JmeterRunRequestDTO(queue.getTestId(), reportId, executionQueue.getRunMode(), null); // 获取可以执行的资源池 BaseSystemConfigDTO baseInfo = CommonBeanFactory.getBean(SystemParameterService.class).getBaseInfo(); - + runRequest.setRetryEnable(queue.getRetryEnable() == null ? false : queue.getRetryEnable()); + runRequest.setRetryNum(queue.getRetryNumber()); // 判断触发资源对象是用例/场景更新对应报告状态 if (!StringUtils.equals(executionQueue.getReportType(), RunModeConstants.SET_REPORT.toString()) || StringUtils.equalsIgnoreCase(executionQueue.getRunMode(), ApiRunMode.DEFINITION.name())) { @@ -87,7 +95,7 @@ public class ApiScenarioSerialService { LoggerUtil.info("进入串行模式,准备执行资源:[ " + report.getName() + " ], 报告ID [ " + report.getId() + " ]"); } } else { - ApiDefinitionExecResult execResult = apiDefinitionExecResultMapper.selectByPrimaryKey(queue.getReportId()); + ApiDefinitionExecResultWithBLOBs execResult = apiDefinitionExecResultMapper.selectByPrimaryKey(queue.getReportId()); if (execResult != null) { runRequest.setExtendedParameters(new HashMap() {{ this.put("userId", execResult.getUserId()); @@ -180,6 +188,14 @@ public class ApiScenarioSerialService { envId = envMap.get(caseWithBLOBs.getProjectId()); } if (caseWithBLOBs != null) { + String data = caseWithBLOBs.getRequest(); + // 失败重试 + if (runRequest.isRetryEnable() && runRequest.getRetryNum() > 0) { + ApiRetryOnFailureService apiRetryOnFailureService = CommonBeanFactory.getBean(ApiRetryOnFailureService.class); + String retryData = apiRetryOnFailureService.retry(data, runRequest.getRetryNum()); + data = StringUtils.isNotEmpty(retryData) ? retryData : data; + } + HashTree jmeterHashTree = new HashTree(); MsTestPlan testPlan = new MsTestPlan(); testPlan.setHashTree(new LinkedList<>()); @@ -189,7 +205,7 @@ public class ApiScenarioSerialService { group.setName(runRequest.getReportId()); group.setProjectId(caseWithBLOBs.getProjectId()); - MsTestElement testElement = parse(caseWithBLOBs, testId, envId); + MsTestElement testElement = parse(data, testId, envId); group.setHashTree(new LinkedList<>()); group.getHashTree().add(testElement); testPlan.getHashTree().add(group); @@ -209,9 +225,8 @@ public class ApiScenarioSerialService { return null; } - private MsTestElement parse(ApiTestCaseWithBLOBs caseWithBLOBs, String planId, String envId) { + private MsTestElement parse(String api, String planId, String envId) { try { - String api = caseWithBLOBs.getRequest(); JSONObject element = JSON.parseObject(api, Feature.DisableSpecialKeyDetect); ElementUtil.dataFormatting(element); diff --git a/backend/src/main/java/io/metersphere/api/exec/utils/ApiDefinitionExecResultUtil.java b/backend/src/main/java/io/metersphere/api/exec/utils/ApiDefinitionExecResultUtil.java index 05694c2d76..57310fddd8 100644 --- a/backend/src/main/java/io/metersphere/api/exec/utils/ApiDefinitionExecResultUtil.java +++ b/backend/src/main/java/io/metersphere/api/exec/utils/ApiDefinitionExecResultUtil.java @@ -1,7 +1,8 @@ package io.metersphere.api.exec.utils; +import com.alibaba.fastjson.JSON; import io.metersphere.api.dto.definition.BatchRunDefinitionRequest; -import io.metersphere.base.domain.ApiDefinitionExecResult; +import io.metersphere.base.domain.ApiDefinitionExecResultWithBLOBs; import io.metersphere.base.domain.ApiTestCase; import io.metersphere.base.domain.TestPlanApiCase; import io.metersphere.commons.constants.ApiRunMode; @@ -16,8 +17,8 @@ import java.util.Objects; import java.util.UUID; public class ApiDefinitionExecResultUtil { - public static ApiDefinitionExecResult initBase(String resourceId, String status, String reportId, RunModeConfigDTO config) { - ApiDefinitionExecResult apiResult = new ApiDefinitionExecResult(); + public static ApiDefinitionExecResultWithBLOBs initBase(String resourceId, String status, String reportId, RunModeConfigDTO config) { + ApiDefinitionExecResultWithBLOBs apiResult = new ApiDefinitionExecResultWithBLOBs(); if (StringUtils.isEmpty(reportId)) { apiResult.setId(UUID.randomUUID().toString()); } else { @@ -37,12 +38,14 @@ public class ApiDefinitionExecResultUtil { apiResult.setStartTime(System.currentTimeMillis()); apiResult.setType(ApiRunMode.DEFINITION.name()); apiResult.setStatus(status); + apiResult.setEnvConfig(JSON.toJSONString(config)); + return apiResult; } - public static ApiDefinitionExecResult addResult(BatchRunDefinitionRequest request, TestPlanApiCase key, String status, - Map caseMap, String poolId) { - ApiDefinitionExecResult apiResult = new ApiDefinitionExecResult(); + public static ApiDefinitionExecResultWithBLOBs addResult(BatchRunDefinitionRequest request, TestPlanApiCase key, String status, + Map caseMap, String poolId) { + ApiDefinitionExecResultWithBLOBs apiResult = new ApiDefinitionExecResultWithBLOBs(); apiResult.setId(UUID.randomUUID().toString()); apiResult.setCreateTime(System.currentTimeMillis()); apiResult.setStartTime(System.currentTimeMillis()); @@ -72,11 +75,13 @@ public class ApiDefinitionExecResultUtil { apiResult.setType(ApiRunMode.API_PLAN.name()); apiResult.setStatus(status); apiResult.setContent(request.getPlanReportId()); + apiResult.setEnvConfig(JSON.toJSONString(request.getConfig())); + return apiResult; } - public static ApiDefinitionExecResult add(String resourceId, String status, String reportId, String userId) { - ApiDefinitionExecResult apiResult = new ApiDefinitionExecResult(); + public static ApiDefinitionExecResultWithBLOBs add(String resourceId, String status, String reportId, String userId) { + ApiDefinitionExecResultWithBLOBs apiResult = new ApiDefinitionExecResultWithBLOBs(); if (StringUtils.isEmpty(reportId)) { apiResult.setId(UUID.randomUUID().toString()); } else { diff --git a/backend/src/main/java/io/metersphere/api/exec/utils/GenerateHashTreeUtil.java b/backend/src/main/java/io/metersphere/api/exec/utils/GenerateHashTreeUtil.java index bd5f4a781a..d22d7e435b 100644 --- a/backend/src/main/java/io/metersphere/api/exec/utils/GenerateHashTreeUtil.java +++ b/backend/src/main/java/io/metersphere/api/exec/utils/GenerateHashTreeUtil.java @@ -10,6 +10,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.metersphere.api.dto.EnvironmentType; import io.metersphere.api.dto.definition.request.*; import io.metersphere.api.dto.definition.request.variable.ScenarioVariable; +import io.metersphere.api.exec.api.ApiRetryOnFailureService; import io.metersphere.api.jmeter.ResourcePoolCalculation; import io.metersphere.api.service.ApiExecutionQueueService; import io.metersphere.api.service.RemakeReportService; @@ -134,7 +135,15 @@ public class GenerateHashTreeUtil { } else { setScenarioEnv(scenario, item); } - GenerateHashTreeUtil.parse(item.getScenarioDefinition(), scenario); + String data = item.getScenarioDefinition(); + // 失败重试 + if (runRequest.isRetryEnable() && runRequest.getRetryNum() > 0) { + ApiRetryOnFailureService apiRetryOnFailureService = CommonBeanFactory.getBean(ApiRetryOnFailureService.class); + String retryData = apiRetryOnFailureService.retry(data, runRequest.getRetryNum()); + data = StringUtils.isNotEmpty(retryData) ? retryData : data; + } + + GenerateHashTreeUtil.parse(data, scenario); group.setEnableCookieShare(scenario.isEnableCookieShare()); LinkedList scenarios = new LinkedList<>(); @@ -156,6 +165,8 @@ public class GenerateHashTreeUtil { config.setScenarioId(item.getId()); config.setReportType(runRequest.getReportType()); testPlan.toHashTree(jmeterHashTree, testPlan.getHashTree(), config); + + LogUtil.info(testPlan.getJmx(jmeterHashTree)); return jmeterHashTree; } diff --git a/backend/src/main/java/io/metersphere/api/jmeter/APISingleResultListener.java b/backend/src/main/java/io/metersphere/api/jmeter/APISingleResultListener.java index 982b94eae7..11eb666eb8 100644 --- a/backend/src/main/java/io/metersphere/api/jmeter/APISingleResultListener.java +++ b/backend/src/main/java/io/metersphere/api/jmeter/APISingleResultListener.java @@ -10,38 +10,49 @@ import io.metersphere.dto.ResultDTO; import io.metersphere.jmeter.JMeterBase; import io.metersphere.jmeter.MsExecListener; import io.metersphere.utils.LoggerUtil; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.apache.jmeter.samplers.SampleResult; -import java.util.List; -import java.util.Map; +import java.util.*; +import java.util.stream.Collectors; public class APISingleResultListener implements MsExecListener { - private ApiExecutionQueueService apiExecutionQueueService; + private final static String RETRY = "MsRetry_"; + private List queues; /** * 参数初始化方法 */ @Override public void setupTest() { + queues = new ArrayList<>(); LoggerUtil.info("初始化监听"); } @Override public void handleTeardownTest(List results, ResultDTO dto, Map kafkaConfig) { LoggerUtil.info("接收到执行结果开始处理报告【" + dto.getReportId() + " 】,资源【 " + dto.getTestId() + " 】"); - dto.setConsole(FixedCapacityUtils.getJmeterLogger(dto.getReportId())); - JMeterBase.resultFormatting(results, dto); - CommonBeanFactory.getBean(TestResultService.class).saveResults(dto); + // 暂时保留 + // dto.setConsole(FixedCapacityUtils.getJmeterLogger(dto.getReportId())); + // JMeterBase.resultFormatting(results, dto); + // CommonBeanFactory.getBean(TestResultService.class).saveResults(dto); + queues.addAll(results); } @Override public void testEnded(ResultDTO dto, Map kafkaConfig) { try { - if (JMeterEngineCache.runningEngine.containsKey(dto.getReportId())) { - JMeterEngineCache.runningEngine.remove(dto.getReportId()); + if (dto.isRetryEnable()) { + LoggerUtil.info("进入TEST-END处理报告【" + dto.getReportId() + " 】,进入重试结果处理"); + mergeRetryResults(queues); } + + JMeterBase.resultFormatting(queues, dto); + // 入库存储 + CommonBeanFactory.getBean(TestResultService.class).saveResults(dto); + LoggerUtil.info("进入TEST-END处理报告【" + dto.getReportId() + " 】" + dto.getRunMode() + " 整体执行完成"); // 全局并发队列 PoolExecBlockingQueueUtil.offer(dto.getReportId()); @@ -66,6 +77,40 @@ public class APISingleResultListener implements MsExecListener { if (FixedCapacityUtils.jmeterLogTask.containsKey(dto.getReportId())) { FixedCapacityUtils.jmeterLogTask.remove(dto.getReportId()); } + if (JMeterEngineCache.runningEngine.containsKey(dto.getReportId())) { + JMeterEngineCache.runningEngine.remove(dto.getReportId()); + } + queues.clear(); + } + } + + /** + * 合并掉重试结果;保留最后一次重试结果 + * + * @param results + */ + public void mergeRetryResults(List results) { + if (CollectionUtils.isNotEmpty(results)) { + Map> resultMap = results.stream().collect(Collectors.groupingBy(SampleResult::getResourceId)); + List list = new LinkedList<>(); + resultMap.forEach((k, v) -> { + // 校验是否含重试结果 + List isRetryResults = v + .stream() + .filter(c -> StringUtils.isNotEmpty(c.getSampleLabel()) && c.getSampleLabel().startsWith(RETRY)) + .collect(Collectors.toList()); + + if (CollectionUtils.isNotEmpty(isRetryResults) && v.size() > 1) { + Collections.sort(v, Comparator.comparing(SampleResult::getResourceId)); + SampleResult sampleResult = v.get(v.size() - 1); + sampleResult.setSampleLabel(v.get(0).getSampleLabel()); + list.add(sampleResult); + } else { + list.addAll(v); + } + }); + results.clear(); + results.addAll(list); } } } diff --git a/backend/src/main/java/io/metersphere/api/service/ApiCaseResultService.java b/backend/src/main/java/io/metersphere/api/service/ApiCaseResultService.java index f371ef8e24..d8aca00f10 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiCaseResultService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiCaseResultService.java @@ -1,6 +1,6 @@ package io.metersphere.api.service; -import io.metersphere.base.domain.ApiDefinitionExecResult; +import io.metersphere.base.domain.ApiDefinitionExecResultWithBLOBs; import io.metersphere.base.mapper.ext.ExtApiDefinitionExecResultMapper; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; @@ -16,7 +16,7 @@ public class ApiCaseResultService { private ExtApiDefinitionExecResultMapper resultMapper; @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class) - public void batchSave(Map executeQueue) { + public void batchSave(Map executeQueue) { if (!executeQueue.isEmpty()) { resultMapper.sqlInsert(new LinkedList<>(executeQueue.values())); } diff --git a/backend/src/main/java/io/metersphere/api/service/ApiDefinitionExecResultService.java b/backend/src/main/java/io/metersphere/api/service/ApiDefinitionExecResultService.java index 298f75bfc8..fcf56eb945 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiDefinitionExecResultService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiDefinitionExecResultService.java @@ -436,7 +436,7 @@ public class ApiDefinitionExecResultService { private ApiDefinitionExecResult editResult(RequestResult item, String reportId, String console, String type, String testId, ApiDefinitionExecResultMapper batchMapper) { if (!StringUtils.startsWithAny(item.getName(), "PRE_PROCESSOR_ENV_", "POST_PROCESSOR_ENV_")) { - ApiDefinitionExecResult saveResult = new ApiDefinitionExecResult(); + ApiDefinitionExecResultWithBLOBs saveResult = new ApiDefinitionExecResultWithBLOBs(); item.getResponseResult().setConsole(console); saveResult.setId(reportId); //对响应内容进行进一步解析。如果有附加信息(比如误报库信息),则根据附加信息内的数据进行其他判读 diff --git a/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java b/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java index 3fa2fdcc77..6f4300986e 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiDefinitionService.java @@ -1107,7 +1107,7 @@ public class ApiDefinitionService { CollectionUtils.isNotEmpty(request.getTestElement().getHashTree()) && CollectionUtils.isNotEmpty(request.getTestElement().getHashTree().get(0).getHashTree()) ? request.getTestElement().getHashTree().get(0).getHashTree().get(0).getName() : request.getId(); - ApiDefinitionExecResult result = ApiDefinitionExecResultUtil.add(testId, APITestStatus.Running.name(), request.getId(), Objects.requireNonNull(SessionUtils.getUser()).getId()); + ApiDefinitionExecResultWithBLOBs result = ApiDefinitionExecResultUtil.add(testId, APITestStatus.Running.name(), request.getId(), Objects.requireNonNull(SessionUtils.getUser()).getId()); result.setProjectId(request.getProjectId()); result.setTriggerMode(TriggerMode.MANUAL.name()); apiDefinitionExecResultMapper.insert(result); @@ -1129,21 +1129,21 @@ public class ApiDefinitionService { * @return */ public APIReportResult getDbResult(String testId) { - ApiDefinitionExecResult result = extApiDefinitionExecResultMapper.selectMaxResultByResourceId(testId); + ApiDefinitionExecResultWithBLOBs result = extApiDefinitionExecResultMapper.selectMaxResultByResourceId(testId); return buildAPIReportResult(result); } public APIReportResult getByResultId(String reportId) { - ApiDefinitionExecResult result = apiDefinitionExecResultMapper.selectByPrimaryKey(reportId); + ApiDefinitionExecResultWithBLOBs result = apiDefinitionExecResultMapper.selectByPrimaryKey(reportId); return buildAPIReportResult(result); } public APIReportResult getReportById(String testId) { - ApiDefinitionExecResult result = apiDefinitionExecResultMapper.selectByPrimaryKey(testId); + ApiDefinitionExecResultWithBLOBs result = apiDefinitionExecResultMapper.selectByPrimaryKey(testId); return buildAPIReportResult(result); } - public APIReportResult buildAPIReportResult(ApiDefinitionExecResult result) { + public APIReportResult buildAPIReportResult(ApiDefinitionExecResultWithBLOBs result) { if (result == null) { return null; } @@ -1154,7 +1154,7 @@ public class ApiDefinitionService { } public APIReportResult getDbResult(String testId, String type) { - ApiDefinitionExecResult result = extApiDefinitionExecResultMapper.selectMaxResultByResourceIdAndType(testId, type); + ApiDefinitionExecResultWithBLOBs result = extApiDefinitionExecResultMapper.selectMaxResultByResourceIdAndType(testId, type); return buildAPIReportResult(result); } @@ -1842,7 +1842,7 @@ public class ApiDefinitionService { } public APIReportResult getTestPlanApiCaseReport(String testId, String type) { - ApiDefinitionExecResult result = extApiDefinitionExecResultMapper.selectPlanApiMaxResultByTestIdAndType(testId, type); + ApiDefinitionExecResultWithBLOBs result = extApiDefinitionExecResultMapper.selectPlanApiMaxResultByTestIdAndType(testId, type); return buildAPIReportResult(result); } diff --git a/backend/src/main/java/io/metersphere/api/service/ApiExecutionQueueService.java b/backend/src/main/java/io/metersphere/api/service/ApiExecutionQueueService.java index 232d708ae1..3e192b2695 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiExecutionQueueService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiExecutionQueueService.java @@ -98,6 +98,8 @@ public class ApiExecutionQueueService { resQueue.setQueue(queue); } sort[0]++; + queue.setRetryEnable(config.isRetryEnable()); + queue.setRetryNumber(config.getRetryNum()); queueDetails.add(queue); detailMap.put(k, queue.getId()); }); @@ -114,6 +116,8 @@ public class ApiExecutionQueueService { resQueue.setQueue(queue); } sort[0]++; + queue.setRetryEnable(config.isRetryEnable()); + queue.setRetryNumber(config.getRetryNum()); queueDetails.add(queue); detailMap.put(k, queue.getId()); }); @@ -127,6 +131,8 @@ public class ApiExecutionQueueService { resQueue.setQueue(queue); } sort[0]++; + queue.setRetryEnable(config.isRetryEnable()); + queue.setRetryNumber(config.getRetryNum()); queueDetails.add(queue); detailMap.put(k, queue.getId()); }); @@ -362,7 +368,7 @@ public class ApiExecutionQueueService { ApiRunMode.SCHEDULE_SCENARIO_PLAN.name(), ApiRunMode.SCHEDULE_SCENARIO.name(), ApiRunMode.JENKINS_SCENARIO_PLAN.name())) { - ApiScenarioReport report = apiScenarioReportMapper.selectByPrimaryKey(item.getReportId()); + ApiScenarioReportWithBLOBs report = apiScenarioReportMapper.selectByPrimaryKey(item.getReportId()); // 报告已经被删除则队列也删除 if (report == null) { executionQueueDetailMapper.deleteByPrimaryKey(item.getId()); @@ -394,7 +400,7 @@ public class ApiExecutionQueueService { } } else { // 用例/接口超时结果处理 - ApiDefinitionExecResult result = apiDefinitionExecResultMapper.selectByPrimaryKey(item.getReportId()); + ApiDefinitionExecResultWithBLOBs result = apiDefinitionExecResultMapper.selectByPrimaryKey(item.getReportId()); if (result != null && StringUtils.equalsAnyIgnoreCase(result.getStatus(), TestPlanReportStatus.RUNNING.name())) { result.setStatus(ScenarioStatus.Timeout.name()); apiDefinitionExecResultMapper.updateByPrimaryKeySelective(result); @@ -408,7 +414,7 @@ public class ApiExecutionQueueService { List executionQueues = queueMapper.selectByExample(queueDetailExample); if (CollectionUtils.isNotEmpty(executionQueues)) { executionQueues.forEach(item -> { - ApiScenarioReport report = apiScenarioReportMapper.selectByPrimaryKey(item.getReportId()); + ApiScenarioReportWithBLOBs report = apiScenarioReportMapper.selectByPrimaryKey(item.getReportId()); if (report != null && StringUtils.equalsAnyIgnoreCase(report.getStatus(), TestPlanReportStatus.RUNNING.name(), APITestStatus.Waiting.name()) && (report.getUpdateTime() < timeout)) { report.setStatus(ScenarioStatus.Timeout.name()); diff --git a/backend/src/main/java/io/metersphere/api/service/ApiScenarioReportService.java b/backend/src/main/java/io/metersphere/api/service/ApiScenarioReportService.java index ccbebbdd2e..b0d9337687 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiScenarioReportService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiScenarioReportService.java @@ -209,10 +209,10 @@ public class ApiScenarioReportService { return report; } - public ApiScenarioReport editReport(String reportType, String reportId, String status, String runMode) { - ApiScenarioReport report = apiScenarioReportMapper.selectByPrimaryKey(reportId); + public ApiScenarioReportWithBLOBs editReport(String reportType, String reportId, String status, String runMode) { + ApiScenarioReportWithBLOBs report = apiScenarioReportMapper.selectByPrimaryKey(reportId); if (report == null) { - report = new ApiScenarioReport(); + report = new ApiScenarioReportWithBLOBs(); report.setId(reportId); } if (StringUtils.equals(reportType, RunModeConstants.SET_REPORT.toString())) { @@ -234,7 +234,7 @@ public class ApiScenarioReportService { public ApiScenarioReport updateReport(APIScenarioReportResult test) { checkNameExist(test); - ApiScenarioReport report = new ApiScenarioReport(); + ApiScenarioReportWithBLOBs report = new ApiScenarioReportWithBLOBs(); report.setId(test.getId()); report.setProjectId(test.getProjectId()); report.setName(test.getName()); @@ -301,7 +301,7 @@ public class ApiScenarioReportService { long errorSize = requestResults.stream().filter(requestResult -> StringUtils.equalsIgnoreCase(requestResult.getStatus(), ScenarioStatus.Error.name())).count(); String status = getStatus(requestResults, dto); - ApiScenarioReport report = editReport(dto.getReportType(), dto.getReportId(), status, dto.getRunMode()); + ApiScenarioReportWithBLOBs report = editReport(dto.getReportType(), dto.getReportId(), status, dto.getRunMode()); if (report != null) { if (StringUtils.isNotEmpty(dto.getTestPlanReportId()) && !testPlanReportIdList.contains(dto.getTestPlanReportId())) { testPlanReportIdList.add(dto.getTestPlanReportId()); @@ -348,21 +348,25 @@ public class ApiScenarioReportService { public void margeReport(String reportId, String runMode) { // 更新场景状态 if (StringUtils.equalsIgnoreCase(runMode, ApiRunMode.DEFINITION.name())) { - ApiDefinitionExecResult result = definitionExecResultMapper.selectByPrimaryKey(reportId); + ApiDefinitionExecResultWithBLOBs result = definitionExecResultMapper.selectByPrimaryKey(reportId); + if (!StringUtils.equalsAnyIgnoreCase(result.getStatus(), APITestStatus.Rerunning.name())) { + result.setEndTime(System.currentTimeMillis()); + } ApiDefinitionExecResultExample execResultExample = new ApiDefinitionExecResultExample(); execResultExample.createCriteria().andIntegratedReportIdEqualTo(reportId).andStatusEqualTo("Error"); long size = definitionExecResultMapper.countByExample(execResultExample); result.setStatus(size > 0 ? ScenarioStatus.Error.name() : ScenarioStatus.Success.name()); - result.setEndTime(System.currentTimeMillis()); definitionExecResultMapper.updateByPrimaryKeySelective(result); } else { ApiScenarioReport report = apiScenarioReportMapper.selectByPrimaryKey(reportId); if (report != null) { + if (!StringUtils.equalsAnyIgnoreCase(report.getStatus(), APITestStatus.Rerunning.name())) { + report.setEndTime(System.currentTimeMillis()); + } ApiScenarioReportResultExample example = new ApiScenarioReportResultExample(); example.createCriteria().andReportIdEqualTo(reportId).andStatusEqualTo(ScenarioStatus.Error.name()); long size = apiScenarioReportResultMapper.countByExample(example); report.setStatus(size > 0 ? ScenarioStatus.Error.name() : ScenarioStatus.Success.name()); - report.setEndTime(System.currentTimeMillis()); // 更新报告 apiScenarioReportMapper.updateByPrimaryKey(report); } @@ -828,6 +832,9 @@ public class ApiScenarioReportService { report.setProjectId(projectId); report.setScenarioName(scenarioName); report.setScenarioId(scenarioId); + if (config != null) { + report.setEnvConfig(JSON.toJSONString(config)); + } report.setReportType(ReportTypeConstants.SCENARIO_INDEPENDENT.name()); return report; } @@ -899,7 +906,7 @@ public class ApiScenarioReportService { public void reName(ApiScenarioReport reportRequest) { if (StringUtils.equalsIgnoreCase(reportRequest.getReportType(), ReportTypeConstants.API_INDEPENDENT.name())) { - ApiDefinitionExecResult result = definitionExecResultMapper.selectByPrimaryKey(reportRequest.getId()); + ApiDefinitionExecResultWithBLOBs result = definitionExecResultMapper.selectByPrimaryKey(reportRequest.getId()); if (result != null) { result.setName(reportRequest.getName()); definitionExecResultMapper.updateByPrimaryKeySelective(result); diff --git a/backend/src/main/java/io/metersphere/api/service/ApiScenarioReportStructureService.java b/backend/src/main/java/io/metersphere/api/service/ApiScenarioReportStructureService.java index d5586fdda4..a13a5227be 100644 --- a/backend/src/main/java/io/metersphere/api/service/ApiScenarioReportStructureService.java +++ b/backend/src/main/java/io/metersphere/api/service/ApiScenarioReportStructureService.java @@ -69,6 +69,18 @@ public class ApiScenarioReportStructureService { this.save(reportId, dtoList); } + public List get(List apiScenarios, String reportType) { + List dtoList = new LinkedList<>(); + for (ApiScenarioWithBLOBs bos : apiScenarios) { + StepTreeDTO dto = dataFormatting(bos, reportType); + dtoList.add(dto); + } + if (LoggerUtil.getLogger().isDebugEnabled()) { + LoggerUtil.debug("Scenario run-执行脚本装载-生成场景报告结构:" + JSON.toJSONString(dtoList)); + } + return dtoList; + } + public void saveUi(List uiScenarios, String reportId, String reportType) { List dtoList = new LinkedList<>(); for (UiScenarioWithBLOBs bos : uiScenarios) { @@ -104,6 +116,17 @@ public class ApiScenarioReportStructureService { mapper.insert(structure); } + public void update(String reportId, List dtoList) { + ApiScenarioReportStructureExample example = new ApiScenarioReportStructureExample(); + example.createCriteria().andReportIdEqualTo(reportId); + List structures = mapper.selectByExampleWithBLOBs(example); + if (CollectionUtils.isNotEmpty(structures)) { + ApiScenarioReportStructureWithBLOBs structure = structures.get(0); + structure.setResourceTree(JSON.toJSONString(dtoList).getBytes(StandardCharsets.UTF_8)); + mapper.updateByPrimaryKeySelective(structure); + } + } + public void update(String reportId, String console) { ApiScenarioReportStructureExample example = new ApiScenarioReportStructureExample(); example.createCriteria().andReportIdEqualTo(reportId); @@ -225,6 +248,7 @@ public class ApiScenarioReportStructureService { private void calculateStep(List dtoList, AtomicLong stepTotal, AtomicLong stepError, AtomicLong stepErrorCode, AtomicLong stepUnExecute) { for (StepTreeDTO root : dtoList) { + int unExecSize = 0; if (CollectionUtils.isNotEmpty(root.getChildren())) { stepTotal.set((stepTotal.longValue() + root.getChildren().size())); for (StepTreeDTO step : root.getChildren()) { @@ -234,9 +258,11 @@ public class ApiScenarioReportStructureService { stepErrorCode.set(stepErrorCode.longValue() + 1); } else if (!StringUtils.equalsIgnoreCase(step.getTotalStatus(), "success")) { stepUnExecute.set(stepUnExecute.longValue() + 1); + unExecSize++; } } } + root.setUnExecuteTotal(unExecSize); } } @@ -413,10 +439,10 @@ public class ApiScenarioReportStructureService { ApiDefinitionExecResultExample example = new ApiDefinitionExecResultExample(); example.createCriteria().andIntegratedReportIdEqualTo(reportId); example.setOrderByClause("create_time asc"); - List reportResults = definitionExecResultMapper.selectByExampleWithBLOBs(example); + List reportResults = definitionExecResultMapper.selectByExampleWithBLOBs(example); List resultVos = new LinkedList<>(); for (int i = 0; i < reportResults.size(); i++) { - ApiDefinitionExecResult item = reportResults.get(i); + ApiDefinitionExecResultWithBLOBs item = reportResults.get(i); if (StringUtils.equalsIgnoreCase(item.getErrorCode(), "null")) { item.setErrorCode(null); } @@ -502,11 +528,15 @@ public class ApiScenarioReportStructureService { } public ApiScenarioReportDTO assembleReport(String reportId, boolean selectReportContent) { - ApiScenarioReport report = scenarioReportMapper.selectByPrimaryKey(reportId); + ApiScenarioReportWithBLOBs report = scenarioReportMapper.selectByPrimaryKey(reportId); if (report != null && report.getReportType().equals(ReportTypeConstants.API_INTEGRATED.name())) { return this.apiIntegratedReport(reportId); } else { - return this.getReport(reportId, selectReportContent); + ApiScenarioReportDTO dto = this.getReport(reportId, selectReportContent); + dto.setActuator(report.getActuator()); + dto.setName(report.getName()); + dto.setEnvConfig(report.getEnvConfig()); + return dto; } } diff --git a/backend/src/main/java/io/metersphere/api/service/RemakeReportService.java b/backend/src/main/java/io/metersphere/api/service/RemakeReportService.java index fe0a4a989a..6d2e6fe7f2 100644 --- a/backend/src/main/java/io/metersphere/api/service/RemakeReportService.java +++ b/backend/src/main/java/io/metersphere/api/service/RemakeReportService.java @@ -40,7 +40,7 @@ public class RemakeReportService { try { // 清理零时报告 if (StringUtils.equalsAnyIgnoreCase(request.getRunMode(), ApiRunMode.API_PLAN.name(), ApiRunMode.SCHEDULE_API_PLAN.name(), ApiRunMode.JENKINS_API_PLAN.name())) { - ApiDefinitionExecResult result = execResultMapper.selectByPrimaryKey(request.getReportId()); + ApiDefinitionExecResultWithBLOBs result = execResultMapper.selectByPrimaryKey(request.getReportId()); if (result != null) { result.setStatus("error"); result.setEndTime(System.currentTimeMillis()); @@ -60,7 +60,7 @@ public class RemakeReportService { } } } else if (StringUtils.equals(request.getRunMode(), ApiRunMode.DEFINITION.name())) { - ApiDefinitionExecResult result = execResultMapper.selectByPrimaryKey(request.getReportId()); + ApiDefinitionExecResultWithBLOBs result = execResultMapper.selectByPrimaryKey(request.getReportId()); if (result != null) { result.setStatus("error"); result.setEndTime(System.currentTimeMillis()); @@ -89,7 +89,7 @@ public class RemakeReportService { apiScenarioReportMapper.updateByPrimaryKey(report); } } else if (StringUtils.equalsAny(request.getRunMode(), ApiRunMode.SCHEDULE_SCENARIO_PLAN.name(), ApiRunMode.JENKINS_SCENARIO_PLAN.name())) { - ApiScenarioReport report = apiScenarioReportMapper.selectByPrimaryKey(request.getReportId()); + ApiScenarioReportWithBLOBs report = apiScenarioReportMapper.selectByPrimaryKey(request.getReportId()); if (report != null) { report.setEndTime(System.currentTimeMillis()); report.setStatus(APITestStatus.Error.name()); @@ -108,7 +108,7 @@ public class RemakeReportService { apiScenarioReportMapper.updateByPrimaryKeySelective(report); } } else { - ApiScenarioReport report = apiScenarioReportMapper.selectByPrimaryKey(request.getReportId()); + ApiScenarioReportWithBLOBs report = apiScenarioReportMapper.selectByPrimaryKey(request.getReportId()); if (report != null) { report.setStatus(APITestStatus.Error.name()); apiScenarioReportMapper.updateByPrimaryKeySelective(report); @@ -135,7 +135,7 @@ public class RemakeReportService { } } - public void remakeScenario(String runMode, String scenarioId, ApiScenarioWithBLOBs scenarioWithBLOBs, ApiScenarioReport report) { + public void remakeScenario(String runMode, String scenarioId, ApiScenarioWithBLOBs scenarioWithBLOBs, ApiScenarioReportWithBLOBs report) { // 生成失败报告 if (StringUtils.equalsAny(runMode, ApiRunMode.SCHEDULE_SCENARIO_PLAN.name(), ApiRunMode.JENKINS_SCENARIO_PLAN.name(), ApiRunMode.SCENARIO_PLAN.name())) { TestPlanApiScenario testPlanApiScenario = testPlanApiScenarioMapper.selectByPrimaryKey(scenarioId); diff --git a/backend/src/main/java/io/metersphere/api/service/TestResultService.java b/backend/src/main/java/io/metersphere/api/service/TestResultService.java index 340bbf3195..8e185a546a 100644 --- a/backend/src/main/java/io/metersphere/api/service/TestResultService.java +++ b/backend/src/main/java/io/metersphere/api/service/TestResultService.java @@ -209,7 +209,7 @@ public class TestResultService { } } } else if (StringUtils.equals(dto.getRunMode(), ApiRunMode.DEFINITION.name())) { - ApiDefinitionExecResult record = new ApiDefinitionExecResult(); + ApiDefinitionExecResultWithBLOBs record = new ApiDefinitionExecResultWithBLOBs(); record.setId(dto.getReportId()); record.setStatus("STOP"); diff --git a/backend/src/main/java/io/metersphere/base/domain/ApiDefinitionExecResult.java b/backend/src/main/java/io/metersphere/base/domain/ApiDefinitionExecResult.java index 932112533e..62f81775b3 100644 --- a/backend/src/main/java/io/metersphere/base/domain/ApiDefinitionExecResult.java +++ b/backend/src/main/java/io/metersphere/base/domain/ApiDefinitionExecResult.java @@ -38,7 +38,5 @@ public class ApiDefinitionExecResult implements Serializable { private String reportType; - private String content; - private static final long serialVersionUID = 1L; } \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/domain/ApiDefinitionExecResultWithBLOBs.java b/backend/src/main/java/io/metersphere/base/domain/ApiDefinitionExecResultWithBLOBs.java index 9106692cfa..1a084ab65f 100644 --- a/backend/src/main/java/io/metersphere/base/domain/ApiDefinitionExecResultWithBLOBs.java +++ b/backend/src/main/java/io/metersphere/base/domain/ApiDefinitionExecResultWithBLOBs.java @@ -1,17 +1,18 @@ package io.metersphere.base.domain; -import java.io.Serializable; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; +import java.io.Serializable; + @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) public class ApiDefinitionExecResultWithBLOBs extends ApiDefinitionExecResult implements Serializable { private String content; - private String errorCode; + private String envConfig; private static final long serialVersionUID = 1L; } \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/domain/ApiExecutionQueueDetail.java b/backend/src/main/java/io/metersphere/base/domain/ApiExecutionQueueDetail.java index 68e242a48f..db9a908877 100644 --- a/backend/src/main/java/io/metersphere/base/domain/ApiExecutionQueueDetail.java +++ b/backend/src/main/java/io/metersphere/base/domain/ApiExecutionQueueDetail.java @@ -1,8 +1,9 @@ package io.metersphere.base.domain; -import java.io.Serializable; import lombok.Data; +import java.io.Serializable; + @Data public class ApiExecutionQueueDetail implements Serializable { private String id; @@ -19,6 +20,10 @@ public class ApiExecutionQueueDetail implements Serializable { private Long createTime; + private Boolean retryEnable; + + private Long retryNumber; + private String evnMap; private static final long serialVersionUID = 1L; diff --git a/backend/src/main/java/io/metersphere/base/domain/ApiExecutionQueueDetailExample.java b/backend/src/main/java/io/metersphere/base/domain/ApiExecutionQueueDetailExample.java index 0de8451b8b..233253d439 100644 --- a/backend/src/main/java/io/metersphere/base/domain/ApiExecutionQueueDetailExample.java +++ b/backend/src/main/java/io/metersphere/base/domain/ApiExecutionQueueDetailExample.java @@ -573,6 +573,126 @@ public class ApiExecutionQueueDetailExample { addCriterion("create_time not between", value1, value2, "createTime"); return (Criteria) this; } + + public Criteria andRetryEnableIsNull() { + addCriterion("retry_enable is null"); + return (Criteria) this; + } + + public Criteria andRetryEnableIsNotNull() { + addCriterion("retry_enable is not null"); + return (Criteria) this; + } + + public Criteria andRetryEnableEqualTo(Boolean value) { + addCriterion("retry_enable =", value, "retryEnable"); + return (Criteria) this; + } + + public Criteria andRetryEnableNotEqualTo(Boolean value) { + addCriterion("retry_enable <>", value, "retryEnable"); + return (Criteria) this; + } + + public Criteria andRetryEnableGreaterThan(Boolean value) { + addCriterion("retry_enable >", value, "retryEnable"); + return (Criteria) this; + } + + public Criteria andRetryEnableGreaterThanOrEqualTo(Boolean value) { + addCriterion("retry_enable >=", value, "retryEnable"); + return (Criteria) this; + } + + public Criteria andRetryEnableLessThan(Boolean value) { + addCriterion("retry_enable <", value, "retryEnable"); + return (Criteria) this; + } + + public Criteria andRetryEnableLessThanOrEqualTo(Boolean value) { + addCriterion("retry_enable <=", value, "retryEnable"); + return (Criteria) this; + } + + public Criteria andRetryEnableIn(List values) { + addCriterion("retry_enable in", values, "retryEnable"); + return (Criteria) this; + } + + public Criteria andRetryEnableNotIn(List values) { + addCriterion("retry_enable not in", values, "retryEnable"); + return (Criteria) this; + } + + public Criteria andRetryEnableBetween(Boolean value1, Boolean value2) { + addCriterion("retry_enable between", value1, value2, "retryEnable"); + return (Criteria) this; + } + + public Criteria andRetryEnableNotBetween(Boolean value1, Boolean value2) { + addCriterion("retry_enable not between", value1, value2, "retryEnable"); + return (Criteria) this; + } + + public Criteria andRetryNumberIsNull() { + addCriterion("retry_number is null"); + return (Criteria) this; + } + + public Criteria andRetryNumberIsNotNull() { + addCriterion("retry_number is not null"); + return (Criteria) this; + } + + public Criteria andRetryNumberEqualTo(Long value) { + addCriterion("retry_number =", value, "retryNumber"); + return (Criteria) this; + } + + public Criteria andRetryNumberNotEqualTo(Long value) { + addCriterion("retry_number <>", value, "retryNumber"); + return (Criteria) this; + } + + public Criteria andRetryNumberGreaterThan(Long value) { + addCriterion("retry_number >", value, "retryNumber"); + return (Criteria) this; + } + + public Criteria andRetryNumberGreaterThanOrEqualTo(Long value) { + addCriterion("retry_number >=", value, "retryNumber"); + return (Criteria) this; + } + + public Criteria andRetryNumberLessThan(Long value) { + addCriterion("retry_number <", value, "retryNumber"); + return (Criteria) this; + } + + public Criteria andRetryNumberLessThanOrEqualTo(Long value) { + addCriterion("retry_number <=", value, "retryNumber"); + return (Criteria) this; + } + + public Criteria andRetryNumberIn(List values) { + addCriterion("retry_number in", values, "retryNumber"); + return (Criteria) this; + } + + public Criteria andRetryNumberNotIn(List values) { + addCriterion("retry_number not in", values, "retryNumber"); + return (Criteria) this; + } + + public Criteria andRetryNumberBetween(Long value1, Long value2) { + addCriterion("retry_number between", value1, value2, "retryNumber"); + return (Criteria) this; + } + + public Criteria andRetryNumberNotBetween(Long value1, Long value2) { + addCriterion("retry_number not between", value1, value2, "retryNumber"); + return (Criteria) this; + } } public static class Criteria extends GeneratedCriteria { diff --git a/backend/src/main/java/io/metersphere/base/domain/ApiScenarioReport.java b/backend/src/main/java/io/metersphere/base/domain/ApiScenarioReport.java index c21154034b..48da6d947e 100644 --- a/backend/src/main/java/io/metersphere/base/domain/ApiScenarioReport.java +++ b/backend/src/main/java/io/metersphere/base/domain/ApiScenarioReport.java @@ -40,7 +40,5 @@ public class ApiScenarioReport implements Serializable { private String reportType; - private String description; - private static final long serialVersionUID = 1L; } \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/domain/ApiScenarioReportWithBLOBs.java b/backend/src/main/java/io/metersphere/base/domain/ApiScenarioReportWithBLOBs.java new file mode 100644 index 0000000000..94ac76cfd5 --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/domain/ApiScenarioReportWithBLOBs.java @@ -0,0 +1,18 @@ +package io.metersphere.base.domain; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.io.Serializable; + +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class ApiScenarioReportWithBLOBs extends ApiScenarioReport implements Serializable { + private String description; + + private String envConfig; + + private static final long serialVersionUID = 1L; +} \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/mapper/ApiDefinitionExecResultMapper.java b/backend/src/main/java/io/metersphere/base/mapper/ApiDefinitionExecResultMapper.java index 174b87cc6d..eaa00c6f5c 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ApiDefinitionExecResultMapper.java +++ b/backend/src/main/java/io/metersphere/base/mapper/ApiDefinitionExecResultMapper.java @@ -2,9 +2,11 @@ package io.metersphere.base.mapper; import io.metersphere.base.domain.ApiDefinitionExecResult; import io.metersphere.base.domain.ApiDefinitionExecResultExample; -import java.util.List; +import io.metersphere.base.domain.ApiDefinitionExecResultWithBLOBs; import org.apache.ibatis.annotations.Param; +import java.util.List; + public interface ApiDefinitionExecResultMapper { long countByExample(ApiDefinitionExecResultExample example); @@ -12,25 +14,25 @@ public interface ApiDefinitionExecResultMapper { int deleteByPrimaryKey(String id); - int insert(ApiDefinitionExecResult record); + int insert(ApiDefinitionExecResultWithBLOBs record); - int insertSelective(ApiDefinitionExecResult record); + int insertSelective(ApiDefinitionExecResultWithBLOBs record); - List selectByExampleWithBLOBs(ApiDefinitionExecResultExample example); + List selectByExampleWithBLOBs(ApiDefinitionExecResultExample example); List selectByExample(ApiDefinitionExecResultExample example); - ApiDefinitionExecResult selectByPrimaryKey(String id); + ApiDefinitionExecResultWithBLOBs selectByPrimaryKey(String id); - int updateByExampleSelective(@Param("record") ApiDefinitionExecResult record, @Param("example") ApiDefinitionExecResultExample example); + int updateByExampleSelective(@Param("record") ApiDefinitionExecResultWithBLOBs record, @Param("example") ApiDefinitionExecResultExample example); - int updateByExampleWithBLOBs(@Param("record") ApiDefinitionExecResult record, @Param("example") ApiDefinitionExecResultExample example); + int updateByExampleWithBLOBs(@Param("record") ApiDefinitionExecResultWithBLOBs record, @Param("example") ApiDefinitionExecResultExample example); int updateByExample(@Param("record") ApiDefinitionExecResult record, @Param("example") ApiDefinitionExecResultExample example); - int updateByPrimaryKeySelective(ApiDefinitionExecResult record); + int updateByPrimaryKeySelective(ApiDefinitionExecResultWithBLOBs record); - int updateByPrimaryKeyWithBLOBs(ApiDefinitionExecResult record); + int updateByPrimaryKeyWithBLOBs(ApiDefinitionExecResultWithBLOBs record); int updateByPrimaryKey(ApiDefinitionExecResult record); } \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/mapper/ApiDefinitionExecResultMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ApiDefinitionExecResultMapper.xml index e5473889ee..1f0a9a36c5 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ApiDefinitionExecResultMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ApiDefinitionExecResultMapper.xml @@ -19,8 +19,9 @@ - + + @@ -86,7 +87,7 @@ report_type - content + content, env_config @@ -319,6 +328,9 @@ content = #{record.content,jdbcType=LONGVARCHAR}, + + env_config = #{record.envConfig,jdbcType=LONGVARCHAR}, + @@ -342,7 +354,8 @@ project_id = #{record.projectId,jdbcType=VARCHAR}, integrated_report_id = #{record.integratedReportId,jdbcType=VARCHAR}, report_type = #{record.reportType,jdbcType=VARCHAR}, - content = #{record.content,jdbcType=LONGVARCHAR} + content = #{record.content,jdbcType=LONGVARCHAR}, + env_config = #{record.envConfig,jdbcType=LONGVARCHAR} @@ -369,7 +382,7 @@ - + update api_definition_exec_result @@ -420,10 +433,13 @@ content = #{content,jdbcType=LONGVARCHAR}, + + env_config = #{envConfig,jdbcType=LONGVARCHAR}, + where id = #{id,jdbcType=VARCHAR} - + update api_definition_exec_result set `name` = #{name,jdbcType=VARCHAR}, resource_id = #{resourceId,jdbcType=VARCHAR}, @@ -440,7 +456,8 @@ project_id = #{projectId,jdbcType=VARCHAR}, integrated_report_id = #{integratedReportId,jdbcType=VARCHAR}, report_type = #{reportType,jdbcType=VARCHAR}, - content = #{content,jdbcType=LONGVARCHAR} + content = #{content,jdbcType=LONGVARCHAR}, + env_config = #{envConfig,jdbcType=LONGVARCHAR} where id = #{id,jdbcType=VARCHAR} diff --git a/backend/src/main/java/io/metersphere/base/mapper/ApiExecutionQueueDetailMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ApiExecutionQueueDetailMapper.xml index 47fc27fc86..2f6fa4be82 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ApiExecutionQueueDetailMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ApiExecutionQueueDetailMapper.xml @@ -9,6 +9,8 @@ + + @@ -72,7 +74,7 @@ - id, queue_id, sort, report_id, test_id, `type`, create_time + id, queue_id, sort, report_id, test_id, `type`, create_time, retry_enable, retry_number evn_map @@ -128,10 +130,12 @@ insert into api_execution_queue_detail (id, queue_id, sort, report_id, test_id, `type`, - create_time, evn_map) + create_time, retry_enable, retry_number, + evn_map) values (#{id,jdbcType=VARCHAR}, #{queueId,jdbcType=VARCHAR}, #{sort,jdbcType=INTEGER}, #{reportId,jdbcType=VARCHAR}, #{testId,jdbcType=VARCHAR}, #{type,jdbcType=VARCHAR}, - #{createTime,jdbcType=BIGINT}, #{evnMap,jdbcType=LONGVARCHAR}) + #{createTime,jdbcType=BIGINT}, #{retryEnable,jdbcType=BIT}, #{retryNumber,jdbcType=BIGINT}, + #{evnMap,jdbcType=LONGVARCHAR}) insert into api_execution_queue_detail @@ -157,6 +161,12 @@ create_time, + + retry_enable, + + + retry_number, + evn_map, @@ -183,6 +193,12 @@ #{createTime,jdbcType=BIGINT}, + + #{retryEnable,jdbcType=BIT}, + + + #{retryNumber,jdbcType=BIGINT}, + #{evnMap,jdbcType=LONGVARCHAR}, @@ -218,6 +234,12 @@ create_time = #{record.createTime,jdbcType=BIGINT}, + + retry_enable = #{record.retryEnable,jdbcType=BIT}, + + + retry_number = #{record.retryNumber,jdbcType=BIGINT}, + evn_map = #{record.evnMap,jdbcType=LONGVARCHAR}, @@ -235,6 +257,8 @@ test_id = #{record.testId,jdbcType=VARCHAR}, `type` = #{record.type,jdbcType=VARCHAR}, create_time = #{record.createTime,jdbcType=BIGINT}, + retry_enable = #{record.retryEnable,jdbcType=BIT}, + retry_number = #{record.retryNumber,jdbcType=BIGINT}, evn_map = #{record.evnMap,jdbcType=LONGVARCHAR} @@ -248,7 +272,9 @@ report_id = #{record.reportId,jdbcType=VARCHAR}, test_id = #{record.testId,jdbcType=VARCHAR}, `type` = #{record.type,jdbcType=VARCHAR}, - create_time = #{record.createTime,jdbcType=BIGINT} + create_time = #{record.createTime,jdbcType=BIGINT}, + retry_enable = #{record.retryEnable,jdbcType=BIT}, + retry_number = #{record.retryNumber,jdbcType=BIGINT} @@ -274,6 +300,12 @@ create_time = #{createTime,jdbcType=BIGINT}, + + retry_enable = #{retryEnable,jdbcType=BIT}, + + + retry_number = #{retryNumber,jdbcType=BIGINT}, + evn_map = #{evnMap,jdbcType=LONGVARCHAR}, @@ -288,6 +320,8 @@ test_id = #{testId,jdbcType=VARCHAR}, `type` = #{type,jdbcType=VARCHAR}, create_time = #{createTime,jdbcType=BIGINT}, + retry_enable = #{retryEnable,jdbcType=BIT}, + retry_number = #{retryNumber,jdbcType=BIGINT}, evn_map = #{evnMap,jdbcType=LONGVARCHAR} where id = #{id,jdbcType=VARCHAR} @@ -298,7 +332,9 @@ report_id = #{reportId,jdbcType=VARCHAR}, test_id = #{testId,jdbcType=VARCHAR}, `type` = #{type,jdbcType=VARCHAR}, - create_time = #{createTime,jdbcType=BIGINT} + create_time = #{createTime,jdbcType=BIGINT}, + retry_enable = #{retryEnable,jdbcType=BIT}, + retry_number = #{retryNumber,jdbcType=BIGINT} where id = #{id,jdbcType=VARCHAR} \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/mapper/ApiScenarioReportMapper.java b/backend/src/main/java/io/metersphere/base/mapper/ApiScenarioReportMapper.java index d2d9a7f9af..1f946f5e0b 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ApiScenarioReportMapper.java +++ b/backend/src/main/java/io/metersphere/base/mapper/ApiScenarioReportMapper.java @@ -2,9 +2,11 @@ package io.metersphere.base.mapper; import io.metersphere.base.domain.ApiScenarioReport; import io.metersphere.base.domain.ApiScenarioReportExample; -import java.util.List; +import io.metersphere.base.domain.ApiScenarioReportWithBLOBs; import org.apache.ibatis.annotations.Param; +import java.util.List; + public interface ApiScenarioReportMapper { long countByExample(ApiScenarioReportExample example); @@ -12,25 +14,25 @@ public interface ApiScenarioReportMapper { int deleteByPrimaryKey(String id); - int insert(ApiScenarioReport record); + int insert(ApiScenarioReportWithBLOBs record); - int insertSelective(ApiScenarioReport record); + int insertSelective(ApiScenarioReportWithBLOBs record); - List selectByExampleWithBLOBs(ApiScenarioReportExample example); + List selectByExampleWithBLOBs(ApiScenarioReportExample example); List selectByExample(ApiScenarioReportExample example); - ApiScenarioReport selectByPrimaryKey(String id); + ApiScenarioReportWithBLOBs selectByPrimaryKey(String id); - int updateByExampleSelective(@Param("record") ApiScenarioReport record, @Param("example") ApiScenarioReportExample example); + int updateByExampleSelective(@Param("record") ApiScenarioReportWithBLOBs record, @Param("example") ApiScenarioReportExample example); - int updateByExampleWithBLOBs(@Param("record") ApiScenarioReport record, @Param("example") ApiScenarioReportExample example); + int updateByExampleWithBLOBs(@Param("record") ApiScenarioReportWithBLOBs record, @Param("example") ApiScenarioReportExample example); int updateByExample(@Param("record") ApiScenarioReport record, @Param("example") ApiScenarioReportExample example); - int updateByPrimaryKeySelective(ApiScenarioReport record); + int updateByPrimaryKeySelective(ApiScenarioReportWithBLOBs record); - int updateByPrimaryKeyWithBLOBs(ApiScenarioReport record); + int updateByPrimaryKeyWithBLOBs(ApiScenarioReportWithBLOBs record); int updateByPrimaryKey(ApiScenarioReport record); } \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/mapper/ApiScenarioReportMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ApiScenarioReportMapper.xml index 6d4baac41e..af5af4da22 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ApiScenarioReportMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ApiScenarioReportMapper.xml @@ -20,8 +20,9 @@ - + + @@ -87,7 +88,7 @@ version_id, report_type - description + description, env_config @@ -331,6 +338,9 @@ description = #{record.description,jdbcType=LONGVARCHAR}, + + env_config = #{record.envConfig,jdbcType=LONGVARCHAR}, + @@ -355,7 +365,8 @@ report_version = #{record.reportVersion,jdbcType=INTEGER}, version_id = #{record.versionId,jdbcType=VARCHAR}, report_type = #{record.reportType,jdbcType=VARCHAR}, - description = #{record.description,jdbcType=LONGVARCHAR} + description = #{record.description,jdbcType=LONGVARCHAR}, + env_config = #{record.envConfig,jdbcType=LONGVARCHAR} @@ -383,7 +394,7 @@ - + update api_scenario_report @@ -437,10 +448,13 @@ description = #{description,jdbcType=LONGVARCHAR}, + + env_config = #{envConfig,jdbcType=LONGVARCHAR}, + where id = #{id,jdbcType=VARCHAR} - + update api_scenario_report set project_id = #{projectId,jdbcType=VARCHAR}, `name` = #{name,jdbcType=VARCHAR}, @@ -458,7 +472,8 @@ report_version = #{reportVersion,jdbcType=INTEGER}, version_id = #{versionId,jdbcType=VARCHAR}, report_type = #{reportType,jdbcType=VARCHAR}, - description = #{description,jdbcType=LONGVARCHAR} + description = #{description,jdbcType=LONGVARCHAR}, + env_config = #{envConfig,jdbcType=LONGVARCHAR} where id = #{id,jdbcType=VARCHAR} diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionExecResultMapper.java b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionExecResultMapper.java index e73209a69b..a06bcb5cd2 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionExecResultMapper.java +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionExecResultMapper.java @@ -4,6 +4,7 @@ import io.metersphere.api.dto.QueryAPIReportRequest; import io.metersphere.api.dto.datacount.ExecutedCaseInfoResult; import io.metersphere.base.domain.ApiDefinitionExecResult; import io.metersphere.base.domain.ApiDefinitionExecResultExpand; +import io.metersphere.base.domain.ApiDefinitionExecResultWithBLOBs; import io.metersphere.task.dto.TaskCenterRequest; import io.metersphere.track.dto.PlanReportCaseDTO; import org.apache.ibatis.annotations.InsertProvider; @@ -18,9 +19,9 @@ public interface ExtApiDefinitionExecResultMapper { void deleteByResourceId(String id); - ApiDefinitionExecResult selectMaxResultByResourceId(String resourceId); + ApiDefinitionExecResultWithBLOBs selectMaxResultByResourceId(String resourceId); - ApiDefinitionExecResult selectMaxResultByResourceIdAndType(String resourceId, String type); + ApiDefinitionExecResultWithBLOBs selectMaxResultByResourceIdAndType(String resourceId, String type); long countByProjectIDAndCreateInThisWeek(@Param("projectId") String projectId, @Param("firstDayTimestamp") long firstDayTimestamp, @Param("lastDayTimestamp") long lastDayTimestamp); @@ -31,7 +32,7 @@ public interface ExtApiDefinitionExecResultMapper { String selectExecResult(String resourceId); - ApiDefinitionExecResult selectPlanApiMaxResultByTestIdAndType(String resourceId, String type); + ApiDefinitionExecResultWithBLOBs selectPlanApiMaxResultByTestIdAndType(String resourceId, String type); List selectStatusByIdList(@Param("ids") Collection values); diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionExecResultMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionExecResultMapper.xml index 546e1ac589..46b795890c 100644 --- a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionExecResultMapper.xml +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtApiDefinitionExecResultMapper.xml @@ -6,12 +6,12 @@ from api_definition_exec_result where resource_id = #{id,jdbcType=VARCHAR} - select * from api_definition_exec_result where resource_id = #{resourceId,jdbcType=VARCHAR} ORDER BY create_time DESC LIMIT 1 + + + DELETE from api_scenario_report_result where report_id = #{reportId,jdbcType=VARCHAR} and + + resource_id like CONCAT('%',#{id},'%') + + + + diff --git a/backend/src/main/java/io/metersphere/commons/constants/APITestStatus.java b/backend/src/main/java/io/metersphere/commons/constants/APITestStatus.java index 7508808a2b..011d5050b3 100644 --- a/backend/src/main/java/io/metersphere/commons/constants/APITestStatus.java +++ b/backend/src/main/java/io/metersphere/commons/constants/APITestStatus.java @@ -1,5 +1,5 @@ package io.metersphere.commons.constants; public enum APITestStatus { - Saved, Starting, Running, Reporting, Completed, Debug, Error, Success,Underway,Waiting + Saved, Starting, Running, Reporting, Completed, Debug, Error, Success,Underway,Waiting,Rerunning } diff --git a/backend/src/main/java/io/metersphere/commons/constants/ReportTypeConstants.java b/backend/src/main/java/io/metersphere/commons/constants/ReportTypeConstants.java index b7a6cf1fc3..e033423042 100644 --- a/backend/src/main/java/io/metersphere/commons/constants/ReportTypeConstants.java +++ b/backend/src/main/java/io/metersphere/commons/constants/ReportTypeConstants.java @@ -6,5 +6,6 @@ public enum ReportTypeConstants { API_INTEGRATED, API_INDEPENDENT, UI_INTEGRATED, - UI_INDEPENDENT + UI_INDEPENDENT, + TEST_PLAN } diff --git a/backend/src/main/java/io/metersphere/task/service/TaskService.java b/backend/src/main/java/io/metersphere/task/service/TaskService.java index c0de84af0c..5214ccae51 100644 --- a/backend/src/main/java/io/metersphere/task/service/TaskService.java +++ b/backend/src/main/java/io/metersphere/task/service/TaskService.java @@ -148,7 +148,7 @@ public class TaskService { apiExecutionQueueService.stop(request.getReportId()); PoolExecBlockingQueueUtil.offer(request.getReportId()); if (StringUtils.equals(request.getType(), "API")) { - ApiDefinitionExecResult result = apiDefinitionExecResultMapper.selectByPrimaryKey(request.getReportId()); + ApiDefinitionExecResultWithBLOBs result = apiDefinitionExecResultMapper.selectByPrimaryKey(request.getReportId()); if (result != null) { result.setStatus("STOP"); apiDefinitionExecResultMapper.updateByPrimaryKeySelective(result); @@ -156,7 +156,7 @@ public class TaskService { } } if (StringUtils.equals(request.getType(), "SCENARIO")) { - ApiScenarioReport report = apiScenarioReportMapper.selectByPrimaryKey(request.getReportId()); + ApiScenarioReportWithBLOBs report = apiScenarioReportMapper.selectByPrimaryKey(request.getReportId()); if (report != null) { report.setStatus("STOP"); apiScenarioReportMapper.updateByPrimaryKeySelective(report); diff --git a/backend/src/main/java/io/metersphere/track/request/testplan/TestplanRunRequest.java b/backend/src/main/java/io/metersphere/track/request/testplan/TestplanRunRequest.java index 3fd272fd3f..6f134a6993 100644 --- a/backend/src/main/java/io/metersphere/track/request/testplan/TestplanRunRequest.java +++ b/backend/src/main/java/io/metersphere/track/request/testplan/TestplanRunRequest.java @@ -24,7 +24,11 @@ public class TestplanRunRequest { private String environmentGroupId; private List testPlanIds; private Boolean isAll; - private String reportId; + private String reportId; private QueryTestPlanRequest queryTestPlanRequest; + + // 失败重试 + private boolean retryEnable; + private long retryNum; } diff --git a/backend/src/main/java/io/metersphere/track/service/TestCaseService.java b/backend/src/main/java/io/metersphere/track/service/TestCaseService.java index 68e37f73fa..36c08adaf5 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestCaseService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestCaseService.java @@ -1913,8 +1913,6 @@ public class TestCaseService { testCaseMap = testCaseWithBLOBs.stream().collect(Collectors.toMap(TestCaseWithBLOBs::getId, t -> t)); } - String lastAddId = null; - for (TestCaseMinderEditRequest.TestCaseMinderEditItem item : data) { if (StringUtils.isBlank(item.getNodeId()) || item.getNodeId().equals("root")) { item.setNodeId(""); diff --git a/backend/src/main/java/io/metersphere/track/service/TestPlanReportService.java b/backend/src/main/java/io/metersphere/track/service/TestPlanReportService.java index f036175d10..47c09e121f 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestPlanReportService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestPlanReportService.java @@ -3,7 +3,6 @@ package io.metersphere.track.service; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; -import com.esotericsoftware.minlog.Log; import io.metersphere.api.dto.automation.TestPlanFailureApiDTO; import io.metersphere.api.dto.automation.TestPlanFailureScenarioDTO; import io.metersphere.api.service.ApiDefinitionExecResultService; @@ -15,15 +14,9 @@ import io.metersphere.base.mapper.ext.*; import io.metersphere.commons.constants.*; import io.metersphere.commons.utils.*; import io.metersphere.constants.RunModeConstants; -import io.metersphere.dto.BaseSystemConfigDTO; import io.metersphere.dto.TestPlanExecuteReportDTO; -import io.metersphere.dto.UserDTO; -import io.metersphere.i18n.Translator; import io.metersphere.log.vo.OperatingLogDetails; -import io.metersphere.notice.sender.NoticeModel; -import io.metersphere.notice.service.NoticeSendService; import io.metersphere.service.ProjectService; -import io.metersphere.service.SystemParameterService; import io.metersphere.service.UserService; import io.metersphere.track.dto.*; import io.metersphere.track.request.report.QueryTestPlanReportRequest; @@ -31,7 +24,6 @@ import io.metersphere.track.request.report.TestPlanReportSaveRequest; import io.metersphere.track.request.testcase.QueryTestPlanRequest; import io.metersphere.track.request.testplan.LoadCaseRequest; import io.metersphere.track.request.testplan.TestplanRunRequest; -import org.apache.commons.beanutils.BeanMap; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.MapUtils; import org.apache.commons.lang3.StringUtils; @@ -438,7 +430,7 @@ public class TestPlanReportService { } if (testPlanReport != null) { boolean isSendMessage = false; - if(StringUtils.equalsIgnoreCase(testPlanReport.getStatus(),ExecuteResult.RUNNING.name())){ + if (StringUtils.equalsIgnoreCase(testPlanReport.getStatus(), ExecuteResult.RUNNING.name())) { isSendMessage = true; } //初始化测试计划包含组件信息 @@ -451,7 +443,7 @@ public class TestPlanReportService { long endTime = System.currentTimeMillis(); long testCaseCount = testPlanTestCaseMapper.countByExample(testPlanTestCaseExample); boolean updateTestPlanTime = testCaseCount > 0; - if (updateTestPlanTime) { + if (updateTestPlanTime && !StringUtils.equalsAnyIgnoreCase(testPlanReport.getStatus(), APITestStatus.Rerunning.name())) { testPlanReport.setEndTime(endTime); testPlanReport.setUpdateTime(endTime); } @@ -533,8 +525,10 @@ public class TestPlanReportService { } } //更新content表对结束日期 - content.setStartTime(testPlanReport.getStartTime()); - content.setEndTime(endTime); + if (!StringUtils.equalsAnyIgnoreCase(testPlanReport.getStatus(), APITestStatus.Rerunning.name())) { + content.setStartTime(testPlanReport.getStartTime()); + content.setEndTime(endTime); + } testPlanReportContentMapper.updateByExampleSelective(content, contentExample); } //计算测试计划状态 diff --git a/backend/src/main/java/io/metersphere/track/service/TestPlanService.java b/backend/src/main/java/io/metersphere/track/service/TestPlanService.java index 0462a797ff..39387522e1 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestPlanService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestPlanService.java @@ -471,7 +471,7 @@ public class TestPlanService { statusList.addAll(testPlanLoadCaseService.getStatus(testPlanId)); TestPlanWithBLOBs testPlanWithBLOBs = testPlanMapper.selectByPrimaryKey(testPlanId); //如果测试计划是已归档状态,不处理 - if(testPlanWithBLOBs.getStatus().equals(TestPlanStatus.Archived.name())){ + if (testPlanWithBLOBs.getStatus().equals(TestPlanStatus.Archived.name())) { return; } testPlanWithBLOBs.setId(testPlanId); @@ -1936,6 +1936,8 @@ public class TestPlanService { } else { runModeConfig.setReportType(testplanRunRequest.getReportType()); } + runModeConfig.setRetryEnable(testplanRunRequest.isRetryEnable()); + runModeConfig.setRetryNum(testplanRunRequest.getRetryNum()); return runModeConfig; } diff --git a/frontend/src/business/components/api/automation/report/ApiReportDetail.vue b/frontend/src/business/components/api/automation/report/ApiReportDetail.vue index d99821e4d0..f3e50d81c6 100644 --- a/frontend/src/business/components/api/automation/report/ApiReportDetail.vue +++ b/frontend/src/business/components/api/automation/report/ApiReportDetail.vue @@ -3,65 +3,92 @@
- -
+ + + +
+ -
- - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - -
- + +
@@ -71,337 +98,298 @@ diff --git a/frontend/src/business/components/api/automation/report/ApiReportList.vue b/frontend/src/business/components/api/automation/report/ApiReportList.vue index ab8ef728c7..ccb2c3e0f6 100644 --- a/frontend/src/business/components/api/automation/report/ApiReportList.vue +++ b/frontend/src/business/components/api/automation/report/ApiReportList.vue @@ -197,6 +197,7 @@ export default { {text: 'Error', value: 'Error'}, {text: 'Success', value: 'Success'}, {text: 'stopped', value: 'stop'}, + {text: 'Rerunning', value: 'Rerunning'}, {text: this.$t('error_report_library.option.name'), value: 'errorReportResult'}, ], reportTypeFilters:[], @@ -300,7 +301,7 @@ export default { }, handleView(report) { this.reportId = report.id; - if (report.status === 'Running') { + if (report.status === 'Running' || report.status ==='Rerunning') { this.$warning(this.$t('commons.run_warning')) return; } diff --git a/frontend/src/business/components/api/automation/report/ApiReportViewHeader.vue b/frontend/src/business/components/api/automation/report/ApiReportViewHeader.vue index 80aa423ad9..f1f3390652 100644 --- a/frontend/src/business/components/api/automation/report/ApiReportViewHeader.vue +++ b/frontend/src/business/components/api/automation/report/ApiReportViewHeader.vue @@ -45,7 +45,11 @@ - + + {{$t('api_test.automation.rerun')}} + + + {{$t('commons.cancel')}} @@ -68,6 +72,10 @@ export default { type: Boolean, default: true, }, + showRerunButton: { + type: Boolean, + default: false, + }, isTemplate: Boolean, exportFlag: { type: Boolean, @@ -120,6 +128,17 @@ export default { handleSaveKeyUp($event) { $event.target.blur(); }, + rerun(){ + let type = this.report.reportType; + let rerunObj = {type :type,reportId:this.report.id} + this.$post('/api/test/exec/rerun',rerunObj, res => { + if (res.data !=='SUCCESS') { + this.$error(res.data); + }else{ + this.$success("已经开始重跑,稍后刷新结果查看"); + } + }); + }, returnView(){ if (this.isUi) { this.$router.push('/ui/report'); @@ -169,4 +188,10 @@ export default { margin-right: 10px; } +.rerun-button{ + float: right; + margin-right: 10px; + background-color: #F2F9EF; + color: #87C45D; +} diff --git a/frontend/src/business/components/api/automation/report/components/RequestResult.vue b/frontend/src/business/components/api/automation/report/components/RequestResult.vue index 1a90b32590..6843f9ad90 100644 --- a/frontend/src/business/components/api/automation/report/components/RequestResult.vue +++ b/frontend/src/business/components/api/automation/report/components/RequestResult.vue @@ -10,7 +10,10 @@
{{ indexNumber }}
- {{ getName(request.name) }} + + {{ request.name }} + + {{ getName(request.name) }} @@ -168,11 +171,13 @@ export default { }, props: { request: Object, + resourceId: String, scenarioName: String, stepId: String, indexNumber: Number, console: String, totalStatus: String, + redirect: Boolean, errorCode: { type: String, default: "" @@ -233,6 +238,27 @@ export default { } }, methods: { + isLink() { + let uri = "/#/api/definition?caseId=" + this.resourceId; + this.clickResource(uri) + }, + clickResource(uri) { + this.$get('/user/update/currentByResourceId/' + this.resourceId, () => { + this.toPage(uri); + }); + }, + toPage(uri) { + let id = "new_a"; + let a = document.createElement("a"); + a.setAttribute("href", uri); + a.setAttribute("target", "_blank"); + a.setAttribute("id", id); + document.body.appendChild(a); + a.click(); + + let element = document.getElementById(id); + element.parentNode.removeChild(element); + }, loadRequestInfoExpand() { if (!this.requestInfo.hasData) { if (this.request.responseResult && this.request.responseResult.body) { @@ -406,4 +432,9 @@ export default { .ms-req-name-col { overflow-x: hidden; } + +.report-label-req { + height: 20px; + border-bottom: 1px solid #303133; +} diff --git a/frontend/src/business/components/api/automation/report/components/ScenarioResult.vue b/frontend/src/business/components/api/automation/report/components/ScenarioResult.vue index bd633f64c7..9403b7baed 100644 --- a/frontend/src/business/components/api/automation/report/components/ScenarioResult.vue +++ b/frontend/src/business/components/api/automation/report/components/ScenarioResult.vue @@ -9,7 +9,10 @@ - {{ node.label }} + + {{ node.label }} + + {{ node.label }} @@ -25,9 +28,11 @@ { + this.toPage(uri); + }); + }, + toPage(uri) { + let id = "new_a"; + let a = document.createElement("a"); + a.setAttribute("href", uri); + a.setAttribute("target", "_blank"); + a.setAttribute("id", id); + document.body.appendChild(a); + a.click(); + + let element = document.getElementById(id); + element.parentNode.removeChild(element); + }, active() { this.isActive = !this.isActive; }, @@ -127,6 +152,12 @@ export default { color: #008080; } +.report-label-head { + border-bottom: 1px solid #303133; + color: #303133; + font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", Arial, sans-serif; + font-size: 13px; +} /deep/ .el-step__icon { width: 20px; diff --git a/frontend/src/business/components/track/plan/common/PlanRunModeWithEnv.vue b/frontend/src/business/components/track/plan/common/PlanRunModeWithEnv.vue index c8d47f5f00..b307d99fbc 100644 --- a/frontend/src/business/components/track/plan/common/PlanRunModeWithEnv.vue +++ b/frontend/src/business/components/track/plan/common/PlanRunModeWithEnv.vue @@ -74,11 +74,32 @@ + +
+ + +   + + + + {{ $t('run_mode.retry_on_failure') }} + + + +
重试接口/UI用例,重试n次后,仍然失败,则用失败用例
+ +
+ 重试  次 +
+
+
+
+ diff --git a/frontend/src/business/components/track/plan/components/ScheduleMaintain.vue b/frontend/src/business/components/track/plan/components/ScheduleMaintain.vue index 4b82005006..37516b2ac6 100644 --- a/frontend/src/business/components/track/plan/components/ScheduleMaintain.vue +++ b/frontend/src/business/components/track/plan/components/ScheduleMaintain.vue @@ -103,6 +103,29 @@ + + +
+ + +   + + + + {{ $t('run_mode.retry_on_failure') }} + + + +
重试接口/UI用例,重试n次后,仍然失败,则用失败用例
+ +
+ 重试  次 +
+
+
+
+ + import { + hasLicense, getCurrentProjectID, getCurrentUser, getCurrentWorkspaceId, @@ -196,6 +220,7 @@ export default { } }; return { + isHasLicense: hasLicense(), result: {}, scheduleReceiverOptions: [], operation: true, @@ -310,6 +335,9 @@ export default { listenGoBack(this.close); this.activeName = 'first'; this.getResourcePools(); + this.runConfig.environmentType = ENV_TYPE.JSON; + this.runConfig.retryEnable = false; + this.runConfig.retryNum = 0; }, findSchedule() { let scheduleResourceID = this.testId; @@ -502,5 +530,7 @@ export default { left: -42px; padding-top: 0px; } - +.ms-failure-div-right { + padding-right: 10px; +} diff --git a/frontend/src/business/components/track/plan/components/TestPlanList.vue b/frontend/src/business/components/track/plan/components/TestPlanList.vue index aaeb73a3c2..bfd798b49c 100644 --- a/frontend/src/business/components/track/plan/components/TestPlanList.vue +++ b/frontend/src/business/components/track/plan/components/TestPlanList.vue @@ -779,6 +779,8 @@ export default { param.environmentType = environmentType; param.environmentGroupId = environmentGroupId; param.requestOriginator = "TEST_PLAN"; + param.retryEnable = config.retryEnable; + param.retryNum = config.retryNum; if(config.isRun === true){ this.$refs.taskCenter.open(); this.result = this.$post('test/plan/run/', param, () => { diff --git a/frontend/src/business/components/track/plan/view/comonents/report/detail/TestPlanApiReport.vue b/frontend/src/business/components/track/plan/view/comonents/report/detail/TestPlanApiReport.vue index 5a15cf6076..d8dc6f0441 100644 --- a/frontend/src/business/components/track/plan/view/comonents/report/detail/TestPlanApiReport.vue +++ b/frontend/src/business/components/track/plan/view/comonents/report/detail/TestPlanApiReport.vue @@ -10,6 +10,9 @@ + + {{$t('api_test.automation.rerun')}} + + + {{$t('api_test.automation.rerun')}} + + + + + {{$t('api_test.automation.rerun')}} + @@ -32,6 +43,10 @@ + + {{$t('api_test.automation.rerun')}} + + @@ -44,6 +59,7 @@ import TestPlanReportContainer from "@/business/components/track/plan/view/comonents/report/detail/TestPlanReportContainer"; import ApiCases from "@/business/components/track/plan/view/comonents/report/detail/component/ApiCases"; import TabPaneCount from "@/business/components/track/plan/view/comonents/report/detail/component/TabPaneCount"; +import {hasLicense} from "@/common/js/utils"; export default { name: "TestPlanApiReport", @@ -55,8 +71,12 @@ export default { errorReportSize: 0, unExecuteSize:0, allSize: 0, + showRerunBtn: true, }; }, + created(){ + this.showRerunBtn = hasLicense(); + }, props: [ 'report', 'planId', 'isTemplate', 'isShare', 'shareId', 'isDb' ], @@ -124,12 +144,68 @@ export default { this.allSize = size; }, handleClick(tab, event) { + }, + rerun(){ + let type = "TEST_PLAN"; + let scenarios = []; + let cases = []; + let performanceCases = []; + let rerunObj = { + type: type, + reportId: this.report.id, + scenarios: scenarios, + cases: cases, + performanceCases: performanceCases + } + // 获取需要重跑的用例 + if(this.report && this.report.apiFailureCases){ + this.format(cases,this.report.apiFailureCases); + } + if(this.report && this.report.unExecuteCases){ + this.format(cases,this.report.unExecuteCases); + } + // 获取需要重跑的场景 + if(this.report && this.report.scenarioFailureCases){ + this.format(scenarios,this.report.scenarioFailureCases); + } + if(this.report && this.report.unExecuteScenarios){ + this.format(scenarios,this.report.unExecuteScenarios); + } + // 获取需要重跑的性能用例 + if(this.report && this.report.loadFailureCases){ + this.format(performanceCases,this.report.loadFailureCases); + } + + this.$post('/api/test/exec/rerun', rerunObj, res => { + if (res.data !== 'SUCCESS') { + this.$error(res.data); + } else { + this.$success("已经开始重跑,稍后刷新结果查看"); + } + }); + }, + format(cases, datas){ + if(this.report && datas){ + datas.forEach(item=>{ + if(item){ + let obj = {id: item.id, reportId: item.reportId,userId:item.createUser}; + cases.push(obj); + } + }); + } } } - } diff --git a/frontend/src/i18n/en-US.js b/frontend/src/i18n/en-US.js index f403f37b21..6f227ad786 100644 --- a/frontend/src/i18n/en-US.js +++ b/frontend/src/i18n/en-US.js @@ -1421,6 +1421,7 @@ export default { request_success: "success", request_error: "error", generate_report: "Generate report", + rerun: "fail and rerun", }, environment: { id: 'Environment ID', @@ -2692,6 +2693,7 @@ export default { set_report: "Set report", report_name: "Report name", run_with_resource_pool: "Run Within Resource pool", + retry_on_failure: "retry on failure", }, operating_log: { title: "Operating Log", diff --git a/frontend/src/i18n/zh-CN.js b/frontend/src/i18n/zh-CN.js index b3889a9e5a..b0e511a935 100644 --- a/frontend/src/i18n/zh-CN.js +++ b/frontend/src/i18n/zh-CN.js @@ -1426,6 +1426,7 @@ export default { request_success: "成功", request_error: "失败", generate_report: "生成报告", + rerun: "失败重跑", }, environment: { id: '环境ID', @@ -2696,6 +2697,7 @@ export default { set_report: "集合报告", report_name: "报告名称", run_with_resource_pool: "资源池运行", + retry_on_failure: "失败重试", }, operating_log: { title: "操作日志", diff --git a/frontend/src/i18n/zh-TW.js b/frontend/src/i18n/zh-TW.js index e92de7cf26..ba95e84bc1 100644 --- a/frontend/src/i18n/zh-TW.js +++ b/frontend/src/i18n/zh-TW.js @@ -1426,6 +1426,7 @@ export default { request_success: "成功", request_error: "失敗", generate_report: "生成報告", + rerun: "失敗重跑", }, environment: { id: '環境ID', @@ -2695,6 +2696,7 @@ export default { set_report: "集合報告", report_name: "報告名稱", run_with_resource_pool: "資源池運行", + retry_on_failure: "失敗重試", }, operating_log: { title: "操作日誌",