fix(测试跟踪): 修复失败重跑404的问题

--bug=1018576 --user=宋天阳 【测试跟踪】测试报告-接口测试-失败重跑-报404
https://www.tapd.cn/55049933/s/1271024
This commit is contained in:
song-tianyang 2022-10-21 15:38:27 +08:00 committed by 刘瑞斌
parent a83c5f4899
commit 5f01e0b7aa
10 changed files with 277 additions and 18 deletions

View File

@ -1,11 +1,11 @@
package io.metersphere.base.mapper.plan.ext;
import io.metersphere.api.dto.plan.TestPlanApiCaseInfoDTO;
import io.metersphere.api.dto.QueryReferenceRequest;
import io.metersphere.api.dto.automation.TestPlanDTO;
import io.metersphere.api.dto.automation.TestPlanFailureApiDTO;
import io.metersphere.api.dto.definition.ApiTestCaseRequest;
import io.metersphere.api.dto.definition.TestPlanApiCaseDTO;
import io.metersphere.api.dto.QueryReferenceRequest;
import io.metersphere.api.dto.plan.TestPlanApiCaseInfoDTO;
import io.metersphere.base.domain.ApiTestCaseWithBLOBs;
import io.metersphere.base.domain.TestPlanApiCase;
import org.apache.ibatis.annotations.Param;
@ -53,5 +53,6 @@ public interface ExtTestPlanApiCaseMapper {
List<TestPlanApiCase> selectPlanByIdsAndStatusIsNotTrash(@Param("ids") List<String> ids);
List<TestPlanApiCase> selectByIdsAndStatusIsNotTrash(@Param("ids") List<String> ids);
}

View File

