refactor(接口测试): 优化场景执行接口

This commit is contained in:
AgAngle 2024-03-17 19:15:34 +08:00 committed by Craftsman
parent 88a4e8b1e3
commit 70af6c3718
12 changed files with 214 additions and 61 deletions

View File

@ -2,6 +2,7 @@ package io.metersphere.api.controller;
import io.metersphere.api.service.ApiExecuteService;
import io.metersphere.api.service.definition.ApiReportService;
import io.metersphere.api.service.scenario.ApiScenarioReportService;
import io.metersphere.sdk.file.FileRequest;
import io.metersphere.sdk.util.LogUtils;
import jakarta.annotation.Resource;
@ -26,6 +27,8 @@ public class ApiExecuteResourceController {
private ApiExecuteService apiExecuteService;
@Resource
private ApiReportService apiReportService;
@Resource
private ApiScenarioReportService apiScenarioReportService;
/**
* 获取执行脚本
@ -41,6 +44,7 @@ public class ApiExecuteResourceController {
String script = stringRedisTemplate.opsForValue().get(key);
stringRedisTemplate.delete(key);
apiReportService.updateRunningReport(reportId);
apiScenarioReportService.updateRunningReport(reportId);
return Optional.ofNullable(script).orElse(StringUtils.EMPTY);
}

View File

@ -160,11 +160,18 @@ public class ApiScenarioController {
return apiScenarioService.debug(request);
}
@GetMapping("/run/{id}/{reportId}")
@PostMapping("/run")
@Operation(summary = "接口测试-接口场景管理-场景执行")
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_EXECUTE)
public TaskRequestDTO run(@PathVariable String id, @PathVariable String reportId) {
return apiScenarioService.run(id, reportId);
public TaskRequestDTO run(@Validated @RequestBody ApiScenarioDebugRequest request) {
return apiScenarioService.run(request, SessionUtils.getUserId());
}
@GetMapping("/run/{id}")
@Operation(summary = "接口测试-接口场景管理-场景执行")
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_EXECUTE)
public TaskRequestDTO run(@PathVariable String id, @RequestParam(required = false) String reportId) {
return apiScenarioService.run(id, reportId, SessionUtils.getUserId());
}
@GetMapping(value = "/update-status/{id}/{status}")

View File

@ -7,7 +7,6 @@ import jakarta.validation.constraints.Size;
import lombok.Data;
import java.util.List;
import java.util.Map;
/**
* @Author: jianxing
@ -20,8 +19,7 @@ public class ApiScenarioDebugRequest extends ApiScenarioParseParam {
@Size(max = 50, message = "{api_scenario.id.length_range}")
private String id;
@Schema(description = "报告ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank
@Schema(description = "报告ID传了可以实时获取结果不传则不支持实时获取")
@Size(max = 50)
private String reportId;

View File

@ -60,6 +60,19 @@ public class ApiScenarioStepCommonDTO {
@NotBlank
private String projectId;
@Schema(description = "步骤名称")
@NotBlank(message = "{api_scenario_step.name.not_blank}")
@Size(min = 1, max = 255, message = "{api_scenario_step.name.length_range}")
private String name;
@Schema(description = "资源编号")
@Size(min = 1, max = 50, message = "{api_scenario_step.resource_num.length_range}")
private String resourceNum;
@Schema(description = "版本号")
@Size(min = 1, max = 50, message = "{api_scenario_step.version_id.length_range}")
private String versionId;
@Valid
@Schema(description = "子步骤")
private List<? extends ApiScenarioStepCommonDTO> children;

View File

@ -10,15 +10,6 @@ import lombok.Data;
@Data
public class ApiScenarioStepDTO extends ApiScenarioStepCommonDTO {
@Schema(description = "步骤名称")
private String name;
@Schema(description = "资源编号")
private String resourceNum;
@Schema(description = "版本号")
private String versionId;
@Schema(description = "场景id")
private String scenarioId;

View File

@ -1,8 +1,5 @@
package io.metersphere.api.dto.scenario;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
/**
@ -11,16 +8,4 @@ import lombok.Data;
*/
@Data
public class ApiScenarioStepRequest extends ApiScenarioStepCommonDTO {
@Schema(description = "步骤名称")
@NotBlank(message = "{api_scenario_step.name.not_blank}")
@Size(min = 1, max = 255, message = "{api_scenario_step.name.length_range}")
private String name;
@Schema(description = "资源编号")
@Size(min = 1, max = 50, message = "{api_scenario_step.resource_num.length_range}")
private String resourceNum;
@Schema(description = "版本号")
@Size(min = 1, max = 50, message = "{api_scenario_step.version_id.length_range}")
private String versionId;
}

View File

@ -83,9 +83,12 @@ public class MsScenarioConverter extends AbstractJmeterElementConverter<MsScenar
* @param envInfo
*/
private void addArguments(HashTree tree, MsScenario msScenario, EnvironmentInfoDTO envInfo) {
if (envInfo == null) {
return;
}
ScenarioConfig scenarioConfig = msScenario.getScenarioConfig();
ScenarioVariable scenarioVariable = scenarioConfig == null ? new ScenarioVariable() : scenarioConfig.getVariable();
scenarioVariable = scenarioVariable == null ? new ScenarioVariable() : scenarioVariable;
List<CommonVariables> commonVariables = scenarioVariable.getCommonVariables();
List<CommonVariables> envCommonVariables = List.of();

View File

@ -13,7 +13,10 @@ import io.metersphere.api.service.queue.ApiExecutionSetService;
import io.metersphere.api.utils.ApiDataUtils;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.project.service.EnvironmentService;
import io.metersphere.sdk.constants.*;
import io.metersphere.sdk.constants.ApiBatchRunMode;
import io.metersphere.sdk.constants.ApiExecuteResourceType;
import io.metersphere.sdk.constants.ApiExecuteRunMode;
import io.metersphere.sdk.constants.TaskTriggerMode;
import io.metersphere.sdk.dto.api.task.ApiRunModeConfigDTO;
import io.metersphere.sdk.dto.api.task.CollectionReportDTO;
import io.metersphere.sdk.dto.api.task.TaskRequestDTO;
@ -22,7 +25,6 @@ import io.metersphere.sdk.dto.queue.ExecutionQueueDetail;
import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.sdk.util.LogUtils;
import io.metersphere.sdk.util.SubListUtils;
import io.metersphere.system.uid.IDGenerator;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
@ -191,10 +193,9 @@ public class ApiTestCaseBatchRunService {
*/
private void initApiReportSteps(List<String> ids, Map<String, ApiTestCase> apiCaseMap, String reportId) {
AtomicLong sort = new AtomicLong(1);
List<ApiReportStep> apiReportSteps = ids.stream().map(id -> {
ApiReportStep apiReportStep = getApiReportStep(apiCaseMap.get(id), reportId, sort.getAndIncrement());
return apiReportStep;
}).collect(Collectors.toList());
List<ApiReportStep> apiReportSteps = ids.stream()
.map(id -> getApiReportStep(apiCaseMap.get(id), reportId, sort.getAndIncrement()))
.collect(Collectors.toList());
apiReportService.insertApiReportStep(apiReportSteps);
}

View File

@ -675,14 +675,15 @@ public class ApiTestCaseService extends MoveNodeService {
if (StringUtils.isEmpty(taskRequest.getReportId())) {
taskRequest.setRealTime(false);
taskRequest.setReportId(IDGenerator.nextStr());
reportId = IDGenerator.nextStr();
taskRequest.setReportId(reportId);
} else {
// 如果传了报告ID则实时获取结果
taskRequest.setRealTime(true);
}
// 初始化报告
initApiReport(apiTestCase, taskRequest.getReportId(), poolId, userId);
initApiReport(apiTestCase, reportId, poolId, userId);
return doExecute(taskRequest, runRequest, apiTestCase.getEnvironmentId());
}
@ -721,15 +722,12 @@ public class ApiTestCaseService extends MoveNodeService {
* @param userId
* @return
*/
public List<ApiTestCaseRecord> initApiReport(ApiTestCase apiTestCase, String reportId, String poolId, String userId) {
List<ApiReport> apiReports = new ArrayList<>();
List<ApiTestCaseRecord> apiTestCaseRecords = new ArrayList<>();
public ApiTestCaseRecord initApiReport(ApiTestCase apiTestCase, String reportId, String poolId, String userId) {
// 初始化报告
ApiReport apiReport = getApiReport(userId);
apiReport.setId(reportId);
apiReport.setTriggerMode(TaskTriggerMode.MANUAL.name());
apiReports.add(apiReport);
apiReport.setName(apiTestCase.getName());
apiReport.setRunMode(ApiBatchRunMode.PARALLEL.name());
apiReport.setPoolId(poolId);
@ -737,10 +735,9 @@ public class ApiTestCaseService extends MoveNodeService {
// 创建报告和用例的关联关系
ApiTestCaseRecord apiTestCaseRecord = getApiTestCaseRecord(apiTestCase, apiReport);
apiTestCaseRecords.add(apiTestCaseRecord);
apiReportService.insertApiReport(apiReports, apiTestCaseRecords);
return apiTestCaseRecords;
apiReportService.insertApiReport(List.of(apiReport), List.of(apiTestCaseRecord));
return apiTestCaseRecord;
}
public ApiTestCaseRecord getApiTestCaseRecord(ApiTestCase apiTestCase, ApiReport apiReport) {

View File

@ -9,6 +9,7 @@ import io.metersphere.api.dto.scenario.ApiScenarioReportDetailDTO;
import io.metersphere.api.dto.scenario.ApiScenarioReportStepDTO;
import io.metersphere.api.mapper.*;
import io.metersphere.api.utils.ApiDataUtils;
import io.metersphere.sdk.constants.ApiReportStatus;
import io.metersphere.sdk.dto.api.result.RequestResult;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.BeanUtils;
@ -209,5 +210,15 @@ public class ApiScenarioReportService {
return apiReportDetails;
}
/**
* 更新执行中的场景报告
* @param reportId
*/
public void updateRunningReport(String reportId) {
ApiScenarioReport scenarioReport = new ApiScenarioReport();
scenarioReport.setId(reportId);
scenarioReport.setStatus(ApiReportStatus.RUNNING.name());
scenarioReport.setUpdateTime(System.currentTimeMillis());
apiScenarioReportMapper.updateByPrimaryKeySelective(scenarioReport);
}
}

View File

@ -94,6 +94,7 @@ import org.springframework.web.multipart.MultipartFile;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ -102,7 +103,7 @@ import static io.metersphere.api.controller.result.ApiResultCode.API_SCENARIO_EX
@Service
@Transactional(rollbackFor = Exception.class)
public class ApiScenarioService extends MoveNodeService{
public class ApiScenarioService extends MoveNodeService {
@Resource
private ApiScenarioMapper apiScenarioMapper;
@ -179,7 +180,8 @@ public class ApiScenarioService extends MoveNodeService{
private OperationHistoryService operationHistoryService;
@Resource
private ApiCommonService apiCommonService;
@Resource
private ApiScenarioReportService apiScenarioReportService;
public static final String PRIORITY = "Priority";
public static final String STATUS = "Status";
@ -1069,13 +1071,13 @@ public class ApiScenarioService extends MoveNodeService{
taskRequest.setSaveResult(false);
taskRequest.setRealTime(true);
ApiScenarioParamConfig parseConfig = getApiScenarioParamConfig(request, tmpParam);
ApiScenarioParamConfig parseConfig = getApiScenarioParamConfig(request, tmpParam.getScenarioParseEnvInfo());
parseConfig.setReportId(request.getReportId());
return apiExecuteService.execute(runRequest, taskRequest, parseConfig);
}
public TaskRequestDTO run(String id, String reportId) {
public TaskRequestDTO run(String id, String reportId, String userId) {
ApiScenarioDetail apiScenarioDetail = get(id);
// 解析生成待执行的场景树
@ -1090,31 +1092,157 @@ public class ApiScenarioService extends MoveNodeService{
parseParam.setEnvironmentId(apiScenarioDetail.getEnvironmentId());
parseParam.setGrouped(apiScenarioDetail.getGrouped());
ApiScenarioParseTmpParam tmpParam = parse(msScenario, apiScenarioDetail.getSteps(), parseParam);
return executeRun(apiScenarioDetail, msScenario, apiScenarioDetail.getSteps(), parseParam, reportId, userId);
}
public TaskRequestDTO run(ApiScenarioDebugRequest request, String userId) {
ApiScenario apiScenario = apiScenarioMapper.selectByPrimaryKey(request.getId());
// 解析生成待执行的场景树
MsScenario msScenario = new MsScenario();
msScenario.setRefType(ApiScenarioStepRefType.DIRECT.name());
msScenario.setScenarioConfig(getScenarioConfig(request, true));
msScenario.setProjectId(request.getProjectId());
return executeRun(apiScenario, msScenario, request.getSteps(), request, request.getReportId(), userId);
}
public TaskRequestDTO executeRun(ApiScenario apiScenario,
MsScenario msScenario,
List<? extends ApiScenarioStepCommonDTO> steps,
ApiScenarioParseParam parseParam,
String reportId, String userId) {
ApiScenarioParseTmpParam tmpParam = parse(msScenario, steps, parseParam);
ApiResourceRunRequest runRequest = getApiResourceRunRequest(msScenario, tmpParam);
runRequest.setRefResourceIds(tmpParam.getRefResourceIds());
runRequest.setRefProjectIds(tmpParam.getRefProjectIds());
runRequest.setTestElement(msScenario);
TaskRequestDTO taskRequest = getTaskRequest(reportId, id, apiScenarioDetail.getProjectId(), ApiExecuteRunMode.RUN.name());
String poolId = apiExecuteService.getProjectApiResourcePoolId(apiScenario.getProjectId());
TaskRequestDTO taskRequest = getTaskRequest(reportId, apiScenario.getId(), apiScenario.getProjectId(), ApiExecuteRunMode.RUN.name());
taskRequest.getRunModeConfig().setPoolId(poolId);
taskRequest.setSaveResult(true);
taskRequest.setRealTime(true);
taskRequest.getRunModeConfig().setEnvironmentId(parseParam.getEnvironmentId());
ApiScenarioParamConfig parseConfig = getApiScenarioParamConfig(parseParam, tmpParam);
if (StringUtils.isEmpty(taskRequest.getReportId())) {
taskRequest.setRealTime(false);
reportId = IDGenerator.nextStr();
taskRequest.setReportId(reportId);
} else {
// 如果传了报告ID则实时获取结果
taskRequest.setRealTime(true);
}
ApiScenarioParamConfig parseConfig = getApiScenarioParamConfig(parseParam, tmpParam.getScenarioParseEnvInfo());
parseConfig.setReportId(reportId);
// 初始化报告
initApiReport(apiScenario, reportId, poolId, userId);
// 初始化报告步骤
initScenarioReportSteps(steps, taskRequest.getReportId());
return apiExecuteService.execute(runRequest, taskRequest, parseConfig);
}
private ApiScenarioParamConfig getApiScenarioParamConfig(ApiScenarioParseParam request, ApiScenarioParseTmpParam tmpParam) {
/**
* 预生成用例的执行报告
*
* @param apiScenario
* @param poolId
* @param userId
* @return
*/
public ApiScenarioRecord initApiReport(ApiScenario apiScenario, String reportId, String poolId, String userId) {
// 初始化报告
ApiScenarioReport scenarioReport = getScenarioReport(userId);
scenarioReport.setId(reportId);
scenarioReport.setTriggerMode(TaskTriggerMode.MANUAL.name());
scenarioReport.setName(apiScenario.getName());
scenarioReport.setRunMode(ApiBatchRunMode.PARALLEL.name());
scenarioReport.setPoolId(poolId);
scenarioReport.setProjectId(apiScenario.getProjectId());
// 创建报告和用例的关联关系
ApiScenarioRecord scenarioRecord = getApiTestCaseRecord(apiScenario, scenarioReport);
apiScenarioReportService.insertApiScenarioReport(List.of(scenarioReport), List.of(scenarioRecord));
return scenarioRecord;
}
/**
* 初始化场景报告步骤
* @param steps
* @param reportId
*/
private void initScenarioReportSteps(List<? extends ApiScenarioStepCommonDTO> steps, String reportId) {
List<ApiScenarioReportStep> scenarioReportSteps = getScenarioReportSteps(steps, reportId);
apiScenarioReportService.insertApiScenarioReportStep(scenarioReportSteps);
}
/**
* 获取场景报告步骤
*
* @param steps
* @param reportId
*/
private List<ApiScenarioReportStep> getScenarioReportSteps(List<? extends ApiScenarioStepCommonDTO> steps, String reportId) {
AtomicLong sort = new AtomicLong(1);
List<ApiScenarioReportStep> scenarioReportSteps = new ArrayList<>();
for (ApiScenarioStepCommonDTO step : steps) {
scenarioReportSteps.add(getScenarioReportStep(step, reportId, sort.getAndIncrement()));
List<? extends ApiScenarioStepCommonDTO> children = step.getChildren();
if (CollectionUtils.isNotEmpty(children)) {
scenarioReportSteps.addAll(getScenarioReportSteps(steps, reportId));
}
}
return scenarioReportSteps;
}
private ApiScenarioReportStep getScenarioReportStep(ApiScenarioStepCommonDTO step, String reportId, long sort) {
ApiScenarioReportStep scenarioReportStep = new ApiScenarioReportStep();
scenarioReportStep.setReportId(reportId);
scenarioReportStep.setStepId(step.getId());
scenarioReportStep.setSort(sort);
scenarioReportStep.setName(step.getName());
scenarioReportStep.setStepType(ApiExecuteResourceType.API_CASE.name());
return scenarioReportStep;
}
public ApiScenarioRecord getApiTestCaseRecord(ApiScenario apiScenario, ApiScenarioReport scenarioReport) {
ApiScenarioRecord scenarioRecord = new ApiScenarioRecord();
scenarioRecord.setApiScenarioId(apiScenario.getId());
scenarioRecord.setApiScenarioReportId(scenarioReport.getId());
return scenarioRecord;
}
public ApiScenarioReport getScenarioReport(String userId) {
ApiScenarioReport scenarioReport = new ApiScenarioReport();
scenarioReport.setId(IDGenerator.nextStr());
scenarioReport.setDeleted(false);
scenarioReport.setIntegrated(false);
scenarioReport.setStatus(ApiReportStatus.PENDING.name());
scenarioReport.setStartTime(System.currentTimeMillis());
scenarioReport.setUpdateTime(System.currentTimeMillis());
scenarioReport.setUpdateUser(userId);
scenarioReport.setCreateUser(userId);
return scenarioReport;
}
private ApiScenarioParamConfig getApiScenarioParamConfig(ApiScenarioParseParam request, ApiScenarioParseEnvInfo scenarioParseEnvInfo) {
ApiScenarioParamConfig parseConfig = new ApiScenarioParamConfig();
parseConfig.setTestElementClassPluginIdMap(apiPluginService.getTestElementPluginMap());
parseConfig.setTestElementClassProtocalMap(apiPluginService.getTestElementProtocolMap());
parseConfig.setGrouped(request.getGrouped());
if (BooleanUtils.isTrue(request.getGrouped())) {
// 设置环境组 map
parseConfig.setProjectEnvMap(getProjectEnvMap(tmpParam.getScenarioParseEnvInfo(), request.getEnvironmentId()));
parseConfig.setProjectEnvMap(getProjectEnvMap(scenarioParseEnvInfo, request.getEnvironmentId()));
} else {
// 设置环境
parseConfig.setEnvConfig(tmpParam.getScenarioParseEnvInfo().getEnvMap().get(request.getEnvironmentId()));
parseConfig.setEnvConfig(scenarioParseEnvInfo.getEnvMap().get(request.getEnvironmentId()));
}
return parseConfig;
}
@ -1127,6 +1255,14 @@ public class ApiScenarioService extends MoveNodeService{
return runRequest;
}
/**
* 将步骤转换成场景树
* 并保存临时变量
* @param msScenario
* @param steps
* @param parseParam
* @return
*/
public ApiScenarioParseTmpParam parse(MsScenario msScenario,
List<? extends ApiScenarioStepCommonDTO> steps,
ApiScenarioParseParam parseParam) {
@ -1317,6 +1453,7 @@ public class ApiScenarioService extends MoveNodeService{
}
msTestElement.setProjectId(step.getProjectId());
msTestElement.setResourceId(step.getResourceId());
msTestElement.setStepId(step.getId());
// 记录引用的资源ID和项目ID下载执行文件时需要使用
parseParam.getRefProjectIds().add(step.getProjectId());

View File

@ -96,7 +96,8 @@ public class ApiScenarioControllerTests extends BaseTest {
protected static final String DELETE_TO_GC = "delete-to-gc/{0}";
protected static final String STEP_GET = "step/get";
protected static final String DEBUG = "debug";
protected static final String RUN = "run/{0}/{1}";
protected static final String RUN = "run/{0}";
protected static final String RUN_REAL_TIME = "run/{0}?reportId={1}";
private static final String UPDATE_STATUS = "update-status";
private static final String UPDATE_PRIORITY = "update-priority";
@ -1003,10 +1004,15 @@ public class ApiScenarioControllerTests extends BaseTest {
@Order(6)
public void run() throws Exception {
mockPost("/api/run", "");
this.requestGetWithOk(RUN, addApiScenario.getId(), "11111");
this.requestGetWithOk(RUN, addApiScenario.getId());
// @@校验权限
requestGetPermissionTest(PermissionConstants.PROJECT_API_SCENARIO_EXECUTE, RUN, addApiScenario.getId(), "11111");
requestGetPermissionTest(PermissionConstants.PROJECT_API_SCENARIO_EXECUTE, RUN, addApiScenario.getId());
this.requestGetWithOk(RUN_REAL_TIME, addApiScenario.getId(), "reportId");
// @@校验权限
requestGetPermissionTest(PermissionConstants.PROJECT_API_SCENARIO_EXECUTE, RUN_REAL_TIME, addApiScenario.getId(), "reportId");
}
public Plugin addEnvTestPlugin() throws Exception {