feat(功能用例): 新增功能用例关联取消关联接口用例

This commit is contained in:
guoyuqi 2023-12-29 19:13:15 +08:00 committed by Craftsman
parent eecdff9846
commit e9cc16cbc3
7 changed files with 166 additions and 3 deletions

View File

@ -73,6 +73,13 @@ public class AssociateApiProviderTest extends BaseTest {
request.setSelectIds(List.of("gyq_associate_api_case_id_1")); request.setSelectIds(List.of("gyq_associate_api_case_id_1"));
selectIds = provider.getSelectIds(request, false); selectIds = provider.getSelectIds(request, false);
Assertions.assertTrue(CollectionUtils.isNotEmpty(selectIds)); Assertions.assertTrue(CollectionUtils.isNotEmpty(selectIds));
request.setSourceType("API");
request.setSourceId("gyq_associate_case_id_1");
request.setSelectAll(true);
request.setProjectId("project-associate-case-test");
selectIds = provider.getSelectIds(request, false);
Assertions.assertTrue(CollectionUtils.isNotEmpty(selectIds));
} }

View File

@ -3,12 +3,14 @@ package io.metersphere.functional.controller;
import com.github.pagehelper.Page; import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageHelper;
import io.metersphere.dto.TestCaseProviderDTO; import io.metersphere.dto.TestCaseProviderDTO;
import io.metersphere.functional.request.CaseApiModuleRequest;
import io.metersphere.functional.request.FunctionalTestCaseDisassociateRequest; import io.metersphere.functional.request.FunctionalTestCaseDisassociateRequest;
import io.metersphere.functional.service.FunctionalTestCaseService; import io.metersphere.functional.service.FunctionalTestCaseService;
import io.metersphere.request.ApiModuleProviderRequest; import io.metersphere.request.ApiModuleProviderRequest;
import io.metersphere.request.TestCasePageProviderRequest;
import io.metersphere.request.AssociateOtherCaseRequest; import io.metersphere.request.AssociateOtherCaseRequest;
import io.metersphere.request.TestCasePageProviderRequest;
import io.metersphere.sdk.constants.PermissionConstants; import io.metersphere.sdk.constants.PermissionConstants;
import io.metersphere.system.dto.sdk.BaseTreeNode;
import io.metersphere.system.security.CheckOwner; import io.metersphere.system.security.CheckOwner;
import io.metersphere.system.utils.PageUtils; import io.metersphere.system.utils.PageUtils;
import io.metersphere.system.utils.Pager; import io.metersphere.system.utils.Pager;
@ -54,6 +56,14 @@ public class FunctionalTestCaseController {
return functionalTestCaseService.moduleCount(request, false); return functionalTestCaseService.moduleCount(request, false);
} }
@PostMapping("/associate/api/module/tree")
@Operation(summary = "接口测试-接口管理-模块-查找模块")
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_READ)
@CheckOwner(resourceId = "#request.projectId", resourceType = "project")
public List<BaseTreeNode> getTree(@RequestBody @Validated CaseApiModuleRequest request) {
return functionalTestCaseService.getTree(request);
}
@PostMapping("/associate/case") @PostMapping("/associate/case")
@Operation(summary = "用例管理-功能用例-关联其他用例-关联用例") @Operation(summary = "用例管理-功能用例-关联其他用例-关联用例")
@RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_READ) @RequiresPermissions(PermissionConstants.PROJECT_API_DEFINITION_READ)

View File

@ -1,5 +1,6 @@
package io.metersphere.functional.mapper; package io.metersphere.functional.mapper;
import io.metersphere.functional.request.CaseApiModuleRequest;
import io.metersphere.project.dto.NodeSortQueryParam; import io.metersphere.project.dto.NodeSortQueryParam;
import io.metersphere.system.dto.sdk.BaseModule; import io.metersphere.system.dto.sdk.BaseModule;
import io.metersphere.system.dto.sdk.BaseTreeNode; import io.metersphere.system.dto.sdk.BaseTreeNode;
@ -24,4 +25,7 @@ public interface ExtFunctionalCaseModuleMapper {
List<BaseTreeNode> selectIdAndParentIdByProjectId(String projectId); List<BaseTreeNode> selectIdAndParentIdByProjectId(String projectId);
List<BaseTreeNode> selectApiCaseModuleByRequest(@Param("request") CaseApiModuleRequest request);
} }

