feat(接口测试): 接口场景的表格批量操作功能开发

This commit is contained in:
song-tianyang 2024-01-27 14:47:59 +08:00 committed by 刘瑞斌
parent 8e3bf3d087
commit 9ebe3a657b
14 changed files with 763 additions and 85 deletions

View File

@ -280,6 +280,7 @@ public class PermissionConstants {
public static final String PROJECT_API_SCENARIO_ADD = "PROJECT_API_SCENARIO:READ+ADD";
public static final String PROJECT_API_SCENARIO_UPDATE = "PROJECT_API_SCENARIO:READ+UPDATE";
public static final String PROJECT_API_SCENARIO_DELETE = "PROJECT_API_SCENARIO:READ+DELETE";
public static final String PROJECT_API_SCENARIO_RECOVER = "PROJECT_API_SCENARIO:READ+RECOVER";
public static final String PROJECT_API_SCENARIO_EXECUTE = "PROJECT_API_SCENARIO:READ+EXECUTE";
public static final String PROJECT_API_SCENARIO_IMPORT = "PROJECT_API_SCENARIO:READ+IMPORT";
public static final String PROJECT_API_SCENARIO_EXPORT = "PROJECT_API_SCENARIO:READ+EXPORT";

View File

@ -22,6 +22,7 @@ api_definition_template.project_id.length_range=项目ID长度必须在1-50之
api_definition_template.project_id.not_blank=项目ID不能为空
#moduleApiScenario
permission.system_api_scenario.name=场景
api_scenario.name.repeat=场景名称重复
api_scenario.id.not_blank=不能为空
api_scenario.name.length_range=场景名称长度必须在1-200之间
api_scenario.name.not_blank=场景名称不能为空

View File

@ -22,6 +22,7 @@ api_definition_template.project_id.length_range=Item fk length must be between 1
api_definition_template.project_id.not_blank=Item fk cannot be empty
#moduleApiScenario
permission.system_api_scenario.name=Scenario
api_scenario.name.repeat=Scenario name is repeat
api_scenario.id.not_blank=Can not be empty
api_scenario.name.length_range=The scene name length must be between 1-200
api_scenario.name.not_blank=Scene name cannot be empty

View File

@ -22,6 +22,7 @@ api_definition_template.project_id.length_range=项目ID长度必须在1-50之
api_definition_template.project_id.not_blank=项目ID不能为空
#moduleApiScenario
permission.system_api_scenario.name=场景
api_scenario.name.repeat=场景名称重复
api_scenario.id.not_blank=不能为空
api_scenario.name.length_range=场景名称长度必须在1-200之间
api_scenario.name.not_blank=场景名称不能为空

View File

@ -22,6 +22,7 @@ api_definition_template.project_id.length_range=項目ID長度必須在1-50之
api_definition_template.project_id.not_blank=項目ID不能為空
#moduleApiScenario
permission.system_api_scenario.name=场景
api_scenario.name.repeat=场景名称重复
api_scenario.id.not_blank=不能為空
api_scenario.name.length_range=場景名稱長度必須在1-200之間
api_scenario.name.not_blank=場景名稱不能為空

View File

@ -2,13 +2,17 @@ package io.metersphere.api.controller.scenario;
import io.metersphere.api.constants.ApiResource;
import io.metersphere.api.dto.response.ApiScenarioBatchOperationResponse;
import io.metersphere.api.dto.scenario.ApiScenarioBatchCopyRequest;
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.service.ApiValidateService;
import io.metersphere.api.service.definition.ApiScenarioNoticeService;
import io.metersphere.api.service.scenario.ApiScenarioService;
import io.metersphere.sdk.constants.HttpMethodConstants;
import io.metersphere.sdk.constants.PermissionConstants;
import io.metersphere.system.dto.LogInsertModule;
import io.metersphere.system.notice.annotation.SendNotice;
import io.metersphere.system.notice.constants.NoticeConstants;
import io.metersphere.system.security.CheckOwner;
import io.metersphere.system.utils.SessionUtils;
import io.swagger.v3.oas.annotations.Operation;
@ -34,65 +38,57 @@ public class ApiScenarioBatchOperationController {
@Operation(summary = "接口测试-接口场景批量操作-批量编辑")
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_UPDATE)
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
@SendNotice(taskType = NoticeConstants.TaskType.API_SCENARIO_TASK, event = NoticeConstants.Event.UPDATE, target = "#targetClass.getBatchOptionScenarios(#request)", targetClass = ApiScenarioNoticeService.class)
public void batchUpdate(@Validated @RequestBody ApiScenarioBatchEditRequest request) {
apiValidateService.validateApiMenuInProject(request.getProjectId(), ApiResource.PROJECT.name());
apiScenarioService.batchEdit(request, SessionUtils.getUserId());
}
/*
todo:
@PostMapping("/gc-batch-delete")
@Operation(summary = "接口测试-接口场景批量操作-回收站列表-批量删除")
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_UPDATE)
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
@SendNotice(taskType = NoticeConstants.TaskType.API_SCENARIO_TASK, event = NoticeConstants.Event.UPDATE, target = "#targetClass.getCaseDTO(#request)", targetClass = ApiTestCaseNoticeService.class)
public void deleteFromGC(@Validated @RequestBody ApiScenarioBatchEditRequest request) {
apiValidateService.validateApiMenuInProject(request.getProjectId(), ApiResource.PROJECT.name());
apiScenarioService.deleteFromGC(request, SessionUtils.getUserId());
}
@PostMapping("/batch-operation/delete-gc")
@Operation(summary = "接口测试-接口场景批量操作-回收站列表-批量删除")
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_DELETE)
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
@SendNotice(taskType = NoticeConstants.TaskType.API_SCENARIO_TASK, event = NoticeConstants.Event.DELETE, target = "#targetClass.getBatchOptionScenarios(#request)", targetClass = ApiScenarioNoticeService.class)
public ApiScenarioBatchOperationResponse deleteFromGC(@Validated @RequestBody ApiScenarioBatchRequest request) {
apiValidateService.validateApiMenuInProject(request.getProjectId(), ApiResource.PROJECT.name());
return apiScenarioService.batchGCOperation(request, true, new LogInsertModule(SessionUtils.getUserId(), "/api/scenario/batch-operation/delete-gc", HttpMethodConstants.POST.name()));
}
//需求补充回收站里的相关操作都不需要发通知
@PostMapping("/batch-operation/recover-gc")
@Operation(summary = "接口测试-接口场景批量操作-回收站列表-批量恢复")
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_RECOVER)
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
public ApiScenarioBatchOperationResponse recoverFromGC(@Validated @RequestBody ApiScenarioBatchRequest request) {
apiValidateService.validateApiMenuInProject(request.getProjectId(), ApiResource.PROJECT.name());
return apiScenarioService.batchGCOperation(request, false, new LogInsertModule(SessionUtils.getUserId(), "/api/scenario/batch-operation/recover-gc", HttpMethodConstants.POST.name()));
}
todo
@PostMapping("/gc-batch-restore")
@Operation(summary = "接口测试-接口场景批量操作-回收站列表-批量恢复")
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_UPDATE)
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
public void restoreFromGC(@Validated @RequestBody ApiScenarioBatchEditRequest request) {
apiValidateService.validateApiMenuInProject(request.getProjectId(), ApiResource.PROJECT.name());
apiScenarioService.restoreFromGC(request, SessionUtils.getUserId());
}
todo
@PostMapping("/batch-delete")
@Operation(summary = "接口测试-接口场景批量操作-场景列表操作-批量删除")
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_UPDATE)
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
public void batchDelete(@Validated @RequestBody ApiScenarioBatchEditRequest request) {
apiValidateService.validateApiMenuInProject(request.getProjectId(), ApiResource.PROJECT.name());
apiScenarioService.batchDelete(request, SessionUtils.getUserId());
}
todo
@PostMapping("/batch-move")
@Operation(summary = "接口测试-接口场景批量操作-场景列表操作-批量移动")
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_UPDATE)
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
public void batchMove(@Validated @RequestBody ApiScenarioBatchEditRequest request) {
apiValidateService.validateApiMenuInProject(request.getProjectId(), ApiResource.PROJECT.name());
apiScenarioService.batchMove(request, new LogInsertModule(SessionUtils.getUserId(), "/api/scenario/batch-operation/batch-move", HttpMethodConstants.POST.name()));
}
*/
//需求补充回收站里的相关操作都不需要发通知
@PostMapping("/batch-operation/delete")
@Operation(summary = "接口测试-接口场景批量操作-场景列表操作-批量删除")
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_DELETE)
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
public ApiScenarioBatchOperationResponse batchDelete(@Validated @RequestBody ApiScenarioBatchRequest request) {
apiValidateService.validateApiMenuInProject(request.getProjectId(), ApiResource.PROJECT.name());
return apiScenarioService.batchDelete(request, new LogInsertModule(SessionUtils.getUserId(), "/api/scenario/batch-operation/delete", HttpMethodConstants.POST.name()));
}
@PostMapping("/batch-operation/move")
@Operation(summary = "接口测试-接口场景批量操作-场景列表操作-批量移动")
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_UPDATE)
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
@SendNotice(taskType = NoticeConstants.TaskType.API_SCENARIO_TASK, event = NoticeConstants.Event.UPDATE, target = "#targetClass.getBatchOptionScenarios(#request)", targetClass = ApiScenarioNoticeService.class)
public ApiScenarioBatchOperationResponse batchMove(@Validated @RequestBody ApiScenarioBatchCopyMoveRequest request) {
apiValidateService.validateApiMenuInProject(request.getProjectId(), ApiResource.PROJECT.name());
return apiScenarioService.batchMove(request, new LogInsertModule(SessionUtils.getUserId(), "/api/scenario/batch-operation/move", HttpMethodConstants.POST.name()));
}
@PostMapping("/batch-operation/copy")
@Operation(summary = "接口测试-接口场景批量操作-场景列表操作-批量复制")
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_UPDATE)
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_ADD)
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
public ApiScenarioBatchOperationResponse batchCopy(@Validated @RequestBody ApiScenarioBatchCopyRequest request) {
public ApiScenarioBatchOperationResponse batchCopy(@Validated @RequestBody ApiScenarioBatchCopyMoveRequest request) {
apiValidateService.validateApiMenuInProject(request.getProjectId(), ApiResource.PROJECT.name());
return apiScenarioService.batchCopy(request, new LogInsertModule(SessionUtils.getUserId(), "/api/scenario/batch-operation/copy", HttpMethodConstants.POST.name()));
}

View File

@ -95,7 +95,7 @@ public class ApiScenarioController {
@Log(type = OperationLogType.DELETE, expression = "#msClass.deleteLog(#id)", msClass = ApiScenarioLogService.class)
@CheckOwner(resourceId = "#id", resourceType = "api_scenario")
public void delete(@PathVariable String id) {
apiScenarioService.delete(id);
apiScenarioService.delete(id, SessionUtils.getUserId());
}
@GetMapping("/delete-to-gc/{id}")
@ -122,14 +122,16 @@ public class ApiScenarioController {
return apiScenarioService.getStepDetail(stepId);
}
@GetMapping("/restore/{id}")
//需求补充回收站里的相关操作都不需要发通知
@GetMapping("/recover/{id}")
@Operation(summary = "接口测试-接口场景管理-删除场景到回收站")
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_DELETE)
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_RECOVER)
@Log(type = OperationLogType.RESTORE, expression = "#msClass.restoreLog(#id)", msClass = ApiScenarioLogService.class)
@CheckOwner(resourceId = "#id", resourceType = "api_scenario")
public void recover(@PathVariable String id) {
apiValidateService.validateApiMenuInProject(id, ApiResource.API_SCENARIO.name());
apiScenarioService.restore(id);
apiScenarioService.recover(id);
}
@PostMapping("/debug")

