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;
import io.metersphere.api.constants.ApiScenarioStepRefType;
import io.metersphere.api.constants.ApiScenarioStepType;
import io.metersphere.api.domain.ApiScenarioCsv;
import io.metersphere.api.dto.converter.ApiDefinitionDetail;
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.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.HashMap;
@ -55,20 +52,4 @@ public class MetersphereApiScenarioExportResponse extends ApiScenarioExportRespo
public void addExportScenario(ApiScenarioDetail 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;
import io.metersphere.api.constants.ApiDefinitionStatus;
import io.metersphere.api.constants.ApiScenarioStepRefType;
import io.metersphere.api.constants.ApiScenarioStepType;
import io.metersphere.api.domain.*;
import io.metersphere.api.dto.ApiFile;
@ -179,7 +180,7 @@ public class ApiScenarioDataTransferService {
}
//解析
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());
}
@ -938,7 +939,7 @@ public class ApiScenarioDataTransferService {
{
for (Map.Entry<String, List<ApiScenarioImportDetail>> entry : projectScenarioMap.entrySet()) {
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, 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)) {
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();
AtomicInteger fileIndex = new AtomicInteger(1);
@ -1150,9 +1151,47 @@ public class ApiScenarioDataTransferService {
}
} else {
// 普通导出,所有的引用都改为复制并且ApiApiCase改为CUSTOM_REQUEST
response.setRefTypeToCopy();
response.setStepTypeToCustomRequest();
Map<String, String> stepApiDefinitionMap = new HashMap<>();
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;
}

View File

@ -35,6 +35,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -65,12 +66,36 @@ public class ApiScenarioModuleService extends ModuleTreeService {
return super.buildTreeAndCountResource(fileModuleList, true, Translator.get(UNPLANNED_SCENARIO));
}
public List<BaseTreeNode> getTree(String projectId) {
public List<BaseTreeNode> getImportTreeNodeList(String projectId) {
//接口的树结构是 模块子模块+接口 接口为非delete状态的
List<BaseTreeNode> fileModuleList = extApiScenarioModuleMapper.selectBaseByRequest(new ApiScenarioModuleRequest() {{
List<BaseTreeNode> traverseList = extApiScenarioModuleMapper.selectBaseByRequest(new ApiScenarioModuleRequest() {{
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) {

View File

@ -1,6 +1,6 @@
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.scenario.ApiScenarioImportRequest;
import io.metersphere.api.utils.ApiDataUtils;
@ -34,6 +34,7 @@ import org.springframework.util.MultiValueMap;
import java.io.File;
import java.io.FileInputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@ -62,11 +63,6 @@ public class ApiScenarioControllerImportAndExportTests extends BaseTest {
initProject.setEnable(true);
initProject.setUserIds(List.of("admin"));
project = commonProjectService.add(initProject, "admin", "/organization-project/add", OperationLogModule.SETTING_ORGANIZATION_PROJECT);
// ArrayList<String> moduleList = new ArrayList<>(List.of("workstation", "testPlan", "bugManagement", "caseManagement", "apiTest", "uiTest", "loadTest"));
// Project updateProject = new Project();
// updateProject.setId(importProject.getId());
// updateProject.setModuleSetting(JSON.toJSONString(moduleList));
// projectMapper.updateByPrimaryKeySelective(updateProject);
}
}
@ -101,47 +97,52 @@ public class ApiScenarioControllerImportAndExportTests extends BaseTest {
public void testExport() throws Exception {
MsFileUtils.deleteDir("/tmp/api-scenario-export/");
ApiDefinitionBatchExportRequest exportRequest = new ApiDefinitionBatchExportRequest();
String fileId = IDGenerator.nextStr();
exportRequest.setProjectId(project.getId());
exportRequest.setFileId(fileId);
exportRequest.setSelectAll(true);
exportRequest.setExportApiCase(true);
exportRequest.setExportApiMock(true);
MvcResult mvcResult = this.requestPostWithOkAndReturn(URL_POST_EXPORT + "metersphere", exportRequest);
String returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
List<Boolean> exportAllRelatedData = new ArrayList<>() {{
this.add(true);
this.add(false);
}};
for (Boolean isAllRelatedData : exportAllRelatedData) {
ApiScenarioBatchExportRequest exportRequest = new ApiScenarioBatchExportRequest();
String fileId = IDGenerator.nextStr();
exportRequest.setProjectId(project.getId());
exportRequest.setFileId(fileId);
exportRequest.setSelectAll(true);
exportRequest.setExportAllRelatedData(isAllRelatedData);
MvcResult mvcResult = this.requestPostWithOkAndReturn(URL_POST_EXPORT + "metersphere", exportRequest);
String returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
JSON.parseObject(returnData, ResultHolder.class).getData().toString();
Assertions.assertTrue(StringUtils.isNotBlank(fileId));
List<ExportTask> taskList = exportTaskManager.getExportTasks(exportRequest.getProjectId(), null, null, "admin", fileId);
while (CollectionUtils.isEmpty(taskList)) {
Thread.sleep(1000);
taskList = exportTaskManager.getExportTasks(exportRequest.getProjectId(), null, null, "admin", fileId);
JSON.parseObject(returnData, ResultHolder.class).getData().toString();
Assertions.assertTrue(StringUtils.isNotBlank(fileId));
List<ExportTask> taskList = exportTaskManager.getExportTasks(exportRequest.getProjectId(), null, null, "admin", fileId);
while (CollectionUtils.isEmpty(taskList)) {
Thread.sleep(1000);
taskList = exportTaskManager.getExportTasks(exportRequest.getProjectId(), null, null, "admin", fileId);
}
ExportTask task = taskList.getFirst();
while (!StringUtils.equalsIgnoreCase(task.getState(), ExportConstants.ExportState.SUCCESS.name())) {
Thread.sleep(1000);
task = exportTaskManager.getExportTasks(exportRequest.getProjectId(), null, null, "admin", fileId).getFirst();
}
mvcResult = this.download(exportRequest.getProjectId(), fileId);
byte[] fileBytes = mvcResult.getResponse().getContentAsByteArray();
File zipFile = new File("/tmp/api-scenario-export/downloadFiles.zip");
FileUtils.writeByteArrayToFile(zipFile, fileBytes);
File[] files = MsFileUtils.unZipFile(zipFile, "/tmp/api-scenario-export/unzip/");
assert files != null;
Assertions.assertEquals(files.length, 1);
String fileContent = FileUtils.readFileToString(files[0], StandardCharsets.UTF_8);
MetersphereApiScenarioExportResponse exportResponse = ApiDataUtils.parseObject(fileContent, MetersphereApiScenarioExportResponse.class);
Assertions.assertEquals(exportResponse.getExportScenarioList().size(), 8);
MsFileUtils.deleteDir("/tmp/api-scenario-export/");
}
ExportTask task = taskList.getFirst();
while (!StringUtils.equalsIgnoreCase(task.getState(), ExportConstants.ExportState.SUCCESS.name())) {
Thread.sleep(1000);
task = exportTaskManager.getExportTasks(exportRequest.getProjectId(), null, null, "admin", fileId).getFirst();
}
mvcResult = this.download(exportRequest.getProjectId(), fileId);
byte[] fileBytes = mvcResult.getResponse().getContentAsByteArray();
File zipFile = new File("/tmp/api-scenario-export/downloadFiles.zip");
FileUtils.writeByteArrayToFile(zipFile, fileBytes);
File[] files = MsFileUtils.unZipFile(zipFile, "/tmp/api-scenario-export/unzip/");
assert files != null;
Assertions.assertEquals(files.length, 1);
String fileContent = FileUtils.readFileToString(files[0], StandardCharsets.UTF_8);
MetersphereApiScenarioExportResponse exportResponse = ApiDataUtils.parseObject(fileContent, MetersphereApiScenarioExportResponse.class);
Assertions.assertEquals(exportResponse.getExportScenarioList().size(), 8);
MsFileUtils.deleteDir("/tmp/api-scenario-export/");
}
private MvcResult download(String projectId, String fileId) throws Exception {