refactor(接口测试): 优化误报执行过程处理逻辑

Signed-off-by: fit2-zhao <yong.zhao@fit2cloud.com>
This commit is contained in:
fit2-zhao 2023-04-26 10:14:45 +08:00 committed by fit2-zhao
parent 1f04e436dc
commit 2b586710db
38 changed files with 436 additions and 344 deletions

View File

@ -2,11 +2,13 @@ package io.metersphere.api.exec.api;
import io.metersphere.api.exec.queue.DBTestQueue;
import io.metersphere.api.jmeter.JMeterService;
import io.metersphere.api.jmeter.utils.ApiFakeErrorUtil;
import io.metersphere.api.jmeter.utils.SmoothWeighted;
import io.metersphere.base.domain.ApiDefinitionExecResultWithBLOBs;
import io.metersphere.commons.constants.CommonConstants;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.GenerateHashTreeUtil;
import io.metersphere.commons.utils.JSON;
import io.metersphere.constants.RunModeConstants;
import io.metersphere.dto.BaseSystemConfigDTO;
import io.metersphere.dto.JmeterRunRequestDTO;
@ -15,15 +17,13 @@ import io.metersphere.service.RemakeReportService;
import io.metersphere.service.SystemParameterService;
import io.metersphere.utils.LoggerUtil;
import io.metersphere.vo.BooleanPool;
import jakarta.annotation.Resource;
import org.apache.commons.collections4.MapUtils;
import org.apache.jorphan.collections.HashTree;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
import java.util.*;
@Service
public class ApiCaseParallelExecuteService {
@ -34,7 +34,8 @@ public class ApiCaseParallelExecuteService {
@Resource
private RedisTemplate<String, Object> redisTemplate;
@Resource
private RemakeReportService remakeReportService;
private RemakeReportService remakeReportService;
public void parallel(Map<String, ApiDefinitionExecResultWithBLOBs> executeQueue, RunModeConfigDTO config, DBTestQueue executionQueue, String runMode) {
BooleanPool pool = GenerateHashTreeUtil.isResourcePool(config.getResourcePoolId());
// 初始化分配策略
@ -73,13 +74,15 @@ public class ApiCaseParallelExecuteService {
}
// 开始执行
runRequest.getExtendedParameters().put("projectId", executionQueue.getDetail().getProjectIds());
runRequest.setFakeErrorMap(ApiFakeErrorUtil.get(
JSON.parseArray(executionQueue.getDetail().getProjectIds())));
LoggerUtil.info("进入并行模式,开始执行用例:[" + result.getName() + "] 报告ID [" + reportId + "]");
} catch (Exception e) {
remakeReportService.testEnded(runRequest, e.getMessage());
LoggerUtil.error("脚本处理失败", runRequest.getReportId(), e);
continue;
}
// 误报规则
jMeterService.run(runRequest);
}
}

View File