View File

@ -9,7 +9,7 @@ import java.io.Serializable;
@Data
@EqualsAndHashCode(callSuper = false)
public class ApiScenarioBatchCopyRequest extends ApiScenarioBatchRequest implements Serializable {
public class ApiScenarioBatchCopyMoveRequest extends ApiScenarioBatchRequest implements Serializable {
private static final long serialVersionUID = 1L;

View File

@ -0,0 +1,42 @@
package io.metersphere.api.service.definition;
import io.metersphere.api.domain.ApiScenario;
import io.metersphere.api.domain.ApiScenarioExample;
import io.metersphere.api.dto.scenario.ApiScenarioBatchRequest;
import io.metersphere.api.mapper.ApiScenarioMapper;
import io.metersphere.api.service.scenario.ApiScenarioService;
import io.metersphere.sdk.util.SubListUtils;
import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class ApiScenarioNoticeService {
@Resource
private ApiScenarioService apiScenarioService;
@Resource
private ApiScenarioMapper apiScenarioMapper;
public List<ApiScenario> getBatchOptionScenarios(ApiScenarioBatchRequest request) {
List<String> ids = apiScenarioService.doSelectIds(request, false);
return handleBatchNotice(ids);
}
private List<ApiScenario> handleBatchNotice(List<String> ids) {
List<ApiScenario> dtoList = new ArrayList<>();
if (CollectionUtils.isNotEmpty(ids)) {
SubListUtils.dealForSubList(ids, 100, (subList) -> {
ApiScenarioExample example = new ApiScenarioExample();
example.createCriteria().andIdIn(subList);
dtoList.addAll(apiScenarioMapper.selectByExample(example));
});
}
return dtoList;
}
}

View File

@ -43,7 +43,7 @@ public class ApiTestCaseNoticeService {
private List<ApiDefinitionCaseDTO> handleBatchNotice(List<String> ids) {
List<ApiDefinitionCaseDTO> dtoList = new ArrayList<>();
if (CollectionUtils.isNotEmpty(ids)) {
SubListUtils.dealForSubList(ids, 500, (subList) -> {
SubListUtils.dealForSubList(ids, 500, subList -> {
ApiTestCaseExample example = new ApiTestCaseExample();
example.createCriteria().andIdIn(subList);
List<ApiTestCase> caseList = apiTestCaseMapper.selectByExample(example);

View File

@ -177,17 +177,16 @@ public class ApiScenarioLogService {
return dto;
}
public void saveBatchCopyLog(ApiScenarioBatchOperationResponse response, String projectId, LogInsertModule logInsertModule) {
public void saveBatchOperationLog(ApiScenarioBatchOperationResponse response, String projectId, String operationType, LogInsertModule logInsertModule) {
Project project = projectMapper.selectByPrimaryKey(projectId);
//取出apiTestCases所有的id为新的list
List<LogDTO> logs = new ArrayList<>();
response.getSuccessData().forEach(item -> {
LogDTO dto = LogDTOBuilder.builder()
.projectId(project.getId())
.organizationId(project.getOrganizationId())
.type(OperationLogType.COPY.name())
.type(operationType)
.module(OperationLogModule.API_SCENARIO)
.method(logInsertModule.getRequestMethod())
.path(logInsertModule.getRequestUrl())

View File

@ -41,6 +41,7 @@ import io.metersphere.sdk.mapper.EnvironmentMapper;
import io.metersphere.sdk.util.*;
import io.metersphere.system.dto.LogInsertModule;
import io.metersphere.system.log.constants.OperationLogModule;
import io.metersphere.system.log.constants.OperationLogType;
import io.metersphere.system.service.UserLoginService;
import io.metersphere.system.uid.IDGenerator;
import io.metersphere.system.uid.NumGenerator;
@ -796,16 +797,81 @@ public class ApiScenarioService {
return projectService.getNextOrder(extApiScenarioMapper::getLastPos, projectId);
}
public void delete(String id) {
public void delete(String id, String operator) {
checkResourceExist(id);
ApiScenario scenario = apiScenarioMapper.selectByPrimaryKey(id);
cascadeDelete(scenario, operator);
apiScenarioMapper.deleteByPrimaryKey(id);
apiScenarioBlobMapper.deleteByPrimaryKey(id);
deleteStepByScenarioId(id);
deleteStepDetailByScenarioId(id);
}
public ApiScenarioBatchOperationResponse delete(@NotEmpty List<String> idList, String operator) {
ApiScenarioBatchOperationResponse response = new ApiScenarioBatchOperationResponse();
ApiScenarioExample example = new ApiScenarioExample();
example.createCriteria().andIdIn(idList).andDeletedEqualTo(true);
List<ApiScenario> scenarioList = apiScenarioMapper.selectByExample(example);
if (CollectionUtils.isNotEmpty(scenarioList)) {
apiScenarioMapper.deleteByExample(example);
cascadeDelete(scenarioList, operator);
}
//构建返回值
for (ApiScenario scenario : scenarioList) {
response.addSuccessData(scenario.getId(), scenario.getNum(), scenario.getName());
}
return response;
}
//级联删除
public void cascadeDelete(ApiScenario scenario, String operator) {
//删除blob
apiScenarioBlobMapper.deleteByPrimaryKey(scenario.getId());
//删除step
deleteStepByScenarioId(scenario.getId());
//删除step-blob
deleteStepDetailByScenarioId(scenario.getId());
//删除文件
String scenarioDir = DefaultRepositoryDir.getApiDebugDir(scenario.getProjectId(), scenario.getId());
try {
apiFileResourceService.deleteByResourceId(scenarioDir, scenario.getId(), scenario.getProjectId(), operator, OperationLogModule.API_DEBUG);
}catch (Exception ignore){}
//todo wang xiao gang: 删除csv相关东西
}
//级联删除
public void cascadeDelete(@NotEmpty List<ApiScenario> scenarioList, String operator) {
List<String> scenarioIdList = scenarioList.stream().map(ApiScenario::getId).toList();
ApiScenarioBlobExample example = new ApiScenarioBlobExample();
example.createCriteria().andIdIn(scenarioIdList);
//删除blob
apiScenarioBlobMapper.deleteByExample(example);
ApiScenarioStepExample stepExample = new ApiScenarioStepExample();
stepExample.createCriteria().andScenarioIdIn(scenarioIdList);
//删除step
apiScenarioStepMapper.deleteByExample(stepExample);
//删除step-blob
ApiScenarioStepBlobExample blobExample = new ApiScenarioStepBlobExample();
blobExample.createCriteria().andScenarioIdIn(scenarioIdList);
apiScenarioStepBlobMapper.deleteByExample(blobExample);
//删除文件
scenarioList.forEach(scenario -> {
String scenarioDir = DefaultRepositoryDir.getApiDebugDir(scenario.getProjectId(), scenario.getId());
try {
apiFileResourceService.deleteByResourceId(scenarioDir, scenario.getId(), scenario.getProjectId(), operator, OperationLogModule.API_DEBUG);
}catch (Exception ignore){}
});
//todo wang xiao gang: 删除csv相关东西
}
public void restore(String id) {
public void recover(String id) {
ApiScenario apiScenario = new ApiScenario();
apiScenario.setId(id);
apiScenario.setDeleted(false);
@ -1243,13 +1309,32 @@ public class ApiScenarioService {
return ServiceUtils.checkResourceExist(apiScenarioStepMapper.selectByPrimaryKey(id), "permission.api_step.name");
}
public ApiScenarioBatchOperationResponse batchCopy(ApiScenarioBatchCopyRequest request, LogInsertModule logInsertModule) {
if (!StringUtils.equals(request.getTargetModuleId(), ModuleConstants.DEFAULT_NODE_ID)) {
ApiScenarioModule module = apiScenarioModuleMapper.selectByPrimaryKey(request.getTargetModuleId());
if (module == null || !StringUtils.equals(module.getProjectId(), request.getProjectId())) {
private void checkTargetModule(String targetModuleId, String projectId) {
if (!StringUtils.equals(targetModuleId, ModuleConstants.DEFAULT_NODE_ID)) {
ApiScenarioModule module = apiScenarioModuleMapper.selectByPrimaryKey(targetModuleId);
if (module == null || !StringUtils.equals(module.getProjectId(), projectId)) {
throw new MSException(Translator.get("module.not.exist"));
}
}
}
public ApiScenarioBatchOperationResponse batchMove(ApiScenarioBatchCopyMoveRequest request, LogInsertModule logInsertModule) {
this.checkTargetModule(request.getTargetModuleId(), request.getProjectId());
List<String> scenarioIds = doSelectIds(request, false);
if (CollectionUtils.isEmpty(scenarioIds)) {
return new ApiScenarioBatchOperationResponse();
}
request.setSelectIds(scenarioIds);
long moveTime = System.currentTimeMillis();
ApiScenarioBatchOperationResponse response =
ApiScenarioBatchOperationUtils.executeWithBatchOperationResponse(scenarioIds, sublist -> move(sublist, request, moveTime, logInsertModule.getOperator()));
apiScenarioLogService.saveBatchOperationLog(response, request.getProjectId(), OperationLogType.UPDATE.name(), logInsertModule);
return response;
}
public ApiScenarioBatchOperationResponse batchCopy(ApiScenarioBatchCopyMoveRequest request, LogInsertModule logInsertModule) {
this.checkTargetModule(request.getTargetModuleId(), request.getProjectId());
List<String> scenarioIds = doSelectIds(request, false);
if (CollectionUtils.isEmpty(scenarioIds)) {
@ -1258,7 +1343,7 @@ public class ApiScenarioService {
request.setSelectIds(scenarioIds);
ApiScenarioBatchOperationResponse response =
ApiScenarioBatchOperationUtils.executeWithBatchOperationResponse(scenarioIds, sublist -> copyAndInsert(sublist, request, logInsertModule.getOperator()));
apiScenarioLogService.saveBatchCopyLog(response, request.getProjectId(), logInsertModule);
apiScenarioLogService.saveBatchOperationLog(response, request.getProjectId(), OperationLogType.COPY.name(), logInsertModule);
return response;
}
@ -1289,7 +1374,45 @@ public class ApiScenarioService {
return newScenarioName;
}
private ApiScenarioBatchOperationResponse copyAndInsert(List<String> scenarioIds, ApiScenarioBatchCopyRequest request, String operator) {
private ApiScenarioBatchOperationResponse move(List<String> scenarioIds, ApiScenarioBatchCopyMoveRequest request, long moveTime, String operator) {
ApiScenarioBatchOperationResponse response = new ApiScenarioBatchOperationResponse();
ApiScenarioExample example = new ApiScenarioExample();
example.createCriteria().andIdIn(scenarioIds);
List<ApiScenario> operationList = apiScenarioMapper.selectByExample(example);
List<String> updateScenarioIds = new ArrayList<>();
operationList.forEach(apiScenario -> {
if (StringUtils.equals(apiScenario.getModuleId(), request.getTargetModuleId())) {
response.addErrorData(Translator.get("api_scenario_exist"), apiScenario.getId(), apiScenario.getNum(), apiScenario.getName());
} else {
ApiScenarioExample checkExample = new ApiScenarioExample();
// 统一模块下名称不能重复
checkExample.createCriteria()
.andNameEqualTo(apiScenario.getName())
.andModuleIdEqualTo(request.getTargetModuleId());
if (apiScenarioMapper.countByExample(checkExample) > 0) {
response.addErrorData(Translator.get("api_scenario.name.repeat"), apiScenario.getId(), apiScenario.getNum(), apiScenario.getName());
} else {
updateScenarioIds.add(apiScenario.getId());
response.addSuccessData(apiScenario.getId(), apiScenario.getNum(), apiScenario.getName());
}
}
});
if (CollectionUtils.isNotEmpty(updateScenarioIds)) {
ApiScenario moveModule = new ApiScenario();
moveModule.setModuleId(request.getTargetModuleId());
moveModule.setUpdateTime(moveTime);
moveModule.setUpdateUser(operator);
example.clear();
example.createCriteria().andIdIn(updateScenarioIds);
apiScenarioMapper.updateByExampleSelective(moveModule, example);
}
return response;
}
private ApiScenarioBatchOperationResponse copyAndInsert(List<String> scenarioIds, ApiScenarioBatchCopyMoveRequest request, String operator) {
ApiScenarioBatchOperationResponse response = new ApiScenarioBatchOperationResponse();
ApiScenarioExample example = new ApiScenarioExample();
@ -1420,4 +1543,53 @@ public class ApiScenarioService {
return apiScenarioStepBlobMapper.selectByExampleWithBLOBs(example);
}
public ApiScenarioBatchOperationResponse batchGCOperation(ApiScenarioBatchRequest request, boolean isDeleteOperation, LogInsertModule logInsertModule) {
List<String> scenarioIds = doSelectIds(request, !isDeleteOperation);
if (CollectionUtils.isEmpty(scenarioIds)) {
return new ApiScenarioBatchOperationResponse();
}
long deleteTime = System.currentTimeMillis();
ApiScenarioBatchOperationResponse response = ApiScenarioBatchOperationUtils.executeWithBatchOperationResponse(
scenarioIds,
sublist -> operationGC(sublist, isDeleteOperation, deleteTime, logInsertModule.getOperator()));
apiScenarioLogService.saveBatchOperationLog(response, request.getProjectId(),
isDeleteOperation ? OperationLogType.DELETE.name() : OperationLogType.RECOVER.name(), logInsertModule);
return response;
}
private ApiScenarioBatchOperationResponse operationGC(List<String> scenarioIds, boolean isDeleteOperation, long deleteTime, String operator) {
ApiScenarioBatchOperationResponse response = new ApiScenarioBatchOperationResponse();
ApiScenarioExample example = new ApiScenarioExample();
example.createCriteria().andIdIn(scenarioIds).andDeletedEqualTo(!isDeleteOperation);
List<ApiScenario> apiScenarioList = apiScenarioMapper.selectByExample(example);
ApiScenario updateScenarioModel = new ApiScenario();
updateScenarioModel.setDeleted(isDeleteOperation);
updateScenarioModel.setDeleteUser(operator);
updateScenarioModel.setDeleteTime(deleteTime);
apiScenarioMapper.updateByExampleSelective(updateScenarioModel, example);
for (ApiScenario scenario : apiScenarioList) {
response.addSuccessData(scenario.getId(), scenario.getNum(), scenario.getName());
}
return response;
}
public ApiScenarioBatchOperationResponse batchDelete(ApiScenarioBatchRequest request, LogInsertModule logInsertModule) {
List<String> scenarioIds = doSelectIds(request, true);
if (CollectionUtils.isEmpty(scenarioIds)) {
return new ApiScenarioBatchOperationResponse();
}
ApiScenarioBatchOperationResponse response = ApiScenarioBatchOperationUtils.executeWithBatchOperationResponse(
scenarioIds,
sublist -> delete(sublist, logInsertModule.getOperator()));
apiScenarioLogService.saveBatchOperationLog(response, request.getProjectId(), OperationLogType.DELETE.name(), logInsertModule);
return response;
}
}

View File

@ -41,6 +41,7 @@ import io.metersphere.system.controller.handler.ResultHolder;
import io.metersphere.system.log.constants.OperationLogType;
import io.metersphere.system.uid.IDGenerator;
import io.metersphere.system.uid.NumGenerator;
import io.metersphere.system.utils.CheckLogModel;
import io.metersphere.system.utils.Pager;
import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
@ -123,6 +124,8 @@ public class ApiScenarioControllerTests extends BaseTest {
private static ApiDefinition apiDefinition;
private static ApiTestCase apiTestCase;
private static final List<CheckLogModel> LOG_CHECK_LIST = new ArrayList<>();
@Override
protected String getBasePath() {
return BASE_PATH;
@ -959,14 +962,19 @@ public class ApiScenarioControllerTests extends BaseTest {
}
/*
要价格与批量测试的数据结构
建国批量测试场景模块_1 ->50条数据 有标签和环境组
Order 21 - 30 是测试批量操作的
批量测试的数据结构
建国批量测试场景模块_default测试批量移动
|
|
建国批量测试场景模块_1 ->50条数据 有标签和环境组 批量测试copy之后的数据为700
|
|
建国批量测试场景模块_51 ->50条数据 有环境
|
|
建国批量测试场景模块_101 ->50条数据 有blob
建国批量测试场景模块_101 ->50条数据 有blob 批量测试copy之后的数据为100
|
|
建国批量测试场景模块_151 ->50条数据 有step
@ -979,19 +987,20 @@ public class ApiScenarioControllerTests extends BaseTest {
*/
@Test
@Order(20)
public void batchCopy() throws Exception {
@Order(21)
void batchCopy() throws Exception {
String testUrl = "/batch-operation/copy";
if (CollectionUtils.isEmpty(BATCH_OPERATION_SCENARIO_ID)) {
this.batchCreateScenarios();
}
/*
正例测试 1.超过300条的批量复制
2.移动到新模块
测试数据使用建国批量测试场景模块_200模块为查询条件和建国批量测试场景模块_250里的所有id复制到 建国批量测试场景模块_1
*/
ApiScenarioBatchCopyRequest request = new ApiScenarioBatchCopyRequest();
ApiScenarioBatchCopyMoveRequest request = new ApiScenarioBatchCopyMoveRequest();
request.setSelectIds(BATCH_OPERATION_SCENARIO_ID.subList(250, 500));
request.setModuleIds(List.of(BATCH_OPERATION_SCENARIO_MODULE_MAP.get("建国批量测试场景模块_201")));
request.setTargetModuleId(BATCH_OPERATION_SCENARIO_MODULE_MAP.get("建国批量测试场景模块_1"));
@ -1012,8 +1021,18 @@ public class ApiScenarioControllerTests extends BaseTest {
//数据库级别的检查
apiScenarioBatchOperationTestService.checkBatchCopy(BATCH_OPERATION_SCENARIO_ID.subList(200, 500), resultResponse.getSuccessData().stream().map(OperationDataInfo::getId).toList(), 50, request);
//本次测试涉及到的场景ID
List<String> operationScenarioIds = new ArrayList<>();
resultResponse.getSuccessData().forEach(item -> operationScenarioIds.add(item.getId()));
//增加日志检查
operationScenarioIds.forEach(item -> {
LOG_CHECK_LIST.add(
new CheckLogModel(item, OperationLogType.COPY, "/api/scenario/batch-operation/copy")
);
});
// 2.没有数据
request = new ApiScenarioBatchCopyRequest();
request = new ApiScenarioBatchCopyMoveRequest();
request.setTargetModuleId(BATCH_OPERATION_SCENARIO_MODULE_MAP.get("建国批量测试场景模块_1"));
request.setProjectId(DEFAULT_PROJECT_ID);
result = this.requestPostAndReturn(testUrl, request);
@ -1036,7 +1055,7 @@ public class ApiScenarioControllerTests extends BaseTest {
List<ApiScenario> reCopyScenarios = apiScenarioMapper.selectByExample(example);
request = new ApiScenarioBatchCopyRequest();
request = new ApiScenarioBatchCopyMoveRequest();
request.setModuleIds(List.of(BATCH_OPERATION_SCENARIO_MODULE_MAP.get("建国批量测试场景模块_1")));
request.setTargetModuleId(BATCH_OPERATION_SCENARIO_MODULE_MAP.get("建国批量测试场景模块_1"));
request.setProjectId(DEFAULT_PROJECT_ID);
@ -1059,7 +1078,7 @@ public class ApiScenarioControllerTests extends BaseTest {
*/
apiScenarioBatchOperationTestService.updateNameToTestByModuleId(BATCH_OPERATION_SCENARIO_MODULE_MAP.get("建国批量测试场景模块_101"));
request = new ApiScenarioBatchCopyRequest();
request = new ApiScenarioBatchCopyMoveRequest();
request.setModuleIds(List.of(BATCH_OPERATION_SCENARIO_MODULE_MAP.get("建国批量测试场景模块_101")));
request.setTargetModuleId(BATCH_OPERATION_SCENARIO_MODULE_MAP.get("建国批量测试场景模块_101"));
request.setProjectId(DEFAULT_PROJECT_ID);
@ -1073,39 +1092,401 @@ public class ApiScenarioControllerTests extends BaseTest {
//数据库级别的检查
apiScenarioBatchOperationTestService.checkBatchCopy(BATCH_OPERATION_SCENARIO_ID.subList(100, 150), resultResponse.getSuccessData().stream().map(OperationDataInfo::getId).toList(), 50, request);
//校验权限
this.requestPostPermissionTest(PermissionConstants.PROJECT_API_SCENARIO_ADD, testUrl, request);
//1.反例测试 ->模块不存在
request = new ApiScenarioBatchCopyRequest();
request = new ApiScenarioBatchCopyMoveRequest();
request.setTargetModuleId(IDGenerator.nextStr());
request.setProjectId(DEFAULT_PROJECT_ID);
request.setSelectAll(true);
this.requestPost(testUrl, request).andExpect(status().is5xxServerError());
//2.反例测试 ->要移动的模块是别的项目下的
String otherProjectModuleId = this.createModule(500, IDGenerator.nextStr(), ModuleConstants.ROOT_NODE_PARENT_ID);
request = new ApiScenarioBatchCopyRequest();
request = new ApiScenarioBatchCopyMoveRequest();
request.setTargetModuleId(otherProjectModuleId);
request.setProjectId(DEFAULT_PROJECT_ID);
request.setSelectAll(true);
this.requestPost(testUrl, request).andExpect(status().is5xxServerError());
//3.反例测试 ->参数校验项目ID为空 模块ID为空
request = new ApiScenarioBatchCopyRequest();
request = new ApiScenarioBatchCopyMoveRequest();
request.setTargetModuleId(otherProjectModuleId);
this.requestPost(testUrl, request).andExpect(status().isBadRequest());
request = new ApiScenarioBatchCopyRequest();
request = new ApiScenarioBatchCopyMoveRequest();
request.setProjectId(DEFAULT_PROJECT_ID);
this.requestPost(testUrl, request).andExpect(status().isBadRequest());
}
@Test
@Order(21)
void batchMove() throws Exception {
String testUrl = "/batch-operation/move";
if (CollectionUtils.isEmpty(BATCH_OPERATION_SCENARIO_ID)) {
this.batchCopy();
}
String moduleIdDefault = BATCH_OPERATION_SCENARIO_MODULE_MAP.get("建国批量测试场景模块_default");
String moduleId201 = BATCH_OPERATION_SCENARIO_MODULE_MAP.get("建国批量测试场景模块_201");
String moduleId251 = BATCH_OPERATION_SCENARIO_MODULE_MAP.get("建国批量测试场景模块_251");
//本次测试涉及到的场景ID
List<String> operationScenarioIds = new ArrayList<>(BATCH_OPERATION_SCENARIO_ID.subList(200, 500));
/*
正例测试
1.超过300条的批量移动
2.移动到当前模块 名称有重复的
测试数据使用建国批量测试场景模块_201模块为查询条件和建国批量测试场景模块_251里的所有id移动到 建国批量测试场景模块_default测试1
然后再指定具体id移动测试2
最后分批移动回去 指定具体id移动回_251, 再指定moduleId为_default移动回_200
*/
ApiScenarioBatchCopyMoveRequest request = new ApiScenarioBatchCopyMoveRequest();
request.setSelectIds(BATCH_OPERATION_SCENARIO_ID.subList(250, 500));
request.setModuleIds(List.of(moduleId201));
request.setTargetModuleId(moduleIdDefault);
request.setProjectId(DEFAULT_PROJECT_ID);
request.setSelectAll(true);
//先测试一下没有开启模块时接口能否使用
apiScenarioBatchOperationTestService.removeApiModule(DEFAULT_PROJECT_ID);
this.requestPost(testUrl, request).andExpect(status().is5xxServerError());
//恢复
apiScenarioBatchOperationTestService.resetProjectModule(DEFAULT_PROJECT_ID);
MvcResult result = this.requestPostAndReturn(testUrl, request);
ResultHolder resultHolder = JSON.parseObject(result.getResponse().getContentAsString(StandardCharsets.UTF_8), ResultHolder.class);
ApiScenarioBatchOperationResponse resultResponse = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), ApiScenarioBatchOperationResponse.class);
//检查返回值
Assertions.assertEquals(resultResponse.getSuccess(), 300);
//数据库级别的检查
apiScenarioBatchOperationTestService.checkBatchMove(
BATCH_OPERATION_SCENARIO_ID.subList(200, 500),
0,
new HashMap<>() {{
this.put(moduleId201, 0L);
this.put(moduleId251, 0L);
}}, request);
//增加日志检查
operationScenarioIds.forEach(item -> {
LOG_CHECK_LIST.add(
new CheckLogModel(item, OperationLogType.UPDATE, "/api/scenario/batch-operation/move")
);
});
//重复关联
result = this.requestPostAndReturn(testUrl, request);
resultHolder = JSON.parseObject(result.getResponse().getContentAsString(StandardCharsets.UTF_8), ResultHolder.class);
resultResponse = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), ApiScenarioBatchOperationResponse.class);
Assertions.assertEquals(resultResponse.getSuccess(), 0);
Assertions.assertEquals(resultResponse.getError(), 250);
//指定具体id移动回_251, 再指定moduleId为_default移动回_200
request = new ApiScenarioBatchCopyMoveRequest();
request.setSelectIds(BATCH_OPERATION_SCENARIO_ID.subList(250, 500));
request.setTargetModuleId(moduleId251);
request.setProjectId(DEFAULT_PROJECT_ID);
request.setSelectAll(false);
result = this.requestPostAndReturn(testUrl, request);
resultHolder = JSON.parseObject(result.getResponse().getContentAsString(StandardCharsets.UTF_8), ResultHolder.class);
resultResponse = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), ApiScenarioBatchOperationResponse.class);
Assertions.assertEquals(resultResponse.getSuccess(), 250);
apiScenarioBatchOperationTestService.checkBatchMove(
BATCH_OPERATION_SCENARIO_ID.subList(250, 500),
0,
new HashMap<>() {{
this.put(moduleIdDefault, 50L);
}}, request);
//指定moduleId为_default移动回_201
request = new ApiScenarioBatchCopyMoveRequest();
request.setModuleIds(Collections.singletonList(moduleIdDefault));
request.setTargetModuleId(moduleId201);
request.setProjectId(DEFAULT_PROJECT_ID);
request.setSelectAll(true);
result = this.requestPostAndReturn(testUrl, request);
resultHolder = JSON.parseObject(result.getResponse().getContentAsString(StandardCharsets.UTF_8), ResultHolder.class);
resultResponse = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), ApiScenarioBatchOperationResponse.class);
Assertions.assertEquals(resultResponse.getSuccess(), 50);
apiScenarioBatchOperationTestService.checkBatchMove(
BATCH_OPERATION_SCENARIO_ID.subList(200, 250),
0,
new HashMap<>() {{
this.put(moduleIdDefault, 0L);
}}, request);
// 没有数据
request = new ApiScenarioBatchCopyMoveRequest();
request.setTargetModuleId(moduleIdDefault);
request.setProjectId(DEFAULT_PROJECT_ID);
result = this.requestPostAndReturn(testUrl, request);
resultHolder = JSON.parseObject(result.getResponse().getContentAsString(StandardCharsets.UTF_8), ResultHolder.class);
resultResponse = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), ApiScenarioBatchOperationResponse.class);
//检查返回值
Assertions.assertEquals(resultResponse.getSuccess(), 0);
//1.反例测试 ->模块不存在
request = new ApiScenarioBatchCopyMoveRequest();
request.setTargetModuleId(IDGenerator.nextStr());
request.setProjectId(DEFAULT_PROJECT_ID);
request.setSelectAll(true);
this.requestPost(testUrl, request).andExpect(status().is5xxServerError());
//2.反例测试 ->要移动的模块是别的项目下的
String otherProjectModuleId = this.createModule(500, IDGenerator.nextStr(), ModuleConstants.ROOT_NODE_PARENT_ID);
request = new ApiScenarioBatchCopyMoveRequest();
request.setTargetModuleId(otherProjectModuleId);
request.setProjectId(DEFAULT_PROJECT_ID);
request.setSelectAll(true);
this.requestPost(testUrl, request).andExpect(status().is5xxServerError());
//3.反例测试 ->参数校验项目ID为空 模块ID为空
request = new ApiScenarioBatchCopyMoveRequest();
request.setTargetModuleId(otherProjectModuleId);
this.requestPost(testUrl, request).andExpect(status().isBadRequest());
request = new ApiScenarioBatchCopyMoveRequest();
request.setProjectId(DEFAULT_PROJECT_ID);
this.requestPost(testUrl, request).andExpect(status().isBadRequest());
}
@Test
@Order(22)
void batchRemoveToGc() throws Exception {
String testUrl = "/batch-operation/delete-gc";
if (CollectionUtils.isEmpty(BATCH_OPERATION_SCENARIO_ID)) {
this.batchCopy();
}
//本次测试涉及到的场景ID
List<String> operationScenarioIds = new ArrayList<>(BATCH_OPERATION_SCENARIO_ID.subList(200, 500));
/*
正例测试
超过300条的批量删除到垃圾站
测试数据使用建国批量测试场景模块_200模块为查询条件和建国批量测试场景模块_250里的所有id复制到 建国批量测试场景模块_1
*/
ApiScenarioBatchRequest request = new ApiScenarioBatchRequest();
request.setSelectIds(BATCH_OPERATION_SCENARIO_ID.subList(250, 500));
request.setModuleIds(List.of(BATCH_OPERATION_SCENARIO_MODULE_MAP.get("建国批量测试场景模块_201")));
request.setProjectId(DEFAULT_PROJECT_ID);
request.setSelectAll(true);
//先测试一下没有开启模块时接口能否使用
apiScenarioBatchOperationTestService.removeApiModule(DEFAULT_PROJECT_ID);
this.requestPost(testUrl, request).andExpect(status().is5xxServerError());
//恢复
apiScenarioBatchOperationTestService.resetProjectModule(DEFAULT_PROJECT_ID);
MvcResult result = this.requestPostAndReturn(testUrl, request);
ResultHolder resultHolder = JSON.parseObject(result.getResponse().getContentAsString(StandardCharsets.UTF_8), ResultHolder.class);
ApiScenarioBatchOperationResponse resultResponse = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), ApiScenarioBatchOperationResponse.class);
//检查返回值
Assertions.assertEquals(resultResponse.getSuccess(), 300);
//数据库级别的检查
apiScenarioBatchOperationTestService.checkBatchGCOperation
(BATCH_OPERATION_SCENARIO_ID.subList(200, 500), true);
//2.重复请求
result = this.requestPostAndReturn(testUrl, request);
resultHolder = JSON.parseObject(result.getResponse().getContentAsString(StandardCharsets.UTF_8), ResultHolder.class);
resultResponse = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), ApiScenarioBatchOperationResponse.class);
//检查返回值
Assertions.assertEquals(resultResponse.getSuccess(), 0);
//数据库级别的检查
apiScenarioBatchOperationTestService.checkBatchGCOperation
(BATCH_OPERATION_SCENARIO_ID.subList(200, 500), true);
//增加日志检查
operationScenarioIds.forEach(item -> {
LOG_CHECK_LIST.add(
new CheckLogModel(item, OperationLogType.DELETE, "/api/scenario/batch-operation/delete-gc")
);
});
// 3.没有数据
request = new ApiScenarioBatchCopyMoveRequest();
request.setProjectId(DEFAULT_PROJECT_ID);
result = this.requestPostAndReturn(testUrl, request);
resultHolder = JSON.parseObject(result.getResponse().getContentAsString(StandardCharsets.UTF_8), ResultHolder.class);
resultResponse = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), ApiScenarioBatchOperationResponse.class);
//检查返回值
Assertions.assertEquals(resultResponse.getSuccess(), 0);
//校验权限
this.requestPostPermissionTest(PermissionConstants.PROJECT_API_SCENARIO_DELETE, testUrl, request);
//反例测试 ->参数校验项目ID为空
request = new ApiScenarioBatchCopyMoveRequest();
this.requestPost(testUrl, request).andExpect(status().isBadRequest());
}
@Test
@Order(23)
//todo
void batchRecoverToGc() throws Exception {
String testUrl = "/batch-operation/recover-gc";
if (CollectionUtils.isEmpty(BATCH_OPERATION_SCENARIO_ID)) {
this.batchRemoveToGc();
}
//本次测试涉及到的场景ID
List<String> operationScenarioIds = new ArrayList<>(BATCH_OPERATION_SCENARIO_ID.subList(200, 500));
/*
正例测试
超过300条的批量删除到垃圾站
测试数据使用建国批量测试场景模块_200模块为查询条件和建国批量测试场景模块_250里的所有id复制到 建国批量测试场景模块_1
*/
ApiScenarioBatchRequest request = new ApiScenarioBatchRequest();
request.setSelectIds(BATCH_OPERATION_SCENARIO_ID.subList(250, 500));
request.setModuleIds(List.of(BATCH_OPERATION_SCENARIO_MODULE_MAP.get("建国批量测试场景模块_201")));
request.setProjectId(DEFAULT_PROJECT_ID);
request.setSelectAll(true);
//先测试一下没有开启模块时接口能否使用
apiScenarioBatchOperationTestService.removeApiModule(DEFAULT_PROJECT_ID);
this.requestPost(testUrl, request).andExpect(status().is5xxServerError());
//恢复
apiScenarioBatchOperationTestService.resetProjectModule(DEFAULT_PROJECT_ID);
MvcResult result = this.requestPostAndReturn(testUrl, request);
ResultHolder resultHolder = JSON.parseObject(result.getResponse().getContentAsString(StandardCharsets.UTF_8), ResultHolder.class);
ApiScenarioBatchOperationResponse resultResponse = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), ApiScenarioBatchOperationResponse.class);
//检查返回值
Assertions.assertEquals(resultResponse.getSuccess(), 300);
//数据库级别的检查
apiScenarioBatchOperationTestService.checkBatchGCOperation
(BATCH_OPERATION_SCENARIO_ID.subList(200, 500), false);
//增加日志检查
operationScenarioIds.forEach(item -> {
LOG_CHECK_LIST.add(
new CheckLogModel(item, OperationLogType.RECOVER, "/api/scenario/batch-operation/recover-gc")
);
});
// 2.重复请求
result = this.requestPostAndReturn(testUrl, request);
resultHolder = JSON.parseObject(result.getResponse().getContentAsString(StandardCharsets.UTF_8), ResultHolder.class);
resultResponse = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), ApiScenarioBatchOperationResponse.class);
//检查返回值
Assertions.assertEquals(resultResponse.getSuccess(), 0);
//数据库级别的检查
apiScenarioBatchOperationTestService.checkBatchGCOperation
(BATCH_OPERATION_SCENARIO_ID.subList(200, 500), false);
// 3.没有数据
request = new ApiScenarioBatchCopyMoveRequest();
request.setProjectId(DEFAULT_PROJECT_ID);
result = this.requestPostAndReturn(testUrl, request);
resultHolder = JSON.parseObject(result.getResponse().getContentAsString(StandardCharsets.UTF_8), ResultHolder.class);
resultResponse = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), ApiScenarioBatchOperationResponse.class);
//检查返回值
Assertions.assertEquals(resultResponse.getSuccess(), 0);
//校验权限
this.requestPostPermissionTest(PermissionConstants.PROJECT_API_SCENARIO_RECOVER, testUrl, request);
//反例测试 ->参数校验项目ID为空
request = new ApiScenarioBatchCopyMoveRequest();
this.requestPost(testUrl, request).andExpect(status().isBadRequest());
}
@Test
@Order(24)
//todo
void batchDelete() throws Exception {
String testUrl = "/batch-operation/delete";
if (CollectionUtils.isEmpty(BATCH_OPERATION_SCENARIO_ID)) {
this.batchRecoverToGc();
}
//本次测试涉及到的场景ID
List<String> deleteScenarioIds = new ArrayList<>(BATCH_OPERATION_SCENARIO_ID.subList(200, 500));
/*
正例测试
超过300条的批量删除
测试数据使用建国批量测试场景模块_200模块为查询条件和建国批量测试场景模块_250里的所有id先删除到回收站再删除
*/
//先将用例挪到回收站
ApiScenarioBatchRequest request = new ApiScenarioBatchRequest();
request.setSelectIds(BATCH_OPERATION_SCENARIO_ID.subList(250, 500));
request.setModuleIds(List.of(BATCH_OPERATION_SCENARIO_MODULE_MAP.get("建国批量测试场景模块_201")));
request.setProjectId(DEFAULT_PROJECT_ID);
request.setSelectAll(true);
this.requestPostWithOk("/batch-operation/delete-gc", request);
//先测试一下没有开启模块时接口能否使用
apiScenarioBatchOperationTestService.removeApiModule(DEFAULT_PROJECT_ID);
this.requestPost(testUrl, request).andExpect(status().is5xxServerError());
//恢复
apiScenarioBatchOperationTestService.resetProjectModule(DEFAULT_PROJECT_ID);
MvcResult result = this.requestPostAndReturn(testUrl, request);
ResultHolder resultHolder = JSON.parseObject(result.getResponse().getContentAsString(StandardCharsets.UTF_8), ResultHolder.class);
ApiScenarioBatchOperationResponse resultResponse = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), ApiScenarioBatchOperationResponse.class);
//检查返回值
Assertions.assertEquals(resultResponse.getSuccess(), 300);
//数据库级别的检查
apiScenarioBatchOperationTestService.checkBatchDelete
(BATCH_OPERATION_SCENARIO_ID.subList(200, 500));
//增加日志检查
deleteScenarioIds.forEach(item -> {
LOG_CHECK_LIST.add(
new CheckLogModel(item, OperationLogType.DELETE, "/api/scenario/batch-operation/delete")
);
});
// 2.重复请求
result = this.requestPostAndReturn(testUrl, request);
resultHolder = JSON.parseObject(result.getResponse().getContentAsString(StandardCharsets.UTF_8), ResultHolder.class);
resultResponse = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), ApiScenarioBatchOperationResponse.class);
//检查返回值
Assertions.assertEquals(resultResponse.getSuccess(), 0);
//数据库级别的检查
apiScenarioBatchOperationTestService.checkBatchDelete
(BATCH_OPERATION_SCENARIO_ID.subList(200, 500));
// 3.没有数据
request = new ApiScenarioBatchCopyMoveRequest();
request.setProjectId(DEFAULT_PROJECT_ID);
result = this.requestPostAndReturn(testUrl, request);
resultHolder = JSON.parseObject(result.getResponse().getContentAsString(StandardCharsets.UTF_8), ResultHolder.class);
resultResponse = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), ApiScenarioBatchOperationResponse.class);
//检查返回值
Assertions.assertEquals(resultResponse.getSuccess(), 0);
//校验权限
this.requestPostPermissionTest(PermissionConstants.PROJECT_API_SCENARIO_DELETE, testUrl, request);
//反例测试 ->参数校验项目ID为空
request = new ApiScenarioBatchCopyMoveRequest();
this.requestPost(testUrl, request).andExpect(status().isBadRequest());
}
//30开始是关于删除和恢复的
@Test
@Order(30)
public void restore() throws Exception {
void recover() throws Exception {
if (CollectionUtils.isEmpty(BATCH_OPERATION_SCENARIO_ID)) {
this.batchCreateScenarios();
}
this.requestGetWithOk("/restore/" + BATCH_OPERATION_SCENARIO_ID.get(0));
this.requestGetWithOk("/recover/" + BATCH_OPERATION_SCENARIO_ID.getFirst());
}
@Test
@Order(999)
public void checkLog() throws Exception {
Thread.sleep(5000);
for (CheckLogModel checkLogModel : LOG_CHECK_LIST) {
if (org.apache.commons.lang3.StringUtils.isEmpty(checkLogModel.getUrl())) {
this.checkLog(checkLogModel.getResourceId(), checkLogModel.getOperationType());
} else {
this.checkLog(checkLogModel.getResourceId(), checkLogModel.getOperationType(), checkLogModel.getUrl());
}
}
}
private String createModule(int i, String projectId, String parentId) {
ApiScenarioModule apiScenarioModule = new ApiScenarioModule();
apiScenarioModule.setId(IDGenerator.nextStr());
@ -1123,6 +1504,9 @@ public class ApiScenarioControllerTests extends BaseTest {
/*
创建模块树结构
建国批量测试场景模块_default 无数据
|
|
建国批量测试场景模块_1 ->50条数据 有标签和环境组
|
|
@ -1148,6 +1532,20 @@ public class ApiScenarioControllerTests extends BaseTest {
List<Environment> environments = environmentMapper.selectByExample(environmentExample);
String parentModuleId = ModuleConstants.ROOT_NODE_PARENT_ID;
ApiScenarioModule apiScenarioModule = new ApiScenarioModule();
apiScenarioModule.setId(IDGenerator.nextStr());
apiScenarioModule.setProjectId(DEFAULT_PROJECT_ID);
apiScenarioModule.setName("建国批量测试场景模块_default");
apiScenarioModule.setCreateTime(System.currentTimeMillis());
apiScenarioModule.setUpdateTime(System.currentTimeMillis());
apiScenarioModule.setCreateUser("admin");
apiScenarioModule.setUpdateUser("admin");
apiScenarioModule.setParentId(parentModuleId);
apiScenarioModule.setPos(16L);
apiScenarioModuleMapper.insertSelective(apiScenarioModule);
BATCH_OPERATION_SCENARIO_MODULE_MAP.put("建国批量测试场景模块_default", apiScenarioModule.getId());
for (int i = 1; i <= size; i++) {
if (moduleId == null) {
moduleId = createModule(i, DEFAULT_PROJECT_ID, parentModuleId);

View File

@ -1,18 +1,20 @@
package io.metersphere.api.service;
import io.metersphere.api.domain.*;
import io.metersphere.api.dto.scenario.ApiScenarioBatchCopyRequest;
import io.metersphere.api.dto.scenario.ApiScenarioBatchCopyMoveRequest;
import io.metersphere.api.mapper.*;
import io.metersphere.project.domain.Project;
import io.metersphere.project.mapper.ProjectMapper;
import io.metersphere.sdk.util.JSON;
import jakarta.annotation.Resource;
import org.apache.commons.collections.MapUtils;
import org.junit.jupiter.api.Assertions;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@Service
@Transactional
@ -55,7 +57,7 @@ public class ApiScenarioBatchOperationTestService {
* @param reCopyCountInModule 关联之前模块里是数据数量
* @param request
*/
public void checkBatchCopy(List<String> sourceScenarioIds, List<String> copyScenarioIdList, int reCopyCountInModule, ApiScenarioBatchCopyRequest request) {
public void checkBatchCopy(List<String> sourceScenarioIds, List<String> copyScenarioIdList, int reCopyCountInModule, ApiScenarioBatchCopyMoveRequest request) {
ApiScenarioExample example = new ApiScenarioExample();
example.createCriteria().andModuleIdEqualTo(request.getTargetModuleId()).andIdIn(copyScenarioIdList);
List<ApiScenario> copyScenarios = apiScenarioMapper.selectByExample(example);
@ -106,6 +108,40 @@ public class ApiScenarioBatchOperationTestService {
Assertions.assertEquals(apiFileResourceMapper.countByExample(sourceFileExample), apiFileResourceMapper.countByExample(copyFileExample));
}
// 批量移动检查
public void checkBatchMove(List<String> moveScenarioIds, int countInModuleBeforeMove, Map<String, Long> oldModuleDataCount, ApiScenarioBatchCopyMoveRequest request) {
ApiScenarioExample example = new ApiScenarioExample();
example.createCriteria().andModuleIdEqualTo(request.getTargetModuleId()).andIdIn(moveScenarioIds);
List<ApiScenario> moveScenarios = apiScenarioMapper.selectByExample(example);
Assertions.assertEquals(moveScenarios.size(), moveScenarioIds.size());
//检查总数据量
example.clear();
example.createCriteria().andModuleIdEqualTo(request.getTargetModuleId());
List<ApiScenario> allScenarioList = apiScenarioMapper.selectByExample(example);
Assertions.assertEquals(allScenarioList.size(), countInModuleBeforeMove + moveScenarioIds.size());
if (MapUtils.isNotEmpty(oldModuleDataCount)) {
oldModuleDataCount.forEach((moduleId, moduleCount) -> {
example.clear();
example.createCriteria().andModuleIdEqualTo(moduleId);
Assertions.assertEquals(apiScenarioMapper.countByExample(example), moduleCount);
});
}
}
/**
* 检查批量回收相关的操作
*
* @param sourceScenarioIds 涉及到的场景id
* @param isDelete 是否是删除状态
*/
public void checkBatchGCOperation(List<String> sourceScenarioIds, boolean isDelete) {
ApiScenarioExample example = new ApiScenarioExample();
example.createCriteria().andDeletedEqualTo(isDelete).andIdIn(sourceScenarioIds);
List<ApiScenario> copyScenarios = apiScenarioMapper.selectByExample(example);
Assertions.assertEquals(copyScenarios.size(), sourceScenarioIds.size());
}
public void updateNameToTestByModuleId(String moduleId) {
ApiScenarioExample example = new ApiScenarioExample();
example.createCriteria().andModuleIdEqualTo(moduleId);
@ -125,4 +161,32 @@ public class ApiScenarioBatchOperationTestService {
apiScenarioMapper.updateByPrimaryKeySelective(apiScenario);
}
}
public void checkBatchDelete(List<String> deleteScenarioIds) {
ApiScenarioExample example = new ApiScenarioExample();
example.createCriteria().andIdIn(deleteScenarioIds);
Assertions.assertEquals(apiScenarioMapper.countByExample(example), 0);
//检查blob
ApiScenarioBlobExample sourceBlobExample = new ApiScenarioBlobExample();
sourceBlobExample.createCriteria().andIdIn(deleteScenarioIds);
Assertions.assertEquals(apiScenarioBlobMapper.countByExample(sourceBlobExample), 0);
//检查step
ApiScenarioStepExample sourceStepExample = new ApiScenarioStepExample();
sourceStepExample.createCriteria().andScenarioIdIn(deleteScenarioIds);
Assertions.assertEquals(apiScenarioStepMapper.countByExample(sourceStepExample), 0);
//检查step_blob
ApiScenarioStepBlobExample sourceStepBlobExample = new ApiScenarioStepBlobExample();
sourceStepBlobExample.createCriteria().andScenarioIdIn(deleteScenarioIds);
Assertions.assertEquals(apiScenarioStepBlobMapper.countByExample(sourceStepBlobExample), 0);
//检查fileRequest
ApiFileResourceExample sourceFileExample = new ApiFileResourceExample();
sourceFileExample.createCriteria().andResourceIdIn(deleteScenarioIds);
Assertions.assertEquals(apiFileResourceMapper.countByExample(sourceFileExample), 0);
}
}