fix(测试跟踪): 优化测试报告异常数据的处理逻辑

--bug=1024864 --user=宋天阳
【测试跟踪】计划执行中-test-track异常启动-报告Completed后-在计划中增加用例-该报告的通过率会重新计算
https://www.tapd.cn/55049933/s/1356453
--bug=1024861 --user=宋天阳
【测试跟踪】测试计划执行中-查看报告-结束时间显示为开始时间
https://www.tapd.cn/55049933/s/1356455
--bug=1024863 --user=宋天阳
【测试跟踪】执行测试计划中test-track服务重启-报告结束时间显示为开始时间
https://www.tapd.cn/55049933/s/1356456
This commit is contained in:
song-tianyang 2023-03-28 11:42:15 +08:00 committed by 建国
parent 5a61859424
commit 37dd2127e5
5 changed files with 90 additions and 31 deletions

View File

@ -14,4 +14,6 @@ public interface ExtTestPlanReportContentMapper {
boolean hasRunningReport(@Param("planId") String planId); boolean hasRunningReport(@Param("planId") String planId);
boolean hasRunningReportByPlanIds(@Param("planIds") List<String> planIds); boolean hasRunningReportByPlanIds(@Param("planIds") List<String> planIds);
boolean isApiBasicCountIsNull(String testPlanReportId);
} }

View File

@ -37,4 +37,10 @@
and status = 'RUNNING' and status = 'RUNNING'
order by create_time desc limit 1; order by create_time desc limit 1;
</select> </select>
<select id="isApiBasicCountIsNull" resultType="java.lang.Boolean">
SELECT (api_base_count IS NULL OR api_base_count = '') AS apiCountIsNull
FROM test_plan_report_content WHERE test_plan_report_id = #{0} limit 1
</select>
</mapper> </mapper>

View File

@ -2,10 +2,14 @@ package io.metersphere.plan.dto;
import io.metersphere.base.domain.TestPlanReportContent; import io.metersphere.base.domain.TestPlanReportContent;
import io.metersphere.commons.constants.PerformanceTestStatus;
import io.metersphere.dto.*; import io.metersphere.dto.*;
import io.metersphere.plan.constant.ApiReportStatus;
import io.metersphere.xpack.track.dto.IssuesDao; import io.metersphere.xpack.track.dto.IssuesDao;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -62,4 +66,53 @@ public class TestPlanReportDataStruct extends TestPlanReportContent {
List<TestPlanUiScenarioDTO> uiAllCases; List<TestPlanUiScenarioDTO> uiAllCases;
List<TestPlanUiScenarioDTO> uiFailureCases; List<TestPlanUiScenarioDTO> uiFailureCases;
public boolean hasRunningCase() {
//判断是否含有运行中的用例 方法内针对集合不开流是为了不影响后续业务中使用stream
if (CollectionUtils.isNotEmpty(apiAllCases)) {
List<TestPlanApiDTO> runningCaseList =
apiAllCases.stream().filter(
dto -> StringUtils.equalsAnyIgnoreCase(dto.getExecResult(),
ApiReportStatus.PENDING.name(),
ApiReportStatus.RERUNNING.name(),
ApiReportStatus.RUNNING.name())).toList();
if (runningCaseList.size() > 0) {
return true;
}
}
if (CollectionUtils.isNotEmpty(scenarioAllCases)) {
List<TestPlanScenarioDTO> runningCaseList =
scenarioAllCases.stream().filter(
dto -> StringUtils.equalsAnyIgnoreCase(dto.getLastResult(),
ApiReportStatus.PENDING.name(),
ApiReportStatus.RERUNNING.name(),
ApiReportStatus.RUNNING.name())).toList();
if (runningCaseList.size() > 0) {
return true;
}
}
if (CollectionUtils.isNotEmpty(loadAllCases)) {
List<TestPlanLoadCaseDTO> runningCaseList =
loadAllCases.stream().filter(
dto -> StringUtils.equalsAnyIgnoreCase(dto.getStatus(),
PerformanceTestStatus.Starting.name(),
PerformanceTestStatus.Running.name(),
PerformanceTestStatus.Reporting.name())).toList();
if (runningCaseList.size() > 0) {
return true;
}
}
if (CollectionUtils.isNotEmpty(uiAllCases)) {
List<TestPlanUiScenarioDTO> runningCaseList =
uiAllCases.stream().filter(
dto -> StringUtils.equalsAnyIgnoreCase(dto.getLastResult(),
ApiReportStatus.PENDING.name(),
ApiReportStatus.RERUNNING.name(),
ApiReportStatus.RUNNING.name())).toList();
if (runningCaseList.size() > 0) {
return true;
}
}
return false;
}
} }

View File