@ -8,6 +8,7 @@ import io.metersphere.api.dto.definition.request.ParameterConfig;
import io.metersphere.api.exec.queue.DBTestQueue;
import io.metersphere.api.jmeter.JMeterService;
import io.metersphere.api.jmeter.NewDriverManager;
import io.metersphere.api.jmeter.utils.ApiFakeErrorUtil;
import io.metersphere.api.jmeter.utils.SmoothWeighted;
import io.metersphere.base.domain.ApiDefinitionExecResultWithBLOBs;
import io.metersphere.base.domain.ApiExecutionQueueDetail;
@ -77,6 +78,8 @@ public class ApiCaseSerialService {
if (runRequest.getPool().isPool()) {
SmoothWeighted.setServerConfig(runRequest.getPoolId(), redisTemplate);
}
runRequest.setFakeErrorMap(ApiFakeErrorUtil.get(
JSON.parseArray(queue.getProjectIds())));
// 开始执行
runRequest.getExtendedParameters().put(PROJECT_ID, queue.getProjectIds());
} catch (Exception e) {

View File

@ -14,6 +14,7 @@ import io.metersphere.api.dto.scenario.Body;
import io.metersphere.api.dto.scenario.environment.EnvironmentConfig;
import io.metersphere.api.jmeter.JMeterService;
import io.metersphere.api.jmeter.NewDriverManager;
import io.metersphere.api.jmeter.utils.ApiFakeErrorUtil;
import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.ApiDefinitionExecResultMapper;
import io.metersphere.base.mapper.ApiDefinitionMapper;
@ -115,27 +116,24 @@ public class ApiExecuteService {
}
public MsExecResponseDTO exec(RunCaseRequest request, Map<String, Object> extendedParameters) {
ApiTestCaseWithBLOBs testCaseWithBLOBs = request.getBloBs();
PerformInspectionUtil.countMatches(testCaseWithBLOBs.getRequest(), testCaseWithBLOBs.getId());
ApiTestCaseWithBLOBs testCase = request.getBloBs();
PerformInspectionUtil.countMatches(testCase.getRequest(), testCase.getId());
if (StringUtils.equals(request.getRunMode(), ApiRunMode.JENKINS_API_PLAN.name())) {
testCaseWithBLOBs = apiTestCaseMapper.selectByPrimaryKey(request.getReportId());
testCase = apiTestCaseMapper.selectByPrimaryKey(request.getReportId());
request.setCaseId(request.getReportId());
//通过测试计划id查询环境
request.setReportId(request.getTestPlanId());
}
LoggerUtil.info("开始执行单条用例【 " + testCaseWithBLOBs.getId() + "", request.getReportId());
LoggerUtil.info("开始执行单条用例【 " + testCase.getId() + "", request.getReportId());
RunModeConfigDTO runModeConfigDTO = new RunModeConfigDTO();
jMeterService.verifyPool(testCaseWithBLOBs.getProjectId(), runModeConfigDTO);
jMeterService.verifyPool(testCase.getProjectId(), runModeConfigDTO);
// 多态JSON普通转换会丢失内容需要通过 ObjectMapper 获取
JmeterRunRequestDTO runRequest = new JmeterRunRequestDTO(testCaseWithBLOBs.getId(), StringUtils.isEmpty(request.getReportId()) ? request.getId() : request.getReportId(), request.getRunMode(), null, null);
if (testCaseWithBLOBs != null && StringUtils.isNotEmpty(testCaseWithBLOBs.getRequest())) {
JmeterRunRequestDTO runRequest = new JmeterRunRequestDTO(testCase.getId(),
StringUtils.isEmpty(request.getReportId()) ? request.getId()
: request.getReportId(), request.getRunMode(), null,null);
if (testCase != null && StringUtils.isNotEmpty(testCase.getRequest())) {
try {
HashTree jmeterHashTree = this.generateHashTree(request, testCaseWithBLOBs, runModeConfigDTO);
if (LoggerUtil.getLogger().isDebugEnabled()) {
LoggerUtil.debug("生成jmx文件" + ElementUtil.hashTreeToString(jmeterHashTree));
}
HashTree jmeterHashTree = this.generateHashTree(request, testCase, runModeConfigDTO);
// 调用执行方法
runRequest.setHashTree(jmeterHashTree);
if (MapUtils.isNotEmpty(extendedParameters)) {
@ -148,11 +146,16 @@ public class ApiExecuteService {
BaseSystemConfigDTO baseInfo = systemParameterService.getBaseInfo();
runRequest.setPlatformUrl(GenerateHashTreeUtil.getPlatformUrl(baseInfo, runRequest, null));
}
jMeterService.run(runRequest);
String projectId = testCase.getProjectId();
runRequest.setFakeErrorMap(ApiFakeErrorUtil.get(new ArrayList<>() {{
this.add(projectId);
}}));
} catch (Exception ex) {
remakeReportService.updateReport(runRequest, ex.getMessage());
LogUtil.error(ex.getMessage(), ex);
return new MsExecResponseDTO(request.getCaseId(), request.getReport().getId(), request.getRunMode());
}
jMeterService.run(runRequest);
}
return new MsExecResponseDTO(request.getCaseId(), request.getReport().getId(), request.getRunMode());
}
@ -165,6 +168,9 @@ public class ApiExecuteService {
request.getTestElement().getHashTree().get(0).setName(request.getId());
}
JmeterRunRequestDTO runRequest = this.initRunRequest(request, bodyFiles);
runRequest.setFakeErrorMap(ApiFakeErrorUtil.get(new ArrayList<>() {{
this.add(request.getProjectId());
}}));
// 开始执行
jMeterService.run(runRequest);
return new MsExecResponseDTO(runRequest.getTestId(), runRequest.getReportId(), runRequest.getRunMode());

View File

@ -13,6 +13,7 @@ import io.metersphere.api.exec.api.ApiCaseExecuteService;
import io.metersphere.api.exec.queue.DBTestQueue;
import io.metersphere.api.jmeter.JMeterService;
import io.metersphere.api.jmeter.NewDriverManager;
import io.metersphere.api.jmeter.utils.ApiFakeErrorUtil;
import io.metersphere.base.domain.ApiScenario;
import io.metersphere.base.domain.ApiScenarioExample;
import io.metersphere.base.domain.ApiScenarioReportWithBLOBs;
@ -466,6 +467,8 @@ public class ApiScenarioExecuteService {
test.setProperty(ApiTestConstants.JAR_PATH, JSON.toJSONString(loadJar.keySet().stream().toList()));
runRequest.setCustomJarInfo(loadJar);
}
runRequest.setFakeErrorMap(ApiFakeErrorUtil.get(
NewDriverManager.getProjectIds(request)));
jMeterService.run(runRequest);
return request.getId();
}

View File

@ -4,8 +4,10 @@ import io.metersphere.api.dto.RunModeDataDTO;
import io.metersphere.api.dto.automation.RunScenarioRequest;
import io.metersphere.api.exec.queue.DBTestQueue;
import io.metersphere.api.jmeter.JMeterService;
import io.metersphere.api.jmeter.utils.ApiFakeErrorUtil;
import io.metersphere.api.jmeter.utils.SmoothWeighted;
import io.metersphere.commons.utils.GenerateHashTreeUtil;
import io.metersphere.commons.utils.JSON;
import io.metersphere.constants.RunModeConstants;
import io.metersphere.dto.BaseSystemConfigDTO;
import io.metersphere.dto.JmeterRunRequestDTO;
@ -57,6 +59,8 @@ public class ApiScenarioParallelService {
dataDTO.getPlanEnvMap(), runRequest));
}
runRequest.getExtendedParameters().put("projectId", executionQueue.getDetail().getProjectIds());
runRequest.setFakeErrorMap(ApiFakeErrorUtil.get(
JSON.parseArray(executionQueue.getDetail().getProjectIds())));
LoggerUtil.info("进入并行模式,准备执行场景:[ " +
executeQueue.get(reportId).getReport().getName() + " ]", reportId);
} catch (Exception e) {

View File

@ -3,6 +3,7 @@ package io.metersphere.api.exec.scenario;
import io.metersphere.api.exec.queue.DBTestQueue;
import io.metersphere.api.jmeter.JMeterService;
import io.metersphere.api.jmeter.utils.ApiFakeErrorUtil;
import io.metersphere.api.jmeter.utils.SmoothWeighted;
import io.metersphere.base.domain.ApiExecutionQueueDetail;
import io.metersphere.base.domain.ApiScenarioReport;
@ -99,6 +100,8 @@ public class ApiScenarioSerialService {
}
// 开始执行
runRequest.getExtendedParameters().put("projectId", queue.getProjectIds());
runRequest.setFakeErrorMap(ApiFakeErrorUtil.get(
JSON.parseArray(queue.getProjectIds())));
} catch (Exception e) {
remakeReportService.testEnded(runRequest, e.getMessage());
LoggerUtil.error("脚本处理失败", runRequest.getReportId(), e);

View File

@ -90,25 +90,25 @@ public class JMeterService {
/**
* 添加调试监听
*
* @param testId
* @param testPlan
*/
private void addDebugListener(String testId, HashTree testPlan, String runMode) {
private void addDebugListener(JmeterRunRequestDTO request) {
MsDebugListener resultCollector = new MsDebugListener();
resultCollector.setName(testId);
resultCollector.setName(request.getReportId());
resultCollector.setProperty(TestElement.TEST_CLASS, MsDebugListener.class.getName());
resultCollector.setProperty(TestElement.GUI_CLASS, SaveService.aliasToClass("ViewResultsFullVisualizer"));
resultCollector.setEnabled(true);
resultCollector.setRunMode(runMode);
resultCollector.setRunMode(request.getRunMode());
resultCollector.setFakeErrorMap(request.getFakeErrorMap());
// 添加DEBUG标示
HashTree test = ArrayUtils.isNotEmpty(testPlan.getArray()) ? testPlan.getTree(testPlan.getArray()[0]) : null;
if (test != null && ArrayUtils.isNotEmpty(test.getArray()) && test.getArray()[0] instanceof ThreadGroup) {
HashTree test = ArrayUtils.isNotEmpty(request.getHashTree().getArray())
? request.getHashTree().getTree(request.getHashTree().getArray()[0]) : null;
if (test != null && ArrayUtils.isNotEmpty(test.getArray())
&& test.getArray()[0] instanceof ThreadGroup) {
ThreadGroup group = (ThreadGroup) test.getArray()[0];
group.setProperty(BackendListenerConstants.MS_DEBUG.name(), true);
}
testPlan.add(testPlan.getArray()[0], resultCollector);
request.getHashTree().add(request.getHashTree().getArray()[0], resultCollector);
}
private void runLocal(JmeterRunRequestDTO request) {
@ -131,12 +131,12 @@ public class JMeterService {
&& request.getExtendedParameters().containsKey(ExtendedParameter.SYNC_STATUS)
&& (Boolean) request.getExtendedParameters().get(ExtendedParameter.SYNC_STATUS)) {
LoggerUtil.debug("为请求 [ " + request.getReportId() + " ] 添加Debug Listener");
addDebugListener(request.getReportId(), request.getHashTree(), request.getRunMode());
addDebugListener(request);
}
if (request.isDebug()) {
LoggerUtil.debug("为请求 [ " + request.getReportId() + " ] 添加Debug Listener");
addDebugListener(request.getReportId(), request.getHashTree(), request.getRunMode());
addDebugListener(request);
} else {
LoggerUtil.debug("为请求 [ " + request.getReportId() + " ] 添加同步接收结果 Listener");
JMeterBase.addBackendListener(request, request.getHashTree(), MsApiBackendListener.class.getCanonicalName());
@ -178,6 +178,7 @@ public class JMeterService {
engine.start();
} catch (Exception e) {
remakeReportService.testEnded(request, e.getMessage());
redisTemplateService.delFilePathAndScript(request.getReportId(), request.getTestId());
LoggerUtil.error("调用K8S执行请求[ " + request.getTestId() + " ]失败:", request.getReportId(), e);
}
} else if ((MapUtils.isNotEmpty(request.getExtendedParameters())
@ -203,6 +204,7 @@ public class JMeterService {
apiPoolDebugService.run(request, resources);
} catch (Exception e) {
remakeReportService.updateReport(request, e.getMessage());
redisTemplateService.delFilePathAndScript(request.getReportId(), request.getTestId());
LoggerUtil.error("发送请求[ " + request.getTestId() + " ] 执行失败,进行数据回滚:", request.getReportId(), e);
MSException.throwException("调用资源池执行失败,请检查资源池是否配置正常");
}
@ -231,6 +233,7 @@ public class JMeterService {
}
} catch (Exception e) {
remakeReportService.testEnded(request, e.getMessage());
redisTemplateService.delFilePath(request.getReportId());
LoggerUtil.error("发送请求[ " + request.getTestId() + " ] 执行失败,进行数据回滚:", request.getReportId(), e);
}
}

View File

@ -2,9 +2,7 @@ package io.metersphere.api.jmeter;
import com.fasterxml.jackson.core.type.TypeReference;
import io.metersphere.api.exec.queue.PoolExecBlockingQueueUtil;
import io.metersphere.api.jmeter.utils.JmxFileUtil;
import io.metersphere.commons.constants.ApiRunMode;
import io.metersphere.commons.constants.ExtendedParameter;
import io.metersphere.commons.utils.JSON;
import io.metersphere.dto.ResultDTO;
import io.metersphere.service.ApiExecutionQueueService;
@ -14,6 +12,7 @@ import io.metersphere.utils.LoggerUtil;
import lombok.Data;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.kafka.clients.consumer.ConsumerRecord;
@ -60,13 +59,12 @@ public class KafkaListenerTask implements Runnable {
return;
}
if (dto.getArbitraryData() != null && dto.getArbitraryData().containsKey(ExtendedParameter.TEST_END)
&& (Boolean) dto.getArbitraryData().get(ExtendedParameter.TEST_END)) {
redisTemplateService.delete(JmxFileUtil.getExecuteFileKeyInRedis(dto.getReportId()));
if (BooleanUtils.isTrue(dto.getHasEnded())) {
redisTemplateService.delFilePath(dto.getReportId());
resultDTOS.add(dto);
// 全局并发队列
PoolExecBlockingQueueUtil.offer(dto.getReportId());
LoggerUtil.info("KAFKA消费结果处理状态:" + dto.getArbitraryData().get(ExtendedParameter.TEST_END), String.valueOf(record.key()));
LoggerUtil.info("KAFKA消费结束:", record.key());
}
// 携带结果
if (CollectionUtils.isNotEmpty(dto.getRequestResults())) {

View File

@ -1,14 +1,15 @@
package io.metersphere.api.jmeter;
import com.fasterxml.jackson.core.type.TypeReference;
import io.metersphere.api.exec.queue.PoolExecBlockingQueueUtil;
import io.metersphere.api.jmeter.utils.JmxFileUtil;
import io.metersphere.api.jmeter.utils.ReportStatusUtil;
import io.metersphere.utils.ReportStatusUtil;
import io.metersphere.commons.constants.CommonConstants;
import io.metersphere.commons.utils.*;
import io.metersphere.commons.vo.ResultVO;
import io.metersphere.vo.ResultVO;
import io.metersphere.constants.BackendListenerConstants;
import io.metersphere.constants.RunModeConstants;
import io.metersphere.dto.MsRegexDTO;
import io.metersphere.dto.ResultDTO;
import io.metersphere.jmeter.JMeterBase;
import io.metersphere.service.ApiExecutionQueueService;
@ -65,8 +66,6 @@ public class MsApiBackendListener extends AbstractBackendListenerClient implemen
if (dto.isRetryEnable()) {
queues.addAll(sampleResults);
} else {
redisTemplateService.delete(JmxFileUtil.getExecuteFileKeyInRedis(dto.getReportId()));
if (!StringUtils.equals(dto.getReportType(), RunModeConstants.SET_REPORT.toString())) {
dto.setConsole(FixedCapacityUtil.getJmeterLogger(getReportId(), false));
}
@ -84,9 +83,6 @@ public class MsApiBackendListener extends AbstractBackendListenerClient implemen
public void teardownTest(BackendListenerContext context) {
try {
LoggerUtil.info("进入TEST-END处理报告" + dto.getRunMode(), dto.getReportId());
redisTemplateService.delete(JmxFileUtil.getExecuteFileKeyInRedis(dto.getReportId()));
super.teardownTest(context);
// 获取执行日志
if (!StringUtils.equals(dto.getReportType(), RunModeConstants.SET_REPORT.toString())) {
@ -125,6 +121,7 @@ public class MsApiBackendListener extends AbstractBackendListenerClient implemen
LoggerUtil.error("结果集处理异常", dto.getReportId(), e);
} finally {
queues.clear();
redisTemplateService.delFilePath(dto.getReportId());
FileUtils.deleteBodyFiles(dto.getReportId());
if (FileServer.getFileServer() != null) {
LoggerUtil.info("进入监听开始关闭CSV", dto.getReportId());
@ -158,6 +155,12 @@ public class MsApiBackendListener extends AbstractBackendListenerClient implemen
if (StringUtils.isNotEmpty(ept)) {
dto.setExtendedParameters(JSON.parseObject(context.getParameter(BackendListenerConstants.EPT.name()), Map.class));
}
if (StringUtils.isNotBlank(context.getParameter(BackendListenerConstants.FAKE_ERROR.name()))) {
Map<String, List<MsRegexDTO>> fakeErrorMap = JSON.parseObject(
context.getParameter(BackendListenerConstants.FAKE_ERROR.name()),
new TypeReference<Map<String, List<MsRegexDTO>>>() {});
dto.setFakeErrorMap(fakeErrorMap);
}
}
private String getReportId() {

View File

@ -23,6 +23,7 @@ import io.metersphere.api.dto.RunningParamKeys;
import io.metersphere.api.exec.queue.PoolExecBlockingQueueUtil;
import io.metersphere.commons.constants.ApiRunMode;
import io.metersphere.commons.utils.*;
import io.metersphere.dto.MsRegexDTO;
import io.metersphere.dto.RequestResult;
import io.metersphere.jmeter.JMeterBase;
import io.metersphere.service.definition.ApiDefinitionEnvService;
@ -38,6 +39,7 @@ import org.apache.jmeter.testelement.property.BooleanProperty;
import org.apache.jmeter.threads.JMeterVariables;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
/**
@ -57,6 +59,7 @@ public class MsDebugListener extends AbstractListenerElement implements SampleLi
private String runMode;
private ApiDefinitionEnvService apiDefinitionEnvService;
private Map<String, List<MsRegexDTO>> fakeErrorMap;
@Override
public Object clone() {
@ -64,6 +67,10 @@ public class MsDebugListener extends AbstractListenerElement implements SampleLi
return clone;
}
public void setFakeErrorMap(Map<String, List<MsRegexDTO>> fakeErrorMap) {
this.fakeErrorMap = fakeErrorMap;
}
public boolean isErrorLogging() {
return getPropertyAsBoolean(ERROR_LOGGING);
}
@ -160,7 +167,7 @@ public class MsDebugListener extends AbstractListenerElement implements SampleLi
SampleResult result = event.getResult();
this.setVars(result);
if (isSampleWanted(result.isSuccessful(), result) && !StringUtils.equals(result.getSampleLabel(), RunningParamKeys.RUNNING_DEBUG_SAMPLER_NAME)) {
RequestResult requestResult = JMeterBase.getRequestResult(result);
RequestResult requestResult = JMeterBase.getRequestResult(result, fakeErrorMap);
if (requestResult != null && ResultParseUtil.isNotAutoGenerateSampler(requestResult)) {
MsgDTO dto = new MsgDTO();
dto.setExecEnd(false);

View File

@ -97,6 +97,14 @@ public class NewDriverManager {
public static Map<String, List<ProjectJarConfig>> loadJar(RunDefinitionRequest request, BooleanPool pool) {
// 加载自定义JAR
MsTestPlan testPlan = (MsTestPlan) request.getTestElement();
List<String> projectIds = getProjectIds(request);
Map<String, List<ProjectJarConfig>> jars = getJars(projectIds, pool);
testPlan.setProjectJarIds(projectIds);
return jars;
}
public static List<String> getProjectIds(RunDefinitionRequest request) {
List<String> projectIds = new ArrayList<>();
projectIds.add(request.getProjectId());
if (MapUtils.isNotEmpty(request.getEnvironmentMap())) {
@ -106,10 +114,6 @@ public class NewDriverManager {
}
});
}
Map<String, List<ProjectJarConfig>> jars = getJars(projectIds, pool);
testPlan.setProjectJarIds(projectIds);
return jars;
return projectIds;
}
}
}

View File

@ -0,0 +1,57 @@
package io.metersphere.api.jmeter.utils;
import io.metersphere.base.domain.ErrorReportLibraryExample;
import io.metersphere.base.domain.ErrorReportLibraryWithBLOBs;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.JSON;
import io.metersphere.commons.utils.JSONUtil;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.dto.MsRegexDTO;
import io.metersphere.xpack.fake.error.ErrorReportLibraryService;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
public class ApiFakeErrorUtil {
private static final String REGEX_CONFIG = "regexConfig";
public static Map<String, List<MsRegexDTO>> get(List<String> projectIds) {
Map<String, List<MsRegexDTO>> fakeErrorMap = new HashMap<>();
if (CollectionUtils.isEmpty(projectIds)) {
return fakeErrorMap;
}
ErrorReportLibraryService service = CommonBeanFactory.getBean(ErrorReportLibraryService.class);
if (service != null) {
ErrorReportLibraryExample example = new ErrorReportLibraryExample();
example.createCriteria().andProjectIdIn(projectIds).andStatusEqualTo(true);
List<ErrorReportLibraryWithBLOBs> bloBs = service.selectByExampleWithBLOBs(example);
bloBs.forEach(item -> {
if (StringUtils.isNotEmpty(item.getContent())) {
try {
Map<String, Object> assertionMap = JSON.parseObject(item.getContent(), Map.class);
if (assertionMap != null) {
MsRegexDTO regexConfig = JSON.parseObject(
JSONUtil.toJSONString(assertionMap.get(REGEX_CONFIG)), MsRegexDTO.class);
regexConfig.setErrorCode(item.getErrorCode());
if (fakeErrorMap.containsKey(item.getProjectId())) {
fakeErrorMap.get(item.getProjectId()).add(regexConfig);
} else {
fakeErrorMap.put(item.getProjectId(), new LinkedList<>() {{
this.add(regexConfig);
}});
}
}
} catch (Exception e) {
LogUtil.error(e);
}
}
});
}
return fakeErrorMap;
}
}

View File

@ -3,5 +3,4 @@ package io.metersphere.commons.constants;
public class ExtendedParameter {
public static final String SYNC_STATUS = "SYN_RES";
public static final String SAVE_RESULT = "SAVE_RESULT";
public static final String TEST_END = "TEST_END";
}

View File

@ -1,108 +0,0 @@
package io.metersphere.commons.utils;
import io.metersphere.api.dto.FakeErrorLibraryDTO;
import io.metersphere.api.dto.definition.FakeError;
import io.metersphere.api.dto.definition.MsRegexDTO;
import io.metersphere.base.domain.ErrorReportLibraryExample;
import io.metersphere.base.domain.ErrorReportLibraryWithBLOBs;
import io.metersphere.commons.enums.ApiReportStatus;
import io.metersphere.dto.RequestResult;
import io.metersphere.utils.JsonUtils;
import io.metersphere.xpack.fake.error.ErrorReportLibraryService;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* 误报解析类
*
* @author xiaogang
*/
public class FakeErrorParse {
public static FakeErrorLibraryDTO parseAssertions(RequestResult result) {
FakeErrorLibraryDTO fakeError = new FakeErrorLibraryDTO();
if (StringUtils.isNotBlank(result.getFakeErrorMessage())) {
FakeError errorReportDTO = JsonUtils.parseObject(result.getFakeErrorMessage(), FakeError.class);
ErrorReportLibraryService service = CommonBeanFactory.getBean(ErrorReportLibraryService.class);
if (service != null) {
ErrorReportLibraryExample example = new ErrorReportLibraryExample();
example.createCriteria().andProjectIdEqualTo(errorReportDTO.getProjectId()).andStatusEqualTo(true);
List<ErrorReportLibraryWithBLOBs> bloBs = service.selectByExampleWithBLOBs(example);
List<MsRegexDTO> regexList = new ArrayList<>();
bloBs.forEach(item -> {
if (StringUtils.isNotEmpty(item.getContent())) {
try {
Map<String, Object> assertionMap = JSON.parseObject(item.getContent(), Map.class);
if (assertionMap != null) {
MsRegexDTO regexConfig = JSON.parseObject(JSONUtil.toJSONString(assertionMap.get("regexConfig")), MsRegexDTO.class);
regexConfig.setErrorCode(item.getErrorCode());
regexList.add(regexConfig);
}
} catch (Exception e) {
LogUtil.error(e);
}
}
});
//根据配置来筛选断言获取误报编码获取接口状态是否是误报
List<String> errorCodeList = new ArrayList<>();
regexList.forEach(item -> {
if (StringUtils.isNotEmpty(item.getSubject())) {
switch (item.getSubject()) {
case "Response Code" ->
item.setPass(parseResponseCode(result.getResponseResult().getResponseCode(), item.getValue(), item.getCondition()));
case "Response Headers" ->
item.setPass(parseResponseCode(result.getResponseResult().getHeaders(), item.getValue(), item.getCondition()));
case "Response Data" ->
item.setPass(parseResponseCode(result.getResponseResult().getBody(), item.getValue(), item.getCondition()));
default -> item.setPass(false);
}
}
if (item.isPass()) {
errorCodeList.add(item.getErrorCode());
}
});
boolean higherThanError = errorReportDTO.isHigherThanError();
boolean higherThanSuccess = errorReportDTO.isHigherThanSuccess();
if (CollectionUtils.isNotEmpty(errorCodeList)) {
if ((higherThanError && !result.isSuccess()) || (higherThanSuccess && result.isSuccess())) {
fakeError.setRequestStatus(ApiReportStatus.FAKE_ERROR.name());
}
fakeError.setErrorCodeList(errorCodeList);
}
LogUtil.info(" FAKE_ERROR result: config-higherThanError:" + higherThanError
+ ", config-higherThanSuccess:" + higherThanSuccess
+ ", resultIsSuccess: " + result.isSuccess()
+ ", isFakeError: " + ((higherThanError && !result.isSuccess()) || (higherThanSuccess && result.isSuccess()))
+ "; status:" + fakeError.getRequestStatus());
}
}
fakeError.setResult(result);
return fakeError;
}
private static boolean parseResponseCode(String result, String regexDTO, String condition) {
return switch (condition.toUpperCase()) {
case "CONTAINS" -> result.contains(regexDTO);
case "NOT_CONTAINS" -> notContains(result, regexDTO);
case "EQUALS" -> StringUtils.equals(result, regexDTO);
case "START_WITH" -> result.startsWith(regexDTO);
case "END_WITH" -> result.endsWith(regexDTO);
default -> false;
};
}
private static boolean notContains(String result, String regexDTO) {
return !result.contains(regexDTO);
}
}

View File

@ -165,7 +165,7 @@ public class GenerateHashTreeUtil {
} catch (Exception ex) {
LoggerUtil.error("场景资源:" + item.getName() + ", 生成执行脚本失败",
runRequest.getReportId(), ex);
MSException.throwException("场景资源:" + item.getName() + ", 生成执行脚本失败");
MSException.throwException("场景资源:" + item.getName() + ", 生成执行脚本失败" + ex.getMessage());
}
LogUtil.info(testPlan.getJmx(jmeterHashTree));

View File

@ -3,13 +3,11 @@ package io.metersphere.commons.utils;
import com.fasterxml.jackson.core.util.DefaultIndenter;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.metersphere.api.dto.FakeErrorLibraryDTO;
import io.metersphere.api.dto.RequestResultExpandDTO;
import io.metersphere.commons.enums.ApiReportStatus;
import io.metersphere.commons.enums.ResponseFormatType;
import io.metersphere.dto.RequestResult;
import io.metersphere.dto.ResponseResult;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.HashMap;
@ -20,29 +18,22 @@ import java.util.Map;
*/
public class ResponseUtil {
public static RequestResultExpandDTO parseByRequestResult(RequestResult baseResult) {
//根据responseheader的信息来处理返回数据
baseResult = ResponseUtil.parseResponseBodyByHeader(baseResult);
public static RequestResultExpandDTO parseByRequestResult(RequestResult requestResult) {
requestResult = ResponseUtil.parseResponseBodyByHeader(requestResult);
//解析是否含有误报库信息
FakeErrorLibraryDTO errorCodeDTO = FakeErrorParse.parseAssertions(baseResult);
RequestResult requestResult = errorCodeDTO.getResult();
RequestResultExpandDTO expandDTO = new RequestResultExpandDTO();
BeanUtils.copyBean(expandDTO, requestResult);
if (CollectionUtils.isNotEmpty(errorCodeDTO.getErrorCodeList())) {
if (StringUtils.isNotBlank(requestResult.getFakeErrorCode())) {
Map<String, String> expandMap = new HashMap<>();
expandMap.put(ApiReportStatus.FAKE_ERROR.name(), errorCodeDTO.getErrorCodeStr());
if (StringUtils.equalsIgnoreCase(errorCodeDTO.getRequestStatus(), ApiReportStatus.FAKE_ERROR.name())) {
expandMap.put(ApiReportStatus.FAKE_ERROR.name(), requestResult.getFakeErrorCode());
if (StringUtils.equalsIgnoreCase(requestResult.getStatus(), ApiReportStatus.FAKE_ERROR.name())) {
expandMap.put("status", ApiReportStatus.FAKE_ERROR.name());
}
expandDTO.setAttachInfoMap(expandMap);
LogUtil.info(" FAKE_ERROR result.id:" + errorCodeDTO.getRequestStatus() + "; AttachInfoMap:" + JSON.toJSONString(expandDTO.getAttachInfoMap()));
}
if (StringUtils.equalsIgnoreCase(errorCodeDTO.getRequestStatus(), ApiReportStatus.FAKE_ERROR.name())) {
expandDTO.setStatus(errorCodeDTO.getRequestStatus());
if (StringUtils.equalsIgnoreCase(requestResult.getStatus(), ApiReportStatus.FAKE_ERROR.name())) {
expandDTO.setStatus(requestResult.getStatus());
}
LogUtil.info(" FAKE_ERROR result.id:" + errorCodeDTO.getRequestStatus()
+ ";status:" + expandDTO.getStatus()
+ " AttachInfoMap:" + (expandDTO.getAttachInfoMap() == null ? "null" : JSON.toJSONString(expandDTO.getAttachInfoMap())));
return expandDTO;
}

View File

@ -2,12 +2,9 @@ package io.metersphere.commons.utils;
import io.metersphere.api.dto.ApiScenarioReportBaseInfoDTO;
import io.metersphere.api.dto.FakeErrorLibraryDTO;
import io.metersphere.base.domain.ApiScenarioReportResultWithBLOBs;
import io.metersphere.commons.enums.ApiReportStatus;
import io.metersphere.dto.RequestResult;
import io.metersphere.utils.LoggerUtil;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import java.nio.charset.StandardCharsets;
@ -15,26 +12,15 @@ import java.util.UUID;
public class ResultConversionUtil {
public static ApiScenarioReportResultWithBLOBs getApiScenarioReportResult(String reportId, RequestResult requestResult) {
//解析误报内容
FakeErrorLibraryDTO errorCodeDTO = FakeErrorParse.parseAssertions(requestResult);
RequestResult result = errorCodeDTO.getResult();
public static ApiScenarioReportResultWithBLOBs getApiScenarioReportResult(String reportId, RequestResult result) {
String resourceId = result.getResourceId();
ApiScenarioReportResultWithBLOBs report = newScenarioReportResult(reportId, resourceId);
report.setTotalAssertions(Long.parseLong(result.getTotalAssertions() + StringUtils.EMPTY));
report.setPassAssertions(Long.parseLong(result.getPassAssertions() + StringUtils.EMPTY));
String status = result.getError() == 0 ? ApiReportStatus.SUCCESS.name() : ApiReportStatus.ERROR.name();
if (CollectionUtils.isNotEmpty(errorCodeDTO.getErrorCodeList())) {
report.setErrorCode(errorCodeDTO.getErrorCodeStr());
}
if (StringUtils.equalsIgnoreCase(errorCodeDTO.getRequestStatus(), ApiReportStatus.FAKE_ERROR.name())) {
status = errorCodeDTO.getRequestStatus();
}
requestResult.setStatus(status);
report.setStatus(status);
report.setErrorCode(result.getFakeErrorCode());
report.setStatus(result.getStatus());
report.setRequestTime(result.getEndTime() - result.getStartTime());
LoggerUtil.info("报告ID [ " + reportId + " ] 执行请求:【 " + requestResult.getName() + "】 入库存储");
LoggerUtil.info("报告ID [ " + reportId + " ] 执行请求:【 " + result.getName() + "】 入库存储");
return report;
}

View File

@ -9,7 +9,7 @@ import org.springframework.stereotype.Component;
public class ApiExecutionQueueListener {
private ApiExecutionQueueService queueService;
@QuartzScheduled(cron = "0 0/10 0/1 * * ?")
@QuartzScheduled(cron = "0 0/30 0/1 * * ?")
public void execute() {
if (queueService == null) {
queueService = CommonBeanFactory.getBean(ApiExecutionQueueService.class);

View File

@ -6,7 +6,6 @@ import io.metersphere.api.exec.queue.DBTestQueue;
import io.metersphere.api.exec.scenario.ApiScenarioSerialService;
import io.metersphere.api.jmeter.JMeterService;
import io.metersphere.api.jmeter.JMeterThreadUtils;
import io.metersphere.api.jmeter.utils.JmxFileUtil;
import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.*;
import io.metersphere.base.mapper.ext.BaseApiExecutionQueueMapper;
@ -46,8 +45,6 @@ public class ApiExecutionQueueService {
@Resource
private ApiExecutionQueueDetailMapper executionQueueDetailMapper;
@Resource
private RedisTemplateService redisTemplateService;
@Resource
private ApiScenarioSerialService apiScenarioSerialService;
@Resource
private ApiScenarioReportService apiScenarioReportService;
@ -227,37 +224,39 @@ public class ApiExecutionQueueService {
public DBTestQueue handleQueue(String id, String testId) {
ApiExecutionQueue executionQueue = queueMapper.selectByPrimaryKey(id);
DBTestQueue queue = new DBTestQueue();
if (executionQueue != null) {
BeanUtils.copyBean(queue, executionQueue);
LoggerUtil.info("Get the next execution point" + id + "");
if (executionQueue == null) {
LoggerUtil.info("The queue was accidentally deleted" + id + "");
return queue;
}
BeanUtils.copyBean(queue, executionQueue);
LoggerUtil.info("Get the next execution point" + id + "");
ApiExecutionQueueDetailExample example = new ApiExecutionQueueDetailExample();
example.setOrderByClause("sort asc");
example.createCriteria().andQueueIdEqualTo(id);
List<ApiExecutionQueueDetail> queues = executionQueueDetailMapper.selectByExampleWithBLOBs(example);
ApiExecutionQueueDetailExample example = new ApiExecutionQueueDetailExample();
example.setOrderByClause("sort asc");
example.createCriteria().andQueueIdEqualTo(id);
List<ApiExecutionQueueDetail> queues = executionQueueDetailMapper.selectByExampleWithBLOBs(example);
if (CollectionUtils.isNotEmpty(queues)) {
// 处理掉当前已经执行完成的资源
List<ApiExecutionQueueDetail> completedQueues = queues.stream()
.filter(item -> StringUtils.equals(item.getTestId(), testId))
.collect(Collectors.toList());
if (CollectionUtils.isNotEmpty(completedQueues)) {
ApiExecutionQueueDetail completed = completedQueues.get(0);
queue.setCompletedReportId(completed.getReportId());
executionQueueDetailMapper.deleteByPrimaryKey(completed.getId());
queues.remove(completed);
}
// 取出下一个要执行的节点
if (CollectionUtils.isNotEmpty(queues)) {
// 处理掉当前已经执行完成的资源
List<ApiExecutionQueueDetail> completedQueues = queues.stream().filter(item -> StringUtils.equals(item.getTestId(), testId)).collect(Collectors.toList());
if (CollectionUtils.isNotEmpty(completedQueues)) {
ApiExecutionQueueDetail completed = completedQueues.get(0);
queue.setCompletedReportId(completed.getReportId());
executionQueueDetailMapper.deleteByPrimaryKey(completed.getId());
queues.remove(completed);
}
// 取出下一个要执行的节点
if (CollectionUtils.isNotEmpty(queues)) {
queue.setDetail(queues.get(0));
} else {
LoggerUtil.info("execution complete,clear queue" + id + "");
queueMapper.deleteByPrimaryKey(id);
}
queue.setDetail(queues.get(0));
} else {
LoggerUtil.info("execution complete,clear queue" + id + "");
queueMapper.deleteByPrimaryKey(id);
}
} else {
LoggerUtil.info("The queue was accidentally deleted" + id + "");
LoggerUtil.info("execution complete,clear queue" + id + "");
queueMapper.deleteByPrimaryKey(id);
}
return queue;
}
@ -312,50 +311,37 @@ public class ApiExecutionQueueService {
}
return;
}
// 获取串行下一个执行节点
// 清理当前节点并获取下一个要执行的资源
DBTestQueue executionQueue = this.handleQueue(dto.getQueueId(), dto.getTestId());
if (executionQueue != null) {
// 串行失败停止
if (BooleanUtils.isTrue(executionQueue.getFailure()) && StringUtils.isNotEmpty(executionQueue.getCompletedReportId())) {
boolean isNext = failure(executionQueue, dto);
if (!isNext) {
return;
}
// 串行失败停止
if (BooleanUtils.isTrue(executionQueue.getFailure()) && StringUtils.isNotEmpty(executionQueue.getCompletedReportId())) {
boolean isNext = failure(executionQueue, dto);
if (!isNext) {
return;
}
LoggerUtil.info("开始处理执行队列:" + executionQueue.getId() + " 当前资源是:" + dto.getTestId() + "报告ID" + dto.getReportId());
if (executionQueue.getDetail() != null && StringUtils.isNotEmpty(executionQueue.getDetail().getTestId())) {
if (StringUtils.equals(dto.getRunType(), RunModeConstants.SERIAL.toString())) {
LoggerUtil.info("当前执行队列是:" + JSON.toJSONString(executionQueue.getDetail()));
// 防止重复执行
String key = StringUtils.join(RunModeConstants.SERIAL.name(), "_", executionQueue.getDetail().getReportId());
boolean isNext = redisTemplateService.setIfAbsent(key, executionQueue.getDetail().getQueueId());
if (!isNext) {
return;
}
redisTemplateService.expire(key);
if (StringUtils.equalsAny(executionQueue.getRunMode(),
ApiRunMode.SCENARIO.name(),
ApiRunMode.SCENARIO_PLAN.name(),
ApiRunMode.SCHEDULE_SCENARIO_PLAN.name(),
ApiRunMode.SCHEDULE_SCENARIO.name(),
ApiRunMode.JENKINS_SCENARIO_PLAN.name())) {
apiScenarioSerialService.serial(executionQueue);
} else {
apiCaseSerialService.serial(executionQueue);
}
}
} else {
// 集合报告合并
this.margeReport(dto);
queueMapper.deleteByPrimaryKey(dto.getQueueId());
LoggerUtil.info("Queue execution ends" + dto.getQueueId());
}
ApiExecutionQueueDetailExample example = new ApiExecutionQueueDetailExample();
example.createCriteria().andQueueIdEqualTo(dto.getQueueId()).andTestIdEqualTo(dto.getTestId());
executionQueueDetailMapper.deleteByExample(example);
}
LoggerUtil.info("处理队列结束:" + dto.getReportId() + "QID" + dto.getQueueId());
LoggerUtil.info("开始处理执行队列:" + executionQueue.getId() + " 当前资源是:" + dto.getTestId() + "报告ID" + dto.getReportId());
if (executionQueue.getDetail() != null && StringUtils.isNotEmpty(executionQueue.getDetail().getTestId())) {
if (StringUtils.equals(dto.getRunType(), RunModeConstants.SERIAL.toString())) {
if (StringUtils.equalsAny(executionQueue.getRunMode(),
ApiRunMode.SCENARIO.name(),
ApiRunMode.SCENARIO_PLAN.name(),
ApiRunMode.SCHEDULE_SCENARIO_PLAN.name(),
ApiRunMode.SCHEDULE_SCENARIO.name(),
ApiRunMode.JENKINS_SCENARIO_PLAN.name())) {
// 场景执行
apiScenarioSerialService.serial(executionQueue);
} else {
// 接口用例执行
apiCaseSerialService.serial(executionQueue);
}
}
} else {
// 集合报告合并
this.margeReport(dto);
queueMapper.deleteByPrimaryKey(dto.getQueueId());
LoggerUtil.info("Queue execution ends" + dto.getQueueId());
}
}
private void margeReport(ResultDTO dto) {
@ -396,7 +382,6 @@ public class ApiExecutionQueueService {
dto.setQueueId(item.getQueueId());
dto.setTestId(item.getTestId());
if (StringUtils.equalsAnyIgnoreCase(queue.getRunMode(), ApiRunMode.SCENARIO.name(), ApiRunMode.SCENARIO_PLAN.name(), ApiRunMode.SCHEDULE_SCENARIO_PLAN.name(), ApiRunMode.SCHEDULE_SCENARIO.name(), ApiRunMode.JENKINS_SCENARIO_PLAN.name())) {
redisTemplateService.delete(JmxFileUtil.getExecuteFileKeyInRedis(item.getReportId()));
ApiScenarioReportWithBLOBs report = apiScenarioReportMapper.selectByPrimaryKey(item.getReportId());
// 报告已经被删除则队列也删除
if (report == null) {
@ -408,10 +393,6 @@ public class ApiExecutionQueueService {
apiScenarioReportMapper.updateByPrimaryKeySelective(report);
LoggerUtil.info("超时处理报告:" + report.getId());
if (queue != null && StringUtils.equalsIgnoreCase(item.getType(), RunModeConstants.SERIAL.toString())) {
// 删除串行资源锁
String key = StringUtils.join(RunModeConstants.SERIAL.name(), "_", dto.getReportId());
redisTemplateService.delete(key);
LoggerUtil.info("超时处理报告:【" + report.getId() + "】进入下一个执行");
dto.setTestPlanReportId(queue.getReportId());
dto.setReportId(queue.getReportId());
@ -424,7 +405,6 @@ public class ApiExecutionQueueService {
}
}
} else {
redisTemplateService.delete(JmxFileUtil.getExecuteFileKeyInRedis(item.getReportId()));
// 用例/接口超时结果处理
ApiDefinitionExecResultWithBLOBs result = apiDefinitionExecResultMapper.selectByPrimaryKey(item.getReportId());
if (result != null && StringUtils.equalsAnyIgnoreCase(result.getStatus(), TestPlanReportStatus.RUNNING.name())) {
@ -476,8 +456,6 @@ public class ApiExecutionQueueService {
checkTestPlanCaseTestEnd(null, null, reportId);
});
}
// 清除异常队列/一般是服务突然停止产生
extApiExecutionQueueMapper.delete();
}
public void stop(String reportId) {
@ -539,11 +517,46 @@ public class ApiExecutionQueueService {
* 服务异常重启处理
*/
public void exceptionHandling() {
LogUtil.info("开始处理服务重启导致执行未完成的报告状态");
// 更新报告状态
extApiScenarioReportMapper.updateAllStatus();
// 更新用例报告状态
extApiDefinitionExecResultMapper.updateAllStatus();
LogUtil.info("处理服务重启导致执行未完成的报告状态完成");
try {
LogUtil.info("开始处理服务重启导致执行未完成的报告状态");
// 更新报告状态
extApiScenarioReportMapper.updateAllStatus();
// 更新用例报告状态
extApiDefinitionExecResultMapper.updateAllStatus();
LogUtil.info("处理服务重启导致执行未完成的报告状态完成");
// 清理队列
final List<String> apiModes = new ArrayList<>() {{
this.add(ApiRunMode.SCENARIO.name());
this.add(ApiRunMode.SCHEDULE_SCENARIO.name());
this.add(ApiRunMode.SCENARIO_PLAN.name());
this.add(ApiRunMode.SCHEDULE_SCENARIO_PLAN.name());
this.add(ApiRunMode.JENKINS_SCENARIO_PLAN.name());
this.add(ApiRunMode.DEFINITION.name());
this.add(ApiRunMode.JENKINS.name());
this.add(ApiRunMode.API_PLAN.name());
this.add(ApiRunMode.SCHEDULE_API_PLAN.name());
this.add(ApiRunMode.JENKINS_API_PLAN.name());
this.add(ApiRunMode.MANUAL_PLAN.name());
}};
ApiExecutionQueueExample queueExample = new ApiExecutionQueueExample();
queueExample.createCriteria().andRunModeIn(apiModes);
List<ApiExecutionQueue> queues = apiExecutionQueueMapper.selectByExample(queueExample);
if (CollectionUtils.isNotEmpty(queues)) {
List<String> ids = queues.stream().map(ApiExecutionQueue::getId).collect(Collectors.toList());
ApiExecutionQueueDetailExample queueDetailExample = new ApiExecutionQueueDetailExample();
queueDetailExample.createCriteria().andQueueIdIn(ids);
apiExecutionQueueDetailMapper.deleteByExample(queueDetailExample);
// 通知测试计划
queues.forEach(item -> {
if (StringUtils.endsWith(item.getRunMode(), "PLAN")) {
checkTestPlanCaseTestEnd(null, null, item.getReportId());
}
});
apiExecutionQueueMapper.deleteByExample(queueExample);
}
} catch (Exception e) {
LoggerUtil.error(e);
}
}
}

View File

@ -374,7 +374,7 @@ public class ApiJMeterFileService {
Object jmxFileInfoObj = redisTemplateService.get(JmxFileUtil.getExecuteFileKeyInRedis(request.getReportId()));
List<AttachmentBodyFile> fileInJmx = JmxFileUtil.formatRedisJmxFileString(jmxFileInfoObj);
redisTemplateService.delete(JmxFileUtil.getExecuteFileKeyInRedis(request.getReportId()));
redisTemplateService.delFilePath(request.getReportId());
if (CollectionUtils.isNotEmpty(request.getBodyFiles())) {
request.getBodyFiles().forEach(attachmentBodyFile -> {

View File

@ -1,17 +1,15 @@
package io.metersphere.service;
import io.metersphere.api.jmeter.utils.JmxFileUtil;
import io.metersphere.utils.LoggerUtil;
import jakarta.annotation.Resource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class RedisTemplateService {
@Resource
private RedisTemplate<String, Object> redisTemplate;
public static final long TIME_OUT = 30;
public boolean setIfAbsent(String key, String value) {
try {
@ -22,15 +20,6 @@ public class RedisTemplateService {
}
}
public boolean expire(String key) {
try {
return redisTemplate.expire(key, TIME_OUT, TimeUnit.MINUTES);
} catch (Exception e) {
LoggerUtil.error(key, e);
return false;
}
}
public Object get(String key) {
try {
return redisTemplate.opsForValue().get(key);
@ -48,4 +37,13 @@ public class RedisTemplateService {
return false;
}
}
public void delFilePathAndScript(String reportId, String testId) {
delete(JmxFileUtil.getExecuteScriptKey(reportId, testId));
delete(JmxFileUtil.getExecuteFileKeyInRedis(reportId));
}
public void delFilePath(String reportId) {
delete(JmxFileUtil.getExecuteFileKeyInRedis(reportId));
}
}

View File

@ -9,15 +9,11 @@ import io.metersphere.commons.utils.FixedCapacityUtil;
import io.metersphere.dto.JmeterRunRequestDTO;
import io.metersphere.dto.ResultDTO;
import io.metersphere.utils.LoggerUtil;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
@Service
public class RemakeReportService {
@Resource
private RedisTemplateService redisTemplateService;
public void queueNext(JmeterRunRequestDTO request, String errorMsg) {
try {
ResultDTO dto = new ResultDTO();
@ -35,11 +31,8 @@ public class RemakeReportService {
LoggerUtil.info("Check Processing Test Plan report status.queueId" + dto.getQueueId() + "runMode:" + dto.getRunMode() + "testId:" + dto.getTestId(), dto.getReportId());
CommonBeanFactory.getBean(ApiExecutionQueueService.class).checkTestPlanCaseTestEnd(dto.getTestId(), dto.getRunMode(), dto.getTestPlanReportId());
} catch (Exception e) {
LoggerUtil.error("回退报告异常", request.getReportId(), e);
} finally {
ApiLocalRunner.clearCache(request.getReportId());
redisTemplateService.delete(JmxFileUtil.getExecuteScriptKey(request.getReportId(), request.getTestId()));
redisTemplateService.delete(JmxFileUtil.getExecuteFileKeyInRedis(request.getReportId()));
LoggerUtil.error("回退报告异常", request.getReportId(), e);
}
}

View File

@ -2,7 +2,7 @@ package io.metersphere.service;
import io.metersphere.api.dto.automation.ApiTestReportVariable;
import io.metersphere.api.exec.scenario.ApiEnvironmentRunningParamService;
import io.metersphere.api.jmeter.utils.ReportStatusUtil;
import io.metersphere.utils.ReportStatusUtil;
import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.ApiDefinitionExecResultMapper;
import io.metersphere.base.mapper.ApiScenarioMapper;
@ -13,7 +13,7 @@ import io.metersphere.commons.enums.ApiReportStatus;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.DateUtils;
import io.metersphere.commons.utils.JSON;
import io.metersphere.commons.vo.ResultVO;
import io.metersphere.vo.ResultVO;
import io.metersphere.constants.RunModeConstants;
import io.metersphere.dto.BaseSystemConfigDTO;
import io.metersphere.dto.RequestResult;
@ -204,11 +204,6 @@ public class TestResultService {
}
public void testEnded(ResultDTO dto) {
// 删除串行资源锁
if (StringUtils.equals(dto.getRunType(), RunModeConstants.SERIAL.toString())) {
String key = StringUtils.join(RunModeConstants.SERIAL.name(), "_", dto.getReportId());
redisTemplateService.delete(key);
}
if (dto.getRequestResults() == null) {
dto.setRequestResults(new LinkedList<>());
}

View File

@ -7,7 +7,7 @@ import io.metersphere.api.dto.automation.ExecuteType;
import io.metersphere.api.dto.automation.RunScenarioRequest;
import io.metersphere.api.dto.datacount.ApiDataCountResult;
import io.metersphere.api.dto.definition.RunDefinitionRequest;
import io.metersphere.api.jmeter.utils.ReportStatusUtil;
import io.metersphere.utils.ReportStatusUtil;
import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.*;
import io.metersphere.base.mapper.ext.ExtApiDefinitionExecResultMapper;
@ -20,7 +20,7 @@ import io.metersphere.commons.enums.ApiReportStatus;
import io.metersphere.commons.enums.ExecutionExecuteTypeEnum;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.*;
import io.metersphere.commons.vo.ResultVO;
import io.metersphere.vo.ResultVO;
import io.metersphere.constants.RunModeConstants;
import io.metersphere.dto.*;
import io.metersphere.i18n.Translator;

View File

@ -1,5 +1,7 @@
package io.metersphere.constants;
public enum BackendListenerConstants {
RETRY_ENABLE, TEST_ID, NAME, REPORT_ID, RUN_MODE, REPORT_TYPE, CLASS_NAME, MS_TEST_PLAN_REPORT_ID, RUN, KAFKA_CONFIG, QUEUE_ID, RUN_TYPE, EPT, MS_DEBUG
RETRY_ENABLE, TEST_ID, NAME, REPORT_ID, RUN_MODE, REPORT_TYPE,
CLASS_NAME, MS_TEST_PLAN_REPORT_ID, RUN, KAFKA_CONFIG, QUEUE_ID,
RUN_TYPE, EPT, MS_DEBUG, FAKE_ERROR
}

View File

@ -0,0 +1,11 @@
package io.metersphere.dto;
import lombok.Data;
@Data
public class FakeErrorDTO {
private String projectId;
private boolean higherThanSuccess;
private boolean higherThanError;
}

View File

@ -1,6 +1,5 @@
package io.metersphere.api.dto;
package io.metersphere.dto;
import io.metersphere.dto.RequestResult;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.collections.CollectionUtils;

View File

@ -97,6 +97,9 @@ public class JmeterRunRequestDTO {
//自定义jar信息
private Map<String, List<ProjectJarConfig>> customJarInfo;
// 自定义误报库
private Map<String, List<MsRegexDTO>> fakeErrorMap;
private String triggerMode;
public JmeterRunRequestDTO() {

View File

@ -1,4 +1,4 @@
package io.metersphere.api.dto.definition;
package io.metersphere.dto;
import lombok.Data;

View File

@ -54,5 +54,6 @@ public class RequestResult {
}
private String fakeErrorMessage;
// 误报编码名称
private String fakeErrorCode;
}

View File

@ -17,6 +17,8 @@ public class ResultDTO {
private String runType;
private String console;
private String runningDebugSampler;
// 是否是结束操作
private Boolean hasEnded;
// 失败重试
private boolean retryEnable;
/**
@ -26,5 +28,7 @@ public class ResultDTO {
// 预留一个参数可以放任何数据
private Map<String, Object> arbitraryData;
// 误报规则
Map<String, List<MsRegexDTO>> fakeErrorMap;
}

View File

@ -0,0 +1,5 @@
package io.metersphere.enums;
public enum ApiReportStatus {
PENDING, RUNNING, RERUNNING, ERROR, SUCCESS, FAKE_ERROR, STOPPED, TIMEOUT
}

View File

@ -1,12 +1,11 @@
package io.metersphere.jmeter;
import com.alibaba.fastjson.JSON;
import io.metersphere.constants.BackendListenerConstants;
import io.metersphere.constants.HttpMethodConstants;
import io.metersphere.dto.*;
import io.metersphere.utils.JMeterVars;
import io.metersphere.utils.JsonUtils;
import io.metersphere.utils.ListenerUtil;
import io.metersphere.utils.LoggerUtil;
import io.metersphere.enums.ApiReportStatus;
import io.metersphere.utils.*;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
@ -26,8 +25,10 @@ import java.util.List;
import java.util.Map;
public class JMeterBase {
private final static String THREAD_SPLIT = " ";
private final static String TRANSACTION = "Transaction=";
private final static String SPLIT_EQ = "split==";
private final static String SPLIT_AND = "split&&";
public static HashTree getHashTree(Object scriptWrapper) throws Exception {
Field field = scriptWrapper.getClass().getDeclaredField("testPlan");
@ -51,6 +52,9 @@ public class JMeterBase {
arguments.addArgument(BackendListenerConstants.QUEUE_ID.name(), request.getQueueId());
arguments.addArgument(BackendListenerConstants.RUN_TYPE.name(), request.getRunType());
arguments.addArgument(BackendListenerConstants.RETRY_ENABLE.name(), String.valueOf(request.isRetryEnable()));
if (MapUtils.isNotEmpty(request.getFakeErrorMap())) {
arguments.addArgument(BackendListenerConstants.FAKE_ERROR.name(), JSON.toJSONString(request.getFakeErrorMap()));
}
if (MapUtils.isNotEmpty(request.getExtendedParameters())) {
arguments.addArgument(BackendListenerConstants.EPT.name(), JsonUtils.toJSONString(request.getExtendedParameters()));
}
@ -65,9 +69,8 @@ public class JMeterBase {
LoggerUtil.info("报告添加BackendListener 结束", request.getTestId());
}
public static RequestResult getRequestResult(SampleResult result) {
public static RequestResult getRequestResult(SampleResult result, Map<String, List<MsRegexDTO>> fakeErrorMap) {
LoggerUtil.debug("开始处理结果资源【" + result.getSampleLabel() + "");
String threadName = StringUtils.substringBeforeLast(result.getThreadName(), THREAD_SPLIT);
RequestResult requestResult = new RequestResult();
requestResult.setThreadName(threadName);
@ -92,7 +95,7 @@ public class JMeterBase {
}
for (SampleResult subResult : result.getSubResults()) {
requestResult.getSubRequestResults().add(getRequestResult(subResult));
requestResult.getSubRequestResults().add(getRequestResult(subResult, fakeErrorMap));
}
ResponseResult responseResult = requestResult.getResponseResult();
// 超过20M的文件不入库
@ -140,6 +143,16 @@ public class JMeterBase {
}
LoggerUtil.debug("处理结果资源【" + result.getSampleLabel() + "】结束");
// 误报处理
FakeErrorLibraryDTO errorCodeDTO = FakeErrorUtils.parseAssertions(requestResult, fakeErrorMap);
if (CollectionUtils.isNotEmpty(errorCodeDTO.getErrorCodeList())) {
requestResult.setFakeErrorCode(errorCodeDTO.getErrorCodeStr());
}
String status = requestResult.getError() == 0 ? "SUCCESS" : "ERROR";
if (StringUtils.equalsIgnoreCase(errorCodeDTO.getRequestStatus(), ApiReportStatus.FAKE_ERROR.name())) {
status = ApiReportStatus.FAKE_ERROR.name();
}
requestResult.setStatus(status);
return requestResult;
}
@ -147,13 +160,13 @@ public class JMeterBase {
ResponseAssertionResult responseAssertionResult = new ResponseAssertionResult();
responseAssertionResult.setName(assertionResult.getName());
if (StringUtils.isNotEmpty(assertionResult.getName()) && assertionResult.getName().indexOf("split==") != -1) {
if (StringUtils.isNotEmpty(assertionResult.getName()) && assertionResult.getName().indexOf(SPLIT_EQ) != -1) {
if (assertionResult.getName().indexOf("JSR223") != -1) {
String[] array = assertionResult.getName().split("split==", 3);
String[] array = assertionResult.getName().split(SPLIT_EQ, 3);
if (array.length > 2 && "JSR223".equals(array[0])) {
responseAssertionResult.setName(array[1]);
if (array[2].indexOf("split&&") != -1) {
String[] content = array[2].split("split&&");
if (array[2].indexOf(SPLIT_AND) != -1) {
String[] content = array[2].split(SPLIT_AND);
responseAssertionResult.setContent(content[0]);
if (content.length > 1) {
responseAssertionResult.setScript(content[1]);
@ -163,7 +176,7 @@ public class JMeterBase {
}
}
} else {
String[] array = assertionResult.getName().split("split==");
String[] array = assertionResult.getName().split(SPLIT_EQ);
responseAssertionResult.setName(array[0]);
StringBuffer content = new StringBuffer();
for (int i = 1; i < array.length; i++) {
@ -214,7 +227,7 @@ public class JMeterBase {
List<String> environmentList = new ArrayList<>();
sampleResults.forEach(result -> {
ListenerUtil.setVars(result);
RequestResult requestResult = JMeterBase.getRequestResult(result);
RequestResult requestResult = JMeterBase.getRequestResult(result, dto.getFakeErrorMap());
if (StringUtils.equals(result.getSampleLabel(), ListenerUtil.RUNNING_DEBUG_SAMPLER_NAME)) {
String evnStr = result.getResponseDataAsString();
environmentList.add(evnStr);
@ -222,7 +235,7 @@ public class JMeterBase {
//检查是否有关系到最终执行结果的全局前后置脚本
boolean resultNotFilterOut = ListenerUtil.checkResultIsNotFilterOut(requestResult);
if (resultNotFilterOut) {
if (StringUtils.isNotEmpty(requestResult.getName()) && requestResult.getName().startsWith("Transaction=")) {
if (StringUtils.isNotEmpty(requestResult.getName()) && requestResult.getName().startsWith(TRANSACTION)) {
transactionFormat(requestResult.getSubRequestResults(), requestResults);
} else {
requestResults.add(requestResult);

View File

@ -0,0 +1,84 @@
package io.metersphere.utils;
import io.metersphere.dto.FakeErrorDTO;
import io.metersphere.dto.FakeErrorLibraryDTO;
import io.metersphere.dto.MsRegexDTO;
import io.metersphere.dto.RequestResult;
import io.metersphere.enums.ApiReportStatus;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* 误报解析类
*
* @author xiaogang
*/
public class FakeErrorUtils {
public static FakeErrorLibraryDTO parseAssertions(RequestResult result, Map<String, List<MsRegexDTO>> fakeErrorMap) {
FakeErrorLibraryDTO fakeError = new FakeErrorLibraryDTO();
try {
if (StringUtils.isNotBlank(result.getFakeErrorMessage())) {
FakeErrorDTO errorReportDTO = JsonUtils.parseObject(result.getFakeErrorMessage(), FakeErrorDTO.class);
if (MapUtils.isNotEmpty(fakeErrorMap) && errorReportDTO != null) {
List<MsRegexDTO> regexList = fakeErrorMap.get(errorReportDTO.getProjectId());
//根据配置来筛选断言获取误报编码获取接口状态是否是误报
List<String> errorCodeList = new ArrayList<>();
regexList.forEach(item -> {
if (StringUtils.isNotEmpty(item.getSubject())) {
switch (item.getSubject()) {
case "Response Code" ->
item.setPass(parseResponseCode(result.getResponseResult().getResponseCode(), item.getValue(), item.getCondition()));
case "Response Headers" ->
item.setPass(parseResponseCode(result.getResponseResult().getHeaders(), item.getValue(), item.getCondition()));
case "Response Data" ->
item.setPass(parseResponseCode(result.getResponseResult().getBody(), item.getValue(), item.getCondition()));
default -> item.setPass(false);
}
}
if (item.isPass()) {
errorCodeList.add(item.getErrorCode());
}
});
boolean higherThanError = errorReportDTO.isHigherThanError();
boolean higherThanSuccess = errorReportDTO.isHigherThanSuccess();
if (CollectionUtils.isNotEmpty(errorCodeList)) {
if ((higherThanError && !result.isSuccess()) || (higherThanSuccess && result.isSuccess())) {
fakeError.setRequestStatus(ApiReportStatus.FAKE_ERROR.name());
}
fakeError.setErrorCodeList(errorCodeList);
}
}
}
} catch (Exception e) {
LoggerUtil.error("误报处理错误:", e);
}
return fakeError;
}
private static boolean parseResponseCode(String result, String regexDTO, String condition) {
return switch (condition.toUpperCase()) {
case "CONTAINS" -> result.contains(regexDTO);
case "NOT_CONTAINS" -> notContains(result, regexDTO);
case "EQUALS" -> StringUtils.equals(result, regexDTO);
case "START_WITH" -> result.startsWith(regexDTO);
case "END_WITH" -> result.endsWith(regexDTO);
default -> false;
};
}
private static boolean notContains(String result, String regexDTO) {
return !result.contains(regexDTO);
}
}

View File

@ -72,4 +72,13 @@ public class JsonUtils {
throw new RuntimeException(e);
}
}
public static <T> T convertValue(Object content, Class<T> valueType) {
try {
return objectMapper.convertValue(content, valueType);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View File

@ -1,13 +1,9 @@
package io.metersphere.api.jmeter.utils;
package io.metersphere.utils;
import io.metersphere.commons.constants.CommonConstants;
import io.metersphere.commons.enums.ApiReportStatus;
import io.metersphere.commons.utils.JSONUtil;
import io.metersphere.commons.vo.ResultVO;
import io.metersphere.dto.RequestResult;
import io.metersphere.dto.ResultDTO;
import io.metersphere.utils.LoggerUtil;
import io.metersphere.utils.RetryResultUtil;
import io.metersphere.enums.ApiReportStatus;
import io.metersphere.vo.ResultVO;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
@ -18,6 +14,9 @@ import java.util.Map;
import java.util.stream.Collectors;
public class ReportStatusUtil {
public static final String LOCAL_STATUS_KEY = "LOCAL_STATUS_KEY";
public static final String REPORT_STATUS = "REPORT_STATUS";
public static List<RequestResult> filterRetryResults(List<RequestResult> results) {
List<RequestResult> list = new LinkedList<>();
if (CollectionUtils.isNotEmpty(results)) {
@ -50,9 +49,9 @@ public class ReportStatusUtil {
if (StringUtils.equals(resultVO.getStatus(), ApiReportStatus.ERROR.name())) {
return resultVO;
}
if (MapUtils.isNotEmpty(dto.getArbitraryData()) && dto.getArbitraryData().containsKey(CommonConstants.REPORT_STATUS)) {
if (MapUtils.isNotEmpty(dto.getArbitraryData()) && dto.getArbitraryData().containsKey(REPORT_STATUS)) {
// 资源池执行整体传输失败单条传输内容获取资源池执行统计的状态
resultVO.setStatus(JSONUtil.convertValue(dto.getArbitraryData().get(CommonConstants.REPORT_STATUS), ResultVO.class).getStatus());
resultVO.setStatus(JsonUtils.convertValue(dto.getArbitraryData().get(REPORT_STATUS), ResultVO.class).getStatus());
}
// 过滤掉重试结果后进行统计
List<RequestResult> requestResults = filterRetryResults(dto.getRequestResults());
@ -84,13 +83,13 @@ public class ReportStatusUtil {
public static ResultVO computedProcess(ResultDTO dto) {
ResultVO result = new ResultVO();
if (MapUtils.isNotEmpty(dto.getArbitraryData()) && dto.getArbitraryData().containsKey(CommonConstants.LOCAL_STATUS_KEY)) {
if (MapUtils.isNotEmpty(dto.getArbitraryData()) && dto.getArbitraryData().containsKey(LOCAL_STATUS_KEY)) {
// 本地执行状态
result = JSONUtil.convertValue(dto.getArbitraryData().get(CommonConstants.LOCAL_STATUS_KEY), ResultVO.class);
result = JsonUtils.convertValue(dto.getArbitraryData().get(LOCAL_STATUS_KEY), ResultVO.class);
}
if (MapUtils.isNotEmpty(dto.getArbitraryData()) && dto.getArbitraryData().containsKey(CommonConstants.REPORT_STATUS)) {
if (MapUtils.isNotEmpty(dto.getArbitraryData()) && dto.getArbitraryData().containsKey(REPORT_STATUS)) {
// 资源池执行整体传输失败单条传输内容获取资源池执行统计的状态
result = JSONUtil.convertValue(dto.getArbitraryData().get(CommonConstants.REPORT_STATUS), ResultVO.class);
result = JsonUtils.convertValue(dto.getArbitraryData().get(REPORT_STATUS), ResultVO.class);
}
result = getStatus(dto, result);
if (result != null && result.getScenarioTotal() > 0 && result.getScenarioTotal() == result.getScenarioSuccess()) {

View File

@ -1,4 +1,4 @@
package io.metersphere.commons.vo;
package io.metersphere.vo;
import lombok.Data;

View File

@ -40,6 +40,7 @@ import org.apache.jmeter.testelement.property.BooleanProperty;
import org.apache.jmeter.threads.JMeterVariables;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
/**
@ -148,7 +149,7 @@ public class MsDebugListener extends AbstractListenerElement implements SampleLi
SampleResult result = event.getResult();
this.setVars(result);
if (isSampleWanted(result.isSuccessful(), result) && !StringUtils.equals(result.getSampleLabel(), RunningParamKeys.RUNNING_DEBUG_SAMPLER_NAME)) {
RequestResult requestResult = JMeterBase.getRequestResult(result);
RequestResult requestResult = JMeterBase.getRequestResult(result, new HashMap<>());
if (requestResult != null && ResultParseUtil.isNotAutoGenerateSampler(requestResult)) {
MsgDto dto = new MsgDto();
dto.setExecEnd(false);