feat(接口测试): 场景执行接口

This commit is contained in:
AgAngle 2024-01-22 18:49:53 +08:00 committed by Craftsman
parent 31562ac774
commit 26f9f6726e
23 changed files with 702 additions and 86 deletions

View File

@ -283,7 +283,7 @@ CREATE TABLE IF NOT EXISTS api_scenario_step(
`project_id` VARCHAR(50) COMMENT '项目fk' , `project_id` VARCHAR(50) COMMENT '项目fk' ,
`parent_id` VARCHAR(50) DEFAULT 'NONE' COMMENT '父级fk' , `parent_id` VARCHAR(50) DEFAULT 'NONE' COMMENT '父级fk' ,
`version_id` VARCHAR(50) COMMENT '版本号' , `version_id` VARCHAR(50) COMMENT '版本号' ,
`ref_type` VARCHAR(10) COMMENT '引用/复制/自定义' , `ref_type` VARCHAR(20) COMMENT '引用/复制/自定义' ,
`config` VARCHAR(500) COMMENT '循环等组件基础数据' , `config` VARCHAR(500) COMMENT '循环等组件基础数据' ,
PRIMARY KEY (id) PRIMARY KEY (id)
) ENGINE = InnoDB ) ENGINE = InnoDB

View File

@ -11,13 +11,13 @@ public enum ApiScenarioStepRefType {
*/ */
DIRECT, DIRECT,
/** /**
* 引用 * 完全引用
*/ */
REF, REF,
/** /**
* 步骤引用 * 部分引用
*/ */
STEP_REF, PARTIAL_REF,
/** /**
* 复制 * 复制
*/ */

View File

@ -70,7 +70,7 @@ public class ApiScenarioController {
} }
@PostMapping("/add") @PostMapping("/add")
@Operation(summary = "创建场景") @Operation(summary = "接口测试-接口场景管理-创建场景")
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_ADD) @RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_ADD)
@Log(type = OperationLogType.ADD, expression = "#msClass.addLog(#request)", msClass = ApiScenarioLogService.class) @Log(type = OperationLogType.ADD, expression = "#msClass.addLog(#request)", msClass = ApiScenarioLogService.class)
public ApiScenario add(@Validated @RequestBody ApiScenarioAddRequest request) { public ApiScenario add(@Validated @RequestBody ApiScenarioAddRequest request) {
@ -78,14 +78,14 @@ public class ApiScenarioController {
} }
@PostMapping("/upload/temp/file") @PostMapping("/upload/temp/file")
@Operation(summary = "上传场景所需的文件资源并返回文件ID") @Operation(summary = "接口测试-接口场景管理-上传场景所需的文件资源并返回文件ID")
@RequiresPermissions(logical = Logical.OR, value = {PermissionConstants.PROJECT_API_SCENARIO_ADD, PermissionConstants.PROJECT_API_SCENARIO_UPDATE}) @RequiresPermissions(logical = Logical.OR, value = {PermissionConstants.PROJECT_API_SCENARIO_ADD, PermissionConstants.PROJECT_API_SCENARIO_UPDATE})
public String uploadTempFile(@RequestParam("file") MultipartFile file) { public String uploadTempFile(@RequestParam("file") MultipartFile file) {
return apiScenarioService.uploadTempFile(file); return apiScenarioService.uploadTempFile(file);
} }
@PostMapping("/update") @PostMapping("/update")
@Operation(summary = "更新场景") @Operation(summary = "接口测试-接口场景管理-更新场景")
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_UPDATE) @RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_UPDATE)
@Log(type = OperationLogType.UPDATE, expression = "#msClass.updateLog(#request)", msClass = ApiScenarioLogService.class) @Log(type = OperationLogType.UPDATE, expression = "#msClass.updateLog(#request)", msClass = ApiScenarioLogService.class)
public ApiScenario update(@Validated @RequestBody ApiScenarioUpdateRequest request) { public ApiScenario update(@Validated @RequestBody ApiScenarioUpdateRequest request) {
@ -93,7 +93,7 @@ public class ApiScenarioController {
} }
@GetMapping("/delete/{id}") @GetMapping("/delete/{id}")
@Operation(summary = "删除场景") @Operation(summary = "接口测试-接口场景管理-删除场景")
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_DELETE) @RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_DELETE)
@Log(type = OperationLogType.DELETE, expression = "#msClass.deleteLog(#id)", msClass = ApiScenarioLogService.class) @Log(type = OperationLogType.DELETE, expression = "#msClass.deleteLog(#id)", msClass = ApiScenarioLogService.class)
public void delete(@PathVariable String id) { public void delete(@PathVariable String id) {
@ -101,10 +101,17 @@ public class ApiScenarioController {
} }
@GetMapping("/delete-to-gc/{id}") @GetMapping("/delete-to-gc/{id}")
@Operation(summary = "删除场景到回收站") @Operation(summary = "接口测试-接口场景管理-删除场景到回收站")
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_DELETE) @RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_DELETE)
@Log(type = OperationLogType.DELETE, expression = "#msClass.deleteLog(#id)", msClass = ApiScenarioLogService.class) @Log(type = OperationLogType.DELETE, expression = "#msClass.deleteLog(#id)", msClass = ApiScenarioLogService.class)
public void deleteToGc(@PathVariable String id) { public void deleteToGc(@PathVariable String id) {
apiScenarioService.deleteToGc(id); apiScenarioService.deleteToGc(id);
} }
@PostMapping("/debug")
@Operation(summary = "接口测试-接口场景管理-场景调试")
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_EXECUTE)
public String debug(@RequestBody ApiScenarioDebugRequest request) {
return apiScenarioService.debug(request);
}
} }

View File

@ -18,5 +18,5 @@ public class ApiDebugRunRequest {
private List<String> tempFileIds; private List<String> tempFileIds;
@NotNull @NotNull
@Schema(description = "请求内容") @Schema(description = "请求内容")
private String request; private Object request;
} }

View File

