feat(接口测试): 接口通过率开发
This commit is contained in:
parent
35af224764
commit
1f46ee6b9a
|
@ -2,6 +2,7 @@ package io.metersphere.api.controller.definition;
|
||||||
|
|
||||||
import com.github.pagehelper.Page;
|
import com.github.pagehelper.Page;
|
||||||
import com.github.pagehelper.PageHelper;
|
import com.github.pagehelper.PageHelper;
|
||||||
|
import io.metersphere.api.constants.ApiScenarioStepType;
|
||||||
import io.metersphere.api.domain.ApiDefinition;
|
import io.metersphere.api.domain.ApiDefinition;
|
||||||
import io.metersphere.api.dto.ReferenceDTO;
|
import io.metersphere.api.dto.ReferenceDTO;
|
||||||
import io.metersphere.api.dto.ReferenceRequest;
|
import io.metersphere.api.dto.ReferenceRequest;
|
||||||
|
@ -10,8 +11,12 @@ import io.metersphere.api.dto.request.ApiEditPosRequest;
|
||||||
import io.metersphere.api.dto.request.ApiTransferRequest;
|
import io.metersphere.api.dto.request.ApiTransferRequest;
|
||||||
import io.metersphere.api.dto.request.ImportRequest;
|
import io.metersphere.api.dto.request.ImportRequest;
|
||||||
import io.metersphere.api.dto.schema.JsonSchemaItem;
|
import io.metersphere.api.dto.schema.JsonSchemaItem;
|
||||||
|
import io.metersphere.api.mapper.ExtApiDefinitionMapper;
|
||||||
|
import io.metersphere.api.mapper.ExtApiScenarioStepMapper;
|
||||||
|
import io.metersphere.api.mapper.ExtApiTestCaseMapper;
|
||||||
import io.metersphere.api.service.ApiFileResourceService;
|
import io.metersphere.api.service.ApiFileResourceService;
|
||||||
import io.metersphere.api.service.definition.*;
|
import io.metersphere.api.service.definition.*;
|
||||||
|
import io.metersphere.api.service.scenario.ApiScenarioService;
|
||||||
import io.metersphere.project.service.FileModuleService;
|
import io.metersphere.project.service.FileModuleService;
|
||||||
import io.metersphere.sdk.constants.DefaultRepositoryDir;
|
import io.metersphere.sdk.constants.DefaultRepositoryDir;
|
||||||
import io.metersphere.sdk.constants.PermissionConstants;
|
import io.metersphere.sdk.constants.PermissionConstants;
|
||||||
|
@ -34,6 +39,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.shiro.authz.annotation.Logical;
|
import org.apache.shiro.authz.annotation.Logical;
|
||||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||||
|
@ -41,6 +47,7 @@ import org.springframework.validation.annotation.Validated;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
@ -51,6 +58,14 @@ import java.util.List;
|
||||||
@RequestMapping(value = "/api/definition")
|
@RequestMapping(value = "/api/definition")
|
||||||
@Tag(name = "接口测试-接口管理-接口定义")
|
@Tag(name = "接口测试-接口管理-接口定义")
|
||||||
public class ApiDefinitionController {
|
public class ApiDefinitionController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ExtApiDefinitionMapper extApiDefinitionMapper;
|
||||||
|
@Resource
|
||||||
|
private ExtApiTestCaseMapper extApiTestCaseMapper;
|
||||||
|
@Resource
|
||||||
|
private ExtApiScenarioStepMapper extApiScenarioStepMapper;
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private ApiDefinitionService apiDefinitionService;
|
private ApiDefinitionService apiDefinitionService;
|
||||||
@Resource
|
@Resource
|
||||||
|
@ -61,12 +76,55 @@ public class ApiDefinitionController {
|
||||||
private ApiDefinitionImportService apiDefinitionImportService;
|
private ApiDefinitionImportService apiDefinitionImportService;
|
||||||
@Resource
|
@Resource
|
||||||
private ApiDefinitionExportService apiDefinitionExportService;
|
private ApiDefinitionExportService apiDefinitionExportService;
|
||||||
|
@Resource
|
||||||
|
private ApiScenarioService apiScenarioService;
|
||||||
|
|
||||||
|
/*
|
||||||
|
接口覆盖率
|
||||||
|
业务注释,误删
|
||||||
|
* 一个接口如果被跨项目的场景给关联了,算不算覆盖? 不算
|
||||||
|
* 自定义请求, 不管它有多少个“/"有多少子域 , 跟接口定义匹配的时候就用末端匹配法。
|
||||||
|
· 例如:https://www.tapd.cn/tapd_fe/my/work?dialog_preview_id=abcdefg
|
||||||
|
·/work能匹配的上
|
||||||
|
·/my/work能匹配的上
|
||||||
|
·/my 不可以
|
||||||
|
·/my/{something}可以匹配的上
|
||||||
|
·/my/{something}/{other-thing}不可以
|
||||||
|
* 剩下的基本上就跟V2一样了. 有用例 or 被场景引用/复制 or 被自定义给命中了 就算覆盖。 且自定义请求可以命中多个接口定义,比如上一点
|
||||||
|
*/
|
||||||
|
@GetMapping("/rage/{projectId}")
|
||||||
|
@Operation(summary = "接口测试-接口管理-接口列表(deleted 状态为 1 时为回收站数据)")
|
||||||
|
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_READ)
|
||||||
|
@CheckOwner(resourceId = "#projectId", resourceType = "project")
|
||||||
|
public ApiCoverageDTO rage(@PathVariable String projectId) {
|
||||||
|
List<String> apiAllIds = new ArrayList<>();
|
||||||
|
List<ApiDefinition> httpApiList = new ArrayList<>();
|
||||||
|
extApiDefinitionMapper.selectBaseInfoByProjectId(projectId).forEach(apiDefinition -> {
|
||||||
|
if (StringUtils.equalsIgnoreCase(apiDefinition.getProtocol(), "http")) {
|
||||||
|
httpApiList.add(apiDefinition);
|
||||||
|
}
|
||||||
|
apiAllIds.add(apiDefinition.getId());
|
||||||
|
});
|
||||||
|
|
||||||
|
List<String> apiDefinitionIdFromCase = extApiTestCaseMapper.selectApiId(projectId);
|
||||||
|
List<String> apiInScenarioStep = extApiScenarioStepMapper.selectResourceId(projectId, ApiScenarioStepType.API.name());
|
||||||
|
List<String> apiCaseIdInStep = extApiScenarioStepMapper.selectResourceId(projectId, ApiScenarioStepType.API_CASE.name());
|
||||||
|
if (CollectionUtils.isNotEmpty(apiCaseIdInStep)) {
|
||||||
|
List<String> apiCaseIdInScenarioStep = extApiTestCaseMapper.selectApiIdByCaseId(apiCaseIdInStep);
|
||||||
|
apiInScenarioStep.addAll(apiCaseIdInScenarioStep);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> apiInStepList = apiScenarioService.selectApiIdInCustomRequest(projectId, httpApiList);
|
||||||
|
apiInStepList.addAll(apiInScenarioStep);
|
||||||
|
|
||||||
|
return new ApiCoverageDTO(apiAllIds, apiDefinitionIdFromCase, apiInStepList);
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping(value = "/add")
|
@PostMapping(value = "/add")
|
||||||
@Operation(summary = "接口测试-接口管理-添加接口定义")
|
@Operation(summary = "接口测试-接口管理-添加接口定义")
|
||||||
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_ADD)
|
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_ADD)
|
||||||
@Log(type = OperationLogType.ADD, expression = "#msClass.addLog(#request)", msClass = ApiDefinitionLogService.class)
|
@Log(type = OperationLogType.ADD, expression = "#msClass.addLog(#request)", msClass = ApiDefinitionLogService.class)
|
||||||
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
|
@CheckOwner(resourceId = "#request.getProjectId()s", resourceType = "project")
|
||||||
@SendNotice(taskType = NoticeConstants.TaskType.API_DEFINITION_TASK, event = NoticeConstants.Event.CREATE, target = "#targetClass.getApiDTO(#request)", targetClass = ApiDefinitionNoticeService.class)
|
@SendNotice(taskType = NoticeConstants.TaskType.API_DEFINITION_TASK, event = NoticeConstants.Event.CREATE, target = "#targetClass.getApiDTO(#request)", targetClass = ApiDefinitionNoticeService.class)
|
||||||
public ApiDefinition add(@Validated @RequestBody ApiDefinitionAddRequest request) {
|
public ApiDefinition add(@Validated @RequestBody ApiDefinitionAddRequest request) {
|
||||||
return apiDefinitionService.create(request, SessionUtils.getUserId());
|
return apiDefinitionService.create(request, SessionUtils.getUserId());
|
||||||
|
|
|
@ -3,10 +3,6 @@ package io.metersphere.api.controller.scenario;
|
||||||
import io.metersphere.api.constants.ApiResource;
|
import io.metersphere.api.constants.ApiResource;
|
||||||
import io.metersphere.api.dto.response.ApiScenarioBatchOperationResponse;
|
import io.metersphere.api.dto.response.ApiScenarioBatchOperationResponse;
|
||||||
import io.metersphere.api.dto.scenario.*;
|
import io.metersphere.api.dto.scenario.*;
|
||||||
import io.metersphere.api.dto.scenario.ApiScenarioBatchCopyMoveRequest;
|
|
||||||
import io.metersphere.api.dto.scenario.ApiScenarioBatchEditRequest;
|
|
||||||
import io.metersphere.api.dto.scenario.ApiScenarioBatchRequest;
|
|
||||||
import io.metersphere.api.dto.scenario.ApiScenarioBatchRunRequest;
|
|
||||||
import io.metersphere.api.service.ApiValidateService;
|
import io.metersphere.api.service.ApiValidateService;
|
||||||
import io.metersphere.api.service.scenario.ApiScenarioBatchRunService;
|
import io.metersphere.api.service.scenario.ApiScenarioBatchRunService;
|
||||||
import io.metersphere.api.service.scenario.ApiScenarioNoticeService;
|
import io.metersphere.api.service.scenario.ApiScenarioNoticeService;
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
package io.metersphere.api.dto.definition;
|
||||||
|
|
||||||
|
import io.metersphere.sdk.util.CalculateUtils;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author lan
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class ApiCoverageDTO {
|
||||||
|
|
||||||
|
@Schema(description = "接口定义总量")
|
||||||
|
private int allApiCount;
|
||||||
|
|
||||||
|
@Schema(description = "接口定义未覆盖")
|
||||||
|
private int unCoverWithApiDefinition;
|
||||||
|
|
||||||
|
@Schema(description = "接口定义已覆盖")
|
||||||
|
private int coverWithApiDefinition;
|
||||||
|
|
||||||
|
@Schema(description = "接口覆盖率 接口(URL)有(用例或场景步骤)数/接口总数*100%")
|
||||||
|
private String apiCoverage;
|
||||||
|
|
||||||
|
@Schema(description = "接口用例未覆盖")
|
||||||
|
private int unCoverWithApiCase;
|
||||||
|
|
||||||
|
@Schema(description = "接口用例已覆盖")
|
||||||
|
private int coverWithApiCase;
|
||||||
|
|
||||||
|
@Schema(description = "用例覆盖率 有用例的接口/接口总数*100%")
|
||||||
|
private String apiCaseCoverage;
|
||||||
|
|
||||||
|
@Schema(description = "接口场景未覆盖")
|
||||||
|
private int unCoverWithApiScenario;
|
||||||
|
|
||||||
|
@Schema(description = "接口场景已覆盖")
|
||||||
|
private int coverWithApiScenario;
|
||||||
|
|
||||||
|
@Schema(description = "场景覆盖率 被场景步骤包含的接口(URL)数/接口总数*100%")
|
||||||
|
private String scenarioCoverage;
|
||||||
|
|
||||||
|
public ApiCoverageDTO(List<String> allApiId, List<String> haveCaseIdList, List<String> apiIdOrUrlInStepList) {
|
||||||
|
// 去重、过滤(只留下apiId中存在的数据,避免已删除的apiId导致统计错误)
|
||||||
|
allApiId = allApiId.stream().distinct().toList();
|
||||||
|
haveCaseIdList = this.elementInList(allApiId, haveCaseIdList.stream().distinct().toList());
|
||||||
|
apiIdOrUrlInStepList = this.elementInList(allApiId, apiIdOrUrlInStepList.stream().distinct().toList());
|
||||||
|
|
||||||
|
this.allApiCount = allApiId.size();
|
||||||
|
// 用例覆盖率: 有用例的接口/接口总数*100%
|
||||||
|
this.coverWithApiCase = haveCaseIdList.size();
|
||||||
|
this.unCoverWithApiCase = this.allApiCount - this.coverWithApiCase;
|
||||||
|
this.apiCaseCoverage = CalculateUtils.reportPercentage(coverWithApiCase, allApiCount);
|
||||||
|
|
||||||
|
// 场景覆盖率: 被场景步骤包含的接口(URL)数/接口总数*100%
|
||||||
|
this.coverWithApiScenario = apiIdOrUrlInStepList.size();
|
||||||
|
this.unCoverWithApiScenario = this.allApiCount - this.coverWithApiScenario;
|
||||||
|
this.scenarioCoverage = CalculateUtils.reportPercentage(coverWithApiScenario, allApiCount);
|
||||||
|
|
||||||
|
// 接口覆盖率
|
||||||
|
apiIdOrUrlInStepList.addAll(haveCaseIdList);
|
||||||
|
List<String> allCoverList = apiIdOrUrlInStepList.stream().distinct().toList();
|
||||||
|
this.coverWithApiDefinition = allCoverList.size();
|
||||||
|
this.unCoverWithApiDefinition = this.allApiCount - this.coverWithApiDefinition;
|
||||||
|
this.apiCoverage = CalculateUtils.reportPercentage(coverWithApiDefinition, allApiCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> elementInList(List<String> allList, List<String> compareList) {
|
||||||
|
List<String> returnList = new ArrayList<>();
|
||||||
|
compareList.forEach(item -> {
|
||||||
|
if (allList.contains(item)) {
|
||||||
|
returnList.add(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return returnList;
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,7 +19,7 @@ public class ApiScenarioBatchEditRequest extends ApiScenarioBatchRequest impleme
|
||||||
|
|
||||||
@Schema(description = "标签")
|
@Schema(description = "标签")
|
||||||
private LinkedHashSet<String> tags;
|
private LinkedHashSet<String> tags;
|
||||||
@Schema(description = "批量编辑的类型 用例等级: Priority,状态 :Status,标签: Tags,用例环境: Environment, 定时任务:Schedule")
|
@Schema(description = "批量编辑的类型 用例等级: Priority,状态 :Status,标签: Tags,用例环境: Environment")
|
||||||
@NotBlank
|
@NotBlank
|
||||||
private String type;
|
private String type;
|
||||||
@Schema(description = "默认覆盖原标签")
|
@Schema(description = "默认覆盖原标签")
|
||||||
|
@ -40,8 +40,6 @@ public class ApiScenarioBatchEditRequest extends ApiScenarioBatchRequest impleme
|
||||||
@Schema(description = "用例等级")
|
@Schema(description = "用例等级")
|
||||||
@Size(max = 50, message = "{api_test_case.priority.length_range}")
|
@Size(max = 50, message = "{api_test_case.priority.length_range}")
|
||||||
private String priority;
|
private String priority;
|
||||||
@Schema(description = "定时任务是否开启")
|
|
||||||
private boolean scheduleOpen;
|
|
||||||
public List<String> getTags() {
|
public List<String> getTags() {
|
||||||
if (tags == null) {
|
if (tags == null) {
|
||||||
return new ArrayList<>(0);
|
return new ArrayList<>(0);
|
||||||
|
|
|
@ -2,7 +2,6 @@ package io.metersphere.api.dto.scenario;
|
||||||
|
|
||||||
import io.metersphere.sdk.dto.api.task.ApiRunModeConfigDTO;
|
import io.metersphere.sdk.dto.api.task.ApiRunModeConfigDTO;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import jakarta.validation.constraints.NotBlank;
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
@ -18,7 +17,6 @@ public class ApiScenarioBatchScheduleConfigRequest extends ApiScenarioBatchReque
|
||||||
private boolean enable;
|
private boolean enable;
|
||||||
|
|
||||||
@Schema(description = "Cron表达式")
|
@Schema(description = "Cron表达式")
|
||||||
@NotBlank
|
|
||||||
private String cron;
|
private String cron;
|
||||||
|
|
||||||
@Schema(description = "定时任务配置")
|
@Schema(description = "定时任务配置")
|
||||||
|
|
|
@ -129,4 +129,5 @@ public interface ExtApiDefinitionMapper {
|
||||||
|
|
||||||
List<ApiDefinition> getCreateApiList(@Param("projectId") String projectId, @Param("startTime") Long startTime, @Param("endTime") Long endTime);
|
List<ApiDefinition> getCreateApiList(@Param("projectId") String projectId, @Param("startTime") Long startTime, @Param("endTime") Long endTime);
|
||||||
|
|
||||||
|
List<ApiDefinition> selectBaseInfoByProjectId(String projectId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -838,4 +838,10 @@
|
||||||
AND api_definition.create_time BETWEEN #{startTime} AND #{endTime}
|
AND api_definition.create_time BETWEEN #{startTime} AND #{endTime}
|
||||||
</if>
|
</if>
|
||||||
</select>
|
</select>
|
||||||
|
<select id="selectBaseInfoByProjectId" resultType="io.metersphere.api.domain.ApiDefinition">
|
||||||
|
SELECT id, path, method, protocol
|
||||||
|
FROM api_definition
|
||||||
|
WHERE project_id = #{0}
|
||||||
|
AND deleted IS FALSE
|
||||||
|
</select>
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package io.metersphere.api.mapper;
|
package io.metersphere.api.mapper;
|
||||||
|
|
||||||
import io.metersphere.api.domain.ApiScenarioCsvStep;
|
import io.metersphere.api.domain.ApiScenarioCsvStep;
|
||||||
|
import io.metersphere.api.domain.ApiScenarioStep;
|
||||||
import io.metersphere.api.dto.scenario.ApiScenarioStepDTO;
|
import io.metersphere.api.dto.scenario.ApiScenarioStepDTO;
|
||||||
import org.apache.ibatis.annotations.Param;
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
@ -25,4 +26,8 @@ public interface ExtApiScenarioStepMapper {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
List<String> getHasBlobRequestStepIds(@Param("scenarioId") String scenarioId);
|
List<String> getHasBlobRequestStepIds(@Param("scenarioId") String scenarioId);
|
||||||
|
|
||||||
|
List<String> selectResourceId(@Param("projectId") String projectId, @Param("stepType") String stepType);
|
||||||
|
|
||||||
|
List<ApiScenarioStep> selectCustomRequestConfigByProjectId(String projectId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,4 +28,21 @@
|
||||||
and step_type in ('API', 'API_CASE', 'CUSTOM_REQUEST')
|
and step_type in ('API', 'API_CASE', 'CUSTOM_REQUEST')
|
||||||
and ref_type in ('COPY', 'DIRECT')
|
and ref_type in ('COPY', 'DIRECT')
|
||||||
</select>
|
</select>
|
||||||
|
<select id="selectResourceId" resultType="java.lang.String">
|
||||||
|
select DISTINCT step.resource_id
|
||||||
|
from api_scenario_step step
|
||||||
|
INNER JOIN api_scenario scenario
|
||||||
|
ON step.scenario_id = scenario.id
|
||||||
|
where step.step_type = #{stepType}
|
||||||
|
AND scenario.project_id = #{projectId}
|
||||||
|
AND scenario.deleted IS FALSE
|
||||||
|
</select>
|
||||||
|
<select id="selectCustomRequestConfigByProjectId" resultType="io.metersphere.api.domain.ApiScenarioStep">
|
||||||
|
select step.id, step.config
|
||||||
|
from api_scenario_step step
|
||||||
|
INNER JOIN api_scenario scenario
|
||||||
|
ON step.scenario_id = scenario.id
|
||||||
|
where scenario.project_id = #{0}
|
||||||
|
AND scenario.deleted IS FALSE
|
||||||
|
</select>
|
||||||
</mapper>
|
</mapper>
|
|
@ -139,4 +139,8 @@ public interface ExtApiTestCaseMapper {
|
||||||
|
|
||||||
List<ApiTestCase> getSimpleApiCaseList(@Param("projectId") String projectId, @Param("startTime") Long startTime, @Param("endTime") Long endTime);
|
List<ApiTestCase> getSimpleApiCaseList(@Param("projectId") String projectId, @Param("startTime") Long startTime, @Param("endTime") Long endTime);
|
||||||
|
|
||||||
|
|
||||||
|
List<String> selectApiId(String projectId);
|
||||||
|
|
||||||
|
List<String> selectApiIdByCaseId(@Param("ids") List<String> apiCaseIdInStep);
|
||||||
}
|
}
|
|
@ -1031,4 +1031,18 @@
|
||||||
</if>
|
</if>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<select id="selectApiId" resultType="java.lang.String">
|
||||||
|
SELECT distinct api.id
|
||||||
|
FROM api_test_case apiCase
|
||||||
|
INNER JOIN api_definition api ON apiCase.api_definition_id = api.id
|
||||||
|
WHERE apiCase.deleted is FALSE
|
||||||
|
AND api.deleted is FALSE
|
||||||
|
AND api.project_id = #{0}
|
||||||
|
</select>
|
||||||
|
<select id="selectApiIdByCaseId" resultType="java.lang.String">
|
||||||
|
SELECT api_definition_id FROM api_test_case WHERE deleted is false AND id in
|
||||||
|
<foreach collection="ids" item="id" separator="," open="(" close=")">
|
||||||
|
#{id}
|
||||||
|
</foreach>
|
||||||
|
</select>
|
||||||
</mapper>
|
</mapper>
|
|
@ -25,10 +25,12 @@ import io.metersphere.api.service.ApiFileResourceService;
|
||||||
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;
|
||||||
|
import io.metersphere.api.utils.ApiDefinitionUtils;
|
||||||
import io.metersphere.api.utils.ApiScenarioBatchOperationUtils;
|
import io.metersphere.api.utils.ApiScenarioBatchOperationUtils;
|
||||||
import io.metersphere.api.utils.ApiScenarioUtils;
|
import io.metersphere.api.utils.ApiScenarioUtils;
|
||||||
import io.metersphere.functional.domain.FunctionalCaseTestExample;
|
import io.metersphere.functional.domain.FunctionalCaseTestExample;
|
||||||
import io.metersphere.functional.mapper.FunctionalCaseTestMapper;
|
import io.metersphere.functional.mapper.FunctionalCaseTestMapper;
|
||||||
|
import io.metersphere.plugin.api.spi.AbstractMsProtocolTestElement;
|
||||||
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
|
import io.metersphere.plugin.api.spi.AbstractMsTestElement;
|
||||||
import io.metersphere.project.domain.*;
|
import io.metersphere.project.domain.*;
|
||||||
import io.metersphere.project.dto.MoveNodeSortDTO;
|
import io.metersphere.project.dto.MoveNodeSortDTO;
|
||||||
|
@ -278,7 +280,6 @@ public class ApiScenarioService extends MoveNodeService {
|
||||||
case STATUS -> batchUpdateStatus(example, updateScenario, request.getStatus(), mapper);
|
case STATUS -> batchUpdateStatus(example, updateScenario, request.getStatus(), mapper);
|
||||||
case TAGS -> batchUpdateTags(example, updateScenario, request, ids, mapper);
|
case TAGS -> batchUpdateTags(example, updateScenario, request, ids, mapper);
|
||||||
case ENVIRONMENT -> batchUpdateEnvironment(example, updateScenario, request, mapper);
|
case ENVIRONMENT -> batchUpdateEnvironment(example, updateScenario, request, mapper);
|
||||||
case SCHEDULE -> batchUpdateSchedule(example, request, mapper, userId);
|
|
||||||
default -> throw new MSException(Translator.get("batch_edit_type_error"));
|
default -> throw new MSException(Translator.get("batch_edit_type_error"));
|
||||||
}
|
}
|
||||||
sqlSession.flushStatements();
|
sqlSession.flushStatements();
|
||||||
|
@ -288,15 +289,6 @@ public class ApiScenarioService extends MoveNodeService {
|
||||||
apiScenarioNoticeService.batchSendNotice(ids, userId, projectId, NoticeConstants.Event.UPDATE);
|
apiScenarioNoticeService.batchSendNotice(ids, userId, projectId, NoticeConstants.Event.UPDATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void batchUpdateSchedule(ApiScenarioExample example, ApiScenarioBatchEditRequest request, ApiScenarioMapper mapper, String userId) {
|
|
||||||
List<ApiScenario> apiScenarioList = mapper.selectByExample(example);
|
|
||||||
//批量编辑定时任务
|
|
||||||
for (ApiScenario apiScenario : apiScenarioList) {
|
|
||||||
scheduleService.updateIfExist(apiScenario.getId(), request.isScheduleOpen(), ApiScenarioScheduleJob.getJobKey(apiScenario.getId()),
|
|
||||||
ApiScenarioScheduleJob.getTriggerKey(apiScenario.getId()), ApiScenarioScheduleJob.class, userId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void batchUpdateEnvironment(ApiScenarioExample example, ApiScenario updateScenario,
|
private void batchUpdateEnvironment(ApiScenarioExample example, ApiScenario updateScenario,
|
||||||
ApiScenarioBatchEditRequest request, ApiScenarioMapper mapper) {
|
ApiScenarioBatchEditRequest request, ApiScenarioMapper mapper) {
|
||||||
if (BooleanUtils.isFalse(request.isGrouped())) {
|
if (BooleanUtils.isFalse(request.isGrouped())) {
|
||||||
|
@ -2566,6 +2558,9 @@ public class ApiScenarioService extends MoveNodeService {
|
||||||
List<ApiScenario> apiScenarios = apiScenarioMapper.selectByExample(example);
|
List<ApiScenario> apiScenarios = apiScenarioMapper.selectByExample(example);
|
||||||
|
|
||||||
if (CollectionUtils.isNotEmpty(apiScenarios)) {
|
if (CollectionUtils.isNotEmpty(apiScenarios)) {
|
||||||
|
if (StringUtils.isBlank(request.getCron()) && request.getConfig() == null) {
|
||||||
|
this.batchUpdateSchedule(apiScenarios, request.isEnable(), operator);
|
||||||
|
} else {
|
||||||
apiScenarios.forEach(apiScenario -> {
|
apiScenarios.forEach(apiScenario -> {
|
||||||
ScheduleConfig scheduleConfig = ScheduleConfig.builder()
|
ScheduleConfig scheduleConfig = ScheduleConfig.builder()
|
||||||
.resourceId(apiScenario.getId())
|
.resourceId(apiScenario.getId())
|
||||||
|
@ -2589,6 +2584,15 @@ public class ApiScenarioService extends MoveNodeService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void batchUpdateSchedule(List<ApiScenario> apiScenarioList, boolean isScheudleOpen, String userId) {
|
||||||
|
//批量编辑定时任务
|
||||||
|
for (ApiScenario apiScenario : apiScenarioList) {
|
||||||
|
scheduleService.updateIfExist(apiScenario.getId(), isScheudleOpen, ApiScenarioScheduleJob.getJobKey(apiScenario.getId()),
|
||||||
|
ApiScenarioScheduleJob.getTriggerKey(apiScenario.getId()), ApiScenarioScheduleJob.class, userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 场景统计相关
|
// 场景统计相关
|
||||||
public List<ApiScenarioDTO> calculateRate(List<String> ids) {
|
public List<ApiScenarioDTO> calculateRate(List<String> ids) {
|
||||||
|
@ -2619,4 +2623,47 @@ public class ApiScenarioService extends MoveNodeService {
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<String> selectApiIdInCustomRequest(String projectId, List<ApiDefinition> apiDefinitions) {
|
||||||
|
List<String> returnList = new ArrayList<>();
|
||||||
|
List<ApiScenarioStep> stepConfigList = extApiScenarioStepMapper.selectCustomRequestConfigByProjectId(projectId);
|
||||||
|
List<String> requestIdList = new ArrayList<>();
|
||||||
|
stepConfigList.forEach(step -> requestIdList.add(step.getId()));
|
||||||
|
if (requestIdList.isEmpty()) {
|
||||||
|
return returnList;
|
||||||
|
}
|
||||||
|
ApiScenarioStepBlobExample scenarioStepBlobExample = new ApiScenarioStepBlobExample();
|
||||||
|
scenarioStepBlobExample.createCriteria().andIdIn(requestIdList);
|
||||||
|
List<ApiScenarioStepBlob> httpRequestStopBlobList = apiScenarioStepBlobMapper.selectByExampleWithBLOBs(scenarioStepBlobExample);
|
||||||
|
Map<String, List<String>> methodPathMap = new HashMap<>();
|
||||||
|
httpRequestStopBlobList.forEach(blob -> {
|
||||||
|
if (blob.getContent() != null) {
|
||||||
|
try {
|
||||||
|
AbstractMsProtocolTestElement protocolTestElement = ApiDataUtils.parseObject(new String(blob.getContent()), AbstractMsProtocolTestElement.class);
|
||||||
|
if (protocolTestElement instanceof MsHTTPElement msHTTPElement) {
|
||||||
|
String method = msHTTPElement.getMethod();
|
||||||
|
if (methodPathMap.containsKey(method)) {
|
||||||
|
methodPathMap.get(method).add(msHTTPElement.getPath());
|
||||||
|
} else {
|
||||||
|
List<String> pathList = new ArrayList<>();
|
||||||
|
pathList.add(msHTTPElement.getPath());
|
||||||
|
methodPathMap.put(method, pathList);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LogUtils.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
for (ApiDefinition apiDefinition : apiDefinitions) {
|
||||||
|
if (methodPathMap.containsKey(apiDefinition.getMethod())) {
|
||||||
|
String apiPath = apiDefinition.getPath();
|
||||||
|
List<String> customUrlList = methodPathMap.get(apiDefinition.getMethod());
|
||||||
|
if (ApiDefinitionUtils.isUrlInList(apiPath, customUrlList)) {
|
||||||
|
returnList.add(apiDefinition.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return returnList;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
package io.metersphere.api.utils;
|
||||||
|
|
||||||
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
public class ApiDefinitionUtils {
|
||||||
|
|
||||||
|
public static boolean isUrlInList(String apiUrl, Collection<String> customRequestUrlList) {
|
||||||
|
if (CollectionUtils.isEmpty(customRequestUrlList)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String urlSuffix = apiUrl.trim();
|
||||||
|
if (urlSuffix.startsWith("/")) {
|
||||||
|
urlSuffix = urlSuffix.substring(1);
|
||||||
|
}
|
||||||
|
String[] urlParams = urlSuffix.split("/");
|
||||||
|
|
||||||
|
for (String customRequestUrl : customRequestUrlList) {
|
||||||
|
if (StringUtils.equalsAny(customRequestUrl, apiUrl, "/" + apiUrl)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
if (StringUtils.isEmpty(customRequestUrl)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (customRequestUrl.startsWith("/")) {
|
||||||
|
customRequestUrl = customRequestUrl.substring(1);
|
||||||
|
}
|
||||||
|
if (StringUtils.isNotEmpty(customRequestUrl)) {
|
||||||
|
String[] customUrlArr = customRequestUrl.split("/");
|
||||||
|
|
||||||
|
if (customUrlArr.length >= urlParams.length) {
|
||||||
|
|
||||||
|
boolean isFetch = true;
|
||||||
|
for (int urlIndex = 0; urlIndex < urlParams.length; urlIndex++) {
|
||||||
|
String urlItem = urlParams[urlIndex];
|
||||||
|
String customUrlItem = customUrlArr[customUrlArr.length - urlParams.length + urlIndex];
|
||||||
|
// 不为rest参数的要进行全匹配。 而且忽略大小写
|
||||||
|
if (isRestUrlParam(customUrlItem) && isRestUrlParam(urlItem)) {
|
||||||
|
if (!StringUtils.equalsIgnoreCase(customUrlItem, urlItem)) {
|
||||||
|
isFetch = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isFetch) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isRestUrlParam(String urlParam) {
|
||||||
|
return !StringUtils.startsWith(urlParam, "{") || !StringUtils.endsWith(urlParam, "}") || StringUtils.equals(urlParam, "{}");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,237 @@
|
||||||
|
package io.metersphere.api.controller;
|
||||||
|
|
||||||
|
import io.metersphere.api.constants.*;
|
||||||
|
import io.metersphere.api.domain.ApiDefinition;
|
||||||
|
import io.metersphere.api.domain.ApiDefinitionExample;
|
||||||
|
import io.metersphere.api.domain.ApiScenario;
|
||||||
|
import io.metersphere.api.domain.ApiTestCase;
|
||||||
|
import io.metersphere.api.dto.definition.ApiCoverageDTO;
|
||||||
|
import io.metersphere.api.dto.definition.ApiDefinitionAddRequest;
|
||||||
|
import io.metersphere.api.dto.definition.ApiTestCaseAddRequest;
|
||||||
|
import io.metersphere.api.dto.definition.HttpResponse;
|
||||||
|
import io.metersphere.api.dto.request.http.MsHTTPElement;
|
||||||
|
import io.metersphere.api.dto.scenario.ApiScenarioAddRequest;
|
||||||
|
import io.metersphere.api.dto.scenario.ApiScenarioStepRequest;
|
||||||
|
import io.metersphere.api.dto.scenario.ApiScenarioUpdateRequest;
|
||||||
|
import io.metersphere.api.mapper.ApiDefinitionMapper;
|
||||||
|
import io.metersphere.api.utils.ApiDataUtils;
|
||||||
|
import io.metersphere.project.domain.Project;
|
||||||
|
import io.metersphere.project.mapper.ExtBaseProjectVersionMapper;
|
||||||
|
import io.metersphere.project.mapper.ProjectMapper;
|
||||||
|
import io.metersphere.sdk.util.CalculateUtils;
|
||||||
|
import io.metersphere.sdk.util.JSON;
|
||||||
|
import io.metersphere.system.base.BaseTest;
|
||||||
|
import io.metersphere.system.dto.AddProjectRequest;
|
||||||
|
import io.metersphere.system.log.constants.OperationLogModule;
|
||||||
|
import io.metersphere.system.service.CommonProjectService;
|
||||||
|
import io.metersphere.system.uid.IDGenerator;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import org.junit.jupiter.api.*;
|
||||||
|
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.test.web.servlet.MvcResult;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||||
|
@AutoConfigureMockMvc
|
||||||
|
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||||
|
public class ApiCalculateTest extends BaseTest {
|
||||||
|
private static Project project;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private CommonProjectService commonProjectService;
|
||||||
|
@Resource
|
||||||
|
private ExtBaseProjectVersionMapper extBaseProjectVersionMapper;
|
||||||
|
@Resource
|
||||||
|
private ProjectMapper projectMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ApiDefinitionMapper apiDefinitionMapper;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void initTestData() throws Exception {
|
||||||
|
//测试计划专用项目
|
||||||
|
if (project == null) {
|
||||||
|
AddProjectRequest initProject = new AddProjectRequest();
|
||||||
|
initProject.setOrganizationId("100001");
|
||||||
|
initProject.setName("文件管理专用项目");
|
||||||
|
initProject.setDescription("建国创建的文件管理专用项目");
|
||||||
|
initProject.setEnable(true);
|
||||||
|
initProject.setUserIds(List.of("admin"));
|
||||||
|
project = commonProjectService.add(initProject, "admin", "/organization-project/add", OperationLogModule.SETTING_ORGANIZATION_PROJECT);
|
||||||
|
|
||||||
|
ArrayList<String> moduleList = new ArrayList<>(List.of(new String[]{"workstation", "testPlan", "bugManagement", "caseManagement", "apiTest", "uiTest", "loadTest"}));
|
||||||
|
Project updateProject = new Project();
|
||||||
|
updateProject.setId(project.getId());
|
||||||
|
updateProject.setModuleSetting(JSON.toJSONString(moduleList));
|
||||||
|
projectMapper.updateByPrimaryKeySelective(updateProject);
|
||||||
|
|
||||||
|
|
||||||
|
ApiScenarioAddRequest apiScenarioAddRequest = new ApiScenarioAddRequest();
|
||||||
|
apiScenarioAddRequest.setProjectId(project.getId());
|
||||||
|
apiScenarioAddRequest.setDescription("desc");
|
||||||
|
apiScenarioAddRequest.setName("test name");
|
||||||
|
apiScenarioAddRequest.setModuleId("default");
|
||||||
|
apiScenarioAddRequest.setGrouped(false);
|
||||||
|
apiScenarioAddRequest.setTags(List.of("tag1", "tag2"));
|
||||||
|
apiScenarioAddRequest.setPriority("P0");
|
||||||
|
apiScenarioAddRequest.setStatus(ApiScenarioStatus.COMPLETED.name());
|
||||||
|
apiScenarioAddRequest.setSteps(new ArrayList<>());
|
||||||
|
MvcResult scenarioMvcResult = this.requestPostWithOkAndReturn("/api/scenario/add", apiScenarioAddRequest);
|
||||||
|
ApiScenario scenario = getResultData(scenarioMvcResult, ApiScenario.class);
|
||||||
|
|
||||||
|
|
||||||
|
// 创建接口定义
|
||||||
|
Map<String, List<String>> methodAndPath = Map.of(
|
||||||
|
"GET", List.of(
|
||||||
|
"/api/get-test/1",
|
||||||
|
"/api/get-test/2",
|
||||||
|
"/api/{/get-test}/3/withCase",
|
||||||
|
"/api/get-test/4/withCase",// 场景关联它的用例
|
||||||
|
"/api/get-test/5/never-compare",
|
||||||
|
"/{api}/{/get-test}/{6}",//这个一定会被匹配到
|
||||||
|
"/api/get-test/{7}",// 这个一定会被匹配到
|
||||||
|
"/api/get-test/8",// 场景关联它的自定义请求
|
||||||
|
"/api/get-test/9",// 场景关联这个接口
|
||||||
|
"/api/get-test/10"),
|
||||||
|
"POST", List.of(
|
||||||
|
"/post/api/test/1",
|
||||||
|
"/post/api/test/2",
|
||||||
|
"/post/api/{test}/3/withCase",
|
||||||
|
"/post/api/test/4/withCase",// 场景关联它的用例
|
||||||
|
"/post/api/test/5/never-compare",
|
||||||
|
"/{post}/{api}/{/get-test}/{6}", //这个一定会被匹配到
|
||||||
|
"/post/api/test/{7}", // 这个一定会被匹配到
|
||||||
|
"/post/api/test/8", // 场景关联它的自定义请求
|
||||||
|
"/post/api/test/9",// 场景关联这个接口
|
||||||
|
"/post/api/test/10"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
List<ApiScenarioStepRequest> steps = new ArrayList<>();
|
||||||
|
Map<String, Object> steptDetailMap = new HashMap<>();
|
||||||
|
// 创建接口用例
|
||||||
|
for (Map.Entry<String, List<String>> entry : methodAndPath.entrySet()) {
|
||||||
|
String method = entry.getKey();
|
||||||
|
for (String path : entry.getValue()) {
|
||||||
|
// 创建接口定义
|
||||||
|
String defaultVersion = extBaseProjectVersionMapper.getDefaultVersion(project.getId());
|
||||||
|
ApiDefinitionAddRequest apiDefinitionAddRequest = new ApiDefinitionAddRequest();
|
||||||
|
apiDefinitionAddRequest.setName(method + "_" + path);
|
||||||
|
apiDefinitionAddRequest.setProtocol(ApiConstants.HTTP_PROTOCOL);
|
||||||
|
apiDefinitionAddRequest.setProjectId(project.getId());
|
||||||
|
apiDefinitionAddRequest.setMethod(method);
|
||||||
|
apiDefinitionAddRequest.setPath(path);
|
||||||
|
apiDefinitionAddRequest.setStatus(ApiDefinitionStatus.PROCESSING.name());
|
||||||
|
apiDefinitionAddRequest.setModuleId("default");
|
||||||
|
apiDefinitionAddRequest.setVersionId(defaultVersion);
|
||||||
|
apiDefinitionAddRequest.setDescription("描述内容");
|
||||||
|
apiDefinitionAddRequest.setTags(new LinkedHashSet<>(List.of("tag1", "tag2")));
|
||||||
|
|
||||||
|
MsHTTPElement msHttpElement = MsHTTPElementTest.getMsHttpElement();
|
||||||
|
msHttpElement.setBody(MsHTTPElementTest.getGeneralBody());
|
||||||
|
msHttpElement.setMethod(method);
|
||||||
|
msHttpElement.setPath(path);
|
||||||
|
|
||||||
|
apiDefinitionAddRequest.setRequest(JSON.parseObject(ApiDataUtils.toJSONString(msHttpElement)));
|
||||||
|
List<HttpResponse> msHttpResponse = MsHTTPElementTest.getMsHttpResponse();
|
||||||
|
apiDefinitionAddRequest.setResponse(msHttpResponse);
|
||||||
|
|
||||||
|
MvcResult mvcResult = this.requestPostWithOkAndReturn("/api/definition/add", apiDefinitionAddRequest);
|
||||||
|
ApiDefinition resultData = getResultData(mvcResult, ApiDefinition.class);
|
||||||
|
|
||||||
|
if (path.endsWith("/withCase")) {
|
||||||
|
ApiTestCaseAddRequest request = new ApiTestCaseAddRequest();
|
||||||
|
request.setApiDefinitionId(resultData.getId());
|
||||||
|
request.setName(resultData.getName() + "_case");
|
||||||
|
request.setProjectId(project.getId());
|
||||||
|
request.setPriority("P0");
|
||||||
|
request.setStatus(ApiDefinitionStatus.PROCESSING.name());
|
||||||
|
request.setTags(new LinkedHashSet<>(List.of("tag1", "tag2")));
|
||||||
|
request.setRequest(JSON.parseObject(ApiDataUtils.toJSONString(msHttpElement)));
|
||||||
|
MvcResult testCaseResult = this.requestPostWithOkAndReturn("/api/case/add", request);
|
||||||
|
ApiTestCase apiTestCase = getResultData(testCaseResult, ApiTestCase.class);
|
||||||
|
|
||||||
|
if (path.endsWith("/4/withCase")) {
|
||||||
|
ApiScenarioStepRequest stepRequest = new ApiScenarioStepRequest();
|
||||||
|
stepRequest.setId(IDGenerator.nextStr());
|
||||||
|
stepRequest.setVersionId(extBaseProjectVersionMapper.getDefaultVersion(project.getId()));
|
||||||
|
stepRequest.setConfig(new HashMap<>());
|
||||||
|
stepRequest.setEnable(true);
|
||||||
|
stepRequest.setStepType(ApiScenarioStepType.API_CASE.name());
|
||||||
|
stepRequest.setResourceId(apiTestCase.getId());
|
||||||
|
stepRequest.setName(apiTestCase.getName() + "_step");
|
||||||
|
stepRequest.setRefType(ApiScenarioStepRefType.REF.name());
|
||||||
|
stepRequest.setProjectId(project.getId());
|
||||||
|
steps.add(stepRequest);
|
||||||
|
steptDetailMap.put(stepRequest.getId(), JSON.parseObject(ApiDataUtils.toJSONString(msHttpElement)));
|
||||||
|
}
|
||||||
|
} else if (path.endsWith("/8")) {
|
||||||
|
|
||||||
|
ApiScenarioStepRequest stepRequest = new ApiScenarioStepRequest();
|
||||||
|
stepRequest.setId(IDGenerator.nextStr());
|
||||||
|
stepRequest.setVersionId(extBaseProjectVersionMapper.getDefaultVersion(project.getId()));
|
||||||
|
stepRequest.setConfig(new HashMap<>());
|
||||||
|
stepRequest.setEnable(true);
|
||||||
|
stepRequest.setStepType(ApiScenarioStepType.CUSTOM_REQUEST.name());
|
||||||
|
stepRequest.setName("custom_step");
|
||||||
|
stepRequest.setRefType(ApiScenarioStepRefType.DIRECT.name());
|
||||||
|
stepRequest.setProjectId(project.getId());
|
||||||
|
steps.add(stepRequest);
|
||||||
|
|
||||||
|
MsHTTPElement customElement = MsHTTPElementTest.getMsHttpElement();
|
||||||
|
customElement.setBody(MsHTTPElementTest.getGeneralBody());
|
||||||
|
customElement.setMethod(method);
|
||||||
|
customElement.setPath(path);
|
||||||
|
steptDetailMap.put(stepRequest.getId(), JSON.parseObject(ApiDataUtils.toJSONString(customElement)));
|
||||||
|
} else if (path.endsWith("/9")) {
|
||||||
|
ApiScenarioStepRequest stepRequest = new ApiScenarioStepRequest();
|
||||||
|
stepRequest.setId(IDGenerator.nextStr());
|
||||||
|
stepRequest.setVersionId(extBaseProjectVersionMapper.getDefaultVersion(project.getId()));
|
||||||
|
stepRequest.setConfig(new HashMap<>());
|
||||||
|
stepRequest.setEnable(true);
|
||||||
|
stepRequest.setStepType(ApiScenarioStepType.API.name());
|
||||||
|
stepRequest.setResourceId(resultData.getId());
|
||||||
|
stepRequest.setName(resultData.getName() + "_step");
|
||||||
|
stepRequest.setRefType(ApiScenarioStepRefType.REF.name());
|
||||||
|
stepRequest.setProjectId(project.getId());
|
||||||
|
steps.add(stepRequest);
|
||||||
|
steptDetailMap.put(stepRequest.getId(), JSON.parseObject(ApiDataUtils.toJSONString(msHttpElement)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ApiScenarioUpdateRequest apiScenarioUpdateRequest = new ApiScenarioUpdateRequest();
|
||||||
|
apiScenarioUpdateRequest.setId(scenario.getId());
|
||||||
|
apiScenarioUpdateRequest.setProjectId(project.getId());
|
||||||
|
apiScenarioUpdateRequest.setSteps(steps);
|
||||||
|
apiScenarioUpdateRequest.setStepDetails(steptDetailMap);
|
||||||
|
this.requestPostWithOkAndReturn("/api/scenario/update", apiScenarioUpdateRequest);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void calculateTest() throws Exception {
|
||||||
|
ApiDefinitionExample apiDefinitionExample = new ApiDefinitionExample();
|
||||||
|
apiDefinitionExample.createCriteria().andProjectIdEqualTo(project.getId());
|
||||||
|
Assertions.assertEquals(apiDefinitionMapper.countByExample(apiDefinitionExample), 20);
|
||||||
|
|
||||||
|
MvcResult mvcResult = this.requestGetWithOkAndReturn("/api/definition/rage/" + project.getId());
|
||||||
|
ApiCoverageDTO apiCoverageDTO = getResultData(mvcResult, ApiCoverageDTO.class);
|
||||||
|
Assertions.assertEquals(apiCoverageDTO.getAllApiCount(), 20);
|
||||||
|
Assertions.assertEquals(apiCoverageDTO.getCoverWithApiCase(), 4);
|
||||||
|
Assertions.assertEquals(apiCoverageDTO.getUnCoverWithApiCase(), 16);
|
||||||
|
Assertions.assertEquals(apiCoverageDTO.getApiCaseCoverage(), CalculateUtils.reportPercentage(apiCoverageDTO.getCoverWithApiCase(), apiCoverageDTO.getAllApiCount()));
|
||||||
|
|
||||||
|
Assertions.assertEquals(apiCoverageDTO.getCoverWithApiScenario(), 8);
|
||||||
|
Assertions.assertEquals(apiCoverageDTO.getUnCoverWithApiScenario(), 12);
|
||||||
|
Assertions.assertEquals(apiCoverageDTO.getScenarioCoverage(), CalculateUtils.reportPercentage(apiCoverageDTO.getCoverWithApiScenario(), apiCoverageDTO.getAllApiCount()));
|
||||||
|
|
||||||
|
Assertions.assertEquals(apiCoverageDTO.getCoverWithApiDefinition(), 10);
|
||||||
|
Assertions.assertEquals(apiCoverageDTO.getUnCoverWithApiDefinition(), 10);
|
||||||
|
Assertions.assertEquals(apiCoverageDTO.getApiCoverage(), CalculateUtils.reportPercentage(apiCoverageDTO.getCoverWithApiDefinition(), apiCoverageDTO.getAllApiCount()));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -173,7 +173,7 @@ public class ApiReportControllerTests extends BaseTest {
|
||||||
Assertions.assertNotNull(returnPager);
|
Assertions.assertNotNull(returnPager);
|
||||||
//返回值的页码和当前页码相同
|
//返回值的页码和当前页码相同
|
||||||
Assertions.assertEquals(returnPager.getCurrent(), request.getCurrent());
|
Assertions.assertEquals(returnPager.getCurrent(), request.getCurrent());
|
||||||
;
|
|
||||||
//返回的数据量不超过规定要返回的数据量相同
|
//返回的数据量不超过规定要返回的数据量相同
|
||||||
Assertions.assertTrue(((List<ApiScenarioDTO>) returnPager.getList()).size() <= request.getPageSize());
|
Assertions.assertTrue(((List<ApiScenarioDTO>) returnPager.getList()).size() <= request.getPageSize());
|
||||||
//过滤
|
//过滤
|
||||||
|
|
|
@ -2144,6 +2144,18 @@ public class ApiScenarioControllerTests extends BaseTest {
|
||||||
batchRequest.setEnable(false);
|
batchRequest.setEnable(false);
|
||||||
this.requestPostWithOk(batchUrl, batchRequest);
|
this.requestPostWithOk(batchUrl, batchRequest);
|
||||||
apiScenarioBatchOperationTestService.checkSchedule(BATCH_OPERATION_SCENARIO_ID.getFirst(), batchRequest.isEnable());
|
apiScenarioBatchOperationTestService.checkSchedule(BATCH_OPERATION_SCENARIO_ID.getFirst(), batchRequest.isEnable());
|
||||||
|
|
||||||
|
// 仅仅是开启/关闭
|
||||||
|
batchRequest = new ApiScenarioBatchScheduleConfigRequest();
|
||||||
|
batchRequest.setProjectId(DEFAULT_PROJECT_ID);
|
||||||
|
batchRequest.setSelectIds(List.of(BATCH_OPERATION_SCENARIO_ID.getFirst()));
|
||||||
|
batchRequest.setEnable(true);
|
||||||
|
this.requestPostWithOk(batchUrl, batchRequest);
|
||||||
|
apiScenarioBatchOperationTestService.checkSchedule(BATCH_OPERATION_SCENARIO_ID.getFirst(), batchRequest.isEnable());
|
||||||
|
batchRequest.setEnable(false);
|
||||||
|
this.requestPostWithOk(batchUrl, batchRequest);
|
||||||
|
apiScenarioBatchOperationTestService.checkSchedule(BATCH_OPERATION_SCENARIO_ID.getFirst(), batchRequest.isEnable());
|
||||||
|
|
||||||
//增加日志检查
|
//增加日志检查
|
||||||
LOG_CHECK_LIST.add(
|
LOG_CHECK_LIST.add(
|
||||||
new CheckLogModel(scenarioId, OperationLogType.UPDATE, "/api/scenario/schedule-config")
|
new CheckLogModel(scenarioId, OperationLogType.UPDATE, "/api/scenario/schedule-config")
|
||||||
|
@ -2151,17 +2163,6 @@ public class ApiScenarioControllerTests extends BaseTest {
|
||||||
LOG_CHECK_LIST.add(
|
LOG_CHECK_LIST.add(
|
||||||
new CheckLogModel(BATCH_OPERATION_SCENARIO_ID.getFirst(), OperationLogType.UPDATE, "/api/scenario/batch-operation/schedule-config")
|
new CheckLogModel(BATCH_OPERATION_SCENARIO_ID.getFirst(), OperationLogType.UPDATE, "/api/scenario/batch-operation/schedule-config")
|
||||||
);
|
);
|
||||||
// 批量定时任务的开关
|
|
||||||
ApiScenarioBatchEditRequest batchEditRequest = new ApiScenarioBatchEditRequest();
|
|
||||||
batchEditRequest.setProjectId(DEFAULT_PROJECT_ID);
|
|
||||||
batchEditRequest.setType("Schedule");
|
|
||||||
batchEditRequest.setScheduleOpen(true);
|
|
||||||
batchEditRequest.setSelectIds(List.of(BATCH_OPERATION_SCENARIO_ID.getFirst()));
|
|
||||||
requestPostAndReturn(BATCH_EDIT, batchEditRequest);
|
|
||||||
apiScenarioBatchOperationTestService.checkSchedule(BATCH_OPERATION_SCENARIO_ID.getFirst(), batchEditRequest.isScheduleOpen());
|
|
||||||
batchEditRequest.setScheduleOpen(false);
|
|
||||||
requestPostAndReturn(BATCH_EDIT, batchEditRequest);
|
|
||||||
apiScenarioBatchOperationTestService.checkSchedule(BATCH_OPERATION_SCENARIO_ID.getFirst(), batchEditRequest.isScheduleOpen());
|
|
||||||
//关闭
|
//关闭
|
||||||
request.setEnable(false);
|
request.setEnable(false);
|
||||||
result = this.requestPostAndReturn(testUrl, request);
|
result = this.requestPostAndReturn(testUrl, request);
|
||||||
|
@ -2240,11 +2241,6 @@ public class ApiScenarioControllerTests extends BaseTest {
|
||||||
request.setScenarioId(IDGenerator.nextStr());
|
request.setScenarioId(IDGenerator.nextStr());
|
||||||
this.requestPost(testUrl, request).andExpect(status().is5xxServerError());
|
this.requestPost(testUrl, request).andExpect(status().is5xxServerError());
|
||||||
|
|
||||||
//反例:不配置cron表达式
|
|
||||||
request = new ApiScenarioScheduleConfigRequest();
|
|
||||||
request.setScenarioId(scenarioId);
|
|
||||||
this.requestPost(testUrl, request).andExpect(status().isBadRequest());
|
|
||||||
|
|
||||||
//反例:配置错误的cron表达式,测试是否会关闭定时任务
|
//反例:配置错误的cron表达式,测试是否会关闭定时任务
|
||||||
request = new ApiScenarioScheduleConfigRequest();
|
request = new ApiScenarioScheduleConfigRequest();
|
||||||
request.setScenarioId(scenarioId);
|
request.setScenarioId(scenarioId);
|
||||||
|
|
|
@ -6,18 +6,7 @@
|
||||||
class="ms-modal-upload ms-modal-medium"
|
class="ms-modal-upload ms-modal-medium"
|
||||||
:width="400"
|
:width="400"
|
||||||
>
|
>
|
||||||
<div class="mb-[16px] flex items-center gap-[16px]">
|
<div class="mb-[16px] flex items-center gap-[8px]">
|
||||||
<div
|
|
||||||
v-for="item of platformList"
|
|
||||||
:key="item.value"
|
|
||||||
:class="`import-item ${exportPlatform === item.value ? 'import-item--active' : ''}`"
|
|
||||||
@click="() => setActiveImportFormat(item.value)"
|
|
||||||
>
|
|
||||||
<div class="text-[var(--color-text-1)]">{{ item.name }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-show="exportPlatform === 'MeterSphere'" class="mb-[16px] flex items-center gap-[8px]">
|
|
||||||
<a-switch v-model:model-value="exportTypeRadio" size="small"></a-switch>
|
<a-switch v-model:model-value="exportTypeRadio" size="small"></a-switch>
|
||||||
{{ t('apiScenario.export.type.all') }}
|
{{ t('apiScenario.export.type.all') }}
|
||||||
<a-tooltip :content="t('apiScenario.export.simple.tooltip')" position="tl">
|
<a-tooltip :content="t('apiScenario.export.simple.tooltip')" position="tl">
|
||||||
|
@ -58,18 +47,6 @@
|
||||||
import useAppStore from '@/store/modules/app';
|
import useAppStore from '@/store/modules/app';
|
||||||
import { downloadByteFile, getGenerateId } from '@/utils';
|
import { downloadByteFile, getGenerateId } from '@/utils';
|
||||||
|
|
||||||
import { RequestImportFormat } from '@/enums/apiEnum';
|
|
||||||
|
|
||||||
const platformList: { name: string; value: RequestImportFormat.MeterSphere | RequestImportFormat.Jmeter }[] = [
|
|
||||||
{
|
|
||||||
name: 'MeterSphere',
|
|
||||||
value: RequestImportFormat.MeterSphere,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Jmeter',
|
|
||||||
value: RequestImportFormat.Jmeter,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
@ -84,13 +61,9 @@
|
||||||
|
|
||||||
const exportLoading = ref(false);
|
const exportLoading = ref(false);
|
||||||
const exportTypeRadio = ref(false);
|
const exportTypeRadio = ref(false);
|
||||||
const exportPlatform = ref(RequestImportFormat.MeterSphere);
|
|
||||||
function cancelExport() {
|
function cancelExport() {
|
||||||
visible.value = false;
|
visible.value = false;
|
||||||
}
|
}
|
||||||
function setActiveImportFormat(format: RequestImportFormat.MeterSphere | RequestImportFormat.Jmeter) {
|
|
||||||
exportPlatform.value = format;
|
|
||||||
}
|
|
||||||
const websocket = ref<WebSocket>();
|
const websocket = ref<WebSocket>();
|
||||||
const reportId = ref('');
|
const reportId = ref('');
|
||||||
const isShowExportingMessage = ref(false); // 正在导出提示显示中
|
const isShowExportingMessage = ref(false); // 正在导出提示显示中
|
||||||
|
@ -217,7 +190,7 @@
|
||||||
sort: props.sorter || {},
|
sort: props.sorter || {},
|
||||||
fileId: reportId.value,
|
fileId: reportId.value,
|
||||||
},
|
},
|
||||||
exportPlatform.value
|
'METERSPHERE'
|
||||||
);
|
);
|
||||||
showExportingMessage(res);
|
showExportingMessage(res);
|
||||||
visible.value = false;
|
visible.value = false;
|
||||||
|
|
Loading…
Reference in New Issue