fix(接口测试): 场景普通导出时数据补足

--bug=1047996 --user=宋天阳 【场景导入】-导入无引用关系的场景文件,CASE类型步骤的参数丢失 https://www.tapd.cn/55049933/s/1597504
This commit is contained in:
Jianguo-Genius 2024-10-24 17:22:33 +08:00 committed by 刘瑞斌
parent e31dc32ec3
commit be5ce9de72
4 changed files with 118 additions and 72 deletions

View File

@ -1,7 +1,5 @@
package io.metersphere.api.dto.export; package io.metersphere.api.dto.export;
import io.metersphere.api.constants.ApiScenarioStepRefType;
import io.metersphere.api.constants.ApiScenarioStepType;
import io.metersphere.api.domain.ApiScenarioCsv; import io.metersphere.api.domain.ApiScenarioCsv;
import io.metersphere.api.dto.converter.ApiDefinitionDetail; import io.metersphere.api.dto.converter.ApiDefinitionDetail;
import io.metersphere.api.dto.definition.ApiTestCaseDTO; import io.metersphere.api.dto.definition.ApiTestCaseDTO;
@ -9,7 +7,6 @@ import io.metersphere.api.dto.scenario.ApiScenarioDetail;
import io.metersphere.api.dto.scenario.ApiScenarioStepDTO; import io.metersphere.api.dto.scenario.ApiScenarioStepDTO;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@ -55,20 +52,4 @@ public class MetersphereApiScenarioExportResponse extends ApiScenarioExportRespo
public void addExportScenario(ApiScenarioDetail apiScenarioDetail) { public void addExportScenario(ApiScenarioDetail apiScenarioDetail) {
exportScenarioList.add(apiScenarioDetail); exportScenarioList.add(apiScenarioDetail);
} }
public void setStepTypeToCustomRequest() {
scenarioStepList.forEach(step -> {
if (StringUtils.equalsAnyIgnoreCase(step.getStepType(), ApiScenarioStepType.API.name(), ApiScenarioStepType.API_CASE.name())) {
step.setStepType(ApiScenarioStepType.CUSTOM_REQUEST.name());
}
});
}
public void setRefTypeToCopy() {
scenarioStepList.forEach(step -> {
if (StringUtils.equalsAnyIgnoreCase(step.getStepType(), ApiScenarioStepType.API.name(), ApiScenarioStepType.API_SCENARIO.name(), ApiScenarioStepType.API_CASE.name())) {
step.setRefType(ApiScenarioStepRefType.COPY.name());
}
});
}
} }

View File

