refactor(测试跟踪): 同一个测试计划禁止重复执行

【【测试计划】执行中的计划禁止再次执行,防止重复执行堆积任务过多】https://www.tapd.cn/55049933/prong/tasks/view/1155049933001012434

Signed-off-by: fit2-zhao <yong.zhao@fit2cloud.com>
This commit is contained in:
fit2-zhao 2023-07-04 18:02:24 +08:00 committed by f2c-ci-robot[bot]
parent e229eb3ae4
commit ba167cee86
15 changed files with 151 additions and 546 deletions

View File

@ -1,8 +1,5 @@
package io.metersphere.api.exec.queue;
import io.metersphere.api.jmeter.JMeterService;
import io.metersphere.api.jmeter.JMeterThreadUtils;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.dto.JmeterRunRequestDTO;
import io.metersphere.utils.LoggerUtil;
@ -19,10 +16,6 @@ public class ExecTask implements Runnable {
@Override
public void run() {
CommonBeanFactory.getBean(JMeterService.class).addQueue(request);
Object res = PoolExecBlockingQueueUtil.take(request.getReportId());
if (res == null && !JMeterThreadUtils.isRunning(request.getReportId(), request.getTestId())) {
LoggerUtil.info("任务执行超时", request.getReportId());
}
}
}

View File

