fix(接口测试): 场景多次引用相同的场景,执行报错

This commit is contained in:
AgAngle 2024-04-02 20:39:50 +08:00 committed by jianxing
parent ffd19fae21
commit 0ab72dcf0e
9 changed files with 125 additions and 130 deletions

View File

@ -44,11 +44,6 @@ public class ExecutionQueue implements Serializable {
*/ */
private ApiRunModeConfigDTO runModeConfig; private ApiRunModeConfigDTO runModeConfig;
/**
* 全部场景的请求请求总量用于计算执行各种指标
*/
private Long requestCount;
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
} }

View File

@ -24,9 +24,4 @@ public class ExecutionQueueDetail implements Serializable {
* 当前资源产生的执行报告id * 当前资源产生的执行报告id
*/ */
private String reportId; private String reportId;
/**
* 单个场景要执行的请求总量用于计算执行各种指标
*/
private Long requestCount;
} }

View File

@ -79,4 +79,11 @@ public class ApiScenarioStepCommonDTO<T extends ApiScenarioStepCommonDTO> {
@Valid @Valid
@Schema(description = "子步骤") @Schema(description = "子步骤")
private List<T> children; private List<T> children;
/**
* 步骤的唯一ID
* 引用相同场景的情况原步骤ID可能会重复
*/
@Schema(description = "执行时需要的步骤唯一ID引用相同场景的情况原步骤ID可能会重复")
private String uniqueId;
} }

View File

@ -57,5 +57,5 @@ public interface ExtApiScenarioMapper {
Long getPos(String projectId); Long getPos(String projectId);
List<ApiScenario> getApiCaseExecuteInfoByIds(List<String> subIds); List<ApiScenario> getScenarioExecuteInfoByIds(@Param("ids") List<String> ids);
} }

View File

@ -520,7 +520,7 @@
ORDER BY pos DESC ORDER BY pos DESC
LIMIT 1; LIMIT 1;
</select> </select>
<select id="getApiCaseExecuteInfoByIds" resultType="io.metersphere.api.domain.ApiScenario"> <select id="getScenarioExecuteInfoByIds" resultType="io.metersphere.api.domain.ApiScenario">
select id, name, environment_id, project_id select id, name, environment_id, project_id
from api_scenario from api_scenario
where id in where id in

View File