@ -1,6 +1,7 @@
package io.metersphere.api.service; package io.metersphere.api.service;
import io.metersphere.api.constants.ApiDefinitionStatus; import io.metersphere.api.constants.ApiDefinitionStatus;
import io.metersphere.api.constants.ApiScenarioStepRefType;
import io.metersphere.api.constants.ApiScenarioStepType; import io.metersphere.api.constants.ApiScenarioStepType;
import io.metersphere.api.domain.*; import io.metersphere.api.domain.*;
import io.metersphere.api.dto.ApiFile; import io.metersphere.api.dto.ApiFile;
@ -179,7 +180,7 @@ public class ApiScenarioDataTransferService {
} }
//解析 //解析
ApiScenarioPreImportAnalysisResult preImportAnalysisResult = this.importAnalysis( ApiScenarioPreImportAnalysisResult preImportAnalysisResult = this.importAnalysis(
parseResult, request.getOperator(), request.getProjectId(), request.getModuleId(), apiScenarioModuleService.getTree(request.getProjectId())); parseResult, request.getOperator(), request.getProjectId(), request.getModuleId(), apiScenarioModuleService.getImportTreeNodeList(request.getProjectId()));
//存储 //存储
this.save(preImportAnalysisResult, request.getProjectId(), request.getOperator(), request.isCoverData()); this.save(preImportAnalysisResult, request.getProjectId(), request.getOperator(), request.isCoverData());
} }
@ -938,7 +939,7 @@ public class ApiScenarioDataTransferService {
{ {
for (Map.Entry<String, List<ApiScenarioImportDetail>> entry : projectScenarioMap.entrySet()) { for (Map.Entry<String, List<ApiScenarioImportDetail>> entry : projectScenarioMap.entrySet()) {
String targetProjectId = entry.getKey(); String targetProjectId = entry.getKey();
List<BaseTreeNode> apiScenarioModules = apiScenarioModuleService.getTree(targetProjectId); List<BaseTreeNode> apiScenarioModules = apiScenarioModuleService.getImportTreeNodeList(targetProjectId);
Map<String, String> moduleIdPathMap = apiScenarioModules.stream().collect(Collectors.toMap(BaseTreeNode::getId, BaseTreeNode::getPath)); Map<String, String> moduleIdPathMap = apiScenarioModules.stream().collect(Collectors.toMap(BaseTreeNode::getId, BaseTreeNode::getPath));
Map<String, BaseTreeNode> modulePathMap = apiScenarioModules.stream().collect(Collectors.toMap(BaseTreeNode::getPath, k -> k, (k1, k2) -> k1)); Map<String, BaseTreeNode> modulePathMap = apiScenarioModules.stream().collect(Collectors.toMap(BaseTreeNode::getPath, k -> k, (k1, k2) -> k1));
@ -1020,7 +1021,7 @@ public class ApiScenarioDataTransferService {
if (CollectionUtils.isEmpty(ids)) { if (CollectionUtils.isEmpty(ids)) {
return null; return null;
} }
Map<String, String> moduleMap = this.apiScenarioModuleService.getTree(request.getProjectId()).stream().collect(Collectors.toMap(BaseTreeNode::getId, BaseTreeNode::getPath)); Map<String, String> moduleMap = this.apiScenarioModuleService.getImportTreeNodeList(request.getProjectId()).stream().collect(Collectors.toMap(BaseTreeNode::getId, BaseTreeNode::getPath));
String fileFolder = tmpDir.getPath() + File.separatorChar + request.getFileId(); String fileFolder = tmpDir.getPath() + File.separatorChar + request.getFileId();
AtomicInteger fileIndex = new AtomicInteger(1); AtomicInteger fileIndex = new AtomicInteger(1);
@ -1150,9 +1151,47 @@ public class ApiScenarioDataTransferService {
} }
} else { } else {
// 普通导出,所有的引用都改为复制并且ApiApiCase改为CUSTOM_REQUEST // 普通导出,所有的引用都改为复制并且ApiApiCase改为CUSTOM_REQUEST
response.setRefTypeToCopy(); Map<String, String> stepApiDefinitionMap = new HashMap<>();
response.setStepTypeToCustomRequest(); Map<String, String> stepApiCaseMap = new HashMap<>();
response.getScenarioStepList().forEach(step -> {
if (StringUtils.equalsAnyIgnoreCase(step.getStepType(), ApiScenarioStepType.API.name(), ApiScenarioStepType.API_SCENARIO.name(), ApiScenarioStepType.API_CASE.name())) {
// 引用的apicase转换为自定义步骤时要对应的apicase也一并导出
if (!response.getScenarioStepBlobMap().containsKey(step.getId())) {
if (StringUtils.equalsIgnoreCase(step.getStepType(), ApiScenarioStepType.API.name())) {
stepApiDefinitionMap.put(step.getId(), step.getResourceId());
} else if (StringUtils.equalsIgnoreCase(step.getStepType(), ApiScenarioStepType.API_CASE.name())) {
stepApiCaseMap.put(step.getId(), step.getResourceId());
}
}
step.setRefType(ApiScenarioStepRefType.COPY.name());
step.setStepType(ApiScenarioStepType.CUSTOM_REQUEST.name());
}
});
Map<String, String> appendBlobMap = new HashMap<>();
if (MapUtils.isNotEmpty(stepApiDefinitionMap)) {
List<ApiDefinitionWithBlob> apiDefinitionWithBlobs = extApiDefinitionMapper.selectApiDefinitionWithBlob(new ArrayList<>(stepApiDefinitionMap.values()));
Map<String, ApiDefinitionWithBlob> idMap = apiDefinitionWithBlobs.stream().collect(Collectors.toMap(ApiDefinitionWithBlob::getId, Function.identity()));
stepApiDefinitionMap.forEach((stepId, apiId) -> {
ApiDefinitionWithBlob api = idMap.get(apiId);
if (api != null) {
appendBlobMap.put(stepId, new String(api.getRequest(), StandardCharsets.UTF_8));
}
});
}
if (MapUtils.isNotEmpty(stepApiCaseMap)) {
List<ApiTestCaseWithBlob> apiTestCaseList = extApiTestCaseMapper.selectAllDetailByIds(new ArrayList<>(stepApiCaseMap.values()));
Map<String, ApiTestCaseWithBlob> idMap = apiTestCaseList.stream().collect(Collectors.toMap(ApiTestCaseWithBlob::getId, Function.identity()));
stepApiCaseMap.forEach((stepId, apiCaseId) -> {
ApiTestCaseWithBlob apiTestCase = idMap.get(apiCaseId);
if (apiTestCase != null) {
appendBlobMap.put(stepId, new String(apiTestCase.getRequest(), StandardCharsets.UTF_8));
}
});
}
response.getScenarioStepBlobMap().putAll(appendBlobMap);
} }
return response; return response;
} }

View File

@ -35,6 +35,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -65,12 +66,36 @@ public class ApiScenarioModuleService extends ModuleTreeService {
return super.buildTreeAndCountResource(fileModuleList, true, Translator.get(UNPLANNED_SCENARIO)); return super.buildTreeAndCountResource(fileModuleList, true, Translator.get(UNPLANNED_SCENARIO));
} }
public List<BaseTreeNode> getTree(String projectId) { public List<BaseTreeNode> getImportTreeNodeList(String projectId) {
//接口的树结构是 模块子模块+接口 接口为非delete状态的 //接口的树结构是 模块子模块+接口 接口为非delete状态的
List<BaseTreeNode> fileModuleList = extApiScenarioModuleMapper.selectBaseByRequest(new ApiScenarioModuleRequest() {{ List<BaseTreeNode> traverseList = extApiScenarioModuleMapper.selectBaseByRequest(new ApiScenarioModuleRequest() {{
this.setProjectId(projectId); this.setProjectId(projectId);
}}); }});
return super.buildTreeAndCountResource(fileModuleList, true, Translator.get(UNPLANNED_SCENARIO));
List<BaseTreeNode> baseTreeNodeList = new ArrayList<>();
BaseTreeNode defaultNode = new BaseTreeNode(ModuleConstants.DEFAULT_NODE_ID, Translator.get(UNPLANNED_SCENARIO), ModuleConstants.NODE_TYPE_DEFAULT, ModuleConstants.ROOT_NODE_PARENT_ID);
defaultNode.setPath(StringUtils.join("/", defaultNode.getName()));
baseTreeNodeList.add(defaultNode);
int lastSize = 0;
Map<String, BaseTreeNode> baseTreeNodeMap = new HashMap<>();
while (CollectionUtils.isNotEmpty(traverseList) && traverseList.size() != lastSize) {
lastSize = traverseList.size();
List<BaseTreeNode> notMatchedList = new ArrayList<>();
for (BaseTreeNode treeNode : traverseList) {
if (!baseTreeNodeMap.containsKey(treeNode.getParentId()) && !StringUtils.equalsIgnoreCase(treeNode.getParentId(), ModuleConstants.ROOT_NODE_PARENT_ID)) {
notMatchedList.add(treeNode);
continue;
}
BaseTreeNode node = new BaseTreeNode(treeNode.getId(), treeNode.getName(), treeNode.getType(), treeNode.getParentId());
node.genModulePath(baseTreeNodeMap.get(treeNode.getParentId()));
baseTreeNodeMap.put(treeNode.getId(), node);
baseTreeNodeList.add(node);
}
traverseList = notMatchedList;
}
return baseTreeNodeList;
} }
public List<BaseTreeNode> getTreeOnlyIdsAndResourceCount(ApiScenarioModuleRequest request, List<ModuleCountDTO> moduleCountDTOList) { public List<BaseTreeNode> getTreeOnlyIdsAndResourceCount(ApiScenarioModuleRequest request, List<ModuleCountDTO> moduleCountDTOList) {

View File

@ -1,6 +1,6 @@
package io.metersphere.api.controller; package io.metersphere.api.controller;
import io.metersphere.api.dto.definition.ApiDefinitionBatchExportRequest; import io.metersphere.api.dto.definition.ApiScenarioBatchExportRequest;
import io.metersphere.api.dto.export.MetersphereApiScenarioExportResponse; import io.metersphere.api.dto.export.MetersphereApiScenarioExportResponse;
import io.metersphere.api.dto.scenario.ApiScenarioImportRequest; import io.metersphere.api.dto.scenario.ApiScenarioImportRequest;
import io.metersphere.api.utils.ApiDataUtils; import io.metersphere.api.utils.ApiDataUtils;
@ -34,6 +34,7 @@ import org.springframework.util.MultiValueMap;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@ -62,11 +63,6 @@ public class ApiScenarioControllerImportAndExportTests extends BaseTest {
initProject.setEnable(true); initProject.setEnable(true);
initProject.setUserIds(List.of("admin")); initProject.setUserIds(List.of("admin"));
project = commonProjectService.add(initProject, "admin", "/organization-project/add", OperationLogModule.SETTING_ORGANIZATION_PROJECT); project = commonProjectService.add(initProject, "admin", "/organization-project/add", OperationLogModule.SETTING_ORGANIZATION_PROJECT);
// ArrayList<String> moduleList = new ArrayList<>(List.of("workstation", "testPlan", "bugManagement", "caseManagement", "apiTest", "uiTest", "loadTest"));
// Project updateProject = new Project();
// updateProject.setId(importProject.getId());
// updateProject.setModuleSetting(JSON.toJSONString(moduleList));
// projectMapper.updateByPrimaryKeySelective(updateProject);
} }
} }
@ -101,13 +97,17 @@ public class ApiScenarioControllerImportAndExportTests extends BaseTest {
public void testExport() throws Exception { public void testExport() throws Exception {
MsFileUtils.deleteDir("/tmp/api-scenario-export/"); MsFileUtils.deleteDir("/tmp/api-scenario-export/");
ApiDefinitionBatchExportRequest exportRequest = new ApiDefinitionBatchExportRequest(); List<Boolean> exportAllRelatedData = new ArrayList<>() {{
this.add(true);
this.add(false);
}};
for (Boolean isAllRelatedData : exportAllRelatedData) {
ApiScenarioBatchExportRequest exportRequest = new ApiScenarioBatchExportRequest();
String fileId = IDGenerator.nextStr(); String fileId = IDGenerator.nextStr();
exportRequest.setProjectId(project.getId()); exportRequest.setProjectId(project.getId());
exportRequest.setFileId(fileId); exportRequest.setFileId(fileId);
exportRequest.setSelectAll(true); exportRequest.setSelectAll(true);
exportRequest.setExportApiCase(true); exportRequest.setExportAllRelatedData(isAllRelatedData);
exportRequest.setExportApiMock(true);
MvcResult mvcResult = this.requestPostWithOkAndReturn(URL_POST_EXPORT + "metersphere", exportRequest); MvcResult mvcResult = this.requestPostWithOkAndReturn(URL_POST_EXPORT + "metersphere", exportRequest);
String returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8); String returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
@ -143,6 +143,7 @@ public class ApiScenarioControllerImportAndExportTests extends BaseTest {
MsFileUtils.deleteDir("/tmp/api-scenario-export/"); MsFileUtils.deleteDir("/tmp/api-scenario-export/");
} }
}
private MvcResult download(String projectId, String fileId) throws Exception { private MvcResult download(String projectId, String fileId) throws Exception {
return mockMvc.perform(MockMvcRequestBuilders.get("/api/scenario/download/file/" + projectId + "/" + fileId) return mockMvc.perform(MockMvcRequestBuilders.get("/api/scenario/download/file/" + projectId + "/" + fileId)