@ -208,6 +208,9 @@ public class HashTreeUtil {
public static List<MsAssertions> getErrorReportByProjectId(String projectId, boolean higherThanSuccess, boolean higherThanError) {
ExtErrorReportLibraryService service = CommonBeanFactory.getBean(ExtErrorReportLibraryService.class);
if (service == null) {
return new ArrayList<>();
}
return service.getAssertionByProjectIdAndStatusIsOpen(projectId, higherThanSuccess, higherThanError);
}

View File

@ -1,7 +1,9 @@
package io.metersphere.controller.scenario;
import io.metersphere.service.scenario.ApiScenarioRerunService;
import io.metersphere.commons.enums.ApiReportStatus;
import io.metersphere.dto.RerunParametersDTO;
import io.metersphere.dto.TestPlanRerunParametersDTO;
import io.metersphere.service.scenario.ApiScenarioRerunService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
@ -20,4 +22,11 @@ public class ApiScenarioRerunController {
public String rerun(@RequestBody RerunParametersDTO parametersDTO) {
return apiScenarioRerunService.rerun(parametersDTO);
}
@PostMapping("/rerun/test/plan")
public String rerunByTestPlan(@RequestBody TestPlanRerunParametersDTO parametersDTO) {
boolean isStart = apiScenarioRerunService.planRerun(parametersDTO);
return isStart ? ApiReportStatus.SUCCESS.name() : ApiReportStatus.ERROR.name();
}
}

View File

@ -27,6 +27,9 @@ import java.util.Map;
public class ExtErrorReportLibraryService {
public List<MsAssertions> getAssertionByProjectIdAndStatusIsOpen(String projectId, boolean higherThanSuccess, boolean higherThanError) {
ErrorReportLibraryService service = CommonBeanFactory.getBean(ErrorReportLibraryService.class);
if (service == null) {
return new ArrayList<>();
}
List<MsAssertions> returnList = new ArrayList<>();
ErrorReportLibraryExample example = new ErrorReportLibraryExample();
example.createCriteria().andProjectIdEqualTo(projectId).andStatusEqualTo(true);

View File

@ -3,22 +3,29 @@ package io.metersphere.service.scenario;
import io.metersphere.api.dto.ApiCaseRunRequest;
import io.metersphere.api.dto.ApiScenarioReportDTO;
import io.metersphere.api.dto.StepTreeDTO;
import io.metersphere.api.dto.automation.ApiScenarioReportResult;
import io.metersphere.api.dto.automation.ExecuteType;
import io.metersphere.api.dto.automation.RunScenarioRequest;
import io.metersphere.api.dto.definition.BatchRunDefinitionRequest;
import io.metersphere.api.exec.api.ApiCaseExecuteService;
import io.metersphere.api.exec.scenario.ApiScenarioExecuteService;
import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.ApiScenarioMapper;
import io.metersphere.base.mapper.*;
import io.metersphere.base.mapper.ext.ExtApiDefinitionExecResultMapper;
import io.metersphere.base.mapper.ext.ExtApiScenarioReportResultMapper;
import io.metersphere.base.mapper.plan.ext.ExtTestPlanApiCaseMapper;
import io.metersphere.base.mapper.plan.ext.ExtTestPlanApiScenarioMapper;
import io.metersphere.commons.constants.ApiRunMode;
import io.metersphere.commons.constants.ElementConstants;
import io.metersphere.commons.constants.ReportTypeConstants;
import io.metersphere.commons.enums.ApiReportStatus;
import io.metersphere.commons.utils.BeanUtils;
import io.metersphere.commons.utils.JSON;
import io.metersphere.constants.RunModeConstants;
import io.metersphere.dto.KeyValueDTO;
import io.metersphere.dto.RerunParametersDTO;
import io.metersphere.dto.RunModeConfigDTO;
import io.metersphere.dto.TestPlanRerunParametersDTO;
import io.metersphere.i18n.Translator;
import io.metersphere.utils.LoggerUtil;
import org.apache.commons.beanutils.BeanComparator;
@ -45,6 +52,18 @@ public class ApiScenarioRerunService {
private ExtApiScenarioReportResultMapper extApiScenarioReportResultMapper;
@Resource
private ApiScenarioMapper apiScenarioMapper;
@Resource
private ApiScenarioReportMapper apiScenarioReportMapper;
@Resource
private ApiScenarioReportResultMapper apiScenarioReportResultMapper;
@Resource
private ApiScenarioReportStructureMapper apiScenarioReportStructureMapper;
@Resource
private ExtTestPlanApiScenarioMapper extTestPlanApiScenarioMapper;
@Resource
private ExtTestPlanApiCaseMapper extTestPlanApiCaseMapper;
@Resource
private ApiDefinitionExecResultMapper apiDefinitionExecResultMapper;
/**
* 失败重跑
@ -179,4 +198,145 @@ public class ApiScenarioRerunService {
});
apiScenarioReportStructureService.update(reportId, all);
}
/**
* 测试计划失败重跑
*
* @param parametersDTO
* @param parametersDTO
*/
public boolean planRerun(TestPlanRerunParametersDTO parametersDTO) {
// 用例
boolean isStart = false;
TestPlanReport testPlanReport = parametersDTO.getTestPlanReport();
if (testPlanReport == null) {
return false;
}
if (CollectionUtils.isNotEmpty(parametersDTO.getCases())) {
RunModeConfigDTO runModeConfig = new RunModeConfigDTO();
runModeConfig.setMode(RunModeConstants.SERIAL.name());
runModeConfig.setReportType(RunModeConstants.INDEPENDENCE.toString());
runModeConfig.setEnvMap(new HashMap<>());
runModeConfig.setOnSampleError(false);
runModeConfig.setMode(ApiRunMode.API_PLAN.name());
List<String> ids = parametersDTO.getCases().stream().map(KeyValueDTO::getId).collect(Collectors.toList());
LoggerUtil.info("重跑测试计划报告:【" + parametersDTO.getReportId() + "】,对应重跑用例资源:" + ids.size());
BatchRunDefinitionRequest request = new BatchRunDefinitionRequest();
request.setTriggerMode(testPlanReport.getTriggerMode());
request.setRerun(true);
request.setPlanIds(ids);
request.setPlanReportId(parametersDTO.getReportId());
request.setUserId(parametersDTO.getCases().get(0).getUserId());
Map<String, ApiDefinitionExecResultWithBLOBs> executeQueue = new LinkedHashMap<>();
if (com.alibaba.nacos.common.utils.CollectionUtils.isNotEmpty(ids)) {
List<TestPlanApiCase> planApiCases = extTestPlanApiCaseMapper.selectByIdsAndStatusIsNotTrash(ids);
if (com.alibaba.nacos.common.utils.CollectionUtils.isNotEmpty(planApiCases)) {
for (KeyValueDTO testPlanApiCase : parametersDTO.getCases()) {
ApiDefinitionExecResultWithBLOBs report = apiDefinitionExecResultMapper.selectByPrimaryKey(testPlanApiCase.getReportId());
if (report == null) {
continue;
}
runModeConfig.setReportType(report.getReportType());
if (!StringUtils.equalsAnyIgnoreCase(report.getActuator(), "LOCAL")) {
runModeConfig.setResourcePoolId(report.getActuator());
}
executeQueue.put(testPlanApiCase.getId(), report);
if (StringUtils.isNotEmpty(report.getEnvConfig())) {
request.setConfig(JSON.parseObject(report.getEnvConfig(), RunModeConfigDTO.class));
}
}
if (request.getConfig() == null) {
request.setConfig(runModeConfig);
}
request.setExecuteQueue(executeQueue);
apiCaseExecuteService.run(request);
isStart = true;
}
}
}
// 场景
if (com.alibaba.nacos.common.utils.CollectionUtils.isNotEmpty(parametersDTO.getScenarios())) {
List<String> ids = parametersDTO.getScenarios().stream().map(KeyValueDTO::getId).collect(Collectors.toList());
if (com.alibaba.nacos.common.utils.CollectionUtils.isEmpty(ids)) {
return isStart;
}
List<TestPlanApiScenario> apiScenarios = extTestPlanApiScenarioMapper.selectByIdsAndStatusIsNotTrash(ids);
if (com.alibaba.nacos.common.utils.CollectionUtils.isEmpty(apiScenarios)) {
return isStart;
}
List<String> scenarioIds = apiScenarios.stream().map(TestPlanApiScenario::getApiScenarioId).collect(Collectors.toList());
LoggerUtil.info("重跑测试计划报告:【" + parametersDTO.getReportId() + "】,对应重跑场景资源:" + scenarioIds.size());
Map<String, String> scenarioTestPlanIdMap = apiScenarios.stream().collect(Collectors.toMap(TestPlanApiScenario::getId, a -> a.getApiScenarioId(), (k1, k2) -> k1));
// 查出原始报告
List<String> reportIds = apiScenarios.stream().map(TestPlanApiScenario::getReportId).collect(Collectors.toList());
if (com.alibaba.nacos.common.utils.CollectionUtils.isEmpty(reportIds)) {
return isStart;
}
ApiScenarioReportExample reportExample = new ApiScenarioReportExample();
reportExample.createCriteria().andIdIn(reportIds);
List<ApiScenarioReport> reports = apiScenarioReportMapper.selectByExample(reportExample);
if (com.alibaba.nacos.common.utils.CollectionUtils.isEmpty(reports)) {
return isStart;
}
// config
RunModeConfigDTO runModeConfig = new RunModeConfigDTO();
runModeConfig.setMode(RunModeConstants.SERIAL.name());
runModeConfig.setReportType(RunModeConstants.INDEPENDENCE.toString());
runModeConfig.setEnvMap(new HashMap<>());
runModeConfig.setOnSampleError(false);
// 执行配置
RunModeConfigDTO config = new RunModeConfigDTO();
config.setMode(RunModeConstants.PARALLEL.toString());
if (!StringUtils.equalsAnyIgnoreCase(reports.get(0).getActuator(), "LOCAL")) {
config.setResourcePoolId(reports.get(0).getActuator());
}
Map<String, ApiScenarioReportResult> reportMap = new HashMap<>();
reports.forEach(item -> {
ApiScenarioReportResult reportResult = new ApiScenarioReportResult();
BeanUtils.copyBean(reportResult, item);
reportMap.put(item.getScenarioId(), reportResult);
});
RunScenarioRequest request = new RunScenarioRequest();
request.setRerun(true);
request.setReportMap(reportMap);
request.setTriggerMode(testPlanReport.getTriggerMode());
request.setExecuteType(ExecuteType.Completed.name());
request.setRunMode(ApiRunMode.SCENARIO_PLAN.name());
request.setIds(scenarioIds); // 场景IDS
request.setScenarioTestPlanIdMap(scenarioTestPlanIdMap);//场景id和计划场景id映射关系
// 一批执行的报告配置都是相同的
ApiScenarioReportWithBLOBs scenario = apiScenarioReportMapper.selectByPrimaryKey(reportIds.get(0));
if (scenario != null && StringUtils.isNotEmpty(scenario.getEnvConfig())) {
request.setConfig(JSON.parseObject(scenario.getEnvConfig(), RunModeConfigDTO.class));
} else {
request.setConfig(runModeConfig);
}
request.setTestPlanReportId(parametersDTO.getReportId());
request.setId(UUID.randomUUID().toString());
request.setRequestOriginator("TEST_PLAN");
LoggerUtil.info("清理原始报告对应结果:【" + request.getTestPlanReportId() + "】," + reportIds.size());
ApiScenarioReportResultExample reportResultExample = new ApiScenarioReportResultExample();
reportResultExample.createCriteria().andReportIdIn(reportIds);
apiScenarioReportResultMapper.deleteByExample(reportResultExample);
ApiScenarioReportStructureExample structureExample = new ApiScenarioReportStructureExample();
structureExample.createCriteria().andReportIdIn(reportIds);
apiScenarioReportStructureMapper.deleteByExample(structureExample);
LoggerUtil.info("重跑测试计划报告:【" + request.getTestPlanReportId() + "】,开始执行:" + reportIds.size());
isStart = true;
apiScenarioExecuteService.run(request);
}
return isStart;
}
}

View File

@ -0,0 +1,9 @@
package io.metersphere.dto;
import io.metersphere.base.domain.TestPlanReport;
import lombok.Data;
@Data
public class TestPlanRerunParametersDTO extends RerunParametersDTO {
private TestPlanReport testPlanReport;
}

View File

@ -6,24 +6,26 @@ import io.metersphere.base.domain.*;
import io.metersphere.commons.constants.*;
import io.metersphere.commons.utils.PageUtils;
import io.metersphere.commons.utils.Pager;
import io.metersphere.dto.ScheduleDTO;
import io.metersphere.dto.TestPlanDTOWithMetric;
import io.metersphere.dto.TestPlanRerunParametersDTO;
import io.metersphere.log.annotation.MsAuditLog;
import io.metersphere.notice.annotation.SendNotice;
import io.metersphere.plan.dto.TestCaseReportStatusResultDTO;
import io.metersphere.plan.dto.TestPlanDTO;
import io.metersphere.plan.dto.TestPlanSimpleReportDTO;
import io.metersphere.log.annotation.MsAuditLog;
import io.metersphere.notice.annotation.SendNotice;
import io.metersphere.dto.*;
import io.metersphere.plan.service.remote.api.PlanApiAutomationService;
import io.metersphere.request.ScheduleRequest;
import io.metersphere.plan.reuest.QueryTestPlanRequest;
import io.metersphere.plan.reuest.AddTestPlanRequest;
import io.metersphere.plan.reuest.function.TestCaseRelevanceRequest;
import io.metersphere.plan.reuest.function.PlanCaseRelevanceRequest;
import io.metersphere.plan.reuest.QueryTestPlanRequest;
import io.metersphere.plan.reuest.ScheduleInfoRequest;
import io.metersphere.plan.reuest.api.TestPlanRunRequest;
import io.metersphere.service.BaseScheduleService;
import io.metersphere.plan.reuest.function.PlanCaseRelevanceRequest;
import io.metersphere.plan.reuest.function.TestCaseRelevanceRequest;
import io.metersphere.plan.service.TestPlanProjectService;
import io.metersphere.plan.service.TestPlanRerunService;
import io.metersphere.plan.service.TestPlanService;
import io.metersphere.plan.service.remote.api.PlanApiAutomationService;
import io.metersphere.request.ScheduleRequest;
import io.metersphere.service.BaseScheduleService;
import io.metersphere.service.wapper.CheckPermissionService;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.web.bind.annotation.*;
@ -49,6 +51,8 @@ public class TestPlanController {
private BaseScheduleService baseScheduleService;
@Resource
private PlanApiAutomationService planApiAutomationService;
@Resource
private TestPlanRerunService testPlanRerunService;
@GetMapping("/auto-check/{testPlanId}")
public void autoCheck(@PathVariable String testPlanId) {
@ -129,7 +133,7 @@ public class TestPlanController {
@PostMapping("/edit/report/config")
@RequiresPermissions(PermissionConstants.PROJECT_TRACK_PLAN_READ_EDIT)
// @MsAuditLog(module = "track_test_plan", type = OperLogConstants.UPDATE, beforeEvent = "#msClass.getLogDetails(#planId)", content = "#msClass.getLogDetails(#planId)", msClass = TestPlanService.class)
// @MsAuditLog(module = "track_test_plan", type = OperLogConstants.UPDATE, beforeEvent = "#msClass.getLogDetails(#planId)", content = "#msClass.getLogDetails(#planId)", msClass = TestPlanService.class)
public void editReportConfig(@RequestBody TestPlanDTO testPlanDTO) {
testPlanService.editReportConfig(testPlanDTO);
}
@ -330,4 +334,10 @@ public class TestPlanController {
Schedule schedule = baseScheduleService.getScheduleByResource(testId, group);
return schedule;
}
@PostMapping(value = "/rerun")
public String rerun(@RequestBody TestPlanRerunParametersDTO request) {
return testPlanRerunService.rerun(request);
}
}

View File

@ -0,0 +1,50 @@
package io.metersphere.plan.service;
import io.metersphere.base.domain.TestPlanReport;
import io.metersphere.base.mapper.TestPlanReportMapper;
import io.metersphere.commons.constants.APITestStatus;
import io.metersphere.commons.constants.ReportTypeConstants;
import io.metersphere.dto.TestPlanRerunParametersDTO;
import io.metersphere.i18n.Translator;
import io.metersphere.plan.constant.ApiReportStatus;
import io.metersphere.service.remote.api.ApiRerunService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class TestPlanRerunService {
@Resource
private TestPlanReportMapper testPlanReportMapper;
@Resource
private ApiRerunService apiRerunService;
public String rerun(TestPlanRerunParametersDTO parametersDTO) {
String rerunResult = ApiReportStatus.ERROR.name();
if (parametersDTO == null || StringUtils.isEmpty(parametersDTO.getType()) || StringUtils.isEmpty(parametersDTO.getReportId())) {
return Translator.get("report_warning");
}
if (StringUtils.equalsAnyIgnoreCase(parametersDTO.getType(), ReportTypeConstants.TEST_PLAN.name())) {
TestPlanReport testPlanReport = testPlanReportMapper.selectByPrimaryKey(parametersDTO.getReportId());
if (StringUtils.equalsAnyIgnoreCase(testPlanReport.getStatus(), APITestStatus.Rerunning.name())) {
return Translator.get("rerun_warning");
}
if (testPlanReport != null) {
parametersDTO.setTestPlanReport(testPlanReport);
rerunResult = apiRerunService.rerun(parametersDTO);
if (StringUtils.equalsIgnoreCase(rerunResult, ApiReportStatus.SUCCESS.name())) {
testPlanReport.setStatus(APITestStatus.Rerunning.name());
testPlanReportMapper.updateByPrimaryKey(testPlanReport);
}
}
} else {
return Translator.get("rerun_warning");
}
if (!StringUtils.equalsIgnoreCase(rerunResult, ApiReportStatus.SUCCESS.name())) {
return Translator.get("rerun_warning");
}
return rerunResult;
}
}

View File

@ -0,0 +1,14 @@
package io.metersphere.service.remote.api;
import io.metersphere.dto.TestPlanRerunParametersDTO;
import org.springframework.stereotype.Service;
@Service
public class ApiRerunService extends TrackApiTestService {
private static final String BASE_URL = "/api/test/exec/rerun/test/plan";
public String rerun(TestPlanRerunParametersDTO paramDTO) {
return microService.postForData(serviceName, BASE_URL, paramDTO, String.class);
}
}

View File

@ -1,7 +1,7 @@
import {get, post} from "metersphere-frontend/src/plugins/request"
import {post} from "metersphere-frontend/src/plugins/request"
const BASE_URL = "/api/test/";
const BASE_URL = "/test/plan/";
export function apiTestExecRerun(param) {
return post(BASE_URL + 'exec/rerun', param);
return post(BASE_URL + 'rerun', param);
}