@ -1,6 +1,5 @@
package io.metersphere.api.service; package io.metersphere.api.service;
import io.metersphere.api.dto.ApiBatchRunInitReportResult;
import io.metersphere.api.service.queue.ApiExecutionQueueService; import io.metersphere.api.service.queue.ApiExecutionQueueService;
import io.metersphere.sdk.dto.api.task.ApiRunModeConfigDTO; import io.metersphere.sdk.dto.api.task.ApiRunModeConfigDTO;
import io.metersphere.sdk.dto.queue.ExecutionQueue; import io.metersphere.sdk.dto.queue.ExecutionQueue;
@ -26,31 +25,11 @@ public class ApiBatchRunBaseService {
*/ */
public ExecutionQueue initExecutionqueue(List<String> resourceIds, ApiRunModeConfigDTO runModeConfig, String resourceType, Map<String, String> caseReportMap, String userId) { public ExecutionQueue initExecutionqueue(List<String> resourceIds, ApiRunModeConfigDTO runModeConfig, String resourceType, Map<String, String> caseReportMap, String userId) {
ExecutionQueue queue = getExecutionQueue(runModeConfig, resourceType, userId); ExecutionQueue queue = getExecutionQueue(runModeConfig, resourceType, userId);
queue.setRequestCount(runModeConfig.isIntegratedReport() ? resourceIds.size() : 1L);
List<ExecutionQueueDetail> queueDetails = getExecutionQueueDetails(resourceIds, caseReportMap); List<ExecutionQueueDetail> queueDetails = getExecutionQueueDetails(resourceIds, caseReportMap);
apiExecutionQueueService.insertQueue(queue, queueDetails); apiExecutionQueueService.insertQueue(queue, queueDetails);
return queue; return queue;
} }
/**
* 初始化执行队列
*
* @param resourceIds
* @param runModeConfig
* @return
*/
public ExecutionQueue initExecutionqueue(List<String> resourceIds, ApiRunModeConfigDTO runModeConfig, String resourceType, ApiBatchRunInitReportResult reportResult, String userId) {
Map<String, Long> scenarioCountMap = reportResult.getScenarioCountMap();
ExecutionQueue queue = getExecutionQueue(runModeConfig, resourceType, userId);
queue.setRequestCount(reportResult.getRequestCount());
List<ExecutionQueueDetail> queueDetails = getExecutionQueueDetails(resourceIds, reportResult.getScenarioReportMap());
for (ExecutionQueueDetail queueDetail : queueDetails) {
queueDetail.setRequestCount(scenarioCountMap.get(queueDetail.getResourceId()));
}
apiExecutionQueueService.insertQueue(queue, queueDetails);
return queue;
}
public List<ExecutionQueueDetail> getExecutionQueueDetails(List<String> resourceIds, Map<String, String> caseReportMap) { public List<ExecutionQueueDetail> getExecutionQueueDetails(List<String> resourceIds, Map<String, String> caseReportMap) {
List<ExecutionQueueDetail> queueDetails = new ArrayList<>(); List<ExecutionQueueDetail> queueDetails = new ArrayList<>();
AtomicInteger sort = new AtomicInteger(1); AtomicInteger sort = new AtomicInteger(1);
@ -58,7 +37,6 @@ public class ApiBatchRunBaseService {
ExecutionQueueDetail queueDetail = new ExecutionQueueDetail(); ExecutionQueueDetail queueDetail = new ExecutionQueueDetail();
queueDetail.setResourceId(resourceId); queueDetail.setResourceId(resourceId);
queueDetail.setSort(sort.getAndIncrement()); queueDetail.setSort(sort.getAndIncrement());
queueDetail.setRequestCount(1L);
// caseReportMap null 说明是集合报告生成一个虚拟的报告ID // caseReportMap null 说明是集合报告生成一个虚拟的报告ID
queueDetail.setReportId(caseReportMap == null ? UUID.randomUUID().toString() : caseReportMap.get(resourceId)); queueDetail.setReportId(caseReportMap == null ? UUID.randomUUID().toString() : caseReportMap.get(resourceId));
queueDetails.add(queueDetail); queueDetails.add(queueDetail);

View File

@ -315,7 +315,7 @@ public class ApiTestCaseBatchRunService {
TaskRequestDTO taskRequest = getTaskRequestDTO(reportId, apiTestCase, runModeConfig); TaskRequestDTO taskRequest = getTaskRequestDTO(reportId, apiTestCase, runModeConfig);
taskRequest.setQueueId(queue.getQueueId()); taskRequest.setQueueId(queue.getQueueId());
taskRequest.setRequestCount(queue.getRequestCount()); taskRequest.setRequestCount(1l);
execute(taskRequest, apiTestCase, apiTestCaseBlob, BeanUtils.copyBean(new ApiDefinitionExecuteInfo(), apiDefinition)); execute(taskRequest, apiTestCase, apiTestCaseBlob, BeanUtils.copyBean(new ApiDefinitionExecuteInfo(), apiDefinition));
} }

View File

@ -5,12 +5,15 @@ import io.metersphere.api.domain.ApiScenario;
import io.metersphere.api.domain.ApiScenarioRecord; import io.metersphere.api.domain.ApiScenarioRecord;
import io.metersphere.api.domain.ApiScenarioReport; import io.metersphere.api.domain.ApiScenarioReport;
import io.metersphere.api.domain.ApiScenarioReportStep; import io.metersphere.api.domain.ApiScenarioReportStep;
import io.metersphere.api.dto.ApiBatchRunInitReportResult;
import io.metersphere.api.dto.ApiScenarioParamConfig; import io.metersphere.api.dto.ApiScenarioParamConfig;
import io.metersphere.api.dto.ApiScenarioParseTmpParam; import io.metersphere.api.dto.ApiScenarioParseTmpParam;
import io.metersphere.api.dto.debug.ApiResourceRunRequest; import io.metersphere.api.dto.debug.ApiResourceRunRequest;
import io.metersphere.api.dto.request.MsScenario; import io.metersphere.api.dto.request.MsScenario;
import io.metersphere.api.dto.scenario.*; import io.metersphere.api.dto.scenario.ApiScenarioBatchRunRequest;
import io.metersphere.api.dto.scenario.ApiScenarioDetail;
import io.metersphere.api.dto.scenario.ApiScenarioParseParam;
import io.metersphere.api.dto.scenario.ApiScenarioStepDTO;
import io.metersphere.api.mapper.ExtApiScenarioMapper;
import io.metersphere.api.service.ApiBatchRunBaseService; import io.metersphere.api.service.ApiBatchRunBaseService;
import io.metersphere.api.service.ApiExecuteService; import io.metersphere.api.service.ApiExecuteService;
import io.metersphere.api.service.queue.ApiExecutionQueueService; import io.metersphere.api.service.queue.ApiExecutionQueueService;
@ -24,19 +27,20 @@ import io.metersphere.sdk.dto.queue.ExecutionQueueDetail;
import io.metersphere.sdk.util.BeanUtils; import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.sdk.util.DateUtils; import io.metersphere.sdk.util.DateUtils;
import io.metersphere.sdk.util.LogUtils; import io.metersphere.sdk.util.LogUtils;
import io.metersphere.sdk.util.SubListUtils;
import io.metersphere.system.uid.IDGenerator; import io.metersphere.system.uid.IDGenerator;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.HashMap; import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
import java.util.stream.Collectors;
@Service @Service
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@ -53,6 +57,8 @@ public class ApiScenarioBatchRunService {
private ApiScenarioReportService apiScenarioReportService; private ApiScenarioReportService apiScenarioReportService;
@Resource @Resource
private ApiBatchRunBaseService apiBatchRunBaseService; private ApiBatchRunBaseService apiBatchRunBaseService;
@Resource
private ExtApiScenarioMapper extApiScenarioMapper;
/** /**
* 异步批量执行 * 异步批量执行
@ -95,13 +101,13 @@ public class ApiScenarioBatchRunService {
initIntegratedReport(runModeConfig, ids, userId, request.getProjectId()); initIntegratedReport(runModeConfig, ids, userId, request.getProjectId());
} }
ApiBatchRunInitReportResult reportResult = initReport(ids, runModeConfig, userId); Map<String, String> scenarioReportMap = initReport(ids, runModeConfig, userId);
// 集成报告执行前先设置成 RUNNING // 集成报告执行前先设置成 RUNNING
setRunningIntegrateReport(runModeConfig); setRunningIntegrateReport(runModeConfig);
// 先初始化集成报告设置好报告ID再初始化执行队列 // 先初始化集成报告设置好报告ID再初始化执行队列
ExecutionQueue queue = apiBatchRunBaseService.initExecutionqueue(ids, runModeConfig, ApiExecuteResourceType.API_SCENARIO.name(), reportResult, userId); ExecutionQueue queue = apiBatchRunBaseService.initExecutionqueue(ids, runModeConfig, ApiExecuteResourceType.API_SCENARIO.name(), scenarioReportMap, userId);
// 执行第一个任务 // 执行第一个任务
ExecutionQueueDetail nextDetail = apiExecutionQueueService.getNextDetail(queue.getQueueId()); ExecutionQueueDetail nextDetail = apiExecutionQueueService.getNextDetail(queue.getQueueId());
executeNextTask(queue, nextDetail); executeNextTask(queue, nextDetail);
@ -124,84 +130,84 @@ public class ApiScenarioBatchRunService {
apiExecutionSetService.initSet(apiScenarioReport.getId(), ids); apiExecutionSetService.initSet(apiScenarioReport.getId(), ids);
} }
ApiBatchRunInitReportResult reportResult = initReport(ids, runModeConfig, userId); Map<String, String> scenarioReportMap = initReport(ids, runModeConfig, userId);
// 集成报告执行前先设置成 RUNNING // 集成报告执行前先设置成 RUNNING
setRunningIntegrateReport(runModeConfig); setRunningIntegrateReport(runModeConfig);
AtomicInteger errorCount = new AtomicInteger(); AtomicInteger errorCount = new AtomicInteger();
// 这里ID顺序和队列的ID顺序保持一致 // 这里ID顺序和队列的ID顺序保持一致
for (String id : ids) { for (String id : ids) {
String reportId = null;
try {
ApiScenarioDetail apiScenarioDetail = apiScenarioService.get(id);
if (apiScenarioDetail == null) {
if (runModeConfig.isIntegratedReport()) {
// 用例不存在则在执行集合中删除
apiExecutionSetService.removeItem(runModeConfig.getCollectionReport().getReportId(), id);
}
LogUtils.info("当前执行任务的用例已删除 {}", id);
break;
}
// 请求数量集合报告放总的数量独立报告放当前场景的请求数量
Long requestCount;
if (runModeConfig.isIntegratedReport()) {
// 集成报告生成虚拟的报告ID
reportId = IDGenerator.nextStr();
requestCount = reportResult.getRequestCount();
} else {
reportId = reportResult.getScenarioReportMap().get(id);
requestCount = Optional.ofNullable(reportResult.getScenarioCountMap().get(id)).orElse(0L);
}
TaskRequestDTO taskRequest = getTaskRequestDTO(reportId, apiScenarioDetail, runModeConfig);
taskRequest.setRequestCount(requestCount);
execute(taskRequest, apiScenarioDetail);
} catch (Exception e) { String reportId = null;
LogUtils.error("执行用例失败 {}-{}", reportId, id); try {
LogUtils.error(e); ApiScenarioDetail apiScenarioDetail = apiScenarioService.get(id);
if (errorCount.getAndIncrement() > 10) { if (apiScenarioDetail == null) {
LogUtils.error("批量执行用例失败错误次数超过10次停止执行"); if (runModeConfig.isIntegratedReport()) {
return; // 用例不存在则在执行集合中删除
apiExecutionSetService.removeItem(runModeConfig.getCollectionReport().getReportId(), id);
}
LogUtils.info("当前执行任务的用例已删除 {}", id);
continue;
}
if (runModeConfig.isIntegratedReport()) {
// 集成报告生成虚拟的报告ID
reportId = IDGenerator.nextStr();
} else {
reportId = scenarioReportMap.get(id);
}
TaskRequestDTO taskRequest = getTaskRequestDTO(reportId, apiScenarioDetail, runModeConfig);
execute(taskRequest, apiScenarioDetail);
} catch (Exception e) {
LogUtils.error("执行用例失败 {}-{}", reportId, id);
LogUtils.error(e);
if (errorCount.getAndIncrement() > 10) {
LogUtils.error("批量执行用例失败错误次数超过10次停止执行");
return;
}
} }
} }
}
} }
private ApiBatchRunInitReportResult initReport(List<String> ids, ApiRunModeConfigDTO runModeConfig, String userId) { private Map<String, String> initReport(List<String> ids, ApiRunModeConfigDTO runModeConfig, String userId) {
Map<String, String> scenarioReportMap = new HashMap<>(); Map<String, String> scenarioReportMap = new HashMap<>();
ApiBatchRunInitReportResult reportResult = new ApiBatchRunInitReportResult();
Map<String, Long> scenarioCountMap = reportResult.getScenarioCountMap();
Boolean isIntegratedReport = runModeConfig.isIntegratedReport(); Boolean isIntegratedReport = runModeConfig.isIntegratedReport();
AtomicInteger sort = new AtomicInteger(1); AtomicInteger sort = new AtomicInteger(1);
List<ApiScenarioReportStep> apiScenarioReportSteps = new ArrayList<>(ids.size());
List<ApiScenario> apiScenarios = new ArrayList<>(ids.size());
// 分批查询
SubListUtils.dealForSubList(ids, 100, subIds -> apiScenarios.addAll(extApiScenarioMapper.getScenarioExecuteInfoByIds(subIds)));
Map<String, ApiScenario> apiScenarioMap = apiScenarios.stream()
.collect(Collectors.toMap(ApiScenario::getId, Function.identity()));
// 这里ID顺序和队列的ID顺序保持一致 // 这里ID顺序和队列的ID顺序保持一致
for (String id : ids) { for (String id : ids) {
ApiScenarioDetail apiScenarioDetail = apiScenarioService.get(id); ApiScenario apiScenario = apiScenarioMap.get(id);
if (apiScenario == null) {
if (apiScenarioDetail == null) {
break; break;
} }
// 记录请求数量
Long itemCount = getRequestCount(apiScenarioDetail.getSteps());
scenarioCountMap.put(id, itemCount);
reportResult.setRequestCount(reportResult.getRequestCount() + itemCount);
if (runModeConfig.isIntegratedReport()) { if (runModeConfig.isIntegratedReport()) {
// 初始化集成报告步骤 // 集合报告初始化一级步骤
initIntegratedReportSteps(apiScenarioDetail, runModeConfig.getCollectionReport().getReportId(), sort.getAndIncrement()); ApiScenarioReportStep apiScenarioReportStep = getApiScenarioReportStep(apiScenario, runModeConfig.getCollectionReport().getReportId(), sort.getAndIncrement());
apiScenarioReportSteps.add(apiScenarioReportStep);
} else { } else {
// 初始化非集成报告 // 非集合报告初始化独立报告执行时初始化步骤
String reportId = initScenarioReport(runModeConfig, apiScenarioDetail, userId).getApiScenarioReportId(); String reportId = initScenarioReport(runModeConfig, apiScenario, userId).getApiScenarioReportId();
// 初始化报告步骤
apiScenarioService.initScenarioReportSteps(apiScenarioDetail.getSteps(), reportId);
scenarioReportMap.put(id, reportId); scenarioReportMap.put(id, reportId);
} }
} }
reportResult.setScenarioReportMap(isIntegratedReport ? null : scenarioReportMap); if (CollectionUtils.isNotEmpty(apiScenarioReportSteps)) {
return reportResult; apiScenarioReportService.insertApiScenarioReportStep(apiScenarioReportSteps);
}
return isIntegratedReport ? null : scenarioReportMap;
} }
@ -226,18 +232,6 @@ public class ApiScenarioBatchRunService {
} }
} }
/**
* 初始化集成报告的报告步骤
*/
private void initIntegratedReportSteps(ApiScenarioDetail apiScenarioDetail, String reportId, long sort) {
// 将当前场景生成一级报告步骤
ApiScenarioReportStep apiScenarioReportStep = getApiScenarioReportStep(apiScenarioDetail, reportId, sort);
// 初始化报告步骤
List<ApiScenarioReportStep> scenarioReportSteps = apiScenarioService.getScenarioReportSteps(apiScenarioReportStep.getStepId(), apiScenarioDetail.getSteps(), reportId);
scenarioReportSteps.addFirst(apiScenarioReportStep);
apiScenarioReportService.insertApiScenarioReportStep(scenarioReportSteps);
}
private ApiScenarioReportStep getApiScenarioReportStep(ApiScenario apiScenario, String reportId, long sort) { private ApiScenarioReportStep getApiScenarioReportStep(ApiScenario apiScenario, String reportId, long sort) {
ApiScenarioReportStep apiReportStep = new ApiScenarioReportStep(); ApiScenarioReportStep apiReportStep = new ApiScenarioReportStep();
apiReportStep.setReportId(reportId); apiReportStep.setReportId(reportId);
@ -296,14 +290,6 @@ public class ApiScenarioBatchRunService {
} }
TaskRequestDTO taskRequest = getTaskRequestDTO(queueDetail.getReportId(), apiScenarioDetail, queue.getRunModeConfig()); TaskRequestDTO taskRequest = getTaskRequestDTO(queueDetail.getReportId(), apiScenarioDetail, queue.getRunModeConfig());
taskRequest.setQueueId(queue.getQueueId()); taskRequest.setQueueId(queue.getQueueId());
// 请求数量集合报告放总的数量独立报告放当前场景的请求数量
Long requestCount;
if (queue.getRunModeConfig().isIntegratedReport()) {
requestCount = queue.getRequestCount();
} else {
requestCount = Optional.ofNullable(queueDetail.getRequestCount()).orElse(0L);
}
taskRequest.setRequestCount(requestCount);
execute(taskRequest, apiScenarioDetail); execute(taskRequest, apiScenarioDetail);
} }
@ -328,7 +314,16 @@ public class ApiScenarioBatchRunService {
parseParam.setEnvironmentId(envId); parseParam.setEnvironmentId(envId);
parseParam.setGrouped(envGroup); parseParam.setGrouped(envGroup);
// 初始化报告步骤
if (runModeConfig.isIntegratedReport()) {
apiScenarioService.initScenarioReportSteps(apiScenarioDetail.getId(), apiScenarioDetail.getSteps(), runModeConfig.getCollectionReport().getReportId());
} else {
apiScenarioService.initScenarioReportSteps(apiScenarioDetail.getSteps(), reportId);
}
taskRequest.setReportId(reportId); taskRequest.setReportId(reportId);
// 记录请求数量
taskRequest.setRequestCount(getRequestCount(apiScenarioDetail.getSteps()));
ApiScenarioParseTmpParam tmpParam = apiScenarioService.parse(msScenario, apiScenarioDetail.getSteps(), parseParam); ApiScenarioParseTmpParam tmpParam = apiScenarioService.parse(msScenario, apiScenarioDetail.getSteps(), parseParam);
@ -340,7 +335,6 @@ public class ApiScenarioBatchRunService {
apiExecuteService.execute(runRequest, taskRequest, parseConfig); apiExecuteService.execute(runRequest, taskRequest, parseConfig);
} }
private TaskRequestDTO getTaskRequestDTO(String reportId, ApiScenarioDetail apiScenarioDetail, ApiRunModeConfigDTO runModeConfig) { private TaskRequestDTO getTaskRequestDTO(String reportId, ApiScenarioDetail apiScenarioDetail, ApiRunModeConfigDTO runModeConfig) {
TaskRequestDTO taskRequest = apiScenarioService.getTaskRequest(reportId, apiScenarioDetail.getId(), apiScenarioDetail.getProjectId(), ApiExecuteRunMode.RUN.name()); TaskRequestDTO taskRequest = apiScenarioService.getTaskRequest(reportId, apiScenarioDetail.getId(), apiScenarioDetail.getProjectId(), ApiExecuteRunMode.RUN.name());
taskRequest.setSaveResult(true); taskRequest.setSaveResult(true);

View File

@ -1379,7 +1379,11 @@ public class ApiScenarioService extends MoveNodeService {
* @param reportId * @param reportId
*/ */
public void initScenarioReportSteps(List<? extends ApiScenarioStepCommonDTO> steps, String reportId) { public void initScenarioReportSteps(List<? extends ApiScenarioStepCommonDTO> steps, String reportId) {
List<ApiScenarioReportStep> scenarioReportSteps = getScenarioReportSteps(null, steps, reportId); initScenarioReportSteps(null, steps, reportId);
}
public void initScenarioReportSteps(String parentId, List<? extends ApiScenarioStepCommonDTO> steps, String reportId) {
List<ApiScenarioReportStep> scenarioReportSteps = getScenarioReportSteps(parentId, steps, reportId);
apiScenarioReportService.insertApiScenarioReportStep(scenarioReportSteps); apiScenarioReportService.insertApiScenarioReportStep(scenarioReportSteps);
} }
@ -1393,12 +1397,16 @@ public class ApiScenarioService extends MoveNodeService {
AtomicLong sort = new AtomicLong(1); AtomicLong sort = new AtomicLong(1);
List<ApiScenarioReportStep> scenarioReportSteps = new ArrayList<>(); List<ApiScenarioReportStep> scenarioReportSteps = new ArrayList<>();
for (ApiScenarioStepCommonDTO step : steps) { for (ApiScenarioStepCommonDTO step : steps) {
if (StringUtils.isBlank(step.getUniqueId())) {
// 如果没有步骤唯一ID则生成唯一ID
step.setUniqueId(IDGenerator.nextStr());
}
ApiScenarioReportStep scenarioReportStep = getScenarioReportStep(step, reportId, sort.getAndIncrement()); ApiScenarioReportStep scenarioReportStep = getScenarioReportStep(step, reportId, sort.getAndIncrement());
scenarioReportStep.setParentId(parentId); scenarioReportStep.setParentId(parentId);
scenarioReportSteps.add(scenarioReportStep); scenarioReportSteps.add(scenarioReportStep);
List<? extends ApiScenarioStepCommonDTO> children = step.getChildren(); List<? extends ApiScenarioStepCommonDTO> children = step.getChildren();
if (CollectionUtils.isNotEmpty(children)) { if (CollectionUtils.isNotEmpty(children)) {
scenarioReportSteps.addAll(getScenarioReportSteps(step.getId(), children, reportId)); scenarioReportSteps.addAll(getScenarioReportSteps(step.getUniqueId(), children, reportId));
} }
} }
return scenarioReportSteps; return scenarioReportSteps;
@ -1407,7 +1415,7 @@ public class ApiScenarioService extends MoveNodeService {
private ApiScenarioReportStep getScenarioReportStep(ApiScenarioStepCommonDTO step, String reportId, long sort) { private ApiScenarioReportStep getScenarioReportStep(ApiScenarioStepCommonDTO step, String reportId, long sort) {
ApiScenarioReportStep scenarioReportStep = new ApiScenarioReportStep(); ApiScenarioReportStep scenarioReportStep = new ApiScenarioReportStep();
scenarioReportStep.setReportId(reportId); scenarioReportStep.setReportId(reportId);
scenarioReportStep.setStepId(step.getId()); scenarioReportStep.setStepId(step.getUniqueId());
scenarioReportStep.setSort(sort); scenarioReportStep.setSort(sort);
scenarioReportStep.setName(step.getName()); scenarioReportStep.setName(step.getName());
scenarioReportStep.setStepType(step.getStepType()); scenarioReportStep.setStepType(step.getStepType());
@ -1637,6 +1645,11 @@ public class ApiScenarioService extends MoveNodeService {
parseParam.getRequestCount().getAndIncrement(); parseParam.getRequestCount().getAndIncrement();
} }
if (StringUtils.isBlank(step.getUniqueId())) {
// 如果调试的时候前端没有传步骤唯一ID则生成唯一ID
step.setUniqueId(IDGenerator.nextStr());
}
// 将步骤详情解析生成对应的MsTestElement // 将步骤详情解析生成对应的MsTestElement
AbstractMsTestElement msTestElement = stepParser.parseTestElement(step, AbstractMsTestElement msTestElement = stepParser.parseTestElement(step,
MapUtils.isNotEmpty(resourceDetailMap) ? resourceDetailMap.getOrDefault(step.getResourceId(), StringUtils.EMPTY) : StringUtils.EMPTY, stepDetailMap.get(step.getId())); MapUtils.isNotEmpty(resourceDetailMap) ? resourceDetailMap.getOrDefault(step.getResourceId(), StringUtils.EMPTY) : StringUtils.EMPTY, stepDetailMap.get(step.getId()));
@ -1648,8 +1661,9 @@ public class ApiScenarioService extends MoveNodeService {
} }
msTestElement.setProjectId(step.getProjectId()); msTestElement.setProjectId(step.getProjectId());
msTestElement.setResourceId(step.getResourceId()); msTestElement.setResourceId(step.getResourceId());
msTestElement.setStepId(step.getId());
msTestElement.setName(step.getName()); msTestElement.setName(step.getName());
// 步骤ID设置为唯一ID
msTestElement.setStepId(step.getUniqueId());
// 记录引用的资源ID和项目ID下载执行文件时需要使用 // 记录引用的资源ID和项目ID下载执行文件时需要使用
parseParam.getRefProjectIds().add(step.getProjectId()); parseParam.getRefProjectIds().add(step.getProjectId());
@ -1961,7 +1975,7 @@ public class ApiScenarioService extends MoveNodeService {
.stream() .stream()
.collect(Collectors.groupingBy(step -> Optional.ofNullable(step.getParentId()).orElse(StringUtils.EMPTY))); .collect(Collectors.groupingBy(step -> Optional.ofNullable(step.getParentId()).orElse(StringUtils.EMPTY)));
List<ApiScenarioStepDTO> steps = buildStepTree(currentScenarioParentStepMap.get(StringUtils.EMPTY), currentScenarioParentStepMap, scenarioStepMap); List<ApiScenarioStepDTO> steps = buildStepTree(currentScenarioParentStepMap.get(StringUtils.EMPTY), currentScenarioParentStepMap, scenarioStepMap, new HashSet<>());
// 设置部分引用的步骤的启用状态 // 设置部分引用的步骤的启用状态
setPartialRefStepsEnable(steps, stepDetailMap); setPartialRefStepsEnable(steps, stepDetailMap);
@ -2011,11 +2025,22 @@ public class ApiScenarioService extends MoveNodeService {
*/ */
private List<ApiScenarioStepDTO> buildStepTree(List<ApiScenarioStepDTO> steps, private List<ApiScenarioStepDTO> buildStepTree(List<ApiScenarioStepDTO> steps,
Map<String, List<ApiScenarioStepDTO>> parentStepMap, Map<String, List<ApiScenarioStepDTO>> parentStepMap,
Map<String, List<ApiScenarioStepDTO>> scenarioStepMap) { Map<String, List<ApiScenarioStepDTO>> scenarioStepMap,
Set<String> stepIdSet) {
if (CollectionUtils.isEmpty(steps)) { if (CollectionUtils.isEmpty(steps)) {
return Collections.emptyList(); return Collections.emptyList();
} }
steps.forEach(step -> {
for (int i = 0; i < steps.size(); i++) {
ApiScenarioStepDTO step = steps.get(i);
if (stepIdSet.contains(step.getId())) {
// 如果步骤ID已存在说明引用了两个相同的场景其子步骤ID可能会重复导致引用的同一个对象
// 这里重新new一个对象避免执行时处理为同一个步骤
step = BeanUtils.copyBean(new ApiScenarioStepDTO(), step);
steps.set(i, step);
}
stepIdSet.add(step.getId());
// 获取当前步骤的子步骤 // 获取当前步骤的子步骤
List<ApiScenarioStepDTO> children = Optional.ofNullable(parentStepMap.get(step.getId())).orElse(new ArrayList<>(0)); List<ApiScenarioStepDTO> children = Optional.ofNullable(parentStepMap.get(step.getId())).orElse(new ArrayList<>(0));
if (isRefOrPartialScenario(step)) { if (isRefOrPartialScenario(step)) {
@ -2029,21 +2054,22 @@ public class ApiScenarioService extends MoveNodeService {
}); });
if (CollectionUtils.isEmpty(children)) { if (CollectionUtils.isEmpty(children)) {
return; continue;
} }
// 如果当前步骤是引用的场景获取该场景的子步骤 // 如果当前步骤是引用的场景获取该场景的子步骤
Map<String, List<ApiScenarioStepDTO>> childStepMap = scenarioSteps Map<String, List<ApiScenarioStepDTO>> childStepMap = scenarioSteps
.stream() .stream()
.collect(Collectors.groupingBy(item -> Optional.ofNullable(item.getParentId()).orElse(StringUtils.EMPTY))); .collect(Collectors.groupingBy(item -> Optional.ofNullable(item.getParentId()).orElse(StringUtils.EMPTY)));
step.setChildren(buildStepTree(children, childStepMap, scenarioStepMap)); step.setChildren(buildStepTree(children, childStepMap, scenarioStepMap, stepIdSet));
} else { } else {
if (CollectionUtils.isEmpty(children)) { if (CollectionUtils.isEmpty(children)) {
return; continue;
} }
step.setChildren(buildStepTree(children, parentStepMap, scenarioStepMap)); step.setChildren(buildStepTree(children, parentStepMap, scenarioStepMap, stepIdSet));
} }
}); }
// 排序 // 排序
return steps.stream() return steps.stream()
.sorted(Comparator.comparing(ApiScenarioStepDTO::getSort)) .sorted(Comparator.comparing(ApiScenarioStepDTO::getSort))