fix(接口测试): 处理批量执行失败停止,报告统计
This commit is contained in:
parent
2dcef4ad0c
commit
00ee3a53a3
|
@ -2,9 +2,11 @@ package io.metersphere.api.listener;
|
||||||
|
|
||||||
import io.metersphere.api.event.ApiEventSource;
|
import io.metersphere.api.event.ApiEventSource;
|
||||||
import io.metersphere.api.service.ApiReportSendNoticeService;
|
import io.metersphere.api.service.ApiReportSendNoticeService;
|
||||||
|
import io.metersphere.api.service.definition.ApiReportService;
|
||||||
import io.metersphere.api.service.definition.ApiTestCaseBatchRunService;
|
import io.metersphere.api.service.definition.ApiTestCaseBatchRunService;
|
||||||
import io.metersphere.api.service.queue.ApiExecutionQueueService;
|
import io.metersphere.api.service.queue.ApiExecutionQueueService;
|
||||||
import io.metersphere.api.service.scenario.ApiScenarioBatchRunService;
|
import io.metersphere.api.service.scenario.ApiScenarioBatchRunService;
|
||||||
|
import io.metersphere.api.service.scenario.ApiScenarioReportService;
|
||||||
import io.metersphere.sdk.constants.ApiExecuteResourceType;
|
import io.metersphere.sdk.constants.ApiExecuteResourceType;
|
||||||
import io.metersphere.sdk.constants.ApiReportStatus;
|
import io.metersphere.sdk.constants.ApiReportStatus;
|
||||||
import io.metersphere.sdk.constants.ApplicationScope;
|
import io.metersphere.sdk.constants.ApplicationScope;
|
||||||
|
@ -38,6 +40,10 @@ public class MessageListener {
|
||||||
private ApiTestCaseBatchRunService apiTestCaseBatchRunService;
|
private ApiTestCaseBatchRunService apiTestCaseBatchRunService;
|
||||||
@Resource
|
@Resource
|
||||||
private ApiScenarioBatchRunService apiScenarioBatchRunService;
|
private ApiScenarioBatchRunService apiScenarioBatchRunService;
|
||||||
|
@Resource
|
||||||
|
private ApiReportService apiReportService;
|
||||||
|
@Resource
|
||||||
|
private ApiScenarioReportService apiScenarioReportService;
|
||||||
|
|
||||||
@KafkaListener(id = MESSAGE_CONSUME_ID, topics = KafkaTopicConstants.API_REPORT_TASK_TOPIC, groupId = MESSAGE_CONSUME_ID)
|
@KafkaListener(id = MESSAGE_CONSUME_ID, topics = KafkaTopicConstants.API_REPORT_TASK_TOPIC, groupId = MESSAGE_CONSUME_ID)
|
||||||
public void messageConsume(ConsumerRecord<?, String> record) {
|
public void messageConsume(ConsumerRecord<?, String> record) {
|
||||||
|
@ -78,9 +84,8 @@ public class MessageListener {
|
||||||
}
|
}
|
||||||
ApiExecuteResourceType resourceType = EnumValidator.validateEnum(ApiExecuteResourceType.class, queue.getResourceType());
|
ApiExecuteResourceType resourceType = EnumValidator.validateEnum(ApiExecuteResourceType.class, queue.getResourceType());
|
||||||
|
|
||||||
if (BooleanUtils.isTrue(queue.getRunModeConfig().getStopOnFailure()) && StringUtils.equals(dto.getReportStatus(), ApiReportStatus.ERROR.name())) {
|
if (isStopOnFailure(dto, queue, resourceType)) {
|
||||||
// 如果是失败停止,清空队列,不继续执行
|
// 失败停止,不执行后续任务
|
||||||
apiExecutionQueueService.deleteQueue(queue.getQueueId());
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,4 +100,30 @@ public class MessageListener {
|
||||||
LogUtils.error("执行任务失败:", e);
|
LogUtils.error("执行任务失败:", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理失败停止后的报告处理
|
||||||
|
* @param dto
|
||||||
|
* @param queue
|
||||||
|
* @param resourceType
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private boolean isStopOnFailure(ApiNoticeDTO dto, ExecutionQueue queue, ApiExecuteResourceType resourceType) {
|
||||||
|
if (BooleanUtils.isTrue(queue.getRunModeConfig().getStopOnFailure()) && StringUtils.equals(dto.getReportStatus(), ApiReportStatus.ERROR.name())) {
|
||||||
|
String reportId = queue.getRunModeConfig().isIntegratedReport() ? queue.getRunModeConfig().getCollectionReport().getReportId() : dto.getReportId();
|
||||||
|
if (resourceType.equals(ApiExecuteResourceType.API_SCENARIO)) {
|
||||||
|
apiScenarioBatchRunService.UpdateStopOnFailureReport(queue);
|
||||||
|
}
|
||||||
|
switch (resourceType) {
|
||||||
|
case API_CASE -> apiReportService.updateReportStatus(reportId, ApiReportStatus.ERROR.name());
|
||||||
|
case API_SCENARIO -> apiScenarioReportService.updateReportStatus(reportId, ApiReportStatus.ERROR.name());
|
||||||
|
default -> {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 如果是失败停止,清空队列,不继续执行
|
||||||
|
apiExecutionQueueService.deleteQueue(queue.getQueueId());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -263,12 +263,12 @@ public class MsScenarioConverter extends AbstractJmeterElementConverter<MsScenar
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addScenarioProcessor(HashTree tree, MsScenario msScenario, ParameterConfig config, boolean isPre) {
|
private void addScenarioProcessor(HashTree tree, MsScenario msScenario, ParameterConfig config, boolean isPre) {
|
||||||
if (isCopy(msScenario.getRefType())) {
|
// 获取场景前后置
|
||||||
|
ScenarioConfig scenarioConfig = msScenario.getScenarioConfig();
|
||||||
|
if (isCopy(msScenario.getRefType()) || scenarioConfig == null) {
|
||||||
// 复制的场景,没有前后置
|
// 复制的场景,没有前后置
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 获取场景前后置
|
|
||||||
ScenarioConfig scenarioConfig = msScenario.getScenarioConfig();
|
|
||||||
MsProcessorConfig processorConfig = isPre ? scenarioConfig.getPreProcessorConfig() : scenarioConfig.getPostProcessorConfig();
|
MsProcessorConfig processorConfig = isPre ? scenarioConfig.getPreProcessorConfig() : scenarioConfig.getPostProcessorConfig();
|
||||||
|
|
||||||
if (processorConfig == null || CollectionUtils.isEmpty(processorConfig.getProcessors())) {
|
if (processorConfig == null || CollectionUtils.isEmpty(processorConfig.getProcessors())) {
|
||||||
|
|
|
@ -9,10 +9,8 @@ import io.metersphere.api.dto.ApiScenarioParamConfig;
|
||||||
import io.metersphere.api.dto.ApiScenarioParseTmpParam;
|
import io.metersphere.api.dto.ApiScenarioParseTmpParam;
|
||||||
import io.metersphere.api.dto.debug.ApiResourceRunRequest;
|
import io.metersphere.api.dto.debug.ApiResourceRunRequest;
|
||||||
import io.metersphere.api.dto.request.MsScenario;
|
import io.metersphere.api.dto.request.MsScenario;
|
||||||
import io.metersphere.api.dto.scenario.ApiScenarioBatchRunRequest;
|
import io.metersphere.api.dto.scenario.*;
|
||||||
import io.metersphere.api.dto.scenario.ApiScenarioDetail;
|
import io.metersphere.api.mapper.ApiScenarioReportMapper;
|
||||||
import io.metersphere.api.dto.scenario.ApiScenarioParseParam;
|
|
||||||
import io.metersphere.api.dto.scenario.ApiScenarioStepDTO;
|
|
||||||
import io.metersphere.api.mapper.ExtApiScenarioMapper;
|
import io.metersphere.api.mapper.ExtApiScenarioMapper;
|
||||||
import io.metersphere.api.service.ApiBatchRunBaseService;
|
import io.metersphere.api.service.ApiBatchRunBaseService;
|
||||||
import io.metersphere.api.service.ApiExecuteService;
|
import io.metersphere.api.service.ApiExecuteService;
|
||||||
|
@ -24,10 +22,7 @@ import io.metersphere.sdk.dto.api.task.CollectionReportDTO;
|
||||||
import io.metersphere.sdk.dto.api.task.TaskRequestDTO;
|
import io.metersphere.sdk.dto.api.task.TaskRequestDTO;
|
||||||
import io.metersphere.sdk.dto.queue.ExecutionQueue;
|
import io.metersphere.sdk.dto.queue.ExecutionQueue;
|
||||||
import io.metersphere.sdk.dto.queue.ExecutionQueueDetail;
|
import io.metersphere.sdk.dto.queue.ExecutionQueueDetail;
|
||||||
import io.metersphere.sdk.util.BeanUtils;
|
import io.metersphere.sdk.util.*;
|
||||||
import io.metersphere.sdk.util.DateUtils;
|
|
||||||
import io.metersphere.sdk.util.LogUtils;
|
|
||||||
import io.metersphere.sdk.util.SubListUtils;
|
|
||||||
import io.metersphere.system.uid.IDGenerator;
|
import io.metersphere.system.uid.IDGenerator;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import org.apache.commons.collections4.CollectionUtils;
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
|
@ -56,6 +51,8 @@ public class ApiScenarioBatchRunService {
|
||||||
@Resource
|
@Resource
|
||||||
private ApiScenarioReportService apiScenarioReportService;
|
private ApiScenarioReportService apiScenarioReportService;
|
||||||
@Resource
|
@Resource
|
||||||
|
private ApiScenarioReportMapper apiScenarioReportMapper;
|
||||||
|
@Resource
|
||||||
private ApiBatchRunBaseService apiBatchRunBaseService;
|
private ApiBatchRunBaseService apiBatchRunBaseService;
|
||||||
@Resource
|
@Resource
|
||||||
private ExtApiScenarioMapper extApiScenarioMapper;
|
private ExtApiScenarioMapper extApiScenarioMapper;
|
||||||
|
@ -224,6 +221,7 @@ public class ApiScenarioBatchRunService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 集成报告,执行前先设置成 RUNNING
|
* 集成报告,执行前先设置成 RUNNING
|
||||||
|
*
|
||||||
* @param runModeConfig
|
* @param runModeConfig
|
||||||
*/
|
*/
|
||||||
private void setRunningIntegrateReport(ApiRunModeConfigDTO runModeConfig) {
|
private void setRunningIntegrateReport(ApiRunModeConfigDTO runModeConfig) {
|
||||||
|
@ -398,4 +396,85 @@ public class ApiScenarioBatchRunService {
|
||||||
return StringUtils.isBlank(runModeConfig.getEnvironmentId()) ? apiScenario.getGrouped() : runModeConfig.getGrouped();
|
return StringUtils.isBlank(runModeConfig.getEnvironmentId()) ? apiScenario.getGrouped() : runModeConfig.getGrouped();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void UpdateStopOnFailureReport(ExecutionQueue queue) {
|
||||||
|
ApiRunModeConfigDTO runModeConfig = queue.getRunModeConfig();
|
||||||
|
try {
|
||||||
|
ExecutionQueueDetail queueDetail = apiExecutionQueueService.getNextDetail(queue.getQueueId());
|
||||||
|
if (queueDetail == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Long requestCount = 0L;
|
||||||
|
while (queueDetail != null) {
|
||||||
|
ApiScenarioDetail apiScenarioDetail = apiScenarioService.get(queueDetail.getResourceId());
|
||||||
|
if (apiScenarioDetail == null) {
|
||||||
|
LogUtils.info("当前场景已删除 {}", queueDetail.getResourceId());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Long requestCountItem = getRequestCount(apiScenarioDetail.getSteps());
|
||||||
|
requestCount += requestCountItem;
|
||||||
|
|
||||||
|
// 初始化报告步骤
|
||||||
|
if (runModeConfig.isIntegratedReport()) {
|
||||||
|
apiScenarioService.initScenarioReportSteps(apiScenarioDetail.getId(), apiScenarioDetail.getSteps(), runModeConfig.getCollectionReport().getReportId());
|
||||||
|
} else {
|
||||||
|
apiScenarioService.initScenarioReportSteps(apiScenarioDetail.getSteps(), queueDetail.getReportId());
|
||||||
|
}
|
||||||
|
queueDetail = apiExecutionQueueService.getNextDetail(queue.getQueueId());
|
||||||
|
}
|
||||||
|
if (runModeConfig.isIntegratedReport()) {
|
||||||
|
// 获取未执行的请求数,更新统计指标
|
||||||
|
String reportId = runModeConfig.getCollectionReport().getReportId();
|
||||||
|
ApiScenarioReport report = apiScenarioReportMapper.selectByPrimaryKey(reportId);
|
||||||
|
Long pendingCount = requestCount + report.getPendingCount();
|
||||||
|
report.setPendingCount(pendingCount);
|
||||||
|
// 计算各种通过率
|
||||||
|
report = computeRequestRate(report);
|
||||||
|
apiScenarioReportMapper.updateByPrimaryKeySelective(report);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtils.error("失败停止,补充报告步骤失败:", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ApiScenarioReport computeRequestRate(ApiScenarioReport report) {
|
||||||
|
long total = apiScenarioReportService.getRequestTotal(report);
|
||||||
|
// 计算各个概率
|
||||||
|
double successRate = calculateRate(report.getSuccessCount(), total);
|
||||||
|
double errorRate = calculateRate(report.getErrorCount(), total);
|
||||||
|
double pendingRate = calculateRate(report.getPendingCount(), total);
|
||||||
|
double fakeErrorRate = calculateRate(report.getFakeErrorCount(), total);
|
||||||
|
|
||||||
|
// 计算总和
|
||||||
|
double sum = successRate + errorRate + pendingRate + fakeErrorRate;
|
||||||
|
|
||||||
|
LogUtils.info("偏移总量重新计算", sum);
|
||||||
|
|
||||||
|
// 避免分母为零
|
||||||
|
double adjustment = sum > 0 ? 1.0 / sum : 0.0;
|
||||||
|
|
||||||
|
// 调整概率,使总和精确为100%
|
||||||
|
successRate *= adjustment;
|
||||||
|
errorRate *= adjustment;
|
||||||
|
pendingRate *= adjustment;
|
||||||
|
fakeErrorRate *= adjustment;
|
||||||
|
|
||||||
|
report.setRequestPassRate(formatRate(successRate));
|
||||||
|
report.setRequestErrorRate(formatRate(errorRate));
|
||||||
|
report.setRequestPendingRate(formatRate(pendingRate));
|
||||||
|
report.setRequestFakeErrorRate(formatRate(fakeErrorRate));
|
||||||
|
|
||||||
|
return report;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算概率
|
||||||
|
private static double calculateRate(long count, double total) {
|
||||||
|
return total > 0 ? count / total : 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化概率,保留两位小数
|
||||||
|
private static String formatRate(double rate) {
|
||||||
|
return String.format("%.2f", rate * 100);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -184,9 +184,6 @@ public class ApiScenarioReportService {
|
||||||
BeanUtils.copyBean(scenarioReportDTO, scenarioReport);
|
BeanUtils.copyBean(scenarioReportDTO, scenarioReport);
|
||||||
//需要查询出所有的步骤
|
//需要查询出所有的步骤
|
||||||
List<ApiScenarioReportStepDTO> scenarioReportSteps = extApiScenarioReportMapper.selectStepByReportId(id);
|
List<ApiScenarioReportStepDTO> scenarioReportSteps = extApiScenarioReportMapper.selectStepByReportId(id);
|
||||||
if (CollectionUtils.isEmpty(scenarioReportSteps)) {
|
|
||||||
throw new MSException(Translator.get("api_scenario_report_not_exist"));
|
|
||||||
}
|
|
||||||
if (BooleanUtils.isFalse(scenarioReport.getIntegrated())) {
|
if (BooleanUtils.isFalse(scenarioReport.getIntegrated())) {
|
||||||
ApiScenarioBlob apiScenarioBlob = extApiScenarioReportMapper.getScenarioBlob(id);
|
ApiScenarioBlob apiScenarioBlob = extApiScenarioReportMapper.getScenarioBlob(id);
|
||||||
if (apiScenarioBlob != null) {
|
if (apiScenarioBlob != null) {
|
||||||
|
@ -236,12 +233,12 @@ public class ApiScenarioReportService {
|
||||||
//将scenarioReportSteps按照parentId进行分组 值为list 然后根据sort进行排序
|
//将scenarioReportSteps按照parentId进行分组 值为list 然后根据sort进行排序
|
||||||
Map<String, List<ApiScenarioReportStepDTO>> scenarioReportStepMap = scenarioReportSteps.stream().collect(Collectors.groupingBy(ApiScenarioReportStepDTO::getParentId));
|
Map<String, List<ApiScenarioReportStepDTO>> scenarioReportStepMap = scenarioReportSteps.stream().collect(Collectors.groupingBy(ApiScenarioReportStepDTO::getParentId));
|
||||||
// TODO 查询修改
|
// TODO 查询修改
|
||||||
List<ApiScenarioReportStepDTO> steps = scenarioReportStepMap.get("NONE");
|
List<ApiScenarioReportStepDTO> steps = Optional.ofNullable(scenarioReportStepMap.get("NONE")).orElse(new ArrayList<>(0));
|
||||||
steps.sort(Comparator.comparingLong(ApiScenarioReportStepDTO::getSort));
|
steps.sort(Comparator.comparingLong(ApiScenarioReportStepDTO::getSort));
|
||||||
getStepTree(steps, scenarioReportStepMap);
|
getStepTree(steps, scenarioReportStepMap);
|
||||||
|
|
||||||
scenarioReportDTO.setStepTotal(steps.size());
|
scenarioReportDTO.setStepTotal(steps.size());
|
||||||
scenarioReportDTO.setRequestTotal(scenarioReportDTO.getErrorCount() + scenarioReportDTO.getPendingCount() + scenarioReportDTO.getSuccessCount() + scenarioReportDTO.getFakeErrorCount());
|
scenarioReportDTO.setRequestTotal(getRequestTotal(scenarioReportDTO));
|
||||||
scenarioReportDTO.setChildren(steps);
|
scenarioReportDTO.setChildren(steps);
|
||||||
|
|
||||||
scenarioReportDTO.setStepErrorCount(steps.stream().filter(step -> StringUtils.equals(ApiReportStatus.ERROR.name(), step.getStatus())).count());
|
scenarioReportDTO.setStepErrorCount(steps.stream().filter(step -> StringUtils.equals(ApiReportStatus.ERROR.name(), step.getStatus())).count());
|
||||||
|
@ -274,6 +271,10 @@ public class ApiScenarioReportService {
|
||||||
return scenarioReportDTO;
|
return scenarioReportDTO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getRequestTotal(ApiScenarioReport report) {
|
||||||
|
return report.getErrorCount() + report.getPendingCount() + report.getSuccessCount() + report.getFakeErrorCount();
|
||||||
|
}
|
||||||
|
|
||||||
private static void getStepTree(List<ApiScenarioReportStepDTO> steps, Map<String, List<ApiScenarioReportStepDTO>> scenarioReportStepMap) {
|
private static void getStepTree(List<ApiScenarioReportStepDTO> steps, Map<String, List<ApiScenarioReportStepDTO>> scenarioReportStepMap) {
|
||||||
if (CollectionUtils.isNotEmpty(steps)) {
|
if (CollectionUtils.isNotEmpty(steps)) {
|
||||||
List<String> stepTypes = Arrays.asList(ApiScenarioStepType.IF_CONTROLLER.name(),
|
List<String> stepTypes = Arrays.asList(ApiScenarioStepType.IF_CONTROLLER.name(),
|
||||||
|
|
|
@ -1710,6 +1710,8 @@ public class ApiScenarioService extends MoveNodeService {
|
||||||
|
|
||||||
// 获取当前场景配置的环境信息
|
// 获取当前场景配置的环境信息
|
||||||
EnvironmentModeDTO environmentModeDTO = scenarioParseEnvInfo.getRefScenarioEnvMap().get(step.getResourceId());
|
EnvironmentModeDTO environmentModeDTO = scenarioParseEnvInfo.getRefScenarioEnvMap().get(step.getResourceId());
|
||||||
|
|
||||||
|
if (environmentModeDTO != null) {
|
||||||
String environmentId = environmentModeDTO.getEnvironmentId();
|
String environmentId = environmentModeDTO.getEnvironmentId();
|
||||||
|
|
||||||
// 设置是否是环境组
|
// 设置是否是环境组
|
||||||
|
@ -1725,6 +1727,7 @@ public class ApiScenarioService extends MoveNodeService {
|
||||||
msScenario.setEnvironmentInfo(envMap.get(environmentId));
|
msScenario.setEnvironmentInfo(envMap.get(environmentId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从 scenarioParseEnvInfo 获取对应环境组的 projectEnvMap
|
* 从 scenarioParseEnvInfo 获取对应环境组的 projectEnvMap
|
||||||
|
@ -2151,7 +2154,7 @@ public class ApiScenarioService extends MoveNodeService {
|
||||||
}
|
}
|
||||||
apiCommonService.setEnableCommonScriptProcessorInfo(msTestElement);
|
apiCommonService.setEnableCommonScriptProcessorInfo(msTestElement);
|
||||||
}
|
}
|
||||||
return stepDetail;
|
return JSON.parseObject(JSON.toJSONString(stepDetail));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkTargetModule(String targetModuleId, String projectId) {
|
private void checkTargetModule(String targetModuleId, String projectId) {
|
||||||
|
|
Loading…
Reference in New Issue