@ -1,5 +1,6 @@
package io.metersphere.api.dto.debug; package io.metersphere.api.dto.debug;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import lombok.Data; import lombok.Data;
import java.util.List; import java.util.List;
@ -20,7 +21,11 @@ public class ApiResourceRunRequest {
*/ */
private String reportId; private String reportId;
/** /**
* 环境ID * 是否为环境组
*/
private Boolean grouped = false;
/**
* 环境或者环境组ID
*/ */
private String environmentId; private String environmentId;
/** /**
@ -33,9 +38,9 @@ public class ApiResourceRunRequest {
*/ */
private String resourceType; private String resourceType;
/** /**
* 请求内容 * 执行组件
*/ */
private String request; private AbstractMsTestElement testElement;
/** /**
* 点击调试时尚未保存的文件列表 * 点击调试时尚未保存的文件列表
*/ */

View File

@ -1,5 +1,6 @@
package io.metersphere.api.dto.request; package io.metersphere.api.dto.request;
import io.metersphere.api.dto.scenario.ScenarioConfig;
import io.metersphere.plugin.api.spi.AbstractMsTestElement; import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
@ -7,4 +8,5 @@ import lombok.EqualsAndHashCode;
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class MsScenario extends AbstractMsTestElement { public class MsScenario extends AbstractMsTestElement {
private ScenarioConfig scenarioConfig;
} }

View File

@ -0,0 +1,48 @@
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;
import java.util.List;
import java.util.Map;
/**
* @Author: jianxing
* @CreateTime: 2024-01-10 11:24
*/
@Data
public class ApiScenarioDebugRequest {
@Schema(description = "id", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_scenario.id.not_blank}")
@Size(max = 50, message = "{api_scenario.id.length_range}")
private String id;
@Schema(description = "是否为环境组")
private Boolean grouped = false;
@Schema(description = "环境或者环境组ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank
private String environmentId;
@Schema(description = "场景的通用配置")
private ScenarioConfig scenarioConfig;
@Schema(description = "步骤集合")
private List<ApiScenarioStepRequest> steps;
/**
* 步骤详情
* key 为步骤ID
* 为详情
*/
@Schema(description = "步骤详情")
private Map<String, Object> stepDetails;
@Schema(description = "项目ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank
private String projectId;
@Schema(description = "点击调试时尚未保存的文件ID列表")
private List<String> tempFileIds;
}

View File

@ -0,0 +1,13 @@
package io.metersphere.api.dto.scenario;
import io.metersphere.api.domain.ApiScenario;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = false)
public class ApiScenarioDetail extends ApiScenario {
@Schema(description = "场景的通用配置")
private ScenarioConfig scenarioConfig;
}

View File

@ -50,7 +50,8 @@ public class ApiScenarioStepRequest {
/** /**
* 引用模式默认完全引用 * 引用模式默认完全引用
* - 完全引用步骤状态不可调整 * - 完全引用步骤状态不可调整
* - 步骤引用步骤状态可调整 * - 部分引用步骤状态可调整
* @see io.metersphere.api.constants.ApiScenarioStepRefType
*/ */
@Schema(description = "引用/复制/自定义") @Schema(description = "引用/复制/自定义")
private String refType; private String refType;

View File

@ -0,0 +1,20 @@
package io.metersphere.api.dto.scenario;
import lombok.Data;
import java.util.Set;
/**
* @Author: jianxing
* @CreateTime: 2024-01-22 15:17
*/
@Data
public class PartialRefStepDetail {
/**
* 记录子步骤中启用的步骤ID
* 不包含部分引用的子步骤
* 部分引用的子步骤也会自行保存子步骤的启用状态
*/
private Set<String> enableStepIds;
// 预留保存其他信息
}

View File

@ -0,0 +1,24 @@
package io.metersphere.api.parser.step;
import io.metersphere.api.dto.scenario.ApiScenarioStepRequest;
import io.metersphere.api.utils.ApiDataUtils;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import org.apache.commons.lang3.StringUtils;
/**
* @Author: jianxing
* @CreateTime: 2024-01-20 15:43
*/
public class ApiCaseStepParser extends StepParser {
@Override
public AbstractMsTestElement parse(ApiScenarioStepRequest step, String resourceBlob, String stepDetail) {
if (isRef(step.getRefType())) {
return StringUtils.isBlank(resourceBlob) ? null : parse2MsTestElement(resourceBlob);
} else {
if (StringUtils.isBlank(stepDetail)) {
return null;
}
return StringUtils.isBlank(stepDetail) ? null : ApiDataUtils.parseObject(stepDetail, AbstractMsTestElement.class);
}
}
}

View File

@ -0,0 +1,29 @@
package io.metersphere.api.parser.step;
import io.metersphere.api.dto.request.MsScenario;
import io.metersphere.api.dto.scenario.ApiScenarioStepRequest;
import io.metersphere.api.dto.scenario.ScenarioConfig;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.sdk.util.JSON;
import org.apache.commons.lang3.StringUtils;
/**
* @Author: jianxing
* @CreateTime: 2024-01-20 15:43
*/
public class ApiScenarioStepParser extends StepParser {
@Override
public AbstractMsTestElement parse(ApiScenarioStepRequest step, String resourceBlob, String stepDetail) {
MsScenario msScenario = new MsScenario();
if (isRef(step.getRefType())) {
if (StringUtils.isNotBlank(resourceBlob)) {
msScenario.setScenarioConfig(JSON.parseObject(resourceBlob, ScenarioConfig.class));
}
} else {
if (StringUtils.isNotBlank(stepDetail)) {
msScenario.setScenarioConfig(JSON.parseObject(stepDetail, ScenarioConfig.class));
}
}
return msScenario;
}
}

View File

@ -0,0 +1,89 @@
package io.metersphere.api.parser.step;
import io.metersphere.api.dto.request.http.KeyValueParam;
import io.metersphere.api.dto.request.http.MsHTTPElement;
import io.metersphere.api.dto.request.http.body.Body;
import io.metersphere.api.dto.scenario.ApiScenarioStepRequest;
import io.metersphere.api.utils.ApiDataUtils;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.List;
/**
* @Author: jianxing
* @CreateTime: 2024-01-20 15:43
*/
public class ApiStepParser extends StepParser {
@Override
public AbstractMsTestElement parse(ApiScenarioStepRequest step, String resourceBlob, String stepDetail) {
if (isRef(step.getRefType())) {
if (StringUtils.isBlank(resourceBlob)) {
return null;
}
AbstractMsTestElement refResourceElement = parse2MsTestElement(resourceBlob);
if (refResourceElement instanceof MsHTTPElement && StringUtils.isNotBlank(stepDetail)) {
// 如果是 http 并且有修改请求参数则替换请求参数
AbstractMsTestElement msTestElement = ApiDataUtils.parseObject(stepDetail, AbstractMsTestElement.class);
return replaceParams((MsHTTPElement) msTestElement, (MsHTTPElement) refResourceElement);
} else {
return refResourceElement;
}
} else {
return StringUtils.isBlank(stepDetail) ? null : ApiDataUtils.parseObject(stepDetail, AbstractMsTestElement.class);
}
}
private AbstractMsTestElement replaceParams(MsHTTPElement msTestElement, MsHTTPElement refResourceElement) {
replaceKvParam(msTestElement.getHeaders(), refResourceElement.getHeaders());
replaceKvParam(msTestElement.getQuery(), refResourceElement.getQuery());
replaceKvParam(msTestElement.getRest(), refResourceElement.getRest());
replaceBodyParams(msTestElement.getBody(), refResourceElement.getBody());
return refResourceElement;
}
/**
* 替换请求体中的参数
*
* @param valueBody
* @param refBody
*/
private void replaceBodyParams(Body valueBody, Body refBody) {
if (refBody == null || valueBody == null) {
return;
}
if (StringUtils.equals(refBody.getBodyType(), Body.BodyType.FORM_DATA.name()) &&
valueBody.getFormDataBody() != null && refBody.getFormDataBody() != null) {
replaceKvParam(valueBody.getFormDataBody().getFromValues(), valueBody.getFormDataBody().getFromValues());
}
if (StringUtils.equals(refBody.getBodyType(), Body.BodyType.WWW_FORM.name()) &&
valueBody.getWwwFormBody() != null && refBody.getWwwFormBody() != null) {
replaceKvParam(valueBody.getWwwFormBody().getFromValues(), valueBody.getWwwFormBody().getFromValues());
}
// todo JsonSchema body
}
/**
* 替换参数
*
* @param valueList
* @param refList
*/
private void replaceKvParam(List valueList, List refList) {
if (CollectionUtils.isEmpty(refList) || CollectionUtils.isEmpty(valueList)) {
return;
}
refList.forEach(item -> {
KeyValueParam keyValueParam = (KeyValueParam) item;
for (Object valueItem : valueList) {
KeyValueParam valueParam = (KeyValueParam) valueItem;
if (StringUtils.equals(keyValueParam.getKey(), valueParam.getKey())) {
keyValueParam.setValue(valueParam.getValue());
break;
}
}
});
}
}

View File

@ -0,0 +1,24 @@
package io.metersphere.api.parser.step;
import io.metersphere.api.constants.ApiScenarioStepRefType;
import io.metersphere.api.dto.scenario.ApiScenarioStepRequest;
import io.metersphere.api.utils.ApiDataUtils;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import org.apache.commons.lang3.StringUtils;
/**
* @Author: jianxing
* @CreateTime: 2024-01-20 15:43
*/
public abstract class StepParser {
public abstract AbstractMsTestElement parse(ApiScenarioStepRequest step, String resourceBlob, String stepDetail);
protected boolean isRef(String refType) {
return StringUtils.equalsAny(refType, ApiScenarioStepRefType.REF.name(), ApiScenarioStepRefType.PARTIAL_REF.name());
}
protected static AbstractMsTestElement parse2MsTestElement(String blobContent) {
return ApiDataUtils.parseObject(blobContent, AbstractMsTestElement.class);
}
}

View File

@ -0,0 +1,25 @@
package io.metersphere.api.parser.step;
import io.metersphere.api.constants.ApiScenarioStepType;
import java.util.HashMap;
import java.util.Map;
/**
* @Author: jianxing
* @CreateTime: 2024-01-20 15:43
*/
public abstract class StepParserFactory {
private static Map<String, StepParser> stepParserMap = new HashMap<>();
static {
stepParserMap.put(ApiScenarioStepType.API.name(), new ApiStepParser());
stepParserMap.put(ApiScenarioStepType.API_CASE.name(), new ApiCaseStepParser());
stepParserMap.put(ApiScenarioStepType.API_SCENARIO.name(), new ApiScenarioStepParser());
}
public static StepParser getStepParser(String stepType) {
return stepParserMap.get(stepType);
}
}

View File

@ -131,7 +131,7 @@ public class ApiExecuteService {
// todo 接口用例 method 获取定义中的数据库字段 // todo 接口用例 method 获取定义中的数据库字段
ParameterConfig parameterConfig = new ParameterConfig(); ParameterConfig parameterConfig = new ParameterConfig();
parameterConfig.setReportId(reportId); parameterConfig.setReportId(reportId);
String executeScript = parseExecuteScript(request.getRequest(), parameterConfig); String executeScript = parseExecuteScript(request.getTestElement(), parameterConfig);
TestResourceNodeDTO testResourceNodeDTO = getProjectExecuteNode(request.getProjectId()); TestResourceNodeDTO testResourceNodeDTO = getProjectExecuteNode(request.getProjectId());
@ -306,15 +306,10 @@ public class ApiExecuteService {
/** /**
* 生成执行脚本 * 生成执行脚本
* *
* @param testElementStr * @param msTestElement
* @param config * @param config
* @return * @return
*/ */
private static String parseExecuteScript(String testElementStr, ParameterConfig config) {
// 解析生成脚本
return parseExecuteScript(ApiDataUtils.parseObject(testElementStr, AbstractMsTestElement.class), config);
}
private static String parseExecuteScript(AbstractMsTestElement msTestElement, ParameterConfig config) { private static String parseExecuteScript(AbstractMsTestElement msTestElement, ParameterConfig config) {
// 解析生成脚本 // 解析生成脚本
TestElementParser defaultParser = TestElementParserFactory.getDefaultParser(); TestElementParser defaultParser = TestElementParserFactory.getDefaultParser();

View File

@ -18,6 +18,7 @@ import io.metersphere.sdk.constants.DefaultRepositoryDir;
import io.metersphere.sdk.exception.MSException; import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.BeanUtils; import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.sdk.util.FileAssociationSourceUtil; import io.metersphere.sdk.util.FileAssociationSourceUtil;
import io.metersphere.sdk.util.JSON;
import io.metersphere.system.log.constants.OperationLogModule; import io.metersphere.system.log.constants.OperationLogModule;
import io.metersphere.system.uid.IDGenerator; import io.metersphere.system.uid.IDGenerator;
import io.metersphere.system.utils.ServiceUtils; import io.metersphere.system.utils.ServiceUtils;
@ -187,6 +188,8 @@ public class ApiDebugService {
runRequest.setReportId(id); runRequest.setReportId(id);
runRequest.setResourceType(ApiResourceType.API_DEBUG.name()); runRequest.setResourceType(ApiResourceType.API_DEBUG.name());
runRequest.setRunMode(ApiExecuteRunMode.BACKEND_DEBUG.name()); runRequest.setRunMode(ApiExecuteRunMode.BACKEND_DEBUG.name());
runRequest.setEnvironmentId(request.getEnvironmentId());
runRequest.setTestElement(ApiDataUtils.parseObject(JSON.toJSONString(request.getRequest()), AbstractMsTestElement.class));
apiExecuteService.debug(runRequest); apiExecuteService.debug(runRequest);

View File

@ -1011,4 +1011,13 @@ public class ApiDefinitionService {
// 记录操作日志 // 记录操作日志
return apiDefinitionLogService.recoverOperationHistoryLog(apiDefinitionDTO, apiDefinition.getCreateUser(), apiDefinition.getProjectId()); return apiDefinitionLogService.recoverOperationHistoryLog(apiDefinitionDTO, apiDefinition.getCreateUser(), apiDefinition.getProjectId());
} }
public List<ApiDefinitionBlob> getBlobByIds(List<String> apiIds) {
if (CollectionUtils.isEmpty(apiIds)) {
return Collections.emptyList();
}
ApiDefinitionBlobExample apiDefinitionBlobExample = new ApiDefinitionBlobExample();
apiDefinitionBlobExample.createCriteria().andIdIn(apiIds);
return apiDefinitionBlobMapper.selectByExampleWithBLOBs(apiDefinitionBlobExample);
}
} }

View File

@ -40,6 +40,7 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Function; import java.util.function.Function;
@ -529,4 +530,13 @@ public class ApiTestCaseService {
} }
} }
} }
public List<ApiTestCaseBlob> getBlobByIds(List<String> apiCaseIds) {
if (CollectionUtils.isEmpty(apiCaseIds)) {
return Collections.emptyList();
}
ApiTestCaseBlobExample example = new ApiTestCaseBlobExample();
example.createCriteria().andIdIn(apiCaseIds);
return apiTestCaseBlobMapper.selectByExampleWithBLOBs(example);
}
} }