@ -10,16 +10,15 @@ import io.metersphere.api.jmeter.utils.ServerConfig;
import io.metersphere.api.jmeter.utils.SmoothWeighted;
import io.metersphere.base.domain.TestResource;
import io.metersphere.commons.config.KafkaConfig;
import io.metersphere.commons.constants.ApiRunMode;
import io.metersphere.commons.constants.ExtendedParameter;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.*;
import io.metersphere.commons.utils.ApiFileUtil;
import io.metersphere.commons.utils.GenerateHashTreeUtil;
import io.metersphere.commons.utils.HashTreeUtil;
import io.metersphere.commons.utils.JSON;
import io.metersphere.config.JmeterProperties;
import io.metersphere.constants.BackendListenerConstants;
import io.metersphere.constants.RunModeConstants;
import io.metersphere.dto.*;
import io.metersphere.engine.Engine;
import io.metersphere.jmeter.JMeterBase;
import io.metersphere.service.ApiPoolDebugService;
import io.metersphere.service.PluginService;
import io.metersphere.service.RedisTemplateService;
@ -29,13 +28,8 @@ import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.save.SaveService;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jmeter.threads.ThreadGroup;
import org.apache.jmeter.util.JMeterUtils;
import org.apache.jorphan.collections.HashTree;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.ResponseEntity;
@ -88,65 +82,6 @@ public class JMeterService {
}
}
/**
* 添加调试监听
*/
private void addDebugListener(JmeterRunRequestDTO request) {
MsDebugListener resultCollector = new MsDebugListener();
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(request.getRunMode());
resultCollector.setFakeErrorMap(request.getFakeErrorMap());
// 添加DEBUG标示
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);
}
request.getHashTree().add(request.getHashTree().getArray()[0], resultCollector);
}
private void runLocal(JmeterRunRequestDTO request) {
init();
// 接口用例集成报告/测试计划报告日志记录
if (StringUtils.isNotEmpty(request.getTestPlanReportId())
&& StringUtils.equals(request.getReportType(), RunModeConstants.SET_REPORT.toString())) {
FixedCapacityUtil.put(request.getTestPlanReportId(), new StringBuffer());
} else {
// 报告日志记录
FixedCapacityUtil.put(request.getReportId(), new StringBuffer());
}
LoggerUtil.debug("监听MessageCache.tasks当前容量" + FixedCapacityUtil.size());
if (request.isDebug() && !StringUtils.equalsAny(request.getRunMode(), ApiRunMode.DEFINITION.name())) {
LoggerUtil.debug("为请求 [ " + request.getReportId() + " ] 添加同步接收结果 Listener");
JMeterBase.addBackendListener(request, request.getHashTree(), MsApiBackendListener.class.getCanonicalName());
}
if (MapUtils.isNotEmpty(request.getExtendedParameters())
&& request.getExtendedParameters().containsKey(ExtendedParameter.SYNC_STATUS)
&& (Boolean) request.getExtendedParameters().get(ExtendedParameter.SYNC_STATUS)) {
LoggerUtil.debug("为请求 [ " + request.getReportId() + " ] 添加Debug Listener");
addDebugListener(request);
}
if (request.isDebug()) {
LoggerUtil.debug("为请求 [ " + request.getReportId() + " ] 添加Debug Listener");
addDebugListener(request);
} else {
LoggerUtil.debug("为请求 [ " + request.getReportId() + " ] 添加同步接收结果 Listener");
JMeterBase.addBackendListener(request, request.getHashTree(), MsApiBackendListener.class.getCanonicalName());
}
LoggerUtil.info("资源:[" + request.getTestId() + "] 加入JMETER中开始执行", request.getReportId());
ApiLocalRunner runner = new ApiLocalRunner(request.getHashTree());
runner.run(request.getReportId());
}
private void fileProcessing(JmeterRunRequestDTO request) {
ElementUtil.coverArguments(request.getHashTree());
//解析HashTree里的文件信息
@ -251,10 +186,6 @@ public class JMeterService {
}
}
public void addQueue(JmeterRunRequestDTO request) {
this.runLocal(request);
}
public boolean getRunningQueue(String poolId, String reportId) {
try {
List<TestResource> resources = GenerateHashTreeUtil.setPoolResource(poolId);

View File

@ -1,174 +0,0 @@
package io.metersphere.api.jmeter;
import com.fasterxml.jackson.core.type.TypeReference;
import io.metersphere.api.exec.queue.PoolExecBlockingQueueUtil;
import io.metersphere.utils.ReportStatusUtil;
import io.metersphere.commons.constants.CommonConstants;
import io.metersphere.commons.utils.*;
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;
import io.metersphere.service.RedisTemplateService;
import io.metersphere.service.TestResultService;
import io.metersphere.utils.LoggerUtil;
import io.metersphere.utils.RetryResultUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.services.FileServer;
import org.apache.jmeter.visualizers.backend.AbstractBackendListenerClient;
import org.apache.jmeter.visualizers.backend.BackendListenerContext;
import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
public class MsApiBackendListener extends AbstractBackendListenerClient implements Serializable {
private ApiExecutionQueueService apiExecutionQueueService;
private TestResultService testResultService;
private List<SampleResult> queues;
private ResultDTO dto;
// 当前场景报告/用例结果状态
private ResultVO resultVO;
private RedisTemplateService redisTemplateService;
/**
* 参数初始化方法
*/
@Override
public void setupTest(BackendListenerContext context) throws Exception {
LoggerUtil.info("初始化监听");
queues = new LinkedList<>();
this.setParam(context);
if (apiExecutionQueueService == null) {
apiExecutionQueueService = CommonBeanFactory.getBean(ApiExecutionQueueService.class);
}
if (testResultService == null) {
testResultService = CommonBeanFactory.getBean(TestResultService.class);
}
if (redisTemplateService == null) {
redisTemplateService = CommonBeanFactory.getBean(RedisTemplateService.class);
}
resultVO = new ResultVO();
super.setupTest(context);
}
@Override
public void handleSampleResults(List<SampleResult> sampleResults, BackendListenerContext context) {
LoggerUtil.info("接收到JMETER执行数据【" + sampleResults.size() + "", dto.getReportId());
if (dto.isRetryEnable()) {
queues.addAll(sampleResults);
} else {
if (!StringUtils.equals(dto.getReportType(), RunModeConstants.SET_REPORT.toString())) {
dto.setConsole(FixedCapacityUtil.getJmeterLogger(getReportId(), false));
}
sampleResults = RetryResultUtil.clearLoops(sampleResults);
JMeterBase.resultFormatting(sampleResults, dto);
testResultService.saveResults(dto);
resultVO = ReportStatusUtil.getStatus(dto, resultVO);
dto.getArbitraryData().put(CommonConstants.LOCAL_STATUS_KEY, resultVO);
sampleResults.clear();
}
}
@Override
public void teardownTest(BackendListenerContext context) {
try {
LoggerUtil.info("进入TEST-END处理报告" + dto.getRunMode(), dto.getReportId());
super.teardownTest(context);
// 获取执行日志
if (!StringUtils.equals(dto.getReportType(), RunModeConstants.SET_REPORT.toString())) {
dto.setConsole(FixedCapacityUtil.getJmeterLogger(getReportId(), true));
}
if (dto.isRetryEnable()) {
LoggerUtil.info("重试结果处理开始", dto.getReportId());
// 清理过程步骤
queues = RetryResultUtil.clearLoops(queues);
JMeterBase.resultFormatting(queues, dto);
LoggerUtil.info("合并重试结果集", dto.getReportId());
RetryResultUtil.mergeRetryResults(dto.getRequestResults());
LoggerUtil.info("执行结果入库存储", dto.getReportId());
testResultService.saveResults(dto);
resultVO = ReportStatusUtil.getStatus(dto, resultVO);
dto.getArbitraryData().put(CommonConstants.LOCAL_STATUS_KEY, resultVO);
LoggerUtil.info("重试结果处理结束", dto.getReportId());
}
// 全局并发队列
PoolExecBlockingQueueUtil.offer(dto.getReportId());
// 整体执行结束更新资源状态
testResultService.testEnded(dto);
if (StringUtils.isNotEmpty(dto.getQueueId())) {
LoggerUtil.info("串行进入下一个执行点", dto.getReportId());
apiExecutionQueueService.queueNext(dto);
}
// 更新测试计划报告
LoggerUtil.info("Check Processing Test Plan report status" + dto.getQueueId() + "" + dto.getTestId());
apiExecutionQueueService.checkTestPlanCaseTestEnd(dto.getTestId(), dto.getRunMode(), dto.getTestPlanReportId());
LoggerUtil.info("TEST-END处理结果集完成", dto.getReportId());
JvmUtil.memoryInfo();
} catch (Exception e) {
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());
FileServer.getFileServer().closeCsv(dto.getReportId());
}
ApiLocalRunner.clearCache(dto.getReportId());
}
}
/**
* 初始化参数
*
* @param context
*/
private void setParam(BackendListenerContext context) {
dto = new ResultDTO();
dto.setTestId(context.getParameter(BackendListenerConstants.TEST_ID.name()));
dto.setRunMode(context.getParameter(BackendListenerConstants.RUN_MODE.name()));
dto.setReportId(context.getParameter(BackendListenerConstants.REPORT_ID.name()));
dto.setReportType(context.getParameter(BackendListenerConstants.REPORT_TYPE.name()));
dto.setTestPlanReportId(context.getParameter(BackendListenerConstants.MS_TEST_PLAN_REPORT_ID.name()));
if (context.getParameter(BackendListenerConstants.RETRY_ENABLE.name()) != null) {
dto.setRetryEnable(Boolean.parseBoolean(context.getParameter(BackendListenerConstants.RETRY_ENABLE.name())));
}
dto.setQueueId(context.getParameter(BackendListenerConstants.QUEUE_ID.name()));
dto.setRunType(context.getParameter(BackendListenerConstants.RUN_TYPE.name()));
if (dto.getArbitraryData() == null) {
dto.setArbitraryData(new LinkedHashMap<>());
}
String ept = context.getParameter(BackendListenerConstants.EPT.name());
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() {
String reportId = dto.getReportId();
if (StringUtils.isNotEmpty(dto.getTestPlanReportId())
&& !FixedCapacityUtil.containsKey(dto.getTestPlanReportId())
&& StringUtils.equals(dto.getReportType(), RunModeConstants.SET_REPORT.toString())) {
reportId = dto.getTestPlanReportId();
}
return reportId;
}
}

View File

@ -49,8 +49,6 @@ public class TestResultService {
@Resource
private ApiEnvironmentRunningParamService apiEnvironmentRunningParamService;
@Resource
private RedisTemplateService redisTemplateService;
@Resource
private ApiScenarioExecutionInfoService scenarioExecutionInfoService;
@Resource
private ApiScenarioReportStructureService apiScenarioReportStructureService;
@ -80,54 +78,12 @@ public class TestResultService {
this.add(ApiRunMode.JENKINS_SCENARIO_PLAN.name());
}};
// 接口测试 用例/接口
private static final List<String> caseRunModes = new ArrayList<>() {{
this.add(ApiRunMode.DEFINITION.name());
this.add(ApiRunMode.JENKINS.name());
this.add(ApiRunMode.API_PLAN.name());
}};
// 测试计划 用例/接口
private static final List<String> planCaseRunModes = new ArrayList<>() {{
this.add(ApiRunMode.SCHEDULE_API_PLAN.name());
this.add(ApiRunMode.JENKINS_API_PLAN.name());
this.add(ApiRunMode.MANUAL_PLAN.name());
}};
private static final List<String> apiRunModes = new ArrayList<>() {{
this.add(ApiRunMode.DEFINITION.name());
this.add(ApiRunMode.API_PLAN.name());
this.add(ApiRunMode.SCHEDULE_API_PLAN.name());
}};
/**
* 执行结果存储
*
* @param dto 执行结果
*/
public void saveResults(ResultDTO dto) {
// 处理环境
List<String> environmentList = new LinkedList<>();
if (dto.getArbitraryData() != null && dto.getArbitraryData().containsKey("ENV")) {
environmentList = (List<String>) dto.getArbitraryData().get("ENV");
}
//处理环境参数
if (CollectionUtils.isNotEmpty(environmentList)) {
apiEnvironmentRunningParamService.parseEnvironment(environmentList);
}
// 测试计划用例触发结果处理
if (planCaseRunModes.contains(dto.getRunMode())) {
apiDefinitionExecResultService.saveApiResultByScheduleTask(dto);
} else if (caseRunModes.contains(dto.getRunMode())) {
// 手动触发/批量触发 用例结果处理
apiDefinitionExecResultService.saveApiResult(dto);
} else if (scenarioRunModes.contains(dto.getRunMode())) {
// 场景报告结果处理
apiScenarioReportService.saveResult(dto);
}
}
/**
* 批量存储来自NODE/K8s的执行结果
*/

View File

@ -5,15 +5,12 @@ import io.metersphere.api.dto.RequestResultExpandDTO;
import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.*;
import io.metersphere.base.mapper.ext.ExtApiDefinitionExecResultMapper;
import io.metersphere.base.mapper.ext.ExtApiTestCaseMapper;
import io.metersphere.base.mapper.plan.TestPlanApiCaseMapper;
import io.metersphere.base.mapper.plan.ext.ExtTestPlanApiCaseMapper;
import io.metersphere.commons.constants.ApiRunMode;
import io.metersphere.commons.constants.CommonConstants;
import io.metersphere.commons.constants.NoticeConstants;
import io.metersphere.commons.constants.TriggerMode;
import io.metersphere.commons.enums.ApiReportStatus;
import io.metersphere.commons.enums.ExecutionExecuteTypeEnum;
import io.metersphere.commons.utils.*;
import io.metersphere.dto.PlanReportCaseDTO;
import io.metersphere.dto.RequestResult;
@ -49,8 +46,6 @@ public class ApiDefinitionExecResultService {
@Resource
private ApiTestCaseMapper apiTestCaseMapper;
@Resource
private ApiDefinitionMapper apiDefinitionMapper;
@Resource
private NoticeSendService noticeSendService;
@Resource
private UserMapper userMapper;
@ -61,14 +56,6 @@ public class ApiDefinitionExecResultService {
@Resource
private ApiExecutionInfoService apiExecutionInfoService;
@Resource
private TestPlanApiCaseMapper testPlanApiCaseMapper;
@Resource
private ApiCaseExecutionInfoService apiCaseExecutionInfoService;
@Resource
private ExtApiTestCaseMapper extApiTestCaseMapper;
@Resource
private ExtTestPlanApiCaseMapper extTestPlanApiCaseMapper;
@Resource
private RedisTemplateService redisTemplateService;
/**
@ -89,65 +76,43 @@ public class ApiDefinitionExecResultService {
}
}
public void saveApiResult(ResultDTO dto) {
LoggerUtil.info("接收到API/CASE执行结果【 " + dto.getRequestResults().size() + " 】条");
this.mergeRetryResults(dto);
for (RequestResult item : dto.getRequestResults()) {
if (item.getResponseResult() != null && item.getResponseResult().getResponseTime() <= 0) {
item.getResponseResult().setResponseTime((item.getEndTime() - item.getStartTime()));
}
if (!StringUtils.startsWithAny(item.getName(), "PRE_PROCESSOR_ENV_", "POST_PROCESSOR_ENV_")) {
ApiDefinitionExecResult result = this.editResult(item, dto.getReportId(), dto.getConsole(), dto.getRunMode(), dto.getTestId(), null);
if (result != null) {
result.setResourceId(dto.getTestId());
apiExecutionInfoService.insertExecutionInfo(result);
User user = getUser(dto, result);
//如果是测试计划用例更新接口用例的上次执行结果
TestPlanApiCase testPlanApiCase = testPlanApiCaseMapper.selectByPrimaryKey(dto.getTestId());
if (testPlanApiCase != null) {
ApiTestCaseWithBLOBs apiTestCase = apiTestCaseMapper.selectByPrimaryKey(testPlanApiCase.getApiCaseId());
if (apiTestCase != null) {
apiTestCase.setLastResultId(dto.getReportId());
apiTestCaseMapper.updateByPrimaryKeySelective(apiTestCase);
}
redisTemplateService.unlock(dto.getTestId(), dto.getReportId());
}
// 发送通知
LoggerUtil.info("执行结果【 " + result.getName() + " 】入库存储完成");
sendNotice(result, user);
}
}
}
}
public void batchSaveApiResult(List<ResultDTO> resultDTOS) {
if (CollectionUtils.isEmpty(resultDTOS)) {
LoggerUtil.info("未接收到处理结果 ");
return;
}
LoggerUtil.info("接收到API/CASE执行结果【 " + resultDTOS.size() + "");
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
ApiDefinitionExecResultMapper definitionExecResultMapper = sqlSession.getMapper(ApiDefinitionExecResultMapper.class);
ApiDefinitionExecResultMapper batchExecResultMapper = sqlSession.getMapper(ApiDefinitionExecResultMapper.class);
ApiTestCaseMapper batchApiTestCaseMapper = sqlSession.getMapper(ApiTestCaseMapper.class);
TestPlanApiCaseMapper planApiCaseMapper = sqlSession.getMapper(TestPlanApiCaseMapper.class);
for (ResultDTO dto : resultDTOS) {
this.mergeRetryResults(dto);
LoggerUtil.info("开始存储报告结果[ " + dto.getRequestResults().size() + " ]", dto.getReportId());
if (CollectionUtils.isNotEmpty(dto.getRequestResults())) {
for (RequestResult item : dto.getRequestResults()) {
if (!StringUtils.startsWithAny(item.getName(), "PRE_PROCESSOR_ENV_", "POST_PROCESSOR_ENV_")) {
ApiDefinitionExecResult result = this.editResult(item, dto.getReportId(), dto.getConsole(), dto.getRunMode(), dto.getTestId(), definitionExecResultMapper);
if (result != null) {
if (CollectionUtils.isEmpty(dto.getRequestResults())) {
LoggerUtil.info("未解析到执行结果", dto.getReportId());
continue;
}
// 过滤掉全局脚本
List<RequestResult> requestResults = dto.getRequestResults().stream()
.filter(item -> !StringUtils.startsWithAny(item.getName(), "PRE_PROCESSOR_ENV_", "POST_PROCESSOR_ENV_"))
.collect(Collectors.toList());
for (RequestResult item : requestResults) {
ApiDefinitionExecResultWithBLOBs result = this.initResult(item, dto);
if (StringUtils.isBlank(result.getProjectId()) && dto.getExtendedParameters().containsKey(MsHashTreeService.PROJECT_ID)) {
result.setProjectId(this.getProjectIdByResultDTO(dto.getExtendedParameters().get(MsHashTreeService.PROJECT_ID).toString()));
result.setProjectId(this.getProjectId(dto.getExtendedParameters().get(MsHashTreeService.PROJECT_ID).toString()));
}
result.setResourceId(dto.getTestId());
// 更新结果数据
batchExecResultMapper.updateByPrimaryKeySelective(result);
apiExecutionInfoService.insertExecutionInfo(result);
// 批量更新关联关系状态
batchEditStatus(dto.getRunMode(), result.getStatus(), result.getId(), dto.getTestId(), planApiCaseMapper, batchApiTestCaseMapper);
}
if (result != null && !StringUtils.startsWithAny(dto.getRunMode(), NoticeConstants.Mode.SCHEDULE)) {
batchEditStatus(result.getStatus(), result.getId(), dto, planApiCaseMapper, batchApiTestCaseMapper);
// 发送通知
if (!StringUtils.startsWithAny(dto.getRunMode(), NoticeConstants.Mode.SCHEDULE)) {
User user = getUser(dto, result);
if (MapUtils.isNotEmpty(dto.getExtendedParameters())
&& dto.getExtendedParameters().containsKey(CommonConstants.USER)
@ -160,15 +125,13 @@ public class ApiDefinitionExecResultService {
}
}
}
}
}
sqlSession.flushStatements();
if (sqlSession != null && sqlSessionFactory != null) {
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
}
}
private String getProjectIdByResultDTO(String projectIdFromResultDTO) {
private String getProjectId(String projectIdFromResultDTO) {
String returnStr = projectIdFromResultDTO;
if (StringUtils.startsWith(projectIdFromResultDTO, "[") && StringUtils.endsWith(projectIdFromResultDTO, "]")) {
try {
@ -239,142 +202,33 @@ public class ApiDefinitionExecResultService {
}
}
public void setExecResult(String id, String status, Long time) {
TestPlanApiCase apiCase = new TestPlanApiCase();
apiCase.setId(id);
apiCase.setStatus(status);
apiCase.setUpdateTime(time);
testPlanApiCaseMapper.updateByPrimaryKeySelective(apiCase);
}
public void editStatus(ApiDefinitionExecResult saveResult, String type, String status, Long time, String reportId, String testId) {
String name = testId;
String version = StringUtils.EMPTY;
String projectId = StringUtils.EMPTY;
if (StringUtils.equalsAnyIgnoreCase(type,
ApiRunMode.API_PLAN.name(),
ApiRunMode.SCHEDULE_API_PLAN.name(),
ApiRunMode.JENKINS_API_PLAN.name(),
ApiRunMode.MANUAL_PLAN.name())) {
TestPlanApiCase testPlanApiCase = testPlanApiCaseMapper.selectByPrimaryKey(testId);
ApiTestCaseWithBLOBs caseWithBLOBs = null;
if (testPlanApiCase != null) {
this.setExecResult(testId, status, time);
caseWithBLOBs = apiTestCaseMapper.selectByPrimaryKey(testPlanApiCase.getApiCaseId());
testPlanApiCase.setStatus(status);
testPlanApiCase.setUpdateTime(System.currentTimeMillis());
testPlanApiCaseMapper.updateByPrimaryKeySelective(testPlanApiCase);
if (LoggerUtil.getLogger().isDebugEnabled()) {
LoggerUtil.debug("更新测试计划用例【 " + testPlanApiCase.getId() + "");
}
redisTemplateService.unlock(testId, saveResult.getId());
}
if (caseWithBLOBs != null) {
name = caseWithBLOBs.getName();
version = caseWithBLOBs.getVersionId();
projectId = caseWithBLOBs.getProjectId();
}
} else {
ApiDefinition apiDefinition = apiDefinitionMapper.selectByPrimaryKey(testId);
if (apiDefinition != null) {
name = apiDefinition.getName();
projectId = apiDefinition.getProjectId();
} else {
ApiTestCaseWithBLOBs caseWithBLOBs = apiTestCaseMapper.selectByPrimaryKey(testId);
if (caseWithBLOBs != null) {
// 更新用例最后执行结果
caseWithBLOBs.setLastResultId(reportId);
caseWithBLOBs.setStatus(status);
caseWithBLOBs.setUpdateTime(System.currentTimeMillis());
apiTestCaseMapper.updateByPrimaryKey(caseWithBLOBs);
if (LoggerUtil.getLogger().isDebugEnabled()) {
LoggerUtil.debug("更新用例【 " + caseWithBLOBs.getId() + "");
}
name = caseWithBLOBs.getName();
version = caseWithBLOBs.getVersionId();
projectId = caseWithBLOBs.getProjectId();
redisTemplateService.unlock(testId, saveResult.getId());
}
}
}
if (StringUtils.isEmpty(saveResult.getProjectId()) && StringUtils.isNotEmpty(projectId)) {
saveResult.setProjectId(projectId);
}
saveResult.setVersionId(version);
saveResult.setName(name);
}
public void batchEditStatus(String type, String status, String reportId, String testId,
public void batchEditStatus(
String status, String reportId, ResultDTO dto,
TestPlanApiCaseMapper batchTestPlanApiCaseMapper,
ApiTestCaseMapper batchApiTestCaseMapper) {
if (StringUtils.equalsAnyIgnoreCase(type,
ApiRunMode.API_PLAN.name(),
ApiRunMode.SCHEDULE_API_PLAN.name(),
ApiRunMode.JENKINS_API_PLAN.name(),
ApiRunMode.MANUAL_PLAN.name())) {
ApiRunMode runMode = ApiRunMode.fromString(dto.getRunMode());
switch (runMode) {
case API_PLAN:
case SCHEDULE_API_PLAN:
case JENKINS_API_PLAN:
case MANUAL_PLAN:
TestPlanApiCase apiCase = new TestPlanApiCase();
apiCase.setId(testId);
apiCase.setId(dto.getTestId());
apiCase.setStatus(status);
apiCase.setUpdateTime(System.currentTimeMillis());
batchTestPlanApiCaseMapper.updateByPrimaryKeySelective(apiCase);
TestCaseReviewApiCase reviewApiCase = new TestCaseReviewApiCase();
reviewApiCase.setId(testId);
reviewApiCase.setStatus(status);
reviewApiCase.setUpdateTime(System.currentTimeMillis());
redisTemplateService.unlock(testId, reportId);
} else {
// 更新用例最后执行结果
redisTemplateService.unlock(dto.getTestId(), reportId);
break;
default:
ApiTestCaseWithBLOBs caseWithBLOBs = new ApiTestCaseWithBLOBs();
caseWithBLOBs.setId(testId);
caseWithBLOBs.setId(dto.getTestId());
caseWithBLOBs.setLastResultId(reportId);
caseWithBLOBs.setStatus(status);
caseWithBLOBs.setUpdateTime(System.currentTimeMillis());
batchApiTestCaseMapper.updateByPrimaryKeySelective(caseWithBLOBs);
}
}
/**
* 定时任务触发的保存逻辑
* 定时任务时userID要改为定时任务中的用户
*/
public void saveApiResultByScheduleTask(ResultDTO dto) {
if (CollectionUtils.isNotEmpty(dto.getRequestResults())) {
LoggerUtil.info("接收到API/CASE执行结果【 " + dto.getRequestResults().size() + " 】条");
for (RequestResult item : dto.getRequestResults()) {
LoggerUtil.info("执行结果【 " + item.getName() + " 】入库存储");
if (!StringUtils.startsWithAny(item.getName(), "PRE_PROCESSOR_ENV_", "POST_PROCESSOR_ENV_")) {
ApiDefinitionExecResult reportResult = this.editResult(item, dto.getReportId(), dto.getConsole(), dto.getRunMode(), dto.getTestId(), null);
if (MapUtils.isNotEmpty(dto.getExtendedParameters()) && dto.getExtendedParameters().containsKey(CommonConstants.USER_ID)) {
reportResult.setUserId(String.valueOf(dto.getExtendedParameters().get(CommonConstants.USER_ID)));
}
String triggerMode = StringUtils.EMPTY;
if (reportResult != null) {
triggerMode = reportResult.getTriggerMode();
}
if (StringUtils.equalsAny(dto.getRunMode(), ApiRunMode.SCHEDULE_API_PLAN.name(), ApiRunMode.JENKINS_API_PLAN.name())) {
TestPlanApiCase apiCase = testPlanApiCaseMapper.selectByPrimaryKey(dto.getTestId());
if (apiCase != null && redisTemplateService.has(dto.getTestId(), dto.getReportId())) {
String projectId = extTestPlanApiCaseMapper.selectProjectId(apiCase.getId());
ApiDefinition apiDefinition = extApiTestCaseMapper.selectApiBasicInfoByCaseId(apiCase.getId());
String version = apiDefinition == null ? "" : apiDefinition.getVersionId();
apiCaseExecutionInfoService.insertExecutionInfo(apiCase.getId(), reportResult.getStatus(), triggerMode, projectId, ExecutionExecuteTypeEnum.TEST_PLAN.name(), version);
apiCase.setStatus(reportResult.getStatus());
apiCase.setUpdateTime(System.currentTimeMillis());
testPlanApiCaseMapper.updateByPrimaryKeySelective(apiCase);
ApiTestCaseWithBLOBs apiTestCase = apiTestCaseMapper.selectByPrimaryKey(apiCase.getApiCaseId());
if (apiTestCase != null) {
apiTestCase.setLastResultId(dto.getReportId());
apiTestCaseMapper.updateByPrimaryKeySelective(apiTestCase);
}
redisTemplateService.unlock(dto.getTestId(), dto.getReportId());
}
} else {
this.setExecResult(dto.getTestId(), reportResult.getStatus(), item.getStartTime());
}
}
}
break;
}
}
@ -388,11 +242,10 @@ public class ApiDefinitionExecResultService {
}
}
private ApiDefinitionExecResult editResult(RequestResult item, String reportId, String console, String type, String testId, ApiDefinitionExecResultMapper batchMapper) {
if (!StringUtils.startsWithAny(item.getName(), "PRE_PROCESSOR_ENV_", "POST_PROCESSOR_ENV_")) {
private ApiDefinitionExecResultWithBLOBs initResult(RequestResult item, ResultDTO dto) {
ApiDefinitionExecResultWithBLOBs saveResult = new ApiDefinitionExecResultWithBLOBs();
item.getResponseResult().setConsole(console);
saveResult.setId(reportId);
item.getResponseResult().setConsole(dto.getConsole());
saveResult.setId(dto.getReportId());
//对响应内容进行进一步解析如果有附加信息比如误报库信息则根据附加信息内的数据进行其他判读
RequestResultExpandDTO expandDTO = ResponseUtil.parseByRequestResult(item);
String status = item.isSuccess() ? ApiReportStatus.SUCCESS.name() : ApiReportStatus.ERROR.name();
@ -404,7 +257,7 @@ public class ApiDefinitionExecResultService {
} else {
saveResult.setContent(JSON.toJSONString(item));
}
saveResult.setType(type);
saveResult.setType(dto.getRunMode());
saveResult.setStatus(status);
saveResult.setStartTime(item.getStartTime());
saveResult.setEndTime(item.getEndTime());
@ -414,16 +267,9 @@ public class ApiDefinitionExecResultService {
if (StringUtils.isNotEmpty(saveResult.getTriggerMode()) && saveResult.getTriggerMode().equals(CommonConstants.CASE)) {
saveResult.setTriggerMode(TriggerMode.MANUAL.name());
}
if (batchMapper == null) {
editStatus(saveResult, type, status, saveResult.getCreateTime(), saveResult.getId(), testId);
apiDefinitionExecResultMapper.updateByPrimaryKeySelective(saveResult);
} else {
batchMapper.updateByPrimaryKeySelective(saveResult);
}
saveResult.setResourceId(dto.getTestId());
return saveResult;
}
return null;
}
public Map<String, String> selectReportResultByReportIds(Collection<String> values) {
if (CollectionUtils.isEmpty(values)) {

View File

@ -89,11 +89,6 @@ public class ApiScenarioReportService {
@Resource
private RedisTemplateService redisTemplateService;
public void saveResult(ResultDTO dto) {
// 报告详情内容
apiScenarioReportResultService.save(dto.getReportId(), dto.getRequestResults());
}
public void batchSaveResult(List<ResultDTO> dtos) {
apiScenarioReportResultService.batchSave(dtos);
}

View File

@ -1,8 +1,41 @@
package io.metersphere.commons.constants;
import org.apache.commons.lang3.StringUtils;
public enum ApiRunMode {
RUN, DEBUG, DEFINITION, TEST_CASE, SCENARIO, API_PLAN, JENKINS_API_PLAN, JENKINS_SCENARIO_PLAN, JENKINS_PERFORMANCE_TEST, JENKINS,
RUN,
DEBUG,
DEFINITION,
TEST_CASE,
SCENARIO,
API_PLAN,
JENKINS_API_PLAN,
JENKINS_SCENARIO_PLAN,
JENKINS_PERFORMANCE_TEST,
JENKINS,
TEST_PLAN_PERFORMANCE_TEST,
SCENARIO_PLAN, API, SCHEDULE_API_PLAN, SCHEDULE_SCENARIO, SCHEDULE_SCENARIO_PLAN, SCHEDULE_PERFORMANCE_TEST, MANUAL_PLAN,
UI_SCENARIO, UI_SCENARIO_PLAN, UI_SCHEDULE_SCENARIO_PLAN, UI_JENKINS_SCENARIO_PLAN, UI_SCHEDULE_SCENARIO
SCENARIO_PLAN,
API,
SCHEDULE_API_PLAN,
SCHEDULE_SCENARIO,
SCHEDULE_SCENARIO_PLAN,
SCHEDULE_PERFORMANCE_TEST,
MANUAL_PLAN,
UI_SCENARIO,
UI_SCENARIO_PLAN,
UI_SCHEDULE_SCENARIO_PLAN,
UI_JENKINS_SCENARIO_PLAN,
UI_SCHEDULE_SCENARIO,
DEFAULT;
public static ApiRunMode fromString(String mode) {
if (StringUtils.isNotBlank(mode)) {
for (ApiRunMode runMode : ApiRunMode.values()) {
if (runMode.name().equalsIgnoreCase(mode)) {
return runMode;
}
}
}
return DEFAULT;
}
}

View File

@ -24,4 +24,6 @@ public interface ExtTestPlanReportMapper {
void setApiBaseCountAndPassRateIsNullById(String id);
void updateAllStatus();
String selectLastReportByTestPlanId(@Param("testPlanId") String testPlanId);
}

View File

@ -154,6 +154,10 @@
GROUP BY t.test_plan_id
</select>
<select id="selectLastReportByTestPlanId" resultType="java.lang.String">
select `status` from test_plan_report where test_plan_id = #{testPlanId} ORDER BY create_time DESC LIMIT 1
</select>
<update id="setApiBaseCountAndPassRateIsNullById">
update test_plan_report_content

View File

@ -2,10 +2,12 @@ package io.metersphere.plan.service;
import io.metersphere.base.domain.TestPlanWithBLOBs;
import io.metersphere.base.mapper.TestPlanMapper;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.JSON;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.constants.RunModeConstants;
import io.metersphere.dto.*;
import io.metersphere.i18n.Translator;
import io.metersphere.plan.dto.ExecutionWay;
import io.metersphere.plan.request.api.TestPlanRunRequest;
import io.metersphere.plan.service.remote.api.PlanTestPlanApiCaseService;
@ -51,6 +53,11 @@ public class TestPlanExecuteService {
*/
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
public String runTestPlan(String testPlanId, String projectId, String userId, String triggerMode, String planReportId, String executionWay, String apiRunConfig) {
// 校验测试计划是否在执行中
if (testPlanService.checkTestPlanIsRunning(testPlanId)) {
LogUtil.info("当前测试计划正在执行中,请稍后再试", testPlanId);
MSException.throwException(Translator.get("test_plan_run_message"));
}
RunModeConfigDTO runModeConfig = null;
try {
runModeConfig = JSON.parseObject(apiRunConfig, RunModeConfigDTO.class);

View File

@ -1769,4 +1769,8 @@ public class TestPlanReportService {
testPlanReportContentMapper.updateByExampleSelective(reportContentWithBLOBs, example);
}
}
public String selectLastReportByTestPlanId(String testPlanId) {
return extTestPlanReportMapper.selectLastReportByTestPlanId(testPlanId);
}
}

View File

@ -2359,4 +2359,9 @@ public class TestPlanService {
// 进行中0 < 测试进度 < 100%
return TestPlanStatus.Underway.name();
}
public boolean checkTestPlanIsRunning(String testPlanId) {
String status = testPlanReportService.selectLastReportByTestPlanId(testPlanId);
return StringUtils.equalsIgnoreCase(status, TestPlanReportStatus.RUNNING.name());
}
}

View File

@ -240,3 +240,4 @@ rerun_warning=The report is being rerun, check it later
case_export_text_validate_tip=Contains extremely long text, currently supported up to %s!
case_import_table_header_missing=Header information is missing!
relate_resource=relate
test_plan_run_message=The current test plan is running, please try again later

View File

@ -211,3 +211,4 @@ rerun_warning=报告正在重跑中,稍后查看
case_export_text_validate_tip=包含超长文本,目前支持最大长度为 %s
case_import_table_header_not_exist=缺少表头信息!
relate_resource=关联
test_plan_run_message=当前测试计划正在执行中,请稍后再试!

View File

@ -211,3 +211,4 @@ rerun_warning=報告正在重跑中,稻後查看
case_export_text_validate_tip=包含超長文本,目前支持最大長度為 %s
case_import_table_header_not_exist=缺少表頭信息!
relate_resource=關聯
test_plan_run_message=當前測試計劃正在執行中,請稍後再試!