View File

@ -61,4 +61,34 @@
FROM functional_case_module FROM functional_case_module
WHERE project_id = #{0} WHERE project_id = #{0}
</select> </select>
<select id="selectApiCaseModuleByRequest" resultType="io.metersphere.system.dto.sdk.BaseTreeNode">
SELECT m.id,
m.parent_id AS parentId,
m.name,
m.pos,
m.project_id,
'MODULE' AS type
FROM api_definition_module m
<include refid="module_request"/>
ORDER BY pos
</select>
<sql id="module_request">
<where>
<if test="request.projectId != null and request.projectId != ''">
AND m.project_id = #{request.projectId}
</if>
<if test="request.keyword != null and request.keyword != ''">
AND m.name like CONCAT('%', #{request.keyword},'%')
</if>
<if test="request.moduleIds != null and request.moduleIds.size() != 0">
AND m.module_id IN
<foreach collection="request.moduleIds" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>
</where>
</sql>
</mapper> </mapper>

View File

@ -0,0 +1,38 @@
package io.metersphere.functional.request;
import io.metersphere.sdk.constants.ModuleConstants;
import io.metersphere.system.dto.sdk.BaseCondition;
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;
@Data
public class CaseApiModuleRequest extends BaseCondition {
@Schema(description = "模块ID(根据模块树查询时要把当前节点以及子节点都放在这里。)")
private List<@NotBlank String> moduleIds;
@Schema(description = "协议", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_definition_module.protocol.not_blank}")
@Size(min = 1, max = 20, message = "{api_definition_module.protocol.length_range}")
private String protocol = ModuleConstants.NODE_PROTOCOL_HTTP;
@Schema(description = "项目ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{api_definition_module.project_id.not_blank}")
@Size(min = 1, max = 50, message = "{api_definition_module.project_id.length_range}")
private String projectId;
@Schema(description = "关键字")
private String keyword;
@Schema(description = "版本fk")
private String versionId;
@Schema(description = "版本引用fk")
private String refId;
@Schema(description = "关联用例的类型(API,SCENARIO,UI,PERFORMANCE)", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{functional_test_case_disassociate_request.type.not_blank}")
private String sourceType;
}

View File

@ -4,14 +4,18 @@ import io.metersphere.dto.TestCaseProviderDTO;
import io.metersphere.functional.constants.AssociateCaseType; import io.metersphere.functional.constants.AssociateCaseType;
import io.metersphere.functional.domain.FunctionalCaseTest; import io.metersphere.functional.domain.FunctionalCaseTest;
import io.metersphere.functional.domain.FunctionalCaseTestExample; import io.metersphere.functional.domain.FunctionalCaseTestExample;
import io.metersphere.functional.mapper.ExtFunctionalCaseModuleMapper;
import io.metersphere.functional.mapper.ExtFunctionalCaseTestMapper; import io.metersphere.functional.mapper.ExtFunctionalCaseTestMapper;
import io.metersphere.functional.mapper.FunctionalCaseTestMapper; import io.metersphere.functional.mapper.FunctionalCaseTestMapper;
import io.metersphere.functional.request.CaseApiModuleRequest;
import io.metersphere.functional.request.FunctionalTestCaseDisassociateRequest; import io.metersphere.functional.request.FunctionalTestCaseDisassociateRequest;
import io.metersphere.provider.BaseAssociateApiProvider; import io.metersphere.provider.BaseAssociateApiProvider;
import io.metersphere.request.ApiModuleProviderRequest; import io.metersphere.request.ApiModuleProviderRequest;
import io.metersphere.request.TestCasePageProviderRequest;
import io.metersphere.request.AssociateOtherCaseRequest; import io.metersphere.request.AssociateOtherCaseRequest;
import io.metersphere.request.TestCasePageProviderRequest;
import io.metersphere.sdk.util.LogUtils; import io.metersphere.sdk.util.LogUtils;
import io.metersphere.sdk.util.Translator;
import io.metersphere.system.dto.sdk.BaseTreeNode;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.apache.ibatis.session.ExecutorType; import org.apache.ibatis.session.ExecutorType;
@ -45,6 +49,15 @@ public class FunctionalTestCaseService {
@Resource @Resource
private FunctionalCaseTestMapper functionalCaseTestMapper; private FunctionalCaseTestMapper functionalCaseTestMapper;
@Resource
private FunctionalCaseModuleService functionalCaseModuleService;
@Resource
private ExtFunctionalCaseModuleMapper extFunctionalCaseModuleMapper;
private static final String UNPLANNED_API = "api_unplanned_request";
/** /**
* 获取功能用例未关联的接口用例列表 * 获取功能用例未关联的接口用例列表
* *
@ -78,6 +91,7 @@ public class FunctionalTestCaseService {
switch (request.getSourceType()) { switch (request.getSourceType()) {
case AssociateCaseType.API -> associateApi(request, deleted, userId); case AssociateCaseType.API -> associateApi(request, deleted, userId);
case AssociateCaseType.SCENARIO -> associateScenario(request, deleted, userId); case AssociateCaseType.SCENARIO -> associateScenario(request, deleted, userId);
default -> LogUtils.info("AssociateCaseType: " + request.getSourceType());
} }
@ -137,4 +151,9 @@ public class FunctionalTestCaseService {
return request.getSelectIds(); return request.getSelectIds();
} }
} }
public List<BaseTreeNode> getTree(CaseApiModuleRequest request) {
List<BaseTreeNode> fileModuleList = extFunctionalCaseModuleMapper.selectApiCaseModuleByRequest(request);
return functionalCaseModuleService.buildTreeAndCountResource(fileModuleList, true, Translator.get(UNPLANNED_API));
}
} }

View File

@ -1,9 +1,12 @@
package io.metersphere.functional.controller; package io.metersphere.functional.controller;
import io.metersphere.api.domain.ApiDefinitionModule;
import io.metersphere.api.mapper.ApiDefinitionModuleMapper;
import io.metersphere.dto.TestCaseProviderDTO; import io.metersphere.dto.TestCaseProviderDTO;
import io.metersphere.functional.constants.AssociateCaseType; import io.metersphere.functional.constants.AssociateCaseType;
import io.metersphere.functional.domain.FunctionalCaseTest; import io.metersphere.functional.domain.FunctionalCaseTest;
import io.metersphere.functional.mapper.FunctionalCaseTestMapper; import io.metersphere.functional.mapper.FunctionalCaseTestMapper;
import io.metersphere.functional.request.CaseApiModuleRequest;
import io.metersphere.functional.request.FunctionalTestCaseDisassociateRequest; import io.metersphere.functional.request.FunctionalTestCaseDisassociateRequest;
import io.metersphere.provider.BaseAssociateApiProvider; import io.metersphere.provider.BaseAssociateApiProvider;
import io.metersphere.request.ApiModuleProviderRequest; import io.metersphere.request.ApiModuleProviderRequest;
@ -12,6 +15,7 @@ import io.metersphere.request.TestCasePageProviderRequest;
import io.metersphere.sdk.util.JSON; import io.metersphere.sdk.util.JSON;
import io.metersphere.system.base.BaseTest; import io.metersphere.system.base.BaseTest;
import io.metersphere.system.controller.handler.ResultHolder; import io.metersphere.system.controller.handler.ResultHolder;
import io.metersphere.system.dto.sdk.BaseTreeNode;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.junit.jupiter.api.*; import org.junit.jupiter.api.*;
import org.mockito.Mockito; import org.mockito.Mockito;
@ -38,6 +42,7 @@ public class FunctionalTestCaseControllerTests extends BaseTest {
private static final String URL_CASE_PAGE_DISASSOCIATE = "/functional/case/test/disassociate/case"; private static final String URL_CASE_PAGE_DISASSOCIATE = "/functional/case/test/disassociate/case";
private static final String URL_CASE_MODULE_TREE = "/functional/case/test/associate/api/module/tree";
@ -47,6 +52,10 @@ public class FunctionalTestCaseControllerTests extends BaseTest {
@Resource @Resource
private FunctionalCaseTestMapper functionalCaseTestMapper; private FunctionalCaseTestMapper functionalCaseTestMapper;
@Resource
private ApiDefinitionModuleMapper apiDefinitionModuleMapper;
@Test @Test
@Order(1) @Order(1)
public void getPageSuccess() throws Exception { public void getPageSuccess() throws Exception {
@ -119,9 +128,24 @@ public class FunctionalTestCaseControllerTests extends BaseTest {
String returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8); String returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
ResultHolder resultHolder = JSON.parseObject(returnData, ResultHolder.class); ResultHolder resultHolder = JSON.parseObject(returnData, ResultHolder.class);
Assertions.assertNotNull(resultHolder); Assertions.assertNotNull(resultHolder);
List<String>operations = new ArrayList<>();
operations.add("gyq_associate_case_id_1");
Mockito.when(provider.getSelectIds(request, false)).thenReturn(operations);
Assertions.assertNotNull(resultHolder);
request.setSelectAll(false); request.setSelectAll(false);
request.setProjectId("project-associate-case-test"); request.setProjectId("project-associate-case-test");
request.setSelectIds(List.of("gyq_associate_api_case_id_1")); request.setSelectIds(operations);
mvcResult = this.requestPostWithOkAndReturn(URL_CASE_PAGE_ASSOCIATE, request);
returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
resultHolder = JSON.parseObject(returnData, ResultHolder.class);
Assertions.assertNotNull(resultHolder);
request = new AssociateOtherCaseRequest();
request.setSourceType(AssociateCaseType.SCENARIO);
request.setSourceId("gyq_associate_case_id_1");
request.setSelectAll(true);
request.setProjectId("project-associate-case-test");
request.setExcludeIds(List.of("gyq_associate_api_case_id_2"));
mvcResult = this.requestPostWithOkAndReturn(URL_CASE_PAGE_ASSOCIATE, request); mvcResult = this.requestPostWithOkAndReturn(URL_CASE_PAGE_ASSOCIATE, request);
returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8); returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
resultHolder = JSON.parseObject(returnData, ResultHolder.class); resultHolder = JSON.parseObject(returnData, ResultHolder.class);
@ -165,6 +189,37 @@ public class FunctionalTestCaseControllerTests extends BaseTest {
Assertions.assertNotNull(resultHolder); Assertions.assertNotNull(resultHolder);
} }
@Test
@Order(6)
public void getTreeSuccess() throws Exception {
ApiDefinitionModule apiDefinitionModule = new ApiDefinitionModule();
apiDefinitionModule.setId("case_module");
apiDefinitionModule.setPos(100L);
apiDefinitionModule.setName("api_case_module");
apiDefinitionModule.setParentId("NONE");
apiDefinitionModule.setProjectId("project-associate-case-test");
apiDefinitionModule.setCreateUser("admin");
apiDefinitionModule.setCreateTime(System.currentTimeMillis());
apiDefinitionModule.setUpdateUser("admin");
apiDefinitionModule.setUpdateTime(System.currentTimeMillis());
apiDefinitionModuleMapper.insert(apiDefinitionModule);
List<BaseTreeNode> moduleTreeNode = this.getModuleTreeNode();
Assertions.assertNotNull(moduleTreeNode);
System.out.println(JSON.toJSONString(moduleTreeNode));
}
private List<BaseTreeNode> getModuleTreeNode() throws Exception {
MvcResult result = this.requestPostWithOkAndReturn(URL_CASE_MODULE_TREE, new CaseApiModuleRequest() {{
this.setProtocol("HTTP");
this.setProjectId("project-associate-case-test");
this.setSourceType(AssociateCaseType.API);
}});
String returnData = result.getResponse().getContentAsString(StandardCharsets.UTF_8);
ResultHolder resultHolder = JSON.parseObject(returnData, ResultHolder.class);
return JSON.parseArray(JSON.toJSONString(resultHolder.getData()), BaseTreeNode.class);
}
private void addFunctionalCaseTest() { private void addFunctionalCaseTest() {
FunctionalCaseTest functionalCaseTest = new FunctionalCaseTest(); FunctionalCaseTest functionalCaseTest = new FunctionalCaseTest();
functionalCaseTest.setId("functionalCaseTestHasId"); functionalCaseTest.setId("functionalCaseTestHasId");