feat(接口测试): 支持场景接口用例批量执行选择环境

--story=1004209 --user=赵勇 3.case列表和场景列表批量执行支持选择环境 和测试计划保持一致 https://www.tapd.cn/55049933/s/1088817
This commit is contained in:
fit2-zhao 2021-12-30 19:19:10 +08:00 committed by fit2-zhao
parent a1700ece0c
commit a29e9951f8
24 changed files with 731 additions and 360 deletions

View File

@ -396,5 +396,10 @@ public class ApiAutomationController {
public void deleteApiScenario(@PathVariable String version, @PathVariable String refId) {
apiAutomationService.deleteApiScenarioByVersion(refId, version);
}
@PostMapping(value = "/env")
public List<String> getEnvProjects(@RequestBody RunScenarioRequest request) {
return apiAutomationService.getProjects(request);
}
}

View File

@ -47,8 +47,9 @@ public class ApiJmeterFileController {
}
@GetMapping("download")
public ResponseEntity<byte[]> downloadJmeterFiles(@RequestParam("testId") String testId, @RequestParam("reportId") String reportId, @RequestParam("runMode") String runMode, @RequestParam("reportType") String reportType) {
byte[] bytes = apiJmeterFileService.downloadJmeterFiles(runMode, testId, reportId, reportType);
public ResponseEntity<byte[]> downloadJmeterFiles(@RequestParam("testId") String testId, @RequestParam("reportId") String reportId,
@RequestParam("runMode") String runMode, @RequestParam("reportType") String reportType, @RequestParam("queueId") String queueId) {
byte[] bytes = apiJmeterFileService.downloadJmeterFiles(runMode, testId, reportId, reportType, queueId);
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType("application/octet-stream"))
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + reportId + "_" + testId + ".zip\"")

View File

