feat(测试计划): 取消关联接口&部分问题处理

This commit is contained in:
WangXu10 2024-06-11 19:39:36 +08:00 committed by 刘瑞斌
parent 69f466e071
commit 13ff8addf2
11 changed files with 145 additions and 29 deletions

View File

@ -514,12 +514,6 @@
#{value}
</foreach>
</when>
<when test="key=='caseStatus' or key=='case_status'">
and atc.case_status in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</when>
<when test="key=='lastReportStatus'">
and atc.last_report_status in
<foreach collection="values" item="value" separator="," open="(" close=")">

View File

@ -27,6 +27,7 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.validation.annotation.Validated;
@ -105,6 +106,9 @@ public class TestPlanApiCaseController {
@RequiresPermissions(PermissionConstants.TEST_PLAN_READ_ASSOCIATION)
@CheckOwner(resourceId = "#request.getTestPlanId()", resourceType = "test_plan")
public TestPlanAssociationResponse batchDisassociate(@Validated @RequestBody TestPlanApiCaseBatchRequest request) {
if (CollectionUtils.isEmpty(request.getProtocols())) {
return new TestPlanAssociationResponse();
}
TestPlanAssociationResponse response = testPlanApiCaseService.disassociate(request, new LogInsertModule(SessionUtils.getUserId(), "/test-plan/api/case/batch/disassociate", HttpMethodConstants.POST.name()));
testPlanService.refreshTestPlanStatus(request.getTestPlanId());
return response;

View File

@ -2,18 +2,16 @@ package io.metersphere.plan.controller;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import io.metersphere.plan.dto.request.TestPlanApiCaseTreeRequest;
import io.metersphere.plan.dto.request.TestPlanApiScenarioModuleRequest;
import io.metersphere.plan.dto.request.TestPlanApiScenarioRequest;
import io.metersphere.plan.dto.request.TestPlanApiScenarioTreeRequest;
import io.metersphere.plan.dto.request.*;
import io.metersphere.plan.dto.response.TestPlanApiScenarioPageResponse;
import io.metersphere.plan.dto.request.TestPlanApiCaseBatchRunRequest;
import io.metersphere.plan.dto.request.TestPlanApiScenarioBatchRunRequest;
import io.metersphere.plan.service.TestPlanApiCaseBatchRunService;
import io.metersphere.plan.dto.response.TestPlanAssociationResponse;
import io.metersphere.plan.service.TestPlanApiScenarioBatchRunService;
import io.metersphere.plan.service.TestPlanApiScenarioService;
import io.metersphere.plan.service.TestPlanService;
import io.metersphere.sdk.constants.HttpMethodConstants;
import io.metersphere.sdk.constants.PermissionConstants;
import io.metersphere.sdk.dto.api.task.TaskRequestDTO;
import io.metersphere.system.dto.LogInsertModule;
import io.metersphere.system.dto.sdk.BaseTreeNode;
import io.metersphere.system.security.CheckOwner;
import io.metersphere.system.utils.PageUtils;
@ -39,6 +37,8 @@ public class TestPlanApiScenarioController {
private TestPlanApiScenarioService testPlanApiScenarioService;
@Resource
private TestPlanApiScenarioBatchRunService testPlanApiScenarioBatchRunService;
@Resource
private TestPlanService testPlanService;
@PostMapping("/page")
@Operation(summary = "测试计划-已关联场景用例列表分页查询")
@ -59,7 +59,7 @@ public class TestPlanApiScenarioController {
}
@PostMapping("/tree")
@Operation(summary = "测试计划-已关联接口用例列表模块树")
@Operation(summary = "测试计划-已关联场景用例列表模块树")
@RequiresPermissions(PermissionConstants.TEST_PLAN_READ)
@CheckOwner(resourceId = "#request.getTestPlanId()", resourceType = "test_plan")
public List<BaseTreeNode> getTree(@Validated @RequestBody TestPlanApiScenarioTreeRequest request) {
@ -81,4 +81,17 @@ public class TestPlanApiScenarioController {
public void batchRun(@Validated @RequestBody TestPlanApiScenarioBatchRunRequest request) {
testPlanApiScenarioBatchRunService.asyncBatchRun(request, SessionUtils.getUserId());
}
@PostMapping("/disassociate")
@Operation(summary = "测试计划-计划详情-场景用例列表-取消关联用例")
@RequiresPermissions(PermissionConstants.TEST_PLAN_READ_ASSOCIATION)
@CheckOwner(resourceId = "#request.getTestPlanId()", resourceType = "test_plan")
public TestPlanAssociationResponse disassociate(@Validated @RequestBody TestPlanDisassociationRequest request) {
BasePlanCaseBatchRequest batchRequest = new BasePlanCaseBatchRequest();
batchRequest.setTestPlanId(request.getTestPlanId());
batchRequest.setSelectIds(List.of(request.getId()));
TestPlanAssociationResponse response = testPlanApiScenarioService.disassociate(batchRequest, new LogInsertModule(SessionUtils.getUserId(), "/test-plan/api/scenario/disassociate", HttpMethodConstants.POST.name()));
testPlanService.refreshTestPlanStatus(request.getTestPlanId());
return response;
}
}

View File

@ -68,4 +68,7 @@ public class TestPlanApiCasePageResponse implements Serializable {
@Schema(description = "报告id")
private String lastExecReportId;
@Schema(description = "状态")
private String status;
}

View File

@ -419,7 +419,8 @@
t.last_exec_result,
t.execute_user,
t.last_exec_time,
t.last_exec_report_id
t.last_exec_report_id,
atc.status
FROM
api_test_case atc
INNER JOIN api_definition a ON atc.api_definition_id = a.id
@ -489,8 +490,8 @@
#{value}
</foreach>
</when>
<when test="key=='caseStatus' or key=='case_status'">
and atc.case_status in
<when test="key=='lastExecResult'">
and t.last_exec_result in
<foreach collection="values" item="value" separator="," open="(" close=")">
#{value}
</foreach>

View File

@ -6,6 +6,7 @@ import io.metersphere.plan.domain.TestPlanApiScenario;
import io.metersphere.plan.dto.ApiScenarioModuleDTO;
import io.metersphere.plan.dto.ResourceSelectParam;
import io.metersphere.plan.dto.TestPlanCaseRunResultCount;
import io.metersphere.plan.dto.request.BasePlanCaseBatchRequest;
import io.metersphere.plan.dto.request.TestPlanApiScenarioModuleRequest;
import io.metersphere.plan.dto.request.TestPlanApiScenarioRequest;
import io.metersphere.plan.dto.response.TestPlanApiScenarioPageResponse;
@ -57,4 +58,6 @@ public interface ExtTestPlanApiScenarioMapper {
List<TestPlanApiScenario> getPlanApiScenarioByIds(@Param("planIds") List<String> planIds);
List<TestPlanApiScenario> getScenarioExecuteInfoByIds(@Param("ids") List<String> ids);
List<String> getIds(@Param("request") BasePlanCaseBatchRequest request, @Param("deleted") boolean deleted);
}

View File

@ -395,4 +395,49 @@
#{id}
</foreach>
</select>
<select id="getIds" resultType="java.lang.String">
SELECT
test_plan_api_scenario.id as id
FROM
test_plan_api_scenario
INNER JOIN api_scenario on api_scenario.id = test_plan_api_scenario.api_scenario_id
WHERE
test_plan_api_scenario.test_plan_id = #{request.testPlanId}
AND api_scenario.deleted = #{deleted}
<include refid="queryWhereConditionByBatchQueryRequest"/>
</select>
<sql id="queryWhereConditionByBatchQueryRequest">
<if test="request.condition.keyword != null and request.condition.keyword != ''">
and (
api_scenario.num like concat('%', #{request.condition.keyword},'%')
or api_scenario.name like concat('%', #{request.condition.keyword},'%')
or api_scenario.tags like concat('%', #{request.condition.keyword},'%')
)
</if>
<if test="request.scenarioId != null and request.scenarioId != ''">
and api_scenario.id = #{request.scenarioId}
</if>
<if test="request.moduleIds != null and request.moduleIds.size() > 0">
and api_scenario.module_id in
<foreach collection="request.moduleIds" item="nodeId" separator="," open="(" close=")">
#{nodeId}
</foreach>
</if>
<if test="request.collectionId != null and request.collectionId != ''">
and t.test_plan_collection_id = #{request.collectionId}
</if>
<include refid="filters">
<property name="filter" value="request.condition.filter"/>
</include>
<if test="request.combine != null and request.combine != ''">
<include refid="combine">
<property name="condition" value="request.condition.combine"/>
<property name="name" value="request.name"/>
<property name="ObjectTags" value="request.condition.combine.tags"/>
</include>
</if>
</sql>
</mapper>

View File

@ -233,6 +233,7 @@ public class TestPlanApiCaseService extends TestPlanResourceService implements G
apiCaseList.forEach(item -> {
item.setProjectName(projectMap.get(item.getProjectId()));
item.setCreateUserName(userMap.get(item.getCreateUser()));
item.setExecuteUserName(userMap.get(item.getExecuteUser()));
TestPlanCollectionEnvDTO collectEnv = secondEnvMap.get(item.getTestPlanCollectionId());
if (StringUtils.equalsIgnoreCase(collectEnv.getEnvironmentId(), ModuleConstants.ROOT_NODE_PARENT_ID)) {
//计划集 == 默认环境 处理默认环境
@ -434,9 +435,6 @@ public class TestPlanApiCaseService extends TestPlanResourceService implements G
* @return
*/
public TestPlanAssociationResponse disassociate(TestPlanApiCaseBatchRequest request, LogInsertModule logInsertModule) {
if (CollectionUtils.isEmpty(request.getProtocols())) {
return new TestPlanAssociationResponse();
}
List<String> selectIds = doSelectIds(request);
return super.disassociate(
TestPlanResourceConstants.RESOURCE_API_CASE,
@ -509,8 +507,9 @@ public class TestPlanApiCaseService extends TestPlanResourceService implements G
private void handleApiData(List<BaseCollectionAssociateRequest> apiCaseList, String userId, List<TestPlanApiCase> testPlanApiCaseList, String planId) {
if (CollectionUtils.isNotEmpty(apiCaseList)) {
List<String> ids = apiCaseList.stream().flatMap(item -> item.getIds().stream()).toList();
//todo 优化重复关联问题
ApiTestCaseExample example = new ApiTestCaseExample();
example.createCriteria().andApiDefinitionIdIn(ids);
example.createCriteria().andApiDefinitionIdIn(ids).andDeletedEqualTo(false);
List<ApiTestCase> apiTestCaseList = apiTestCaseMapper.selectByExample(example);
apiCaseList.forEach(apiCase -> {
List<String> apiCaseIds = apiCase.getIds();

View File

@ -17,6 +17,7 @@ import io.metersphere.plan.domain.*;
import io.metersphere.plan.dto.*;
import io.metersphere.plan.dto.request.*;
import io.metersphere.plan.dto.response.TestPlanApiScenarioPageResponse;
import io.metersphere.plan.dto.response.TestPlanAssociationResponse;
import io.metersphere.plan.dto.response.TestPlanOperationResponse;
import io.metersphere.plan.mapper.*;
import io.metersphere.project.domain.Project;
@ -47,6 +48,7 @@ import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.util.ArrayList;
import java.util.HashMap;
@ -295,6 +297,7 @@ public class TestPlanApiScenarioService extends TestPlanResourceService implemen
apiScenarioList.forEach(item -> {
item.setProjectName(projectMap.get(item.getProjectId()));
item.setCreateUserName(userMap.get(item.getCreateUser()));
item.setExecuteUserName(userMap.get(item.getExecuteUser()));
TestPlanCollectionEnvDTO collectEnv = secondEnvMap.get(item.getTestPlanCollectionId());
if (StringUtils.equalsIgnoreCase(collectEnv.getEnvironmentId(), ModuleConstants.ROOT_NODE_PARENT_ID)) {
//计划集 == 默认环境 处理默认环境
@ -479,4 +482,41 @@ public class TestPlanApiScenarioService extends TestPlanResourceService implemen
});
return returnList;
}
/**
* 取消关联
*
* @param request
* @param logInsertModule
* @return
*/
public TestPlanAssociationResponse disassociate(BasePlanCaseBatchRequest request, LogInsertModule logInsertModule) {
List<String> selectIds = doSelectIds(request);
return super.disassociate(
TestPlanResourceConstants.RESOURCE_API_SCENARIO,
request,
logInsertModule,
selectIds,
this::deleteTestPlanResource);
}
public void deleteTestPlanResource(@Validated TestPlanResourceAssociationParam associationParam) {
TestPlanApiScenarioExample testPlanApiScenarioExample = new TestPlanApiScenarioExample();
testPlanApiScenarioExample.createCriteria().andIdIn(associationParam.getResourceIdList());
testPlanApiScenarioMapper.deleteByExample(testPlanApiScenarioExample);
}
public List<String> doSelectIds(BasePlanCaseBatchRequest request) {
if (request.isSelectAll()) {
List<String> ids = extTestPlanApiScenarioMapper.getIds(request, false);
if (CollectionUtils.isNotEmpty(request.getExcludeIds())) {
ids.removeAll(request.getExcludeIds());
}
return ids;
} else {
return request.getSelectIds();
}
}
}

View File

@ -14,10 +14,7 @@ import io.metersphere.api.dto.scenario.*;
import io.metersphere.api.service.scenario.ApiScenarioService;
import io.metersphere.api.utils.ApiDataUtils;
import io.metersphere.plan.domain.TestPlanApiScenario;
import io.metersphere.plan.dto.request.TestPlanApiScenarioBatchRunRequest;
import io.metersphere.plan.dto.request.TestPlanApiScenarioModuleRequest;
import io.metersphere.plan.dto.request.TestPlanApiScenarioRequest;
import io.metersphere.plan.dto.request.TestPlanApiScenarioTreeRequest;
import io.metersphere.plan.dto.request.*;
import io.metersphere.plan.mapper.TestPlanApiScenarioMapper;
import io.metersphere.plan.service.TestPlanApiScenarioService;
import io.metersphere.project.api.assertion.MsResponseCodeAssertion;
@ -57,6 +54,7 @@ public class TestPlanApiScenarioControllerTests extends BaseTest {
public static final String API_SCENARIO_TREE_COUNT = "module/count";
public static final String API_SCENARIO_TREE = "tree";
public static final String BATCH_RUN = "batch/run";
public static final String API_SCENARIO_DISASSOCIATE = "disassociate";
@Resource
private TestPlanApiScenarioService testPlanApiScenarioService;
@ -274,4 +272,18 @@ public class TestPlanApiScenarioControllerTests extends BaseTest {
ResultHolder resultHolder1 = JSON.parseObject(returnData1, ResultHolder.class);
Assertions.assertNotNull(resultHolder1);
}
@Test
@Order(6)
public void testApiScenarioDisassociate() throws Exception {
TestPlanDisassociationRequest request = new TestPlanDisassociationRequest();
request.setTestPlanId("wxxx_plan_2");
request.setId("wxxx_plan_scenario_3");
MvcResult mvcResult = this.requestPostWithOkAndReturn(API_SCENARIO_DISASSOCIATE, request);
String returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
ResultHolder resultHolder = JSON.parseObject(returnData, ResultHolder.class);
Assertions.assertNotNull(resultHolder);
}
}

View File

@ -1,7 +1,7 @@
INSERT INTO `test_plan`(`id`, `num`, `project_id`, `group_id`, `module_id`, `name`, `status`, `type`, `tags`, `create_time`, `create_user`, `update_time`, `update_user`, `planned_start_time`, `planned_end_time`, `actual_start_time`, `actual_end_time`, `description`)
VALUES
('wxxx_plan_1', 5000, 'wxx_1234', 'NONE', '1', 'qwe', 'PREPARED', 'TEST_PLAN', NULL, 1714980158000, 'WX', 1714980158000, 'WX', 1714980158000, 1714980158000, 1714980158000, 1714980158000, '11'),
('wxxx_plan_2', 10000, 'wxx_1234', 'NONE', '1', 'eeew', 'PREPARED', 'TEST_PLAN', NULL, 1714980158000, 'WX', 1714980158000, 'WX', 1714980158000, 1714980158000, 1714980158000, 1714980158000, '11');
('wxxx_plan_1', 5000, 'wxx_project_1234', 'NONE', '1', 'qwe', 'PREPARED', 'TEST_PLAN', NULL, 1714980158000, 'WX', 1714980158000, 'WX', 1714980158000, 1714980158000, 1714980158000, 1714980158000, '11'),
('wxxx_plan_2', 10000, 'wxx_project_1234', 'NONE', '1', 'eeew', 'PREPARED', 'TEST_PLAN', NULL, 1714980158000, 'WX', 1714980158000, 'WX', 1714980158000, 1714980158000, 1714980158000, 1714980158000, '11');
INSERT INTO `test_plan_config`(`test_plan_id`, `automatic_status_update`, `repeat_case`, `pass_threshold`,
@ -18,12 +18,14 @@ VALUES
INSERT INTO `test_plan_api_scenario`(`id`, `test_plan_id`, `api_scenario_id`, `environment_id`, `execute_user`, `last_exec_result`, `last_exec_report_id`, `create_time`, `create_user`, `pos`, `test_plan_collection_id`, `grouped`, `last_exec_time`)
VALUES
('wxxx_plan_scenario_1', 'wxxx_plan_1', 'wxxx_api_scenario_1', '1', 'admin', '', NULL, 1716370415311, 'admin', 64, 'wxxx_collection_2', b'0', 1716370415311),
('wxxx_plan_scenario_2', 'wxxx_plan_1', 'wxxx_api_scenario_2', '123', 'admin', '', NULL, 1716370415311, 'admin', 64, 'wxxx_collection_3', b'0', 1716370415311);
('wxxx_plan_scenario_2', 'wxxx_plan_1', 'wxxx_api_scenario_2', '123', 'admin', '', NULL, 1716370415311, 'admin', 64, 'wxxx_collection_3', b'0', 1716370415311),
('wxxx_plan_scenario_3', 'wxxx_plan_2', 'wxxx_api_scenario_3', '1', 'admin', '', NULL, 1716370415311, 'admin', 64, 'wxxx_collection_2', b'0', 1716370415311);
INSERT INTO `api_scenario`(`id`, `name`, `priority`, `status`, `step_total`, `request_pass_rate`, `last_report_status`, `last_report_id`, `num`, `deleted`, `pos`, `version_id`, `ref_id`, `latest`, `project_id`, `module_id`, `description`, `tags`, `grouped`, `environment_id`, `create_user`, `create_time`, `delete_time`, `delete_user`, `update_user`, `update_time`)
VALUES
('wxxx_api_scenario_1', 'axx', 'P0', 'UNDERWAY', 3, '0.46', 'ERROR', '971160841641984', 100027, b'0', 5568, '718273150722066', '1023696881426432', b'1', 'wxx_project_1234', 'root', '', '[]', b'0', 'wx_env_123', '714940256100352', 1717489987182, NULL, NULL, '714940256100352', 1717557159805),
('wxxx_api_scenario_2', 'xww', 'P0', 'UNDERWAY', 3, '0.46', 'ERROR', '971160841641984', 100027, b'0', 5568, '718273150722066', '1023696881426432', b'1', 'wxx_project_1234', 'wx_scenario_module_123', '', '[]', b'0', 'wx_env_123', '714940256100352', 1717489987182, NULL, NULL, '714940256100352', 1717557159805);
('wxxx_api_scenario_2', 'xww', 'P0', 'UNDERWAY', 3, '0.46', 'ERROR', '971160841641984', 100027, b'0', 5568, '718273150722066', '1023696881426432', b'1', 'wxx_project_1234', 'wx_scenario_module_123', '', '[]', b'0', 'wx_env_123', '714940256100352', 1717489987182, NULL, NULL, '714940256100352', 1717557159805),
('wxxx_api_scenario_3', 'dsa', 'P0', 'UNDERWAY', 3, '0.46', 'ERROR', '971160841641984', 100027, b'0', 5568, '718273150722066', '1023696881426432', b'1', 'wxx_project_1234', 'wx_scenario_module_123', '', '[]', b'0', 'wx_env_123', '714940256100352', 1717489987182, NULL, NULL, '714940256100352', 1717557159805);