fix(接口测试): 执行时设置请求数量参数

This commit is contained in:
AgAngle 2024-04-01 15:08:44 +08:00 committed by Craftsman
parent e8f1ce98cc
commit 08465388c2
15 changed files with 112 additions and 49 deletions

View File

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

View File

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

View File

@ -0,0 +1,17 @@
package io.metersphere.api.dto;
import lombok.Data;
import java.util.HashMap;
import java.util.Map;
/**
* @Author: jianxing
* @CreateTime: 2024-04-01 11:29
*/
@Data
public class ApiBatchRunInitReportResult {
private Map<String, String> scenarioReportMap = new HashMap<>();
private Long requestCount = 0L;
private Map<String, Long> scenarioCountMap = new HashMap<>();
}

View File

@ -1,7 +1,6 @@
package io.metersphere.api.dto;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
/**
@ -14,13 +13,11 @@ public class ApiFile {
* 生成脚本时通过 fileId + fileName(文件名) 获取文件路径
*/
@NotBlank
@Size(max = 50)
private String fileId;
/**
* 文件名
*/
@NotBlank
@Size(max = 255)
private String fileName;
/**
* 是否是本地上传的文件
@ -30,7 +27,6 @@ public class ApiFile {
* 文件别名引用的文件需要展示别名
* 查询时获取最新的
*/
@Size(max = 255)
private String fileAlias;
/**
* 文件是否别删除

View File

@ -7,7 +7,6 @@ import io.metersphere.sdk.constants.HttpMethodConstants;
import io.metersphere.system.valid.EnumValue;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
import lombok.EqualsAndHashCode;
@ -24,22 +23,14 @@ import java.util.List;
@EqualsAndHashCode(callSuper = true)
public class MsHTTPElement extends AbstractMsProtocolTestElement {
/**
* 完整请求地址
* 自定义请求时使用该字段
* 接口定义和用例的请求路径或者完整路径
*/
@Size(max = 500)
private String url;
/**
* 接口定义和用例的请求路径
*/
@Size(max = 500)
private String path;
/**
* 请求方法
* 取值参考{@link HttpMethodConstants}
*/
@NotBlank
@Size(max = 10)
@EnumValue(enumClass = HttpMethodConstants.class)
private String method;
/**

View File

@ -2,7 +2,6 @@ package io.metersphere.api.dto.request.http.body;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
import java.util.HashMap;
@ -21,7 +20,6 @@ public class Body {
* 同时持久化多个类型的请求体
*/
@NotBlank
@Size(max = 20)
private String bodyType;
/**
* None 请求体

View File

@ -2,7 +2,6 @@ package io.metersphere.api.dto.request.http.body;
import io.metersphere.api.dto.ApiFile;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Size;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
@ -26,7 +25,6 @@ public class FormDataKV extends WWWFormKV {
/**
* 参数的 contentType
*/
@Size(max = 100)
private String contentType;
public boolean isFile() {

View File

@ -2,7 +2,6 @@ package io.metersphere.api.dto.request.http.body;
import io.metersphere.project.api.KeyValueEnableParam;
import io.metersphere.system.valid.EnumValue;
import jakarta.validation.constraints.Size;
import lombok.Data;
/**
@ -16,7 +15,6 @@ public class WWWFormKV extends KeyValueEnableParam {
* 参数类型
* 取值参考 {@link BodyParamType} 中的 value 属性
*/
@Size(max = 20)
@EnumValue(enumClass = BodyParamType.class)
private String paramType = BodyParamType.STRING.getValue();
/**

View File

@ -6,7 +6,6 @@ import io.metersphere.sdk.constants.ValueEnum;
import io.metersphere.system.valid.EnumValue;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@ -31,12 +30,10 @@ public class JsonSchemaItem {
* 参数ID
*/
@NotBlank
@Size(max = 50)
private String id;
/**
* 参数名称
*/
@Size(max = 200)
private String title;
/**
* 参数类型

View File

@ -1,5 +1,6 @@
package io.metersphere.api.service;
import io.metersphere.api.dto.ApiBatchRunInitReportResult;
import io.metersphere.api.service.queue.ApiExecutionQueueService;
import io.metersphere.sdk.dto.api.task.ApiRunModeConfigDTO;
import io.metersphere.sdk.dto.queue.ExecutionQueue;
@ -8,10 +9,7 @@ import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
@Service
@ -28,6 +26,31 @@ public class ApiBatchRunBaseService {
*/
public ExecutionQueue initExecutionqueue(List<String> resourceIds, ApiRunModeConfigDTO runModeConfig, String resourceType, Map<String, String> caseReportMap, String userId) {
ExecutionQueue queue = getExecutionQueue(runModeConfig, resourceType, userId);
List<ExecutionQueueDetail> queueDetails = getExecutionQueueDetails(resourceIds, caseReportMap);
apiExecutionQueueService.insertQueue(queue, queueDetails);
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) {
List<ExecutionQueueDetail> queueDetails = new ArrayList<>();
AtomicInteger sort = new AtomicInteger(1);
for (String resourceId : resourceIds) {
@ -38,8 +61,7 @@ public class ApiBatchRunBaseService {
queueDetail.setReportId(caseReportMap == null ? UUID.randomUUID().toString() : caseReportMap.get(resourceId));
queueDetails.add(queueDetail);
}
apiExecutionQueueService.insertQueue(queue, queueDetails);
return queue;
return queueDetails;
}
private ExecutionQueue getExecutionQueue(ApiRunModeConfigDTO runModeConfig, String resourceType, String userId) {
@ -51,4 +73,5 @@ public class ApiBatchRunBaseService {
queue.setUserId(userId);
return queue;
}
}

View File

@ -1,17 +1,17 @@
package io.metersphere.api.service.scenario;
import io.metersphere.api.constants.ApiScenarioStepRefType;
import io.metersphere.api.constants.ApiScenarioStepType;
import io.metersphere.api.domain.ApiScenario;
import io.metersphere.api.domain.ApiScenarioRecord;
import io.metersphere.api.domain.ApiScenarioReport;
import io.metersphere.api.domain.ApiScenarioReportStep;
import io.metersphere.api.dto.ApiBatchRunInitReportResult;
import io.metersphere.api.dto.ApiScenarioParamConfig;
import io.metersphere.api.dto.ApiScenarioParseTmpParam;
import io.metersphere.api.dto.debug.ApiResourceRunRequest;
import io.metersphere.api.dto.request.MsScenario;
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.*;
import io.metersphere.api.service.ApiBatchRunBaseService;
import io.metersphere.api.service.ApiExecuteService;
import io.metersphere.api.service.queue.ApiExecutionQueueService;
@ -27,6 +27,7 @@ import io.metersphere.sdk.util.DateUtils;
import io.metersphere.sdk.util.LogUtils;
import io.metersphere.system.uid.IDGenerator;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -34,7 +35,9 @@ import org.springframework.transaction.annotation.Transactional;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
@Service
@Transactional(rollbackFor = Exception.class)
@ -93,13 +96,13 @@ public class ApiScenarioBatchRunService {
initIntegratedReport(runModeConfig, ids, userId, request.getProjectId());
}
Map<String, String> scenarioReportMap = initReport(ids, runModeConfig, userId);
ApiBatchRunInitReportResult reportResult = initReport(ids, runModeConfig, userId);
// 集成报告执行前先设置成 RUNNING
setRunningIntegrateReport(runModeConfig);
// 先初始化集成报告设置好报告ID再初始化执行队列
ExecutionQueue queue = apiBatchRunBaseService.initExecutionqueue(ids, runModeConfig, ApiExecuteResourceType.API_SCENARIO.name(), scenarioReportMap, userId);
ExecutionQueue queue = apiBatchRunBaseService.initExecutionqueue(ids, runModeConfig, ApiExecuteResourceType.API_SCENARIO.name(), reportResult, userId);
// 执行第一个任务
ExecutionQueueDetail nextDetail = apiExecutionQueueService.getNextDetail(queue.getQueueId());
executeNextTask(queue, nextDetail);
@ -122,7 +125,7 @@ public class ApiScenarioBatchRunService {
apiExecutionSetService.initSet(apiScenarioReport.getId(), ids);
}
Map<String, String> scenarioReportMap = initReport(ids, runModeConfig, userId);
ApiBatchRunInitReportResult reportResult = initReport(ids, runModeConfig, userId);
// 集成报告执行前先设置成 RUNNING
setRunningIntegrateReport(runModeConfig);
@ -141,15 +144,18 @@ public class ApiScenarioBatchRunService {
LogUtils.info("当前执行任务的用例已删除 {}", id);
break;
}
// 请求数量集合报告放总的数量独立报告放当前场景的请求数量
Long requestCount;
if (runModeConfig.isIntegratedReport()) {
// 集成报告生成虚拟的报告ID
reportId = IDGenerator.nextStr();
requestCount = reportResult.getRequestCount();
} else {
reportId = scenarioReportMap.get(id);
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) {
@ -163,16 +169,26 @@ public class ApiScenarioBatchRunService {
}
}
private Map<String, String> initReport(List<String> ids, ApiRunModeConfigDTO runModeConfig, String userId) {
private ApiBatchRunInitReportResult initReport(List<String> ids, ApiRunModeConfigDTO runModeConfig, String userId) {
Map<String, String> scenarioReportMap = new HashMap<>();
ApiBatchRunInitReportResult reportResult = new ApiBatchRunInitReportResult();
Map<String, Long> scenarioCountMap = reportResult.getScenarioCountMap();
Boolean isIntegratedReport = runModeConfig.isIntegratedReport();
AtomicInteger sort = new AtomicInteger(1);
// 这里ID顺序和队列的ID顺序保持一致
for (String id : ids) {
ApiScenarioDetail apiScenarioDetail = apiScenarioService.get(id);
if (apiScenarioDetail == null) {
break;
}
// 记录请求数量
Long itemCount = getRequestCount(apiScenarioDetail.getSteps());
scenarioCountMap.put(id, itemCount);
reportResult.setRequestCount(reportResult.getRequestCount() + itemCount);
if (runModeConfig.isIntegratedReport()) {
// 初始化集成报告步骤
initIntegratedReportSteps(apiScenarioDetail, runModeConfig.getCollectionReport().getReportId(), sort.getAndIncrement());
@ -184,7 +200,25 @@ public class ApiScenarioBatchRunService {
scenarioReportMap.put(id, reportId);
}
}
return isIntegratedReport ? null : scenarioReportMap;
reportResult.setScenarioReportMap(isIntegratedReport ? null : scenarioReportMap);
return reportResult;
}
private Long getRequestCount(List<ApiScenarioStepDTO> steps) {
AtomicLong requestCount = new AtomicLong();
apiScenarioService.traversalStepTree(steps, step -> {
if (BooleanUtils.isTrue(step.getEnable()) && isRequestStep(step)) {
requestCount.getAndIncrement();
}
return true;
});
return requestCount.get();
}
private boolean isRequestStep(ApiScenarioStepCommonDTO step) {
return StringUtils.equalsAny(step.getStepType(), ApiScenarioStepType.API.name(), ApiScenarioStepType.API_CASE.name(), ApiScenarioStepType.CUSTOM_REQUEST.name());
}
/**
@ -267,6 +301,14 @@ public class ApiScenarioBatchRunService {
}
TaskRequestDTO taskRequest = getTaskRequestDTO(queueDetail.getReportId(), apiScenarioDetail, queue.getRunModeConfig());
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);
}

View File

@ -1019,7 +1019,7 @@ public class ApiScenarioService extends MoveNodeService {
/**
* 遍历步骤树
*/
private void traversalStepTree(List<? extends ApiScenarioStepCommonDTO> steps, Function<ApiScenarioStepCommonDTO, Boolean> handleStepFunc) {
public void traversalStepTree(List<? extends ApiScenarioStepCommonDTO> steps, Function<ApiScenarioStepCommonDTO, Boolean> handleStepFunc) {
if (CollectionUtils.isEmpty(steps)) {
return;
}
@ -1998,7 +1998,6 @@ public class ApiScenarioService extends MoveNodeService {
scenarioSteps.forEach(item -> {
// 如果步骤的场景ID不等于当前场景的ID说明是引用的步骤如果 parentId 为空说明是一级子步骤重新挂载到对应的场景中
if (StringUtils.isEmpty(item.getParentId())) {
item.setParentId(step.getId());
children.add(item);
}
});
@ -2010,7 +2009,7 @@ public class ApiScenarioService extends MoveNodeService {
// 如果当前步骤是引用的场景获取该场景的子步骤
Map<String, List<ApiScenarioStepDTO>> childStepMap = scenarioSteps
.stream()
.collect(Collectors.groupingBy(ApiScenarioStepDTO::getParentId));
.collect(Collectors.groupingBy(item -> Optional.ofNullable(item.getParentId()).orElse(StringUtils.EMPTY)));
step.setChildren(buildStepTree(children, childStepMap, scenarioStepMap));
} else {
if (CollectionUtils.isEmpty(children)) {

View File

@ -431,7 +431,6 @@ public class MsHTTPElementTest {
public static MsHTTPElement getMsHttpElement() {
MsHTTPElement msHTTPElement = new MsHTTPElement();
msHTTPElement.setUrl("http://www.test.com");
msHTTPElement.setPath("/test");
msHTTPElement.setMethod("GET");
msHTTPElement.setName("name");

View File

@ -1,6 +1,5 @@
package io.metersphere.project.api;
import jakarta.validation.constraints.Size;
import lombok.Data;
/**
@ -18,6 +17,5 @@ public class KeyValueEnableParam extends KeyValueParam {
/**
* 描述
*/
@Size(max = 500)
private String description;
}

View File

@ -1,6 +1,5 @@
package io.metersphere.project.api;
import jakarta.validation.constraints.Size;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
@ -14,12 +13,10 @@ public class KeyValueParam {
/**
*
*/
@Size(max = 255)
private String key;
/**
*
*/
@Size(max = 255)
private String value;
public boolean isValid() {