@ -6,6 +6,8 @@ import io.metersphere.api.dto.ApiCaseEditRequest;
import io.metersphere.api.dto.ApiCaseRunRequest;
import io.metersphere.api.dto.DeleteCheckResult;
import io.metersphere.api.dto.definition.*;
import io.metersphere.api.exec.api.ApiCaseExecuteService;
import io.metersphere.api.exec.api.ApiExecuteService;
import io.metersphere.api.service.ApiTestCaseService;
import io.metersphere.base.domain.ApiTestCase;
import io.metersphere.base.domain.ApiTestCaseWithBLOBs;
@ -36,6 +38,10 @@ public class ApiTestCaseController {
private ApiTestCaseService apiTestCaseService;
@Resource
private TestPlanApiCaseService testPlanApiCaseService;
@Resource
private ApiCaseExecuteService apiCaseExecuteService;
@Resource
private ApiExecuteService apiExecuteService;
@PostMapping("/list")
public List<ApiTestCaseResult> list(@RequestBody ApiTestCaseRequest request) {
@ -194,13 +200,13 @@ public class ApiTestCaseController {
@MsAuditLog(module = "api_definition_case", type = OperLogConstants.EXECUTE, content = "#msClass.getLogDetails(#request.caseId)", msClass = ApiTestCaseService.class)
public void batchRun(@RequestBody ApiCaseRunRequest request) {
request.setTriggerMode(ReportTriggerMode.BATCH.name());
apiTestCaseService.batchRun(request);
apiCaseExecuteService.run(request);
}
@PostMapping(value = "/jenkins/run")
@MsAuditLog(module = "api_definition_case", type = OperLogConstants.EXECUTE, content = "#msClass.getLogDetails(#request.caseId)", msClass = ApiTestCaseService.class)
public MsExecResponseDTO jenkinsRun(@RequestBody RunCaseRequest request) {
return apiTestCaseService.jenkinsRun(request);
return apiExecuteService.jenkinsRun(request);
}
@GetMapping(value = "/jenkins/exec/result/{id}")

View File

@ -2,6 +2,7 @@ package io.metersphere.api.dto;
import io.metersphere.api.dto.definition.ApiTestCaseRequest;
import io.metersphere.controller.request.OrderRequest;
import io.metersphere.dto.RunModeConfigDTO;
import lombok.Getter;
import lombok.Setter;
@ -17,5 +18,6 @@ public class ApiCaseRunRequest {
private List<OrderRequest> orders;
private String projectId;
private String environmentId;
private RunModeConfigDTO config;
private ApiTestCaseRequest condition;
}

View File

@ -0,0 +1,196 @@
package io.metersphere.api.exec.api;
import com.alibaba.fastjson.JSON;
import io.metersphere.api.cache.TestPlanReportExecuteCatch;
import io.metersphere.api.dto.ApiCaseRunRequest;
import io.metersphere.api.dto.definition.ApiTestCaseRequest;
import io.metersphere.api.dto.definition.BatchRunDefinitionRequest;
import io.metersphere.api.exec.queue.DBTestQueue;
import io.metersphere.api.exec.scenario.ApiScenarioSerialService;
import io.metersphere.api.exec.utils.ApiDefinitionExecResultUtil;
import io.metersphere.api.service.ApiExecutionQueueService;
import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.ApiDefinitionExecResultMapper;
import io.metersphere.base.mapper.ApiTestCaseMapper;
import io.metersphere.base.mapper.TestPlanApiCaseMapper;
import io.metersphere.base.mapper.ext.ExtApiTestCaseMapper;
import io.metersphere.commons.constants.APITestStatus;
import io.metersphere.commons.constants.ApiRunMode;
import io.metersphere.commons.constants.TriggerMode;
import io.metersphere.commons.utils.ServiceUtils;
import io.metersphere.constants.RunModeConstants;
import io.metersphere.dto.MsExecResponseDTO;
import io.metersphere.dto.RunModeConfigDTO;
import io.metersphere.service.EnvironmentGroupProjectService;
import io.metersphere.utils.LoggerUtil;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@Service
@Transactional(rollbackFor = Exception.class)
public class ApiCaseExecuteService {
@Resource
private TestPlanApiCaseMapper testPlanApiCaseMapper;
@Resource
private SqlSessionFactory sqlSessionFactory;
@Resource
private ApiScenarioSerialService apiScenarioSerialService;
@Resource
private ApiExecutionQueueService apiExecutionQueueService;
@Resource
private ApiCaseParallelExecuteService apiCaseParallelExecuteService;
@Resource
private ExtApiTestCaseMapper extApiTestCaseMapper;
@Resource
private ApiTestCaseMapper apiTestCaseMapper;
@Resource
private EnvironmentGroupProjectService environmentGroupProjectService;
/**
* 测试计划case执行
*
* @param request
* @return
*/
public List<MsExecResponseDTO> run(BatchRunDefinitionRequest request) {
List<String> ids = request.getPlanIds();
if (CollectionUtils.isEmpty(ids)) {
return new LinkedList<>();
}
if (request.getConfig() == null) {
request.setConfig(new RunModeConfigDTO());
}
if (StringUtils.equals("GROUP", request.getConfig().getEnvironmentType()) && StringUtils.isNotEmpty(request.getConfig().getEnvironmentGroupId())) {
request.getConfig().setEnvMap(environmentGroupProjectService.getEnvMap(request.getConfig().getEnvironmentGroupId()));
}
LoggerUtil.debug("开始查询测试计划用例");
TestPlanApiCaseExample example = new TestPlanApiCaseExample();
example.createCriteria().andIdIn(ids);
example.setOrderByClause("`order` DESC");
List<TestPlanApiCase> planApiCases = testPlanApiCaseMapper.selectByExample(example);
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
ApiDefinitionExecResultMapper batchMapper = sqlSession.getMapper(ApiDefinitionExecResultMapper.class);
if (StringUtils.isEmpty(request.getTriggerMode())) {
request.setTriggerMode(ApiRunMode.API_PLAN.name());
}
List<MsExecResponseDTO> responseDTOS = new LinkedList<>();
Map<String, ApiDefinitionExecResult> executeQueue = new HashMap<>();
//记录案例线程结果以及执行失败的案例ID
Map<String, String> executeThreadIdMap = new HashMap<>();
String status = request.getConfig().getMode().equals(RunModeConstants.SERIAL.toString()) ? APITestStatus.Waiting.name() : APITestStatus.Running.name();
planApiCases.forEach(testPlanApiCase -> {
ApiDefinitionExecResult report = ApiDefinitionExecResultUtil.addResult(request, testPlanApiCase, status, batchMapper);
executeQueue.put(testPlanApiCase.getId(), report);
executeThreadIdMap.put(testPlanApiCase.getId(), report.getId());
responseDTOS.add(new MsExecResponseDTO(testPlanApiCase.getId(), report.getId(), request.getTriggerMode()));
});
sqlSession.flushStatements();
if (sqlSession != null && sqlSessionFactory != null) {
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
}
LoggerUtil.debug("开始生成测试计划队列");
String reportType = request.getConfig().getReportType();
String poolId = request.getConfig().getResourcePoolId();
String runMode = StringUtils.equals(request.getTriggerMode(), TriggerMode.MANUAL.name()) ? ApiRunMode.API_PLAN.name() : ApiRunMode.SCHEDULE_API_PLAN.name();
DBTestQueue deQueue = apiExecutionQueueService.add(executeQueue, poolId, ApiRunMode.API_PLAN.name(), request.getPlanReportId(), reportType, runMode, request.getConfig().getEnvMap());
//如果是测试计划生成报告的执行则更新执行信息执行线程信息
if (TestPlanReportExecuteCatch.containsReport(request.getPlanReportId())) {
if (!executeThreadIdMap.isEmpty()) {
TestPlanReportExecuteCatch.updateTestPlanThreadInfo(request.getPlanReportId(), executeThreadIdMap, null, null);
}
}
// 开始选择执行模式
if (request.getConfig() != null && request.getConfig().getMode().equals(RunModeConstants.SERIAL.toString())) {
LoggerUtil.debug("开始串行执行");
if (deQueue != null && deQueue.getQueue() != null) {
apiScenarioSerialService.serial(deQueue, deQueue.getQueue());
}
} else {
LoggerUtil.debug("开始并发执行");
if (deQueue != null && deQueue.getQueue() != null) {
apiCaseParallelExecuteService.parallel(executeQueue, request.getConfig(), deQueue, runMode);
}
}
return responseDTOS;
}
/**
* 接口定义case执行
*
* @param request
* @return
*/
public List<MsExecResponseDTO> run(ApiCaseRunRequest request) {
if (LoggerUtil.getLogger().isDebugEnabled()) {
LoggerUtil.debug("进入执行方法,接收到参数:" + JSON.toJSONString(request));
}
if (request.getConfig() == null) {
request.setConfig(new RunModeConfigDTO());
}
if (StringUtils.equals("GROUP", request.getConfig().getEnvironmentType()) && StringUtils.isNotEmpty(request.getConfig().getEnvironmentGroupId())) {
request.getConfig().setEnvMap(environmentGroupProjectService.getEnvMap(request.getConfig().getEnvironmentGroupId()));
}
ServiceUtils.getSelectAllIds(request, request.getCondition(),
(query) -> extApiTestCaseMapper.selectIdsByQuery((ApiTestCaseRequest) query));
ApiTestCaseExample example = new ApiTestCaseExample();
example.createCriteria().andIdIn(request.getIds());
List<ApiTestCaseWithBLOBs> list = apiTestCaseMapper.selectByExampleWithBLOBs(example);
LoggerUtil.debug("查询到执行数据:" + list.size());
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
ApiDefinitionExecResultMapper batchMapper = sqlSession.getMapper(ApiDefinitionExecResultMapper.class);
if (StringUtils.isEmpty(request.getTriggerMode())) {
request.setTriggerMode(ApiRunMode.DEFINITION.name());
}
List<MsExecResponseDTO> responseDTOS = new LinkedList<>();
Map<String, ApiDefinitionExecResult> executeQueue = new HashMap<>();
String status = request.getConfig().getMode().equals(RunModeConstants.SERIAL.toString()) ? APITestStatus.Waiting.name() : APITestStatus.Running.name();
list.forEach(caseWithBLOBs -> {
ApiDefinitionExecResult report = ApiDefinitionExecResultUtil.initBase(caseWithBLOBs.getId(), APITestStatus.Running.name(), null, request.getConfig());
report.setStatus(status);
batchMapper.insert(report);
executeQueue.put(caseWithBLOBs.getId(), report);
responseDTOS.add(new MsExecResponseDTO(caseWithBLOBs.getId(), report.getId(), request.getTriggerMode()));
});
sqlSession.flushStatements();
if (sqlSession != null && sqlSessionFactory != null) {
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
}
String reportType = request.getConfig().getReportType();
String poolId = request.getConfig().getResourcePoolId();
DBTestQueue deQueue = apiExecutionQueueService.add(executeQueue, poolId, ApiRunMode.DEFINITION.name(), null, reportType, ApiRunMode.DEFINITION.name(), request.getConfig().getEnvMap());
// 开始选择执行模式
if (request.getConfig().getMode().equals(RunModeConstants.SERIAL.toString())) {
LoggerUtil.debug("开始串行执行");
if (deQueue != null && deQueue.getQueue() != null) {
apiScenarioSerialService.serial(deQueue, deQueue.getQueue());
}
} else {
LoggerUtil.debug("开始并发执行");
if (deQueue != null && deQueue.getQueue() != null) {
apiCaseParallelExecuteService.parallel(executeQueue, request.getConfig(), deQueue, ApiRunMode.DEFINITION.name());
}
}
return responseDTOS;
}
}

View File

@ -0,0 +1,57 @@
package io.metersphere.api.exec.api;
import io.metersphere.api.exec.queue.DBTestQueue;
import io.metersphere.api.exec.scenario.ApiScenarioSerialService;
import io.metersphere.api.exec.utils.GenerateHashTreeUtil;
import io.metersphere.api.jmeter.JMeterService;
import io.metersphere.base.domain.ApiDefinitionExecResult;
import io.metersphere.constants.RunModeConstants;
import io.metersphere.dto.JmeterRunRequestDTO;
import io.metersphere.dto.RunModeConfigDTO;
import io.metersphere.utils.LoggerUtil;
import org.apache.jorphan.collections.HashTree;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Map;
@Service
public class ApiCaseParallelExecuteService {
@Resource
private ApiScenarioSerialService apiScenarioSerialService;
@Resource
private JMeterService jMeterService;
public void parallel(Map<String, ApiDefinitionExecResult> executeQueue, RunModeConfigDTO config, DBTestQueue executionQueue, String runMode) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5000);
Thread.currentThread().setName("API-CASE-THREAD");
for (String testId : executeQueue.keySet()) {
ApiDefinitionExecResult result = executeQueue.get(testId);
String reportId = result.getId();
HashTree hashTree = null;
if (!GenerateHashTreeUtil.isResourcePool(config.getResourcePoolId()).isPool()) {
hashTree = apiScenarioSerialService.generateHashTree(testId, executionQueue.getRunMode(), config.getEnvMap());
}
JmeterRunRequestDTO runRequest = new JmeterRunRequestDTO(testId, reportId, runMode, hashTree);
runRequest.setPool(GenerateHashTreeUtil.isResourcePool(config.getResourcePoolId()));
runRequest.setPoolId(config.getResourcePoolId());
runRequest.setReportType(executionQueue.getReportType());
runRequest.setRunType(RunModeConstants.PARALLEL.toString());
runRequest.setQueueId(executionQueue.getId());
if (executionQueue.getQueue() != null) {
runRequest.setPlatformUrl(executionQueue.getQueue().getId());
}
jMeterService.run(runRequest);
}
} catch (Exception e) {
LoggerUtil.error("并发执行用例失败:" + e.getMessage());
}
}
});
thread.start();
}
}

View File