@ -24,7 +24,6 @@ import io.metersphere.plan.request.performance.LoadPlanReportDTO;
import io.metersphere.plan.request.ui.TestPlanUiExecuteReportDTO; import io.metersphere.plan.request.ui.TestPlanUiExecuteReportDTO;
import io.metersphere.plan.request.ui.UiPlanReportRequest; import io.metersphere.plan.request.ui.UiPlanReportRequest;
import io.metersphere.plan.service.remote.api.*; import io.metersphere.plan.service.remote.api.*;
import io.metersphere.plan.service.remote.performance.PlanLoadTestReportService;
import io.metersphere.plan.service.remote.performance.PlanTestPlanLoadCaseService; import io.metersphere.plan.service.remote.performance.PlanTestPlanLoadCaseService;
import io.metersphere.plan.service.remote.ui.PlanTestPlanUiScenarioCaseService; import io.metersphere.plan.service.remote.ui.PlanTestPlanUiScenarioCaseService;
import io.metersphere.plan.utils.TestPlanReportUtil; import io.metersphere.plan.utils.TestPlanReportUtil;
@ -70,8 +69,6 @@ public class TestPlanReportService {
@Resource @Resource
PlanTestPlanLoadCaseService planTestPlanLoadCaseService; PlanTestPlanLoadCaseService planTestPlanLoadCaseService;
@Resource @Resource
PlanLoadTestReportService planLoadTestReportService;
@Resource
ExtTestPlanReportMapper extTestPlanReportMapper; ExtTestPlanReportMapper extTestPlanReportMapper;
@Resource @Resource
TestPlanMapper testPlanMapper; TestPlanMapper testPlanMapper;
@ -155,7 +152,7 @@ public class TestPlanReportService {
TestPlanReport testPlanReport = testPlanReportMapper.selectByPrimaryKey(testPlanReportDTO.getId()); TestPlanReport testPlanReport = testPlanReportMapper.selectByPrimaryKey(testPlanReportDTO.getId());
TestPlanReportContentWithBLOBs content = this.selectTestPlanReportContentByReportId(testPlanReportDTO.getId()); TestPlanReportContentWithBLOBs content = this.selectTestPlanReportContentByReportId(testPlanReportDTO.getId());
TestPlanReportDataStruct testPlanReportCountData TestPlanReportDataStruct testPlanReportCountData
= testPlanService.buildTestPlanReportStructByTestPlanReport(testPlanReport, content); = testPlanService.buildOldVersionTestPlanReport(testPlanReport, content);
if (testPlanReportCountData != null) { if (testPlanReportCountData != null) {
testPlanReportDTO.setPassRate(testPlanReportCountData.getPassRate()); testPlanReportDTO.setPassRate(testPlanReportCountData.getPassRate());
} }
@ -546,20 +543,27 @@ public class TestPlanReportService {
} }
//更新测试计划报告的数据结构 //更新测试计划报告的数据结构
private void updateReportStructInfo(TestPlanReportContentWithBLOBs testPlanReportContentWithBLOBs, TestPlanReportDataStruct reportStruct) { public void updateReportStructInfo(TestPlanReportContentWithBLOBs testPlanReportContentWithBLOBs, TestPlanReportDataStruct reportStruct) {
//更新BaseCount统计字段和通过率 //更新BaseCount统计字段和通过率
testPlanReportContentWithBLOBs.setPassRate(reportStruct.getPassRate()); testPlanReportContentWithBLOBs.setPassRate(reportStruct.getPassRate());
testPlanReportContentWithBLOBs.setApiBaseCount(JSON.toJSONString(reportStruct)); testPlanReportContentWithBLOBs.setApiBaseCount(JSON.toJSONString(reportStruct));
testPlanReportContentMapper.updateByPrimaryKeySelective(testPlanReportContentWithBLOBs); testPlanReportContentMapper.updateByPrimaryKeySelective(testPlanReportContentWithBLOBs);
} }
public void testPlanExecuteOver(String testPlanReportId, String finishStatus) { private boolean isTestPlanCountOver(TestPlanReport testPlanReport) {
TestPlanReport testPlanReport = this.getTestPlanReport(testPlanReportId);
if (testPlanReport != null && StringUtils.equalsAnyIgnoreCase(testPlanReport.getStatus(), if (testPlanReport != null && StringUtils.equalsAnyIgnoreCase(testPlanReport.getStatus(),
"stopped", "stopped",
TestPlanReportStatus.COMPLETED.name(), TestPlanReportStatus.COMPLETED.name(),
TestPlanReportStatus.SUCCESS.name(), TestPlanReportStatus.SUCCESS.name(),
TestPlanReportStatus.FAILED.name())) { TestPlanReportStatus.FAILED.name())) {
return !extTestPlanReportContentMapper.isApiBasicCountIsNull(testPlanReport.getId());
}
return false;
}
public void testPlanExecuteOver(String testPlanReportId, String finishStatus) {
TestPlanReport testPlanReport = this.getTestPlanReport(testPlanReportId);
if (this.isTestPlanCountOver(testPlanReport)) {
return; return;
} }
boolean isSendMessage = false; boolean isSendMessage = false;
@ -577,7 +581,7 @@ public class TestPlanReportService {
boolean isRerunningTestPlan = BooleanUtils.isTrue(StringUtils.equalsIgnoreCase(testPlanReport.getStatus(), APITestStatus.Rerunning.name())); boolean isRerunningTestPlan = BooleanUtils.isTrue(StringUtils.equalsIgnoreCase(testPlanReport.getStatus(), APITestStatus.Rerunning.name()));
//测试计划报告结果数据初始化 //测试计划报告结果数据初始化
testPlanReport.setStatus(finishStatus); testPlanReport.setStatus(finishStatus);
content = this.initTestPlanReportInfo(testPlanReport, isRerunningTestPlan); content = this.countAndSaveTestPlanReport(testPlanReport, isRerunningTestPlan);
this.setReportExecuteResult(testPlanReport, finishStatus); this.setReportExecuteResult(testPlanReport, finishStatus);
} catch (Exception e) { } catch (Exception e) {
testPlanReport.setStatus(finishStatus); testPlanReport.setStatus(finishStatus);
@ -605,7 +609,7 @@ public class TestPlanReportService {
/** /**
* 统计测试计划报告信息 * 统计测试计划报告信息
*/ */
public TestPlanReportContentWithBLOBs initTestPlanReportInfo(TestPlanReport testPlanReport, boolean isRerunningTestPlan) throws Exception { public TestPlanReportContentWithBLOBs countAndSaveTestPlanReport(TestPlanReport testPlanReport, boolean isRerunningTestPlan) {
long endTime = System.currentTimeMillis(); long endTime = System.currentTimeMillis();
//原逻辑中要判断包含测试计划功能用例时才会赋予结束时间执行测试计划产生的测试报告它的结束时间感觉没有这种判断必要 //原逻辑中要判断包含测试计划功能用例时才会赋予结束时间执行测试计划产生的测试报告它的结束时间感觉没有这种判断必要
testPlanReport.setEndTime(endTime); testPlanReport.setEndTime(endTime);
@ -620,7 +624,7 @@ public class TestPlanReportService {
content.setApiBaseCount(null); content.setApiBaseCount(null);
} }
TestPlanWithBLOBs testPlan = testPlanMapper.selectByPrimaryKey(testPlanReport.getTestPlanId()); TestPlanWithBLOBs testPlan = testPlanMapper.selectByPrimaryKey(testPlanReport.getTestPlanId());
TestPlanReportDataStruct apiBaseCountStruct = this.genReportStruct(testPlan, testPlanReport, content, isRerunningTestPlan); TestPlanReportDataStruct apiBaseCountStruct = testPlanService.generateReportStruct(testPlan, testPlanReport, content, isRerunningTestPlan);
if (apiBaseCountStruct.getPassRate() == 1) { if (apiBaseCountStruct.getPassRate() == 1) {
testPlanReport.setStatus(TestPlanReportStatus.SUCCESS.name()); testPlanReport.setStatus(TestPlanReportStatus.SUCCESS.name());
} else if (apiBaseCountStruct.getPassRate() < 1) { } else if (apiBaseCountStruct.getPassRate() < 1) {
@ -674,21 +678,6 @@ public class TestPlanReportService {
} }
} }
//构建测试计划报告的数据结构
private TestPlanReportDataStruct genReportStruct(TestPlanWithBLOBs testPlan, TestPlanReport testPlanReport, TestPlanReportContentWithBLOBs reportContent, boolean rebuildReport) {
TestPlanReportDataStruct returnDTO = null;
if (testPlanReport != null && reportContent != null) {
try {
returnDTO = testPlanService.buildReportStruct(testPlan, testPlanReport, reportContent, rebuildReport);
//查找运行环境
this.initRunInformation(returnDTO, testPlanReport);
} catch (Exception e) {
LogUtil.error("计算测试计划报告信息出错!", e);
}
}
return returnDTO == null ? new TestPlanReportDataStruct() : returnDTO;
}
/** /**
* @param planReportId 测试计划报告ID * @param planReportId 测试计划报告ID
* @param resourceRunMode 资源的运行模式,triggerMode非Scedule可以为null * @param resourceRunMode 资源的运行模式,triggerMode非Scedule可以为null
@ -1066,7 +1055,7 @@ public class TestPlanReportService {
} }
if (this.isDynamicallyGenerateReports(testPlanReportContent) || StringUtils.isNotEmpty(testPlanReportContent.getApiBaseCount())) { if (this.isDynamicallyGenerateReports(testPlanReportContent) || StringUtils.isNotEmpty(testPlanReportContent.getApiBaseCount())) {
TestPlanWithBLOBs testPlan = testPlanMapper.selectByPrimaryKey(testPlanReport.getTestPlanId()); TestPlanWithBLOBs testPlan = testPlanMapper.selectByPrimaryKey(testPlanReport.getTestPlanId());
testPlanReportDTO = this.genReportStruct(testPlan, testPlanReport, testPlanReportContent, false); testPlanReportDTO = testPlanService.generateReportStruct(testPlan, testPlanReport, testPlanReportContent, false);
} }
testPlanReportDTO.setId(reportId); testPlanReportDTO.setId(reportId);
testPlanReportDTO.setName(testPlanReport.getName()); testPlanReportDTO.setName(testPlanReport.getName());

View File

@ -1381,9 +1381,11 @@ public class TestPlanService {
/** /**
* @param testPlanReport 测试计划报告 * @param testPlanReport 测试计划报告
* @param testPlanReportContentWithBLOBs 测试计划报告内容 * @param testPlanReportContentWithBLOBs 测试计划报告内容
* @return
*/ */
public TestPlanReportDataStruct buildReportStruct(TestPlanWithBLOBs testPlan, TestPlanReport testPlanReport, TestPlanReportContentWithBLOBs testPlanReportContentWithBLOBs, boolean rebuildReport) { public TestPlanReportDataStruct generateReportStruct(TestPlanWithBLOBs testPlan,
TestPlanReport testPlanReport,
TestPlanReportContentWithBLOBs testPlanReportContentWithBLOBs,
boolean rebuildReport) {
TestPlanReportDataStruct testPlanReportStruct = null; TestPlanReportDataStruct testPlanReportStruct = null;
if (ObjectUtils.allNotNull(testPlanReport, testPlanReportContentWithBLOBs)) { if (ObjectUtils.allNotNull(testPlanReport, testPlanReportContentWithBLOBs)) {
Map config = null; Map config = null;
@ -1524,7 +1526,10 @@ public class TestPlanService {
report.setApiResult(apiResult); report.setApiResult(apiResult);
report.setUiResult(uiResult); report.setUiResult(uiResult);
report.setStartTime(testPlanReport.getCreateTime()); report.setStartTime(testPlanReport.getCreateTime());
if (testPlanReport.getCreateTime() != testPlanReport.getEndTime() && testPlanReport.getEndTime() != 0) { if (!StringUtils.equals(
DateUtils.getTimeString(testPlanReport.getCreateTime()),
DateUtils.getTimeString(testPlanReport.getEndTime()))
&& testPlanReport.getEndTime() != 0) {
//防止测试计划报告非正常状态停止时造成的测试时间显示不对 //防止测试计划报告非正常状态停止时造成的测试时间显示不对
report.setEndTime(testPlanReport.getEndTime()); report.setEndTime(testPlanReport.getEndTime());
} }
@ -2108,11 +2113,15 @@ public class TestPlanService {
return projectIds.stream().distinct().collect(Collectors.toList()); return projectIds.stream().distinct().collect(Collectors.toList());
} }
public TestPlanReportDataStruct buildTestPlanReportStructByTestPlanReport(TestPlanReport testPlanReport, TestPlanReportContentWithBLOBs testPlanReportContent) { public TestPlanReportDataStruct buildOldVersionTestPlanReport(TestPlanReport testPlanReport, TestPlanReportContentWithBLOBs testPlanReportContent) {
TestPlanWithBLOBs testPlanWithBLOBs = this.testPlanMapper.selectByPrimaryKey(testPlanReport.getTestPlanId()); TestPlanWithBLOBs testPlanWithBLOBs = this.testPlanMapper.selectByPrimaryKey(testPlanReport.getTestPlanId());
TestPlanReportDataStruct testPlanReportDataStruct = new TestPlanReportDataStruct(); TestPlanReportDataStruct testPlanReportDataStruct = new TestPlanReportDataStruct();
try { try {
testPlanReportDataStruct = this.buildReportStruct(testPlanWithBLOBs, testPlanReport, testPlanReportContent, false); testPlanReportDataStruct = this.generateReportStruct(testPlanWithBLOBs, testPlanReport, testPlanReportContent, false);
if (StringUtils.isBlank(testPlanReportContent.getApiBaseCount()) && !testPlanReportDataStruct.hasRunningCase()) {
//旧版本的测试计划报告没有重新统计过测试计划报告时且当不存在运行中的用例会将结果保存下来
testPlanReportService.updateReportStructInfo(testPlanReportContent, testPlanReportDataStruct);
}
} catch (Exception e) { } catch (Exception e) {
LoggerUtil.error("统计测试计划数据出错!", e); LoggerUtil.error("统计测试计划数据出错!", e);
} }