View File

@ -5,11 +5,20 @@ import io.metersphere.api.constants.ApiScenarioStepRefType;
import io.metersphere.api.constants.ApiScenarioStepType; import io.metersphere.api.constants.ApiScenarioStepType;
import io.metersphere.api.domain.*; import io.metersphere.api.domain.*;
import io.metersphere.api.dto.debug.ApiFileResourceUpdateRequest; import io.metersphere.api.dto.debug.ApiFileResourceUpdateRequest;
import io.metersphere.api.dto.debug.ApiResourceRunRequest;
import io.metersphere.api.dto.request.MsScenario;
import io.metersphere.api.dto.scenario.*; import io.metersphere.api.dto.scenario.*;
import io.metersphere.api.mapper.*; import io.metersphere.api.mapper.*;
import io.metersphere.api.parser.step.StepParser;
import io.metersphere.api.parser.step.StepParserFactory;
import io.metersphere.api.service.ApiExecuteService;
import io.metersphere.api.service.ApiFileResourceService; import io.metersphere.api.service.ApiFileResourceService;
import io.metersphere.api.service.definition.ApiDefinitionService;
import io.metersphere.api.service.definition.ApiTestCaseService;
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.metersphere.project.mapper.ExtBaseProjectVersionMapper; import io.metersphere.project.mapper.ExtBaseProjectVersionMapper;
import io.metersphere.project.service.ProjectService; import io.metersphere.project.service.ProjectService;
import io.metersphere.sdk.constants.ApiExecuteRunMode;
import io.metersphere.sdk.constants.ApplicationNumScope; import io.metersphere.sdk.constants.ApplicationNumScope;
import io.metersphere.sdk.constants.DefaultRepositoryDir; import io.metersphere.sdk.constants.DefaultRepositoryDir;
import io.metersphere.sdk.domain.Environment; import io.metersphere.sdk.domain.Environment;
@ -45,6 +54,7 @@ import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import static io.metersphere.api.controller.result.ApiResultCode.API_SCENARIO_EXIST; import static io.metersphere.api.controller.result.ApiResultCode.API_SCENARIO_EXIST;
@Service @Service
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public class ApiScenarioService { public class ApiScenarioService {
@ -84,6 +94,12 @@ public class ApiScenarioService {
private ApiScenarioStepBlobMapper apiScenarioStepBlobMapper; private ApiScenarioStepBlobMapper apiScenarioStepBlobMapper;
@Resource @Resource
private ApiScenarioBlobMapper apiScenarioBlobMapper; private ApiScenarioBlobMapper apiScenarioBlobMapper;
@Resource
private ApiExecuteService apiExecuteService;
@Resource
private ApiDefinitionService apiDefinitionService;
@Resource
private ApiTestCaseService apiTestCaseService;
public static final String PRIORITY = "Priority"; public static final String PRIORITY = "Priority";
public static final String STATUS = "Status"; public static final String STATUS = "Status";
public static final String TAGS = "Tags"; public static final String TAGS = "Tags";
@ -284,13 +300,16 @@ public class ApiScenarioService {
// 插入步骤 // 插入步骤
if (CollectionUtils.isNotEmpty(request.getSteps())) { if (CollectionUtils.isNotEmpty(request.getSteps())) {
List<ApiScenarioStepBlob> apiScenarioStepsDetails = new ArrayList<>(); // 获取待添加的步骤
List<ApiScenarioStep> apiScenarioSteps = getUpdateApiScenarioSteps(null, request.getSteps(), apiScenarioStepsDetails); List<ApiScenarioStep> steps = getApiScenarioSteps(null, request.getSteps());
apiScenarioStepsDetails.addAll(getUpdateStepDetails(apiScenarioSteps, request.getStepDetails())); steps.forEach(step -> step.setScenarioId(scenario.getId()));
apiScenarioSteps.forEach(step -> step.setScenarioId(scenario.getId())); // 获取待添加的步骤详情
List<ApiScenarioStepBlob> apiScenarioStepsDetails = getPartialRefStepDetails(request.getSteps());
apiScenarioStepsDetails.addAll(getUpdateStepDetails(steps, request.getStepDetails()));
apiScenarioStepsDetails.forEach(step -> step.setScenarioId(scenario.getId())); apiScenarioStepsDetails.forEach(step -> step.setScenarioId(scenario.getId()));
if (CollectionUtils.isNotEmpty(apiScenarioSteps)) {
apiScenarioStepMapper.batchInsert(apiScenarioSteps); if (CollectionUtils.isNotEmpty(steps)) {
apiScenarioStepMapper.batchInsert(steps);
} }
if (CollectionUtils.isNotEmpty(apiScenarioStepsDetails)) { if (CollectionUtils.isNotEmpty(apiScenarioStepsDetails)) {
apiScenarioStepBlobMapper.batchInsert(apiScenarioStepsDetails); apiScenarioStepBlobMapper.batchInsert(apiScenarioStepsDetails);
@ -359,6 +378,7 @@ public class ApiScenarioService {
/** /**
* 更新场景步骤 * 更新场景步骤
*
* @param request * @param request
* @param scenario * @param scenario
*/ */
@ -372,10 +392,13 @@ public class ApiScenarioService {
return; return;
} }
List<ApiScenarioStepBlob> apiScenarioStepsDetails = new ArrayList<>(); // 获取待更新的步骤
List<ApiScenarioStep> apiScenarioSteps = getUpdateApiScenarioSteps(null, request.getSteps(), apiScenarioStepsDetails); List<ApiScenarioStep> apiScenarioSteps = getApiScenarioSteps(null, request.getSteps());
apiScenarioStepsDetails.addAll(getUpdateStepDetails(apiScenarioSteps, request.getStepDetails()));
apiScenarioSteps.forEach(step -> step.setScenarioId(scenario.getId())); apiScenarioSteps.forEach(step -> step.setScenarioId(scenario.getId()));
// 获取待更新的步骤详情
List<ApiScenarioStepBlob> apiScenarioStepsDetails = getPartialRefStepDetails(request.getSteps());
apiScenarioStepsDetails.addAll(getUpdateStepDetails(apiScenarioSteps, request.getStepDetails()));
apiScenarioStepsDetails.forEach(step -> step.setScenarioId(scenario.getId())); apiScenarioStepsDetails.forEach(step -> step.setScenarioId(scenario.getId()));
List<String> stepIds = apiScenarioSteps.stream().map(ApiScenarioStep::getId).collect(Collectors.toList()); List<String> stepIds = apiScenarioSteps.stream().map(ApiScenarioStep::getId).collect(Collectors.toList());
@ -439,6 +462,7 @@ public class ApiScenarioService {
/** /**
* 获取待更新的 ApiScenarioStepBlob 列表 * 获取待更新的 ApiScenarioStepBlob 列表
*
* @param apiScenarioSteps * @param apiScenarioSteps
* @param stepDetails * @param stepDetails
* @return * @return
@ -457,8 +481,7 @@ public class ApiScenarioService {
if (step == null) { if (step == null) {
return; return;
} }
boolean isRef = StringUtils.equalsAny(step.getRefType(), ApiScenarioStepRefType.REF.name(), ApiScenarioStepRefType.STEP_REF.name()); if (!isRef(step.getRefType()) || StringUtils.equals(step.getRefType(), ApiScenarioStepType.API.name())) {
if (!isRef || StringUtils.equals(step.getRefType(), ApiScenarioStepType.API.name())) {
// 非引用的步骤如果有编辑内容保存到blob表 // 非引用的步骤如果有编辑内容保存到blob表
// 如果引用的是接口定义也保存详情因为应用接口定义允许修改参数值 // 如果引用的是接口定义也保存详情因为应用接口定义允许修改参数值
ApiScenarioStepBlob apiScenarioStepBlob = new ApiScenarioStepBlob(); ApiScenarioStepBlob apiScenarioStepBlob = new ApiScenarioStepBlob();
@ -470,17 +493,20 @@ public class ApiScenarioService {
return apiScenarioStepsDetails; return apiScenarioStepsDetails;
} }
private boolean isRef(String refType) {
return StringUtils.equalsAny(refType, ApiScenarioStepRefType.REF.name(), ApiScenarioStepRefType.PARTIAL_REF.name());
}
/** /**
* 解析步骤树结构 * 解析步骤树结构
* 获取待更新的 ApiScenarioStep 列表 * 获取待更新的 ApiScenarioStep 列表
*
* @param parent * @param parent
* @param steps * @param steps
* @param apiScenarioStepsDetails
* @return * @return
*/ */
private List<ApiScenarioStep> getUpdateApiScenarioSteps(ApiScenarioStepRequest parent, private List<ApiScenarioStep> getApiScenarioSteps(ApiScenarioStepRequest parent,
List<ApiScenarioStepRequest> steps, List<ApiScenarioStepRequest> steps) {
List<ApiScenarioStepBlob> apiScenarioStepsDetails) {
if (CollectionUtils.isEmpty(steps)) { if (CollectionUtils.isEmpty(steps)) {
return Collections.emptyList(); return Collections.emptyList();
@ -499,25 +525,49 @@ public class ApiScenarioService {
} }
apiScenarioSteps.add(apiScenarioStep); apiScenarioSteps.add(apiScenarioStep);
if (StringUtils.equals(step.getRefType(), ApiScenarioStepRefType.STEP_REF.name())) { if (StringUtils.equalsAny(step.getRefType(), ApiScenarioStepRefType.REF.name(), ApiScenarioStepRefType.PARTIAL_REF.name())) {
// 如果是步骤引用blob表保存启用的子步骤ID
Set<String> enableStepSet = getEnableStepSet(step.getChildren());
ApiScenarioStepBlob apiScenarioStepBlob = new ApiScenarioStepBlob();
apiScenarioStepBlob.setId(apiScenarioStep.getId());
apiScenarioStepBlob.setContent(JSON.toJSONString(enableStepSet).getBytes());
apiScenarioStepsDetails.add(apiScenarioStepBlob);
}
if (StringUtils.equalsAny(step.getRefType(), ApiScenarioStepRefType.REF.name(), ApiScenarioStepRefType.STEP_REF.name())) {
// 引用的步骤不解析子步骤 // 引用的步骤不解析子步骤
continue; continue;
} }
// 解析子步骤 // 解析子步骤
apiScenarioSteps.addAll(getUpdateApiScenarioSteps(step, step.getChildren(), apiScenarioStepsDetails)); apiScenarioSteps.addAll(getApiScenarioSteps(step, step.getChildren()));
} }
return apiScenarioSteps; return apiScenarioSteps;
} }
/**
* 解析步骤树结构
* 获取待更新的 ApiScenarioStep 列表
*
* @param steps
* @return
*/
private List<ApiScenarioStepBlob> getPartialRefStepDetails(List<ApiScenarioStepRequest> steps) {
if (CollectionUtils.isEmpty(steps)) {
return Collections.emptyList();
}
List<ApiScenarioStepBlob> apiScenarioStepsDetails = new ArrayList<>();
for (ApiScenarioStepRequest step : steps) {
if (StringUtils.equals(step.getRefType(), ApiScenarioStepRefType.REF.name())) {
// 引用的步骤不解析子步骤
continue;
}
if (StringUtils.equals(step.getRefType(), ApiScenarioStepRefType.PARTIAL_REF.name())) {
// 如果是部分引用blob表保存启用的子步骤ID
Set<String> enableStepSet = getEnableStepSet(step.getChildren());
PartialRefStepDetail stepDetail = new PartialRefStepDetail();
stepDetail.setEnableStepIds(enableStepSet);
ApiScenarioStepBlob apiScenarioStepBlob = new ApiScenarioStepBlob();
apiScenarioStepBlob.setId(step.getId());
apiScenarioStepBlob.setContent(JSON.toJSONString(stepDetail).getBytes());
apiScenarioStepsDetails.add(apiScenarioStepBlob);
}
apiScenarioStepsDetails.addAll(getPartialRefStepDetails(step.getChildren()));
}
return apiScenarioStepsDetails;
}
/** /**
* 获取步骤及子步骤中 enable 的步骤ID * 获取步骤及子步骤中 enable 的步骤ID
* *
@ -533,8 +583,11 @@ public class ApiScenarioService {
if (BooleanUtils.isTrue(step.getEnable())) { if (BooleanUtils.isTrue(step.getEnable())) {
enableSteps.add(step.getId()); enableSteps.add(step.getId());
} }
// 获取子步骤中 enable = true 的步骤 // 完全引用和部分引用不解析子步骤
enableSteps.addAll(getEnableStepSet(step.getChildren())); if (!isRef(step.getRefType())) {
// 获取子步骤中 enable 的步骤
enableSteps.addAll(getEnableStepSet(step.getChildren()));
}
} }
return enableSteps; return enableSteps;
} }
@ -609,4 +662,210 @@ public class ApiScenarioService {
public String uploadTempFile(MultipartFile file) { public String uploadTempFile(MultipartFile file) {
return apiFileResourceService.uploadTempFile(file); return apiFileResourceService.uploadTempFile(file);
} }
public String debug(ApiScenarioDebugRequest request) {
ApiScenario apiScenario = apiScenarioMapper.selectByPrimaryKey(request.getId());
boolean hasSave = apiScenario != null;
String reportId = IDGenerator.nextStr();
List<ApiScenarioStepRequest> steps = request.getSteps();
// 记录引用的资源ID
Map<String, List<String>> refResourceMap = new HashMap<>();
buildRefResourceIdMap(steps, refResourceMap);
// 查询引用的资源详情
Map<String, String> resourceBlobMap = getResourceBlobMap(refResourceMap);
// 查询复制的步骤详情
Map<String, String> detailMap = getStepDetailMap(steps, request.getStepDetails());
// 解析生成待执行的场景树
MsScenario msScenario = new MsScenario();
msScenario.setScenarioConfig(getScenarioConfig(request, hasSave));
parseStep2MsElement(msScenario, steps, resourceBlobMap, detailMap);
ApiResourceRunRequest runRequest = BeanUtils.copyBean(new ApiResourceRunRequest(), request);
runRequest.setProjectId(request.getProjectId());
runRequest.setTestId(request.getId());
runRequest.setReportId(reportId);
runRequest.setResourceType(ApiResourceType.API_SCENARIO.name());
runRequest.setRunMode(ApiExecuteRunMode.BACKEND_DEBUG.name());
runRequest.setTempFileIds(request.getTempFileIds());
runRequest.setGrouped(request.getGrouped());
runRequest.setEnvironmentId(request.getEnvironmentId());
runRequest.setTestElement(msScenario);
apiExecuteService.debug(runRequest);
return reportId;
}
/**
* 将步骤解析成 MsTestElement 树结构
*
* @param parentElement
* @param steps
* @param resourceBlobMap
* @param stepDetailMap
*/
private void parseStep2MsElement(AbstractMsTestElement parentElement,
List<ApiScenarioStepRequest> steps,
Map<String, String> resourceBlobMap,
Map<String, String> stepDetailMap) {
if (CollectionUtils.isNotEmpty(steps)) {
parentElement.setChildren(new LinkedList<>());
}
for (ApiScenarioStepRequest step : steps) {
StepParser stepParser = StepParserFactory.getStepParser(step.getStepType());
if (stepParser == null || BooleanUtils.isFalse(step.getEnable())) {
continue;
}
if (StringUtils.equals(step.getStepType(), ApiScenarioStepRefType.PARTIAL_REF.name())) {
setPartialRefStepEnable(step, stepDetailMap);
}
// 将步骤详情解析生成对应的MsTestElement
AbstractMsTestElement msTestElement = stepParser.parse(step, resourceBlobMap.get(step.getResourceId()), stepDetailMap.get(step.getId()));
if (msTestElement != null) {
parentElement.getChildren().add(msTestElement);
}
if (CollectionUtils.isNotEmpty(step.getChildren())) {
parseStep2MsElement(msTestElement, step.getChildren(), resourceBlobMap, stepDetailMap);
}
}
}
/**
* 设置部分引用的步骤的启用状态
* @param step
* @param stepDetailMap
*/
private void setPartialRefStepEnable(ApiScenarioStepRequest step, Map<String, String> stepDetailMap) {
String stepDetail = stepDetailMap.get(step.getId());
if (StringUtils.isBlank(stepDetail)) {
return;
}
PartialRefStepDetail partialRefStepDetail = JSON.parseObject(stepDetail, PartialRefStepDetail.class);
setChildPartialRefEnable(step.getChildren(), partialRefStepDetail.getEnableStepIds(), stepDetailMap);
}
/**
* 设置部分引用的步骤的启用状态
* @param steps
* @param enableStepIds
* @param stepDetailMap
*/
private void setChildPartialRefEnable(List<ApiScenarioStepRequest> steps, Set<String> enableStepIds, Map<String, String> stepDetailMap) {
for (ApiScenarioStepRequest step : steps) {
if (StringUtils.equals(step.getRefType(), ApiScenarioStepRefType.REF.name())) {
// 引用的启用不修改
continue;
}
// 非完全引用的步骤使用当前场景配置的启用状态
step.setEnable(enableStepIds.contains(step.getId()));
if (StringUtils.equals(step.getRefType(), ApiScenarioStepRefType.PARTIAL_REF.name())) {
// 如果是部分引用的场景重新获取详情再解析
setPartialRefStepEnable(step, stepDetailMap);
continue;
}
// 非完全引用和部分引用的步骤递归设置子步骤
if (CollectionUtils.isNotEmpty(step.getChildren())) {
setChildPartialRefEnable(step.getChildren(), enableStepIds, stepDetailMap);
}
}
}
private Map<String, String> getStepDetailMap(List<ApiScenarioStepRequest> steps, Map<String, Object> stepDetailsParam) {
List<String> needBlobStepIds = new ArrayList<>();
for (ApiScenarioStepRequest step : steps) {
if (BooleanUtils.isFalse(step.getEnable())) {
continue;
}
if (StringUtils.equalsAny(step.getStepType(), ApiScenarioStepRefType.REF.name())
&& !StringUtils.equals(step.getRefType(), ApiScenarioStepType.API.name())) {
// 非完全引用的步骤和接口定义的步骤才需要查blob
continue;
}
if (stepDetailsParam != null && stepDetailsParam.keySet().contains(step.getId())) {
// 前端传了blob不需要再查
continue;
}
needBlobStepIds.add(step.getId());
}
Map<String, String> stepDetails = getStepBlobByIds(needBlobStepIds).stream()
.collect(Collectors.toMap(ApiScenarioStepBlob::getId, blob -> new String(blob.getContent())));
// 前端有传就用前端传的
if (stepDetailsParam != null) {
stepDetailsParam.forEach((stepId, detail) -> stepDetails.put(stepId, JSON.toJSONString(detail)));
}
return stepDetails;
}
private Map<String, String> getResourceBlobMap(Map<String, List<String>> refResourceMap) {
Map<String, String> resourceBlobMap = new HashMap<>();
List<String> apiIds = refResourceMap.get(ApiScenarioStepType.API.name());
List<ApiDefinitionBlob> apiDefinitionBlobs = apiDefinitionService.getBlobByIds(apiIds);
apiDefinitionBlobs.forEach(blob -> resourceBlobMap.put(blob.getId(), new String(blob.getRequest())));
List<String> apiCaseIds = refResourceMap.get(ApiScenarioStepType.API_CASE.name());
List<ApiTestCaseBlob> apiTestCaseBlobs = apiTestCaseService.getBlobByIds(apiCaseIds);
apiTestCaseBlobs.forEach(blob -> resourceBlobMap.put(blob.getId(), new String(blob.getRequest())));
List<String> apiScenarioIds = refResourceMap.get(ApiScenarioStepType.API_SCENARIO.name());
List<ApiScenarioBlob> apiScenarioBlobs = getBlobByIds(apiScenarioIds);
apiScenarioBlobs.forEach(blob -> resourceBlobMap.put(blob.getId(), new String(blob.getConfig())));
return resourceBlobMap;
}
private List<ApiScenarioStepBlob> getStepBlobByIds(List<String> stepIds) {
if (CollectionUtils.isEmpty(stepIds)) {
return Collections.emptyList();
}
ApiScenarioStepBlobExample example = new ApiScenarioStepBlobExample();
example.createCriteria().andIdIn(stepIds);
return apiScenarioStepBlobMapper.selectByExampleWithBLOBs(example);
}
private List<ApiScenarioBlob> getBlobByIds(List<String> apiScenarioIds) {
if (CollectionUtils.isEmpty(apiScenarioIds)) {
return Collections.emptyList();
}
ApiScenarioBlobExample example = new ApiScenarioBlobExample();
example.createCriteria().andIdIn(apiScenarioIds);
return apiScenarioBlobMapper.selectByExampleWithBLOBs(example);
}
private void buildRefResourceIdMap(List<ApiScenarioStepRequest> steps, Map<String, List<String>> refResourceIdMap) {
for (ApiScenarioStepRequest step : steps) {
if (isRef(step.getRefType()) && BooleanUtils.isTrue(step.getEnable())) {
// 记录引用的步骤ID
List<String> resourceIds = refResourceIdMap.get(step.getStepType());
if (resourceIds == null) {
resourceIds = new ArrayList<>();
refResourceIdMap.put(step.getStepType(), resourceIds);
}
resourceIds.add(step.getResourceId());
}
if (CollectionUtils.isNotEmpty(step.getChildren())) {
buildRefResourceIdMap(step.getChildren(), refResourceIdMap);
}
}
}
private ScenarioConfig getScenarioConfig(ApiScenarioDebugRequest request, boolean hasSave) {
ScenarioConfig scenarioConfig = null;
if (request.getScenarioConfig() != null) {
// 优先使用前端传的配置
scenarioConfig = request.getScenarioConfig();
} else if (hasSave) {
// 没传并且保存过则从数据库获取
ApiScenarioBlob apiScenarioBlob = apiScenarioBlobMapper.selectByPrimaryKey(request.getId());
if (apiScenarioBlob != null) {
scenarioConfig = JSON.parseObject(new String(apiScenarioBlob.getConfig()), ScenarioConfig.class);
}
}
return scenarioConfig;
}
} }

View File

@ -3,6 +3,7 @@ package io.metersphere.api.utils;
import io.metersphere.api.parser.jmeter.MsCommentScriptElementConverter; import io.metersphere.api.parser.jmeter.MsCommentScriptElementConverter;
import io.metersphere.api.parser.jmeter.MsCommonElementConverter; import io.metersphere.api.parser.jmeter.MsCommonElementConverter;
import io.metersphere.api.parser.jmeter.MsHTTPElementConverter; import io.metersphere.api.parser.jmeter.MsHTTPElementConverter;
import io.metersphere.api.parser.jmeter.MsScenarioConverter;
import io.metersphere.plugin.api.spi.AbstractJmeterElementConverter; import io.metersphere.plugin.api.spi.AbstractJmeterElementConverter;
import io.metersphere.plugin.api.spi.MsTestElement; import io.metersphere.plugin.api.spi.MsTestElement;
import io.metersphere.plugin.sdk.util.PluginLogUtils; import io.metersphere.plugin.sdk.util.PluginLogUtils;
@ -30,6 +31,7 @@ public class JmeterElementConverterRegister {
register(MsHTTPElementConverter.class); register(MsHTTPElementConverter.class);
register(MsCommonElementConverter.class); register(MsCommonElementConverter.class);
register(MsCommentScriptElementConverter.class); register(MsCommentScriptElementConverter.class);
register(MsScenarioConverter.class);
} }
/** /**

View File

@ -363,7 +363,7 @@ public class ApiDebugControllerTests extends BaseTest {
MsHTTPElement msHTTPElement = new MsHTTPElement(); MsHTTPElement msHTTPElement = new MsHTTPElement();
msHTTPElement.setPath("/test"); msHTTPElement.setPath("/test");
msHTTPElement.setMethod("GET"); msHTTPElement.setMethod("GET");
request.setRequest(ApiDataUtils.toJSONString(msHTTPElement)); request.setRequest(JSON.parseObject(ApiDataUtils.toJSONString(msHTTPElement)));
// @校验组织没有资源池权限异常 // @校验组织没有资源池权限异常
assertErrorCode(this.requestPost(DEBUG, request), ApiResultCode.EXECUTE_RESOURCE_POOL_NOT_CONFIG); assertErrorCode(this.requestPost(DEBUG, request), ApiResultCode.EXECUTE_RESOURCE_POOL_NOT_CONFIG);
@ -392,7 +392,7 @@ public class ApiDebugControllerTests extends BaseTest {
msHTTPElement = MsHTTPElementTest.getMsHttpElement(); msHTTPElement = MsHTTPElementTest.getMsHttpElement();
msHTTPElement.setChildren(linkedList); msHTTPElement.setChildren(linkedList);
msHTTPElement.setEnable(true); msHTTPElement.setEnable(true);
request.setRequest(ApiDataUtils.toJSONString(msHTTPElement)); request.setRequest(getMsElementParam(msHTTPElement));
this.requestPostWithOk(DEBUG, request); this.requestPostWithOk(DEBUG, request);
// 测试请求体 // 测试请求体
@ -407,10 +407,10 @@ public class ApiDebugControllerTests extends BaseTest {
// 增加覆盖率 // 增加覆盖率
msHTTPElement.setMethod("GET"); msHTTPElement.setMethod("GET");
request.setRequest(ApiDataUtils.toJSONString(msHTTPElement)); request.setRequest(getMsElementParam(msHTTPElement));
this.requestPostWithOk(DEBUG, request); this.requestPostWithOk(DEBUG, request);
msHTTPElement.setEnable(false); msHTTPElement.setEnable(false);
request.setRequest(ApiDataUtils.toJSONString(msHTTPElement)); request.setRequest(getMsElementParam(msHTTPElement));
this.requestPostWithOk(DEBUG, request); this.requestPostWithOk(DEBUG, request);
// 增加覆盖率 // 增加覆盖率
@ -426,35 +426,39 @@ public class ApiDebugControllerTests extends BaseTest {
private void testBodyParse(ApiDebugRunRequest request, MsHTTPElement msHTTPElement, Body generalBody) throws Exception { private void testBodyParse(ApiDebugRunRequest request, MsHTTPElement msHTTPElement, Body generalBody) throws Exception {
// 测试 FORM_DATA // 测试 FORM_DATA
generalBody.setBodyType(Body.BodyType.FORM_DATA.name()); generalBody.setBodyType(Body.BodyType.FORM_DATA.name());
request.setRequest(ApiDataUtils.toJSONString(msHTTPElement)); request.setRequest(getMsElementParam(msHTTPElement));
this.requestPostWithOk(DEBUG, request); this.requestPostWithOk(DEBUG, request);
// 测试 WWW_FORM // 测试 WWW_FORM
generalBody.setBodyType(Body.BodyType.WWW_FORM.name()); generalBody.setBodyType(Body.BodyType.WWW_FORM.name());
request.setRequest(ApiDataUtils.toJSONString(msHTTPElement)); request.setRequest(getMsElementParam(msHTTPElement));
this.requestPostWithOk(DEBUG, request); this.requestPostWithOk(DEBUG, request);
// 测试 BINARY // 测试 BINARY
generalBody.setBodyType(Body.BodyType.BINARY.name()); generalBody.setBodyType(Body.BodyType.BINARY.name());
request.setRequest(ApiDataUtils.toJSONString(msHTTPElement)); request.setRequest(getMsElementParam(msHTTPElement));
this.requestPostWithOk(DEBUG, request); this.requestPostWithOk(DEBUG, request);
// 测试 JSON // 测试 JSON
generalBody.setBodyType(Body.BodyType.JSON.name()); generalBody.setBodyType(Body.BodyType.JSON.name());
request.setRequest(ApiDataUtils.toJSONString(msHTTPElement)); request.setRequest(getMsElementParam(msHTTPElement));
this.requestPostWithOk(DEBUG, request); this.requestPostWithOk(DEBUG, request);
// 测试 XML // 测试 XML
generalBody.setBodyType(Body.BodyType.XML.name()); generalBody.setBodyType(Body.BodyType.XML.name());
request.setRequest(ApiDataUtils.toJSONString(msHTTPElement)); request.setRequest(getMsElementParam(msHTTPElement));
this.requestPostWithOk(DEBUG, request); this.requestPostWithOk(DEBUG, request);
// 测试 RAW // 测试 RAW
generalBody.setBodyType(Body.BodyType.RAW.name()); generalBody.setBodyType(Body.BodyType.RAW.name());
request.setRequest(ApiDataUtils.toJSONString(msHTTPElement)); request.setRequest(getMsElementParam(msHTTPElement));
this.requestPostWithOk(DEBUG, request); this.requestPostWithOk(DEBUG, request);
} }
private Object getMsElementParam(MsHTTPElement msHTTPElement) {
return JSON.parseObject(ApiDataUtils.toJSONString(msHTTPElement));
}
@Test @Test
@Order(7) @Order(7)
public void delete() throws Exception { public void delete() throws Exception {

View File

@ -3,6 +3,7 @@ package io.metersphere.api.controller;
import io.metersphere.api.constants.ApiDefinitionStatus; import io.metersphere.api.constants.ApiDefinitionStatus;
import io.metersphere.api.constants.ApiScenarioStatus; import io.metersphere.api.constants.ApiScenarioStatus;
import io.metersphere.api.constants.ApiScenarioStepRefType; import io.metersphere.api.constants.ApiScenarioStepRefType;
import io.metersphere.api.constants.ApiScenarioStepType;
import io.metersphere.api.domain.*; import io.metersphere.api.domain.*;
import io.metersphere.api.dto.definition.ApiDefinitionAddRequest; import io.metersphere.api.dto.definition.ApiDefinitionAddRequest;
import io.metersphere.api.dto.definition.ApiTestCaseAddRequest; import io.metersphere.api.dto.definition.ApiTestCaseAddRequest;
@ -11,6 +12,7 @@ import io.metersphere.api.dto.request.assertion.MsScriptAssertion;
import io.metersphere.api.dto.request.http.MsHTTPElement; import io.metersphere.api.dto.request.http.MsHTTPElement;
import io.metersphere.api.dto.scenario.*; import io.metersphere.api.dto.scenario.*;
import io.metersphere.api.mapper.*; import io.metersphere.api.mapper.*;
import io.metersphere.api.service.BaseResourcePoolTestService;
import io.metersphere.api.service.definition.ApiDefinitionService; import io.metersphere.api.service.definition.ApiDefinitionService;
import io.metersphere.api.service.definition.ApiTestCaseService; import io.metersphere.api.service.definition.ApiTestCaseService;
import io.metersphere.api.utils.ApiDataUtils; import io.metersphere.api.utils.ApiDataUtils;
@ -60,6 +62,7 @@ public class ApiScenarioControllerTests extends BaseTest {
private static final String FOLLOW = "follow/"; private static final String FOLLOW = "follow/";
protected static final String UPLOAD_TEMP_FILE = "upload/temp/file"; protected static final String UPLOAD_TEMP_FILE = "upload/temp/file";
protected static final String DELETE_TO_GC = "delete-to-gc/{0}"; protected static final String DELETE_TO_GC = "delete-to-gc/{0}";
protected static final String DEBUG = "debug";
private static final ResultMatcher ERROR_REQUEST_MATCHER = status().is5xxServerError(); private static final ResultMatcher ERROR_REQUEST_MATCHER = status().is5xxServerError();
@Resource @Resource
@ -86,6 +89,8 @@ public class ApiScenarioControllerTests extends BaseTest {
private ApiDefinitionService apiDefinitionService; private ApiDefinitionService apiDefinitionService;
@Resource @Resource
private ApiTestCaseService apiTestCaseService; private ApiTestCaseService apiTestCaseService;
@Resource
private BaseResourcePoolTestService baseResourcePoolTestService;
private static ApiScenario addApiScenario; private static ApiScenario addApiScenario;
private static ApiScenario anOtherAddApiScenario; private static ApiScenario anOtherAddApiScenario;
private static ApiDefinition apiDefinition; private static ApiDefinition apiDefinition;
@ -227,7 +232,7 @@ public class ApiScenarioControllerTests extends BaseTest {
stepRequest2.setId(IDGenerator.nextStr()); stepRequest2.setId(IDGenerator.nextStr());
stepRequest2.setName(addApiScenario.getName()); stepRequest2.setName(addApiScenario.getName());
stepRequest2.setResourceId(addApiScenario.getId()); stepRequest2.setResourceId(addApiScenario.getId());
stepRequest2.setRefType(ApiScenarioStepRefType.STEP_REF.name()); stepRequest2.setRefType(ApiScenarioStepRefType.PARTIAL_REF.name());
stepRequest2.setChildren(List.of(stepRequest)); stepRequest2.setChildren(List.of(stepRequest));
steps = List.of(stepRequest, stepRequest2); steps = List.of(stepRequest, stepRequest2);
request.setSteps(steps); request.setSteps(steps);
@ -326,8 +331,11 @@ public class ApiScenarioControllerTests extends BaseTest {
stepRequest3.setResourceId(apiDefinition.getId()); stepRequest3.setResourceId(apiDefinition.getId());
stepRequest.setName(apiDefinition.getName() + "3"); stepRequest.setName(apiDefinition.getName() + "3");
stepRequest3.setRefType(ApiScenarioStepRefType.REF.name()); stepRequest3.setRefType(ApiScenarioStepRefType.REF.name());
return new ArrayList<>() {{
return List.of(stepRequest, stepRequest2); add(stepRequest);
add(stepRequest2);
add(stepRequest3);
}};
} }
private void initTestData() { private void initTestData() {
@ -409,30 +417,6 @@ public class ApiScenarioControllerTests extends BaseTest {
requestPostPermissionTest(PermissionConstants.PROJECT_API_SCENARIO_UPDATE, DEFAULT_UPDATE, request); requestPostPermissionTest(PermissionConstants.PROJECT_API_SCENARIO_UPDATE, DEFAULT_UPDATE, request);
} }
@Test
@Order(3)
public void deleteToGc() throws Exception {
// @@请求成功
this.requestGetWithOk(DELETE_TO_GC, addApiScenario.getId());
// todo 校验请求成功数据
// @@校验日志
checkLog(addApiScenario.getId(), OperationLogType.DELETE);
// @@校验权限
requestGetPermissionTest(PermissionConstants.PROJECT_API_SCENARIO_DELETE, DELETE_TO_GC, addApiScenario.getId());
}
@Test
@Order(4)
public void delete() throws Exception {
// @@请求成功
this.requestGetWithOk(DEFAULT_DELETE, addApiScenario.getId());
// todo 校验请求成功数据
// @@校验日志
checkLog(addApiScenario.getId(), OperationLogType.DELETE);
// @@校验权限
requestGetPermissionTest(PermissionConstants.PROJECT_API_SCENARIO_DELETE, DEFAULT_DELETE, addApiScenario.getId());
}
@Test @Test
@Order(5) @Order(5)
public void uploadTempFile() throws Exception { public void uploadTempFile() throws Exception {
@ -450,6 +434,45 @@ public class ApiScenarioControllerTests extends BaseTest {
requestUploadPermissionTest(PermissionConstants.PROJECT_API_SCENARIO_UPDATE, UPLOAD_TEMP_FILE, file); requestUploadPermissionTest(PermissionConstants.PROJECT_API_SCENARIO_UPDATE, UPLOAD_TEMP_FILE, file);
} }
@Test
@Order(6)
public void debug() throws Exception {
mockPost("/api/debug", "");
baseResourcePoolTestService.initProjectResourcePool();
// @@请求成功
ApiScenarioDebugRequest request = new ApiScenarioDebugRequest();
request.setProjectId(DEFAULT_PROJECT_ID);
request.setScenarioConfig(new ScenarioConfig());
request.setEnvironmentId("environmentId");
List<ApiScenarioStepRequest> steps = getApiScenarioStepRequests();
ApiScenarioStepRequest stepRequest = new ApiScenarioStepRequest();
stepRequest.setName("test step");
stepRequest.setId(IDGenerator.nextStr());
stepRequest.setRefType(ApiScenarioStepRefType.REF.name());
stepRequest.setResourceId(addApiScenario.getId());
stepRequest.setResourceNum(addApiScenario.getNum().toString());
stepRequest.setName(addApiScenario.getName());
stepRequest.setStepType(ApiScenarioStepType.API_SCENARIO.name());
stepRequest.setChildren(getApiScenarioStepRequests());
steps.add(stepRequest);
ApiScenarioStepRequest copyScenarioStep = BeanUtils.copyBean(new ApiScenarioStepRequest(), stepRequest);
copyScenarioStep.setRefType(ApiScenarioStepRefType.COPY.name());
copyScenarioStep.setChildren(getApiScenarioStepRequests());
steps.add(stepRequest);
ApiScenarioStepRequest partialScenarioStep = BeanUtils.copyBean(new ApiScenarioStepRequest(), stepRequest);
partialScenarioStep.setRefType(ApiScenarioStepRefType.PARTIAL_REF.name());
partialScenarioStep.setChildren(getApiScenarioStepRequests());
steps.add(partialScenarioStep);
request.setId(addApiScenario.getId());
request.setSteps(steps);
request.setStepDetails(new HashMap<>());
this.requestPostWithOk(DEBUG, request);
// @@校验权限
requestPostPermissionTest(PermissionConstants.PROJECT_API_SCENARIO_EXECUTE, DEBUG, request);
}
private String doUploadTempFile(MockMultipartFile file) throws Exception { private String doUploadTempFile(MockMultipartFile file) throws Exception {
return JSON.parseObject(requestUploadFileWithOkAndReturn(UPLOAD_TEMP_FILE, file) return JSON.parseObject(requestUploadFileWithOkAndReturn(UPLOAD_TEMP_FILE, file)
.getResponse() .getResponse()
@ -467,6 +490,30 @@ public class ApiScenarioControllerTests extends BaseTest {
return file; return file;
} }
@Test
@Order(9)
public void deleteToGc() throws Exception {
// @@请求成功
this.requestGetWithOk(DELETE_TO_GC, addApiScenario.getId());
// todo 校验请求成功数据
// @@校验日志
checkLog(addApiScenario.getId(), OperationLogType.DELETE);
// @@校验权限
requestGetPermissionTest(PermissionConstants.PROJECT_API_SCENARIO_DELETE, DELETE_TO_GC, addApiScenario.getId());
}
@Test
@Order(10)
public void delete() throws Exception {
// @@请求成功
this.requestGetWithOk(DEFAULT_DELETE, addApiScenario.getId());
// todo 校验请求成功数据
// @@校验日志
checkLog(addApiScenario.getId(), OperationLogType.DELETE);
// @@校验权限
requestGetPermissionTest(PermissionConstants.PROJECT_API_SCENARIO_DELETE, DEFAULT_DELETE, addApiScenario.getId());
}
@Test @Test
@Order(11) @Order(11)
public void page() throws Exception { public void page() throws Exception {