@ -5,8 +5,6 @@ import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.metersphere.api.dto.ApiCaseRunRequest;
import io.metersphere.api.dto.definition.ApiTestCaseRequest;
import io.metersphere.api.dto.definition.RunCaseRequest;
import io.metersphere.api.dto.definition.RunDefinitionRequest;
import io.metersphere.api.dto.definition.request.ElementUtil;
@ -30,19 +28,14 @@ import io.metersphere.commons.constants.ApiRunMode;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.FileUtils;
import io.metersphere.commons.utils.LogUtil;
import io.metersphere.commons.utils.ServiceUtils;
import io.metersphere.dto.JmeterRunRequestDTO;
import io.metersphere.dto.MsExecResponseDTO;
import io.metersphere.plugin.core.MsTestElement;
import io.metersphere.utils.LoggerUtil;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.jorphan.collections.HashTree;
import org.apache.jorphan.collections.ListedHashTree;
import org.mybatis.spring.SqlSessionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
@ -56,10 +49,6 @@ public class ApiExecuteService {
@Resource
private ApiTestCaseMapper apiTestCaseMapper;
@Resource
private SqlSessionFactory sqlSessionFactory;
@Resource
private ExtApiTestCaseMapper extApiTestCaseMapper;
@Resource
private JMeterService jMeterService;
@Resource
private ApiDefinitionExecResultMapper apiDefinitionExecResultMapper;
@ -69,68 +58,45 @@ public class ApiExecuteService {
private ApiTestEnvironmentService environmentService;
@Resource
private TcpApiParamService tcpApiParamService;
@Resource
private ExtApiTestCaseMapper extApiTestCaseMapper;
public List<MsExecResponseDTO> run(ApiCaseRunRequest request) {
if (LoggerUtil.getLogger().isDebugEnabled()) {
LoggerUtil.debug("进入执行方法,接收到参数:" + JSON.toJSONString(request));
public MsExecResponseDTO jenkinsRun(RunCaseRequest request) {
ApiTestCaseWithBLOBs caseWithBLOBs = null;
if (request.getBloBs() == null) {
caseWithBLOBs = apiTestCaseMapper.selectByPrimaryKey(request.getCaseId());
if (caseWithBLOBs == null) {
return null;
}
ServiceUtils.getSelectAllIds(request, request.getCondition(),
(query) -> extApiTestCaseMapper.selectIdsByQuery((ApiTestCaseRequest) query));
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
ApiTestCaseExample example = new ApiTestCaseExample();
example.createCriteria().andIdIn(request.getIds());
List<ApiTestCaseWithBLOBs> list = apiTestCaseMapper.selectByExampleWithBLOBs(example);
LoggerUtil.debug("查询到执行数据:" + list.size());
ApiTestCaseMapper sqlSessionMapper = sqlSession.getMapper(ApiTestCaseMapper.class);
ApiDefinitionExecResultMapper batchMapper = sqlSession.getMapper(ApiDefinitionExecResultMapper.class);
List<RunCaseRequest> executeQueue = new LinkedList<>();
for (ApiTestCaseWithBLOBs caseWithBLOBs : list) {
ApiDefinitionExecResult report = ApiDefinitionExecResultUtil.initBase(caseWithBLOBs.getId(), APITestStatus.Running.name(), null);
request.setBloBs(caseWithBLOBs);
} else {
caseWithBLOBs = request.getBloBs();
}
if (caseWithBLOBs == null) {
return null;
}
if (StringUtils.isBlank(request.getEnvironmentId())) {
request.setEnvironmentId(extApiTestCaseMapper.getApiCaseEnvironment(request.getCaseId()));
}
//提前生成报告
ApiDefinitionExecResult report = ApiDefinitionExecResultUtil.add(caseWithBLOBs.getId(), APITestStatus.Running.name(), request.getReportId());
report.setName(caseWithBLOBs.getName());
report.setTriggerMode(request.getTriggerMode());
report.setTriggerMode(ApiRunMode.JENKINS.name());
report.setType(ApiRunMode.JENKINS.name());
apiDefinitionExecResultMapper.insert(report);
//更新接口案例的最后执行状态等信息
caseWithBLOBs.setLastResultId(report.getId());
caseWithBLOBs.setUpdateTime(System.currentTimeMillis());
caseWithBLOBs.setStatus(APITestStatus.Running.name());
sqlSessionMapper.updateByPrimaryKey(caseWithBLOBs);
apiTestCaseMapper.updateByPrimaryKey(caseWithBLOBs);
request.setReport(report);
// 执行对象
RunCaseRequest runCaseRequest = new RunCaseRequest();
runCaseRequest.setRunMode(ApiRunMode.DEFINITION.name());
runCaseRequest.setCaseId(caseWithBLOBs.getId());
runCaseRequest.setReportId(report.getId());
runCaseRequest.setEnvironmentId(request.getEnvironmentId());
runCaseRequest.setBloBs(caseWithBLOBs);
runCaseRequest.setReport(report);
batchMapper.insert(report);
executeQueue.add(runCaseRequest);
if (StringUtils.isEmpty(request.getRunMode())) {
request.setRunMode(ApiRunMode.DEFINITION.name());
}
sqlSession.flushStatements();
if (sqlSession != null && sqlSessionFactory != null) {
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
return this.exec(request);
}
LoggerUtil.info("生成执行队列:" + executeQueue.size());
if (LoggerUtil.getLogger().isDebugEnabled()) {
LoggerUtil.debug("生成执行队列:" + JSON.toJSONString(executeQueue));
}
List<MsExecResponseDTO> responseDTOS = new LinkedList<>();
for (RunCaseRequest runCaseRequest : executeQueue) {
responseDTOS.add(exec(runCaseRequest));
}
return responseDTOS;
}
/**
* 单条执行
*
* @param request
* @return
*/
public MsExecResponseDTO exec(RunCaseRequest request) {
ApiTestCaseWithBLOBs testCaseWithBLOBs = request.getBloBs();
if (StringUtils.equals(request.getRunMode(), ApiRunMode.JENKINS_API_PLAN.name())) {

View File

@ -1,146 +0,0 @@
package io.metersphere.api.exec.api;
import io.metersphere.api.cache.TestPlanReportExecuteCatch;
import io.metersphere.api.dto.definition.BatchRunDefinitionRequest;
import io.metersphere.api.exec.queue.DBTestQueue;
import io.metersphere.api.exec.scenario.ApiScenarioSerialService;
import io.metersphere.api.exec.utils.ApiDefinitionExecResultUtil;
import io.metersphere.api.exec.utils.GenerateHashTreeUtil;
import io.metersphere.api.jmeter.JMeterService;
import io.metersphere.api.service.ApiExecutionQueueService;
import io.metersphere.base.domain.ApiDefinitionExecResult;
import io.metersphere.base.domain.TestPlanApiCase;
import io.metersphere.base.domain.TestPlanApiCaseExample;
import io.metersphere.base.mapper.ApiDefinitionExecResultMapper;
import io.metersphere.base.mapper.TestPlanApiCaseMapper;
import io.metersphere.commons.constants.APITestStatus;
import io.metersphere.commons.constants.ApiRunMode;
import io.metersphere.commons.constants.TriggerMode;
import io.metersphere.constants.RunModeConstants;
import io.metersphere.dto.JmeterRunRequestDTO;
import io.metersphere.dto.MsExecResponseDTO;
import io.metersphere.utils.LoggerUtil;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.jorphan.collections.HashTree;
import org.mybatis.spring.SqlSessionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@Service
@Transactional(rollbackFor = Exception.class)
public class TestPlanApiExecuteService {
@Resource
private TestPlanApiCaseMapper testPlanApiCaseMapper;
@Resource
private SqlSessionFactory sqlSessionFactory;
@Resource
private ApiScenarioSerialService apiScenarioSerialService;
@Resource
private ApiExecutionQueueService apiExecutionQueueService;
@Resource
private JMeterService jMeterService;
public List<MsExecResponseDTO> run(BatchRunDefinitionRequest request) {
List<String> ids = request.getPlanIds();
if (CollectionUtils.isEmpty(ids)) {
return new LinkedList<>();
}
LoggerUtil.debug("开始查询测试计划用例");
TestPlanApiCaseExample example = new TestPlanApiCaseExample();
example.createCriteria().andIdIn(ids);
example.setOrderByClause("`order` DESC");
List<TestPlanApiCase> planApiCases = testPlanApiCaseMapper.selectByExample(example);
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
ApiDefinitionExecResultMapper batchMapper = sqlSession.getMapper(ApiDefinitionExecResultMapper.class);
if (StringUtils.isEmpty(request.getTriggerMode())) {
request.setTriggerMode(ApiRunMode.API_PLAN.name());
}
List<MsExecResponseDTO> responseDTOS = new LinkedList<>();
Map<TestPlanApiCase, ApiDefinitionExecResult> executeQueue = new HashMap<>();
//记录案例线程结果以及执行失败的案例ID
Map<String, String> executeThreadIdMap = new HashMap<>();
String status = request.getConfig() != null && request.getConfig().getMode().equals(RunModeConstants.SERIAL.toString()) ? APITestStatus.Waiting.name() : APITestStatus.Running.name();
planApiCases.forEach(testPlanApiCase -> {
ApiDefinitionExecResult report = ApiDefinitionExecResultUtil.addResult(request, testPlanApiCase, status, batchMapper);
executeQueue.put(testPlanApiCase, report);
executeThreadIdMap.put(testPlanApiCase.getId(), report.getId());
responseDTOS.add(new MsExecResponseDTO(testPlanApiCase.getId(), report.getId(), request.getTriggerMode()));
});
sqlSession.flushStatements();
if (sqlSession != null && sqlSessionFactory != null) {
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
}
LoggerUtil.debug("开始生成测试计划队列");
String reportType = request.getConfig() != null ? request.getConfig().getReportType() : null;
String poolId = request.getConfig() != null ? request.getConfig().getResourcePoolId() : null;
String runMode = StringUtils.equals(request.getTriggerMode(), TriggerMode.MANUAL.name()) ? ApiRunMode.API_PLAN.name() : ApiRunMode.SCHEDULE_API_PLAN.name();
DBTestQueue deQueue = apiExecutionQueueService.add(executeQueue, poolId, ApiRunMode.API_PLAN.name(), request.getPlanReportId(), reportType, runMode);
//如果是测试计划生成报告的执行则更新执行信息执行线程信息
if (TestPlanReportExecuteCatch.containsReport(request.getPlanReportId())) {
if (!executeThreadIdMap.isEmpty()) {
TestPlanReportExecuteCatch.updateTestPlanThreadInfo(request.getPlanReportId(), executeThreadIdMap, null, null);
}
}
// 开始选择执行模式
if (request.getConfig() != null && request.getConfig().getMode().equals(RunModeConstants.SERIAL.toString())) {
LoggerUtil.debug("开始串行执行");
if (deQueue != null && deQueue.getQueue() != null) {
apiScenarioSerialService.serial(deQueue, deQueue.getQueue());
}
} else {
LoggerUtil.debug("开始并发执行");
if (deQueue != null && deQueue.getQueue() != null) {
parallel(executeQueue, request, deQueue);
}
}
return responseDTOS;
}
private void parallel(Map<TestPlanApiCase, ApiDefinitionExecResult> executeQueue, BatchRunDefinitionRequest request, DBTestQueue executionQueue) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5000);
Thread.currentThread().setName("测试计划入列线程");
for (TestPlanApiCase testPlanApiCase : executeQueue.keySet()) {
ApiDefinitionExecResult result = executeQueue.get(testPlanApiCase);
String reportId = result.getId();
HashTree hashTree = null;
if (request.getConfig() == null || !GenerateHashTreeUtil.isResourcePool(request.getConfig().getResourcePoolId()).isPool()) {
hashTree = apiScenarioSerialService.generateHashTree(testPlanApiCase.getId());
}
String runMode = StringUtils.equals(request.getTriggerMode(), TriggerMode.MANUAL.name()) ? ApiRunMode.API_PLAN.name() : ApiRunMode.SCHEDULE_API_PLAN.name();
JmeterRunRequestDTO runRequest = new JmeterRunRequestDTO(testPlanApiCase.getId(), reportId, runMode, hashTree);
if (request.getConfig() != null) {
runRequest.setPool(GenerateHashTreeUtil.isResourcePool(request.getConfig().getResourcePoolId()));
runRequest.setPoolId(request.getConfig().getResourcePoolId());
}
runRequest.setTestPlanReportId(request.getPlanReportId());
runRequest.setReportType(executionQueue.getReportType());
runRequest.setTestPlanReportId(request.getPlanReportId());
runRequest.setRunType(RunModeConstants.PARALLEL.toString());
runRequest.setQueueId(executionQueue.getId());
jMeterService.run(runRequest);
}
} catch (Exception e) {
LoggerUtil.error("并发执行测试计划用例失败:" + e.getMessage());
}
}
});
thread.start();
}
}

View File

@ -4,7 +4,11 @@ import io.metersphere.base.domain.ApiExecutionQueue;
import io.metersphere.base.domain.ApiExecutionQueueDetail;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
@Data
public class DBTestQueue extends ApiExecutionQueue {
private ApiExecutionQueueDetail queue;
private Map<String, String> detailMap = new HashMap<>();
}

View File

@ -32,6 +32,7 @@ import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.constants.RunModeConstants;
import io.metersphere.dto.JmeterRunRequestDTO;
import io.metersphere.dto.MsExecResponseDTO;
import io.metersphere.dto.RunModeConfigDTO;
import io.metersphere.i18n.Translator;
import io.metersphere.service.EnvironmentGroupProjectService;
import io.metersphere.track.service.TestPlanScenarioCaseService;
@ -98,6 +99,14 @@ public class ApiScenarioExecuteService {
if (StringUtils.isEmpty(request.getTriggerMode())) {
request.setTriggerMode(ReportTriggerMode.MANUAL.name());
}
if (request.getConfig() == null) {
request.setConfig(new RunModeConfigDTO());
}
if (StringUtils.equals("GROUP", request.getConfig().getEnvironmentType()) && StringUtils.isNotEmpty(request.getConfig().getEnvironmentGroupId())) {
request.getConfig().setEnvMap(environmentGroupProjectService.getEnvMap(request.getConfig().getEnvironmentGroupId()));
}
// 生成集成报告
String serialReportId = null;
LoggerUtil.info("Scenario run-执行脚本装载-根据条件查询所有场景 ");
@ -156,8 +165,7 @@ public class ApiScenarioExecuteService {
// 开始执行
if (executeQueue != null && executeQueue.size() > 0) {
String reportType = request.getConfig().getReportType();
DBTestQueue executionQueue = apiExecutionQueueService.add(executeQueue, request.getConfig().getResourcePoolId(), ApiRunMode.SCENARIO.name(), serialReportId, reportType, request.getRunMode());
DBTestQueue executionQueue = apiExecutionQueueService.add(executeQueue, request.getConfig().getResourcePoolId(), ApiRunMode.SCENARIO.name(), serialReportId, reportType, request.getRunMode(), request.getConfig().getEnvMap());
if (request.getConfig() != null && request.getConfig().getMode().equals(RunModeConstants.SERIAL.toString())) {
if (StringUtils.isNotEmpty(serialReportId)) {
apiScenarioReportStructureService.save(apiScenarios, serialReportId, request.getConfig() != null ? request.getConfig().getReportType() : null);
@ -181,7 +189,7 @@ public class ApiScenarioExecuteService {
apiScenarioSerialService.serial(executionQueue, executionQueue.getQueue());
}
} else {
apiScenarioParallelService.parallel(executeQueue, request, serialReportId, responseDTOS, executionQueue.getId());
apiScenarioParallelService.parallel(executeQueue, request, serialReportId, responseDTOS, executionQueue);
}
}
return responseDTOS;
@ -309,6 +317,9 @@ public class ApiScenarioExecuteService {
RunModeDataDTO runModeDataDTO = new RunModeDataDTO();
runModeDataDTO.setTestId(item.getId());
runModeDataDTO.setPlanEnvMap(new HashMap<>());
if (request.getConfig().getEnvMap() != null) {
runModeDataDTO.setPlanEnvMap(request.getConfig().getEnvMap());
}
runModeDataDTO.setReport(report);
runModeDataDTO.setReportId(report.getId());
executeQueue.put(report.getId(), runModeDataDTO);
@ -317,9 +328,10 @@ public class ApiScenarioExecuteService {
try {
RunModeDataDTO runModeDataDTO = new RunModeDataDTO(report, item.getId());
if (request.getConfig() != null && !request.getConfig().getMode().equals(RunModeConstants.SERIAL.toString())) {
HashTree hashTree = GenerateHashTreeUtil.generateHashTree(item, StringUtils.isNotEmpty(serialReportId) ? serialReportId + "-" + i : reportId, new HashMap<>(), request.getConfig().getReportType());
HashTree hashTree = GenerateHashTreeUtil.generateHashTree(item, StringUtils.isNotEmpty(serialReportId) ? serialReportId + "-" + i : reportId, request.getConfig().getEnvMap(), request.getConfig().getReportType());
runModeDataDTO.setHashTree(hashTree);
}
runModeDataDTO.setPlanEnvMap(request.getConfig().getEnvMap());
executeQueue.put(report.getId(), runModeDataDTO);
} catch (Exception ex) {
scenarioIds.remove(item.getId());

View File

@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSON;
import io.metersphere.api.dto.RunModeDataDTO;
import io.metersphere.api.dto.automation.APIScenarioReportResult;
import io.metersphere.api.dto.automation.RunScenarioRequest;
import io.metersphere.api.exec.queue.DBTestQueue;
import io.metersphere.api.exec.utils.GenerateHashTreeUtil;
import io.metersphere.api.jmeter.JMeterService;
import io.metersphere.base.mapper.ApiScenarioReportMapper;
@ -30,7 +31,7 @@ public class ApiScenarioParallelService {
@Resource
private JMeterService jMeterService;
public void parallel(Map<String, RunModeDataDTO> executeQueue, RunScenarioRequest request, String serialReportId, List<MsExecResponseDTO> responseDTOS, String queueId) {
public void parallel(Map<String, RunModeDataDTO> executeQueue, RunScenarioRequest request, String serialReportId, List<MsExecResponseDTO> responseDTOS, DBTestQueue executionQueue) {
if (StringUtils.isEmpty(serialReportId)) {
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
ApiScenarioReportMapper batchMapper = sqlSession.getMapper(ApiScenarioReportMapper.class);
@ -49,13 +50,14 @@ public class ApiScenarioParallelService {
for (String reportId : executeQueue.keySet()) {
JmeterRunRequestDTO runRequest = new JmeterRunRequestDTO(executeQueue.get(reportId).getTestId(), StringUtils.isNotEmpty(serialReportId) ? serialReportId : reportId, request.getRunMode(), executeQueue.get(reportId).getHashTree());
runRequest.setReportType(StringUtils.isNotEmpty(serialReportId) ? RunModeConstants.SET_REPORT.toString() : RunModeConstants.INDEPENDENCE.toString());
runRequest.setQueueId(queueId);
runRequest.setQueueId(executionQueue.getId());
if (request.getConfig() != null) {
runRequest.setPool(GenerateHashTreeUtil.isResourcePool(request.getConfig().getResourcePoolId()));
runRequest.setPoolId(request.getConfig().getResourcePoolId());
}
runRequest.setTestPlanReportId(request.getTestPlanReportId());
runRequest.setHashTree(executeQueue.get(reportId).getHashTree());
runRequest.setPlatformUrl(executionQueue.getDetailMap().get(reportId));
if (LoggerUtil.getLogger().isDebugEnabled()) {
LoggerUtil.debug("Scenario run-开始并发执行:" + JSON.toJSONString(request));
}

View File

@ -100,7 +100,11 @@ public class ApiScenarioSerialService {
}
hashTree = GenerateHashTreeUtil.generateHashTree(scenario, queue.getReportId(), planEnvMap, executionQueue.getReportType());
} else {
hashTree = generateHashTree(queue.getTestId());
Map<String, String> map = new LinkedHashMap<>();
if (StringUtils.isNotEmpty(queue.getEvnMap())) {
map = JSON.parseObject(queue.getEvnMap(), Map.class);
}
hashTree = generateHashTree(queue.getTestId(), queue.getType(), map);
}
// 更新环境变量
this.initEnv(hashTree);
@ -116,6 +120,9 @@ public class ApiScenarioSerialService {
runRequest.setRunType(RunModeConstants.SERIAL.toString());
runRequest.setQueueId(executionQueue.getId());
runRequest.setPoolId(executionQueue.getPoolId());
if (queue != null) {
runRequest.setPlatformUrl(queue.getId());
}
// 开始执行
jMeterService.run(runRequest);
} catch (Exception e) {
@ -145,33 +152,44 @@ public class ApiScenarioSerialService {
hashTreeUtil.mergeParamDataMap(null, envParamsMap);
}
public HashTree generateHashTree(String testId) {
public HashTree generateHashTree(String testId, String type, Map<String, String> envMap) {
ApiTestCaseWithBLOBs caseWithBLOBs = null;
String envId = null;
if (StringUtils.equals(type, ApiRunMode.DEFINITION.name())) {
caseWithBLOBs = apiTestCaseMapper.selectByPrimaryKey(testId);
} else {
TestPlanApiCase apiCase = testPlanApiCaseMapper.selectByPrimaryKey(testId);
if (apiCase != null) {
ApiTestCaseWithBLOBs caseWithBLOBs = apiTestCaseMapper.selectByPrimaryKey(apiCase.getApiCaseId());
caseWithBLOBs = apiTestCaseMapper.selectByPrimaryKey(apiCase.getApiCaseId());
envId = apiCase.getEnvironmentId();
}
}
if (envMap != null && envMap.containsKey(caseWithBLOBs.getProjectId())) {
envId = envMap.get(caseWithBLOBs.getProjectId());
}
if (caseWithBLOBs != null) {
HashTree jmeterHashTree = new HashTree();
MsTestPlan testPlan = new MsTestPlan();
testPlan.setHashTree(new LinkedList<>());
if (caseWithBLOBs != null) {
try {
MsThreadGroup group = new MsThreadGroup();
group.setLabel(caseWithBLOBs.getName());
group.setName(caseWithBLOBs.getName());
MsTestElement testElement = parse(caseWithBLOBs, testId);
MsTestElement testElement = parse(caseWithBLOBs, testId, envId);
group.setHashTree(new LinkedList<>());
group.getHashTree().add(testElement);
testPlan.getHashTree().add(group);
} catch (Exception ex) {
MSException.throwException(ex.getMessage());
}
}
testPlan.toHashTree(jmeterHashTree, testPlan.getHashTree(), new ParameterConfig());
return jmeterHashTree;
}
return null;
}
private MsTestElement parse(ApiTestCaseWithBLOBs caseWithBLOBs, String planId) {
private MsTestElement parse(ApiTestCaseWithBLOBs caseWithBLOBs, String planId, String envId) {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
try {
@ -186,31 +204,38 @@ public class ApiScenarioSerialService {
});
list.addAll(elements);
}
TestPlanApiCase apiCase = testPlanApiCaseMapper.selectByPrimaryKey(planId);
if (element.getString("type").equals("HTTPSamplerProxy")) {
MsHTTPSamplerProxy httpSamplerProxy = JSON.parseObject(api, MsHTTPSamplerProxy.class);
httpSamplerProxy.setHashTree(list);
httpSamplerProxy.setName(planId);
httpSamplerProxy.setUseEnvironment(apiCase.getEnvironmentId());
if (StringUtils.isNotEmpty(envId)) {
httpSamplerProxy.setUseEnvironment(envId);
}
return httpSamplerProxy;
}
if (element.getString("type").equals("TCPSampler")) {
MsTCPSampler msTCPSampler = JSON.parseObject(api, MsTCPSampler.class);
msTCPSampler.setUseEnvironment(apiCase.getEnvironmentId());
if (StringUtils.isNotEmpty(envId)) {
msTCPSampler.setUseEnvironment(envId);
}
msTCPSampler.setHashTree(list);
msTCPSampler.setName(planId);
return msTCPSampler;
}
if (element.getString("type").equals("DubboSampler")) {
MsDubboSampler dubboSampler = JSON.parseObject(api, MsDubboSampler.class);
dubboSampler.setUseEnvironment(apiCase.getEnvironmentId());
if (StringUtils.isNotEmpty(envId)) {
dubboSampler.setUseEnvironment(envId);
}
dubboSampler.setHashTree(list);
dubboSampler.setName(planId);
return dubboSampler;
}
if (element.getString("type").equals("JDBCSampler")) {
MsJDBCSampler jDBCSampler = JSON.parseObject(api, MsJDBCSampler.class);
jDBCSampler.setUseEnvironment(apiCase.getEnvironmentId());
if (StringUtils.isNotEmpty(envId)) {
jDBCSampler.setUseEnvironment(envId);
}
jDBCSampler.setHashTree(list);
jDBCSampler.setName(planId);
return jDBCSampler;

View File

@ -10,13 +10,14 @@ import io.metersphere.commons.constants.ApiRunMode;
import io.metersphere.commons.constants.TriggerMode;
import io.metersphere.commons.utils.CommonBeanFactory;
import io.metersphere.commons.utils.SessionUtils;
import io.metersphere.dto.RunModeConfigDTO;
import org.apache.commons.lang3.StringUtils;
import java.util.Objects;
import java.util.UUID;
public class ApiDefinitionExecResultUtil {
public static ApiDefinitionExecResult initBase(String resourceId, String status, String reportId) {
public static ApiDefinitionExecResult initBase(String resourceId, String status, String reportId, RunModeConfigDTO config) {
ApiDefinitionExecResult apiResult = new ApiDefinitionExecResult();
if (StringUtils.isEmpty(reportId)) {
apiResult.setId(UUID.randomUUID().toString());
@ -28,6 +29,9 @@ public class ApiDefinitionExecResultUtil {
apiResult.setEndTime(System.currentTimeMillis());
apiResult.setTriggerMode(TriggerMode.BATCH.name());
apiResult.setActuator("LOCAL");
if (config != null && GenerateHashTreeUtil.isResourcePool(config.getResourcePoolId()).isPool()) {
apiResult.setActuator(config.getResourcePoolId());
}
apiResult.setUserId(Objects.requireNonNull(SessionUtils.getUser()).getId());
apiResult.setResourceId(resourceId);
apiResult.setStartTime(System.currentTimeMillis());

View File

@ -107,11 +107,15 @@ public class JMeterService {
if (baseInfo != null) {
platformUrl = baseInfo.getUrl();
}
// 临时存放
String queueDetailId = request.getPlatformUrl();
platformUrl += "/api/jmeter/download?testId="
+ request.getTestId()
+ "&reportId=" + request.getReportId()
+ "&runMode=" + request.getRunMode()
+ "&reportType=" + request.getReportType();
+ "&reportType=" + request.getReportType()
+ "&queueId=" + queueDetailId;
request.setPlatformUrl(platformUrl);
request.setKafkaConfig(KafkaConfig.getKafka());

View File

@ -1948,4 +1948,20 @@ public class ApiAutomationService {
example.createCriteria().andRefIdEqualTo(refId).andVersionIdEqualTo(version);
apiScenarioMapper.deleteByExample(example);
}
public List<String> getProjects(RunScenarioRequest request) {
ServiceUtils.getSelectAllIds(request, request.getCondition(),
(query) -> extApiScenarioMapper.selectIdsByQuery(query));
List<String> ids = request.getIds();
ApiScenarioExample example = new ApiScenarioExample();
example.createCriteria().andIdIn(ids);
List<ApiScenario> apiScenarios = apiScenarioMapper.selectByExample(example);
List<String> strings = new LinkedList<>();
apiScenarios.forEach(item -> {
if (!strings.contains(item.getProjectId())) {
strings.add(item.getProjectId());
}
});
return strings;
}
}

View File

@ -90,6 +90,7 @@ public class ApiDefinitionExecResultService {
}
private void sendNotice(ApiDefinitionExecResult result) {
try {
String resourceId = result.getResourceId();
ApiTestCaseWithBLOBs apiTestCaseWithBLOBs = apiTestCaseMapper.selectByPrimaryKey(resourceId);
// 接口定义直接执行不发通知
@ -128,6 +129,9 @@ public class ApiDefinitionExecResultService {
} else {
noticeSendService.send(taskType, noticeModel);
}
} catch (Exception e) {
LogUtil.error(e);
}
}
private String editStatus(String type, String status, Long time, String reportId, String testId) {

View File

@ -26,9 +26,7 @@ import org.mybatis.spring.SqlSessionUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.*;
import java.util.stream.Collectors;
@Service
@ -50,7 +48,7 @@ public class ApiExecutionQueueService {
@Resource
private ExtApiExecutionQueueMapper extApiExecutionQueueMapper;
public DBTestQueue add(Object runObj, String poolId, String type, String reportId, String reportType, String runMode) {
public DBTestQueue add(Object runObj, String poolId, String type, String reportId, String reportType, String runMode, Map<String, String> envMap) {
ApiExecutionQueue executionQueue = new ApiExecutionQueue();
executionQueue.setId(UUID.randomUUID().toString());
executionQueue.setCreateTime(System.currentTimeMillis());
@ -61,19 +59,25 @@ public class ApiExecutionQueueService {
queueMapper.insert(executionQueue);
DBTestQueue resQueue = new DBTestQueue();
BeanUtils.copyBean(resQueue, executionQueue);
Map<String, String> detailMap = new HashMap<>();
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
ApiExecutionQueueDetailMapper batchMapper = sqlSession.getMapper(ApiExecutionQueueDetailMapper.class);
if (StringUtils.equals(type, ApiRunMode.API_PLAN.name())) {
if (StringUtils.equalsAny(type, ApiRunMode.DEFINITION.name(), ApiRunMode.API_PLAN.name())) {
final int[] sort = {0};
Map<TestPlanApiCase, ApiDefinitionExecResult> runMap = (Map<TestPlanApiCase, ApiDefinitionExecResult>) runObj;
Map<String, ApiDefinitionExecResult> runMap = (Map<String, ApiDefinitionExecResult>) runObj;
if (envMap == null) {
envMap = new LinkedHashMap<>();
}
String envStr = JSON.toJSONString(envMap);
runMap.forEach((k, v) -> {
ApiExecutionQueueDetail queue = detail(v.getId(), k.getId(), type, sort[0], executionQueue.getId(), null);
ApiExecutionQueueDetail queue = detail(v.getId(), k, type, sort[0], executionQueue.getId(), envStr);
if (sort[0] == 0) {
resQueue.setQueue(queue);
}
sort[0]++;
batchMapper.insert(queue);
detailMap.put(k, queue.getId());
});
} else {
Map<String, RunModeDataDTO> runMap = (Map<String, RunModeDataDTO>) runObj;
@ -86,12 +90,14 @@ public class ApiExecutionQueueService {
}
sort[0]++;
batchMapper.insert(queue);
detailMap.put(k, queue.getId());
});
}
sqlSession.flushStatements();
if (sqlSession != null && sqlSessionFactory != null) {
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
}
resQueue.setDetailMap(detailMap);
return resQueue;
}
@ -117,7 +123,7 @@ public class ApiExecutionQueueService {
ApiExecutionQueueDetailExample example = new ApiExecutionQueueDetailExample();
example.setOrderByClause("sort asc");
example.createCriteria().andQueueIdEqualTo(id);
List<ApiExecutionQueueDetail> queues = executionQueueDetailMapper.selectByExample(example);
List<ApiExecutionQueueDetail> queues = executionQueueDetailMapper.selectByExampleWithBLOBs(example);
if (CollectionUtils.isNotEmpty(queues)) {
List<ApiExecutionQueueDetail> list = queues.stream().filter(item -> StringUtils.equals(item.getTestId(), testId)).collect(Collectors.toList());
if (CollectionUtils.isNotEmpty(list)) {

View File

@ -6,10 +6,8 @@ import io.metersphere.api.dto.definition.request.MsTestPlan;
import io.metersphere.api.dto.scenario.request.BodyFile;
import io.metersphere.api.exec.scenario.ApiScenarioSerialService;
import io.metersphere.api.exec.utils.GenerateHashTreeUtil;
import io.metersphere.base.domain.ApiScenarioWithBLOBs;
import io.metersphere.base.domain.JarConfig;
import io.metersphere.base.domain.Plugin;
import io.metersphere.base.domain.TestPlanApiScenario;
import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.ApiExecutionQueueDetailMapper;
import io.metersphere.base.mapper.ApiScenarioMapper;
import io.metersphere.base.mapper.TestPlanApiScenarioMapper;
import io.metersphere.commons.constants.ApiRunMode;
@ -43,6 +41,9 @@ public class ApiJmeterFileService {
private TestPlanApiScenarioMapper testPlanApiScenarioMapper;
@Resource
private ApiScenarioMapper apiScenarioMapper;
@Resource
private ApiExecutionQueueDetailMapper executionQueueDetailMapper;
@Resource
private EnvironmentGroupProjectService environmentGroupProjectService;
@ -58,7 +59,7 @@ public class ApiJmeterFileService {
return listBytesToZip(files);
}
public byte[] downloadJmeterFiles(String runMode, String remoteTestId, String reportId, String reportType) {
public byte[] downloadJmeterFiles(String runMode, String remoteTestId, String reportId, String reportType, String queueId) {
Map<String, String> planEnvMap = new HashMap<>();
ApiScenarioWithBLOBs scenario = null;
if (StringUtils.equalsAny(runMode, ApiRunMode.SCENARIO_PLAN.name(), ApiRunMode.JENKINS_SCENARIO_PLAN.name(), ApiRunMode.SCHEDULE_SCENARIO_PLAN.name())) {
@ -76,9 +77,14 @@ public class ApiJmeterFileService {
scenario = apiScenarioMapper.selectByPrimaryKey(planApiScenario.getApiScenarioId());
}
}
ApiExecutionQueueDetail detail = executionQueueDetailMapper.selectByPrimaryKey(queueId);
Map<String, String> envMap = new LinkedHashMap<>();
if (detail != null && StringUtils.isNotEmpty(detail.getEvnMap())) {
envMap = JSON.parseObject(detail.getEvnMap(), Map.class);
}
HashTree hashTree;
if (StringUtils.equalsAnyIgnoreCase(runMode, ApiRunMode.DEFINITION.name(), ApiRunMode.JENKINS_API_PLAN.name(), ApiRunMode.API_PLAN.name(), ApiRunMode.SCHEDULE_API_PLAN.name(), ApiRunMode.MANUAL_PLAN.name())) {
hashTree = apiScenarioSerialService.generateHashTree(remoteTestId);
hashTree = apiScenarioSerialService.generateHashTree(remoteTestId, runMode, envMap);
} else {
if (scenario == null) {
scenario = apiScenarioMapper.selectByPrimaryKey(remoteTestId);
@ -86,7 +92,9 @@ public class ApiJmeterFileService {
if (scenario == null) {
MSException.throwException("未找到执行场景。");
}
if (!StringUtils.equalsAny(runMode, ApiRunMode.SCENARIO_PLAN.name(), ApiRunMode.SCHEDULE_SCENARIO_PLAN.name())) {
if (envMap != null && !envMap.isEmpty()) {
planEnvMap = envMap;
} else if (!StringUtils.equalsAny(runMode, ApiRunMode.SCENARIO_PLAN.name(), ApiRunMode.SCHEDULE_SCENARIO_PLAN.name())) {
String envType = scenario.getEnvironmentType();
String envJson = scenario.getEnvironmentJson();
String envGroupId = scenario.getEnvironmentGroupId();

View File

@ -6,7 +6,6 @@ import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.metersphere.api.dto.ApiCaseEditRequest;
import io.metersphere.api.dto.ApiCaseRunRequest;
import io.metersphere.api.dto.DeleteCheckResult;
import io.metersphere.api.dto.JmxInfoDTO;
import io.metersphere.api.dto.datacount.ApiDataCountResult;
@ -16,20 +15,16 @@ import io.metersphere.api.dto.definition.request.MsTestPlan;
import io.metersphere.api.dto.definition.request.MsThreadGroup;
import io.metersphere.api.dto.definition.request.sampler.MsHTTPSamplerProxy;
import io.metersphere.api.dto.scenario.request.RequestType;
import io.metersphere.api.exec.api.ApiExecuteService;
import io.metersphere.api.exec.utils.ApiDefinitionExecResultUtil;
import io.metersphere.base.domain.*;
import io.metersphere.base.mapper.*;
import io.metersphere.base.mapper.ext.*;
import io.metersphere.commons.constants.APITestStatus;
import io.metersphere.commons.constants.ApiRunMode;
import io.metersphere.commons.constants.MsTestElementConstants;
import io.metersphere.commons.constants.TestPlanStatus;
import io.metersphere.commons.exception.MSException;
import io.metersphere.commons.utils.*;
import io.metersphere.controller.request.OrderRequest;
import io.metersphere.controller.request.ResetOrderRequest;
import io.metersphere.dto.MsExecResponseDTO;
import io.metersphere.i18n.Translator;
import io.metersphere.log.utils.ReflexObjectUtil;
import io.metersphere.log.vo.DetailColumn;
@ -83,8 +78,6 @@ public class ApiTestCaseService {
@Resource
private ApiDefinitionMapper apiDefinitionMapper;
@Resource
private ApiExecuteService apiCaseExecuteService;
@Resource
private ApiDefinitionExecResultMapper apiDefinitionExecResultMapper;
@Resource
private EsbApiParamService esbApiParamService;
@ -746,47 +739,6 @@ public class ApiTestCaseService {
return ids;
}
public void batchRun(ApiCaseRunRequest request) {
apiCaseExecuteService.run(request);
}
public MsExecResponseDTO jenkinsRun(RunCaseRequest request) {
ApiTestCaseWithBLOBs caseWithBLOBs = null;
if (request.getBloBs() == null) {
caseWithBLOBs = apiTestCaseMapper.selectByPrimaryKey(request.getCaseId());
if (caseWithBLOBs == null) {
return null;
}
request.setBloBs(caseWithBLOBs);
} else {
caseWithBLOBs = request.getBloBs();
}
if (caseWithBLOBs == null) {
return null;
}
if (StringUtils.isBlank(request.getEnvironmentId())) {
request.setEnvironmentId(extApiTestCaseMapper.getApiCaseEnvironment(request.getCaseId()));
}
//提前生成报告
ApiDefinitionExecResult report = ApiDefinitionExecResultUtil.add(caseWithBLOBs.getId(), APITestStatus.Running.name(), request.getReportId());
report.setName(caseWithBLOBs.getName());
report.setTriggerMode(ApiRunMode.JENKINS.name());
report.setType(ApiRunMode.JENKINS.name());
apiDefinitionExecResultMapper.insert(report);
//更新接口案例的最后执行状态等信息
caseWithBLOBs.setLastResultId(report.getId());
caseWithBLOBs.setUpdateTime(System.currentTimeMillis());
caseWithBLOBs.setStatus(APITestStatus.Running.name());
apiTestCaseMapper.updateByPrimaryKey(caseWithBLOBs);
request.setReport(report);
if (StringUtils.isEmpty(request.getRunMode())) {
request.setRunMode(ApiRunMode.DEFINITION.name());
}
return apiCaseExecuteService.exec(request);
}
public String getExecResult(String id) {
String status = extApiDefinitionExecResultMapper.selectExecResult(id);
return status;

View File

@ -9,7 +9,7 @@ import io.metersphere.api.dto.definition.ApiTestCaseDTO;
import io.metersphere.api.dto.definition.ApiTestCaseRequest;
import io.metersphere.api.dto.definition.BatchRunDefinitionRequest;
import io.metersphere.api.dto.definition.TestPlanApiCaseDTO;
import io.metersphere.api.exec.api.TestPlanApiExecuteService;
import io.metersphere.api.exec.api.ApiCaseExecuteService;
import io.metersphere.api.service.ApiDefinitionExecResultService;
import io.metersphere.api.service.ApiTestCaseService;
import io.metersphere.base.domain.*;
@ -60,7 +60,7 @@ public class TestPlanApiCaseService {
@Resource
ApiTestCaseMapper apiTestCaseMapper;
@Resource
private TestPlanApiExecuteService testPlanApiCaseExecuteService;
private ApiCaseExecuteService testPlanApiCaseExecuteService;
@Resource
SqlSessionFactory sqlSessionFactory;
@Resource

View File

@ -282,7 +282,7 @@
<batch-edit ref="batchEdit" @batchEdit="batchEdit" :typeArr="typeArr" :value-arr="valueArr"
:dialog-title="$t('test_track.case.batch_edit_case')"/>
<batch-move @refresh="search" @moveSave="moveSave" ref="testBatchMove"/>
<ms-run-mode @handleRunBatch="handleRunBatch" ref="runMode"/>
<ms-run-mode @handleRunBatch="handleRunBatch" :request="runRequest" ref="runMode"/>
<ms-run :debug="true" :environment="projectEnvMap" @runRefresh="runRefresh" :reportId="reportId" :saved="true"
:environment-type="environmentType" :environment-group-id="envGroupId"
:run-data="debugData" ref="runTest"/>
@ -553,7 +553,8 @@ export default {
graphData: {},
environmentType: "",
envGroupId: "",
apiscenariofilters:{},
apiscenariofilters: {},
runRequest: {},
};
},
created() {
@ -880,6 +881,14 @@ export default {
param.condition = this.condition;
},
handleBatchExecute() {
let run = {};
run.id = getUUID();
//
let ids = this.orderBySelectRows();
run.ids = ids;
run.projectId = this.projectId;
run.condition = this.condition;
this.runRequest = run;
this.$refs.runMode.open();
},

View File

@ -2,9 +2,22 @@
<el-dialog
destroy-on-close
:title="$t('load_test.runtime_config')"
width="450px"
width="550px"
:visible.sync="runModeVisible"
>
<div style="margin-bottom: 10px;">
<span class="ms-mode-span">{{ $t("commons.environment") }}</span>
<env-popover :project-ids="projectIds"
:placement="'bottom-start'"
:project-list="projectList"
:project-env-map="projectEnvListMap"
:environment-type.sync="runConfig.environmentType"
:group-id="runConfig.environmentGroupId"
@setEnvGroup="setEnvGroup"
@setProjectEnvMap="setProjectEnvMap"
@showPopover="showPopover"
ref="envPopover" class="env-popover"/>
</div>
<div>
<span class="ms-mode-span">{{ $t("run_mode.title") }}</span>
<el-radio-group v-model="runConfig.mode" @change="changeMode">
@ -58,27 +71,39 @@
<script>
import MsDialogFooter from "@/business/components/common/components/MsDialogFooter";
import {ENV_TYPE} from "@/common/js/constants";
import {strMapToObj} from "@/common/js/utils";
import EnvPopover from "@/business/components/api/automation/scenario/EnvPopover";
export default {
name: "RunMode",
components: {MsDialogFooter},
components: {MsDialogFooter, EnvPopover},
data() {
return {
runModeVisible: false,
testType: null,
resourcePools: [],
runConfig: {
reportName: "",
mode: "serial",
reportType: "iddReport",
reportName: "",
onSampleError: false,
runWithinResourcePool: false,
resourcePoolId: null,
envMap: new Map(),
environmentGroupId: "",
environmentType: ENV_TYPE.JSON
},
projectEnvListMap: {},
projectList: [],
projectIds: new Set(),
};
},
watch:{
'runConfig.runWithinResourcePool'(){
if(!this.runConfig.runWithinResourcePool){
props: ['request'],
watch: {
'runConfig.runWithinResourcePool'() {
if (!this.runConfig.runWithinResourcePool) {
this.runConfig = {
mode: this.runConfig.mode,
reportType: "iddReport",
@ -93,6 +118,7 @@ export default {
open() {
this.runModeVisible = true;
this.getResourcePools();
this.getWsProjects();
},
changeMode() {
this.runConfig.runWithinResourcePool = false;
@ -110,6 +136,11 @@ export default {
};
this.runModeVisible = false;
},
getWsProjects() {
this.$get("/project/listAll", res => {
this.projectList = res.data;
})
},
handleRunBatch() {
if (this.runConfig.mode === 'serial' && this.runConfig.reportType === 'setReport' && this.runConfig.reportName.trim() === "") {
this.$warning(this.$t('commons.input_name'));
@ -123,6 +154,25 @@ export default {
this.resourcePools = response.data;
});
},
setEnvGroup(id) {
this.runConfig.environmentGroupId = id;
},
setProjectEnvMap(projectEnvMap) {
this.runConfig.envMap = strMapToObj(projectEnvMap);
},
showPopover() {
this.projectIds.clear();
let url = "/api/automation/env";
this.$post(url, this.request, res => {
let data = res.data;
if (data) {
for (let d in data) {
this.projectIds.add(data[d]);
}
}
this.$refs.envPopover.openEnvSelect();
});
},
},
};
</script>

View File

@ -0,0 +1,185 @@
<template>
<el-dialog
destroy-on-close
:title="$t('load_test.runtime_config')"
width="550px"
:visible.sync="runModeVisible"
>
<div style="margin-bottom: 10px;">
<span class="ms-mode-span">{{ $t("commons.environment") }}</span>
<env-popover :project-ids="projectIds"
:placement="'bottom-start'"
:project-list="projectList"
:project-env-map="projectEnvListMap"
:environment-type.sync="runConfig.environmentType"
:group-id="runConfig.environmentGroupId"
@setEnvGroup="setEnvGroup"
@setProjectEnvMap="setProjectEnvMap"
@showPopover="showPopover"
ref="envPopover" class="env-popover"/>
</div>
<div>
<span class="ms-mode-span">{{ $t("run_mode.title") }}</span>
<el-radio-group v-model="runConfig.mode" @change="changeMode">
<el-radio label="serial">{{ $t("run_mode.serial") }}</el-radio>
<el-radio label="parallel">{{ $t("run_mode.parallel") }}</el-radio>
</el-radio-group>
</div>
<div class="ms-mode-div" v-if="runConfig.mode === 'serial'">
<el-row>
<el-col :span="6">
<span class="ms-mode-span">{{ $t("run_mode.other_config") }}</span>
</el-col>
<el-col :span="18">
<div>
<el-checkbox v-model="runConfig.onSampleError">{{ $t("api_test.fail_to_stop") }}</el-checkbox>
</div>
<div style="padding-top: 10px">
<el-checkbox v-model="runConfig.runWithinResourcePool" style="padding-right: 10px;">
{{ $t('run_mode.run_with_resource_pool') }}
</el-checkbox>
<el-select :disabled="!runConfig.runWithinResourcePool" v-model="runConfig.resourcePoolId" size="mini">
<el-option
v-for="item in resourcePools"
:key="item.id"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
</div>
</el-col>
</el-row>
</div>
<div class="ms-mode-div" v-if="runConfig.mode === 'parallel'">
<el-row>
<el-col :span="6">
<span class="ms-mode-span">{{ $t("run_mode.other_config") }}</span>
</el-col>
<el-col :span="18">
<el-checkbox v-model="runConfig.runWithinResourcePool" style="padding-right: 10px;">
{{ $t('run_mode.run_with_resource_pool') }}
</el-checkbox>
<el-select :disabled="!runConfig.runWithinResourcePool" v-model="runConfig.resourcePoolId" size="mini">
<el-option
v-for="item in resourcePools"
:key="item.id"
:label="item.name"
:disabled="!item.api"
:value="item.id">
</el-option>
</el-select>
</el-col>
</el-row>
</div>
<template v-slot:footer>
<ms-dialog-footer @cancel="close" @confirm="handleRunBatch"/>
</template>
</el-dialog>
</template>
<script>
import MsDialogFooter from "@/business/components/common/components/MsDialogFooter";
import EnvPopover from "@/business/components/api/automation/scenario/EnvPopover";
import {strMapToObj} from "@/common/js/utils";
import {ENV_TYPE} from "@/common/js/constants";
import {parseEnvironment} from "@/business/components/api/test/model/EnvironmentModel";
export default {
name: "MsApiCaseRunModeWithEnv",
components: {EnvPopover, MsDialogFooter},
data() {
return {
runModeVisible: false,
resourcePools: [],
runConfig: {
mode: "serial",
reportType: "iddReport",
onSampleError: false,
runWithinResourcePool: false,
resourcePoolId: null,
envMap: new Map(),
environmentGroupId: "",
environmentType: ENV_TYPE.JSON
},
projectEnvListMap: {},
projectList: [],
projectIds: new Set(),
};
},
props: ['projectId'],
methods: {
open() {
this.runModeVisible = true;
this.getResourcePools();
this.getWsProjects();
},
changeMode() {
this.runConfig.onSampleError = false;
this.runConfig.runWithinResourcePool = false;
this.runConfig.resourcePoolId = null;
},
close() {
this.runConfig = {
mode: "serial",
reportType: "iddReport",
onSampleError: false,
runWithinResourcePool: false,
resourcePoolId: null,
envMap: new Map(),
environmentGroupId: "",
environmentType: ENV_TYPE.JSON
};
this.runModeVisible = false;
},
handleRunBatch() {
this.$emit("handleRunBatch", this.runConfig);
this.close();
},
getResourcePools() {
this.result = this.$get('/testresourcepool/list/quota/valid', response => {
this.resourcePools = response.data;
});
},
setProjectEnvMap(projectEnvMap) {
this.runConfig.envMap = strMapToObj(projectEnvMap);
},
setEnvGroup(id) {
this.runConfig.environmentGroupId = id;
},
getWsProjects() {
this.$get("/project/listAll", res => {
this.projectList = res.data;
})
},
getEnvironments() {
return new Promise((resolve) => {
if (this.projectId) {
this.$get('/api/environment/list/' + this.projectId, response => {
this.environments = response.data;
this.environments.forEach(environment => {
parseEnvironment(environment);
});
resolve();
});
}
})
},
showPopover() {
this.projectIds.clear();
this.projectIds.add(this.projectId);
this.$refs.envPopover.openEnvSelect();
},
},
};
</script>
<style scoped>
.ms-mode-span {
margin-right: 10px;
}
.ms-mode-div {
margin-top: 20px;
}
</style>

View File

@ -218,10 +218,10 @@
<!--高级搜索-->
<ms-table-adv-search-bar :condition.sync="condition" :showLink="false" ref="searchBar" @search="initTable"/>
<api-case-batch-run :project-id="projectId" @batchRun="runBatch" ref="batchRun"/>
<ms-task-center ref="taskCenter" :show-menu="false"/>
<ms-api-case-run-mode-with-env @handleRunBatch="runBatch" ref="batchRun" :project-id="projectId"/>
<el-dialog :close-on-click-modal="false" :title="$t('test_track.plan_view.test_result')" width="60%"
:visible.sync="resVisible" class="api-import" destroy-on-close @close="resVisible=false">
<ms-request-result-tail :response="response" ref="debugResult"/>
@ -244,6 +244,8 @@ import MsContainer from "../../../../common/components/MsContainer";
import MsBottomContainer from "../BottomContainer";
import ShowMoreBtn from "../../../../track/case/components/ShowMoreBtn";
import MsBatchEdit from "../basis/BatchEdit";
import MsApiCaseRunModeWithEnv from "./ApiCaseRunModeWithEnv";
import {API_METHOD_COLOUR, CASE_PRIORITY, DUBBO_METHOD, REQ_METHOD, SQL_METHOD, TCP_METHOD} from "../../model/JsonData";
import {getBodyUploadFiles, getCurrentProjectID, getUUID, hasLicense, strMapToObj} from "@/common/js/utils";
@ -297,8 +299,9 @@ export default {
MsTable,
MsTableColumn,
MsRequestResultTail,
MsApiCaseRunModeWithEnv,
PlanStatusTableItem: () => import("../../../../track/common/tableItems/plan/PlanStatusTableItem"),
MsTaskCenter: () => import("../../../../task/TaskCenter"),
MsTaskCenter: () => import("@/business/components/task/TaskCenter"),
},
data() {
return {
@ -405,6 +408,7 @@ export default {
timeoutIndex: 0,
versionFilters: [],
versionName: '',
runCaseIds: []
};
},
props: {
@ -559,17 +563,16 @@ export default {
return this.$t('api_test.home_page.detail_card.unexecute');
}
},
handleRunBatch() {
this.$refs.batchRun.open();
},
runBatch(environment) {
runBatch(config) {
let obj = {};
obj.projectId = this.projectId;
obj.selectAllDate = this.selectAll;
obj.unSelectIds = this.unSelection;
obj.ids = Array.from(this.selectRows).map(row => row.id);
obj.environmentId = environment.id;
obj.config = config;
obj.condition = this.condition;
obj.condition.status = "";
this.$post('/api/testcase/batch/run', obj, () => {
@ -580,7 +583,7 @@ export default {
} else {
this.$store.state.currentApiCase = {case: true};
}
this.search();
this.$refs.taskCenter.open();
});
},
customHeader() {