fix(接口测试): 场景步骤中上传文件,执行报错

This commit is contained in:
AgAngle 2024-04-11 19:48:40 +08:00 committed by Craftsman
parent f20eed30bf
commit d407f9d352
10 changed files with 127 additions and 65 deletions

View File

@ -15,19 +15,24 @@ public class FileAssociationSourceUtil {
public static final String SOURCE_TYPE_FUNCTIONAL_CASE = "FUNCTIONAL_CASE";
public static final String SOURCE_TYPE_API_DEBUG = "API_DEBUG";
public static final String SOURCE_TYPE_API_SCENARIO= "API_SCENARIO";
public static final String SOURCE_TYPE_API_SCENARIO_STEP = "API_SCENARIO_STEP";
public static final String SOURCE_TYPE_API_TEST_CASE = "API_TEST_CASE";
public static final String SOURCE_TYPE_API_DEFINITION = "API_DEFINITION";
public static final String SOURCE_TYPE_API_DEFINITION_MOCK = "API_DEFINITION_MOCK";
public static final Map<String, String> QUERY_SQL = new HashMap<>();
static {
QUERY_SQL.put(SOURCE_TYPE_BUG, "SELECT id AS sourceId, num AS sourceNum, title AS sourceName FROM bug");
QUERY_SQL.put(SOURCE_TYPE_FUNCTIONAL_CASE, "SELECT id AS sourceId, num AS sourceNum, name AS sourceName FROM functional_case");
QUERY_SQL.put(SOURCE_TYPE_API_DEBUG, "SELECT id AS sourceId, id AS sourceNum, name AS sourceName FROM api_debug");
QUERY_SQL.put(SOURCE_TYPE_API_SCENARIO, "SELECT id AS sourceId, num AS sourceNum, name AS sourceName FROM api_scenario");
QUERY_SQL.put(SOURCE_TYPE_API_TEST_CASE, "SELECT id AS sourceId, num AS sourceNum, name AS sourceName FROM api_test_case");
QUERY_SQL.put(SOURCE_TYPE_API_DEFINITION, "SELECT id AS sourceId, num AS sourceNum, name AS sourceName FROM api_definition");
QUERY_SQL.put(SOURCE_TYPE_API_DEFINITION_MOCK, "SELECT id AS sourceId, expect_num AS sourceNum, name AS sourceName FROM api_definition_mock");
QUERY_SQL.put(SOURCE_TYPE_BUG, "SELECT id AS sourceId, id AS redirectId, num AS sourceNum, title AS sourceName FROM bug t");
QUERY_SQL.put(SOURCE_TYPE_FUNCTIONAL_CASE, "SELECT id AS sourceId, id AS redirectId, num AS sourceNum, name AS sourceName FROM functional_case t");
QUERY_SQL.put(SOURCE_TYPE_API_DEBUG, "SELECT id AS sourceId, id AS redirectId, id AS sourceNum, name AS sourceName FROM api_debug t");
QUERY_SQL.put(SOURCE_TYPE_API_SCENARIO, "SELECT id AS sourceId, id AS redirectId, num AS sourceNum, name AS sourceName FROM api_scenario t");
QUERY_SQL.put(SOURCE_TYPE_API_SCENARIO_STEP, """
SELECT t.id AS sourceId, scenario.id AS redirectId, scenario.num AS sourceNum, CONCAT(scenario.name, ' (', t.name, ')') AS sourceName
FROM api_scenario_step t JOIN api_scenario scenario ON t.scenario_id = scenario.id
""");
QUERY_SQL.put(SOURCE_TYPE_API_TEST_CASE, "SELECT id AS sourceId, id AS redirectId, num AS sourceNum, name AS sourceName FROM api_test_case t");
QUERY_SQL.put(SOURCE_TYPE_API_DEFINITION, "SELECT id AS sourceId, id AS redirectId, num AS sourceNum, name AS sourceName FROM api_definition t");
QUERY_SQL.put(SOURCE_TYPE_API_DEFINITION_MOCK, "SELECT id AS sourceId, id AS redirectId, expect_num AS sourceNum, name AS sourceName FROM api_definition_mock t");
}
public static void validate(String type) {

View File

@ -4,6 +4,7 @@ import io.metersphere.plugin.api.spi.AbstractMsTestElement;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@ -19,12 +20,12 @@ public class ApiResourceRunRequest {
* 创建时先按ID创建目录再把文件放入目录
*/
@Schema(description = "新上传的文件ID")
private List<String> uploadFileIds;
private List<String> uploadFileIds = new ArrayList<>(0);
/**
* 新关联的文件ID
*/
@Schema(description = "关联文件ID")
private List<String> linkFileIds;
private List<String> linkFileIds = new ArrayList<>(0);
/**
* 执行的资源ID列表
* 场景执行时为关联的所有用例和场景列表

View File

@ -1,5 +1,6 @@
package io.metersphere.api.dto.scenario;
import io.metersphere.api.dto.ResourceAddFileParam;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
@ -7,6 +8,7 @@ import jakarta.validation.constraints.Size;
import lombok.Data;
import java.util.List;
import java.util.Map;
/**
* @Author: jianxing
@ -31,19 +33,20 @@ public class ApiScenarioDebugRequest extends ApiScenarioParseParam {
@NotBlank
private String projectId;
/**
* 新上传的文件ID
* 创建时先按ID创建目录再把文件放入目录
*/
@Schema(description = "新上传的文件ID")
private List<String> uploadFileIds;
/**
* 新关联的文件ID
*/
@Schema(description = "关联文件ID")
private List<String> linkFileIds;
@Schema(description = "是否是本地执行")
private Boolean frontendDebug = false;
/**
* 步骤文件操作相关参数
* key 为步骤ID
* 值为文件参数
*/
@Schema(description = "步骤文件操作相关参数")
private Map<String, ResourceAddFileParam> stepFileParam;
/**
* 步骤文件操作相关参数
*/
@Schema(description = "场景文件操作相关参数")
private ResourceAddFileParam fileParam;
}

View File

@ -34,8 +34,8 @@ public class ApiFileAssociationUpdateService implements FileAssociationUpdateSer
CommonBeanFactory.getBean(ApiDefinitionService.class).handleFileAssociationUpgrade(originFileAssociation, newFileMetadata);
case FileAssociationSourceUtil.SOURCE_TYPE_API_TEST_CASE ->
CommonBeanFactory.getBean(ApiTestCaseService.class).handleFileAssociationUpgrade(originFileAssociation, newFileMetadata);
case FileAssociationSourceUtil.SOURCE_TYPE_API_SCENARIO ->
CommonBeanFactory.getBean(ApiScenarioService.class).handleFileAssociationUpgrade(originFileAssociation, newFileMetadata);
case FileAssociationSourceUtil.SOURCE_TYPE_API_SCENARIO_STEP ->
CommonBeanFactory.getBean(ApiScenarioService.class).handleStepFileAssociationUpgrade(originFileAssociation, newFileMetadata);
default -> {
}
}

View File

@ -490,6 +490,7 @@ public class ApiScenarioService extends MoveNodeService {
stepFileParam.forEach((stepId, fileParam) -> {
// 处理步骤文件
ApiFileResourceUpdateRequest resourceUpdateRequest = getApiFileResourceUpdateRequest(stepId, scenario.getProjectId(), creator);
resourceUpdateRequest.setFileAssociationSourceType(FileAssociationSourceUtil.SOURCE_TYPE_API_SCENARIO_STEP);
resourceUpdateRequest.setUploadFileIds(fileParam.getUploadFileIds());
resourceUpdateRequest.setLinkFileIds(fileParam.getLinkFileIds());
apiFileResourceService.addFileResource(resourceUpdateRequest);
@ -692,15 +693,15 @@ public class ApiScenarioService extends MoveNodeService {
ApiScenario originScenario = apiScenarioMapper.selectByPrimaryKey(request.getId());
// 更新场景步骤
updateApiScenarioStep(request, originScenario, updater);
// 处理步骤文件
handleStepFiles(request, updater, originScenario);
// 处理场景文件
handleScenarioFiles(request, updater, originScenario);
// 更新场景步骤
updateApiScenarioStep(request, originScenario, updater);
return scenario;
}
@ -728,8 +729,9 @@ public class ApiScenarioService extends MoveNodeService {
stepFileParam.forEach((stepId, fileParam) -> {
// 处理步骤文件
ApiFileResourceUpdateRequest resourceUpdateRequest = getApiFileResourceUpdateRequest(stepId, scenario.getProjectId(), updater);
resourceUpdateRequest.setFileAssociationSourceType(FileAssociationSourceUtil.SOURCE_TYPE_API_SCENARIO_STEP);
resourceUpdateRequest = BeanUtils.copyBean(resourceUpdateRequest, fileParam);
apiFileResourceService.addFileResource(resourceUpdateRequest);
apiFileResourceService.updateFileResource(resourceUpdateRequest);
});
}
}
@ -740,7 +742,7 @@ public class ApiScenarioService extends MoveNodeService {
private void updateApiScenarioStep(ApiScenarioUpdateRequest request, ApiScenario scenario, String userId) {
List<ApiScenarioStepRequest> steps = request.getSteps();
// steps 不为 null 则修改
if (steps!= null) {
if (steps != null) {
if (CollectionUtils.isEmpty(steps)) {
// 如果是空数组则删除所有步骤
deleteStepByScenarioId(scenario.getId());
@ -808,6 +810,7 @@ public class ApiScenarioService extends MoveNodeService {
/**
* 检测循环依赖
*
* @param scenarioId
* @param steps
*/
@ -1299,6 +1302,8 @@ public class ApiScenarioService extends MoveNodeService {
ApiResourceRunRequest runRequest = getApiResourceRunRequest(msScenario, tmpParam);
runRequest = setFileParam(request, runRequest);
TaskRequestDTO taskRequest = getTaskRequest(request.getReportId(), request.getId(), request.getProjectId(),
apiExecuteService.getDebugRunModule(request.getFrontendDebug()));
taskRequest.setSaveResult(false);
@ -1311,6 +1316,39 @@ public class ApiScenarioService extends MoveNodeService {
return apiExecuteService.execute(runRequest, taskRequest, parseConfig);
}
/**
* 执行时设置临时文件的相关参数
*
* @param request
* @param runRequest
*/
private ApiResourceRunRequest setFileParam(ApiScenarioDebugRequest request, ApiResourceRunRequest runRequest) {
runRequest.getLinkFileIds().addAll(getLinkFileIds(request.getFileParam()));
runRequest.getUploadFileIds().addAll(getUploadFileIds(request.getFileParam()));
Map<String, ResourceAddFileParam> stepFileParam = request.getStepFileParam();
if (MapUtils.isNotEmpty(stepFileParam)) {
stepFileParam.values().forEach(fileParam -> {
runRequest.getLinkFileIds().addAll(getLinkFileIds(fileParam));
runRequest.getUploadFileIds().addAll(getUploadFileIds(fileParam));
});
}
return runRequest;
}
public List<String> getLinkFileIds(ResourceAddFileParam fileParam) {
if (fileParam != null && CollectionUtils.isNotEmpty(fileParam.getLinkFileIds())) {
return fileParam.getLinkFileIds();
}
return List.of();
}
public List<String> getUploadFileIds(ResourceAddFileParam fileParam) {
if (fileParam != null && CollectionUtils.isNotEmpty(fileParam.getUploadFileIds())) {
return fileParam.getUploadFileIds();
}
return List.of();
}
public TaskRequestDTO run(String id, String reportId, String userId) {
ApiScenarioDetail apiScenarioDetail = getForRun(id);
@ -1319,7 +1357,7 @@ public class ApiScenarioService extends MoveNodeService {
ApiScenarioParseParam parseParam = getApiScenarioParseParam(apiScenarioDetail);
return executeRun(apiScenarioDetail, msScenario, apiScenarioDetail.getSteps(), parseParam, reportId, userId);
return executeRun(apiScenarioDetail, msScenario, apiScenarioDetail.getSteps(), parseParam, new ApiResourceRunRequest(), reportId, userId);
}
public ApiScenarioParseParam getApiScenarioParseParam(ApiScenarioDetail apiScenarioDetail) {
@ -1351,19 +1389,24 @@ public class ApiScenarioService extends MoveNodeService {
// 处理特殊的步骤详情
addSpecialStepDetails(request.getSteps(), request.getStepDetails());
return executeRun(apiScenario, msScenario, request.getSteps(), request, request.getReportId(), userId);
ApiResourceRunRequest runRequest = new ApiResourceRunRequest();
runRequest = setFileParam(request, runRequest);
return executeRun(apiScenario, msScenario, request.getSteps(), request, runRequest, request.getReportId(), userId);
}
public TaskRequestDTO executeRun(ApiScenario apiScenario,
MsScenario msScenario,
List<? extends ApiScenarioStepCommonDTO> steps,
ApiScenarioParseParam parseParam,
String reportId, String userId) {
ApiResourceRunRequest runRequest,
String reportId,
String userId) {
// 解析生成场景树并保存临时变量
ApiScenarioParseTmpParam tmpParam = parse(msScenario, steps, parseParam);
ApiResourceRunRequest runRequest = getApiResourceRunRequest(msScenario, tmpParam);
runRequest = setApiResourceRunRequestParam(msScenario, tmpParam, runRequest);
String poolId = apiExecuteService.getProjectApiResourcePoolId(apiScenario.getProjectId());
@ -1511,6 +1554,10 @@ public class ApiScenarioService extends MoveNodeService {
public ApiResourceRunRequest getApiResourceRunRequest(MsScenario msScenario, ApiScenarioParseTmpParam tmpParam) {
ApiResourceRunRequest runRequest = new ApiResourceRunRequest();
return setApiResourceRunRequestParam(msScenario, tmpParam, runRequest);
}
private ApiResourceRunRequest setApiResourceRunRequestParam(MsScenario msScenario, ApiScenarioParseTmpParam tmpParam, ApiResourceRunRequest runRequest) {
runRequest.setRefResourceIds(tmpParam.getRefResourceIds());
runRequest.setRefProjectIds(tmpParam.getRefProjectIds());
runRequest.setTestElement(msScenario);
@ -1575,6 +1622,7 @@ public class ApiScenarioService extends MoveNodeService {
/**
* 设置 MsHTTPElement 中的 method 等信息
*
* @param httpElements
* @param getDefinitionInfoFunc
*/
@ -1889,6 +1937,7 @@ public class ApiScenarioService extends MoveNodeService {
/**
* 查询部分引用的步骤的详情
*
* @param steps
* @return
*/
@ -2775,31 +2824,29 @@ public class ApiScenarioService extends MoveNodeService {
return operationHistoryService.listWidthTable(request, SCENARIO_TABLE);
}
public void handleFileAssociationUpgrade(FileAssociation originFileAssociation, FileMetadata newFileMetadata) {
// 查询有步骤详情的请求类型的步骤
List<String> stepIds = extApiScenarioStepMapper.getHasBlobRequestStepIds(originFileAssociation.getSourceId());
public void handleStepFileAssociationUpgrade(FileAssociation originFileAssociation, FileMetadata newFileMetadata) {
// 查询步骤详情
ApiScenarioStepBlobExample blobExample = new ApiScenarioStepBlobExample();
blobExample.createCriteria().andIdIn(stepIds);
List<ApiScenarioStepBlob> apiScenarioStepBlobs = apiScenarioStepBlobMapper.selectByExampleWithBLOBs(blobExample);
ApiScenarioStepBlob apiScenarioStepBlob = apiScenarioStepBlobMapper.selectByPrimaryKey(originFileAssociation.getSourceId());
for (ApiScenarioStepBlob apiScenarioStepBlob : apiScenarioStepBlobs) {
AbstractMsTestElement msTestElement = null;
try {
msTestElement = ApiDataUtils.parseObject(new String(apiScenarioStepBlob.getContent()), AbstractMsTestElement.class);
// 如果插件删除会转换异常
} catch (Exception e) {
LogUtils.error(e);
}
if (msTestElement instanceof MsHTTPElement msHTTPElement) {
List<ApiFile> updateFiles = apiCommonService.getApiFilesByFileId(originFileAssociation.getFileId(), msHTTPElement);
// 替换文件的Id和name
apiCommonService.replaceApiFileInfo(updateFiles, newFileMetadata);
// 如果有需要更新的文件则更新步骤详情
if (CollectionUtils.isNotEmpty(updateFiles)) {
apiScenarioStepBlob.setContent(ApiDataUtils.toJSONString(msTestElement).getBytes());
apiScenarioStepBlobMapper.updateByPrimaryKeySelective(apiScenarioStepBlob);
}
if (apiScenarioStepBlob == null || apiScenarioStepBlob.getContent() == null) {
return;
}
AbstractMsTestElement msTestElement = null;
try {
msTestElement = ApiDataUtils.parseObject(new String(apiScenarioStepBlob.getContent()), AbstractMsTestElement.class);
// 如果插件删除会转换异常
} catch (Exception e) {
LogUtils.error(e);
}
if (msTestElement instanceof MsHTTPElement msHTTPElement) {
List<ApiFile> updateFiles = apiCommonService.getApiFilesByFileId(originFileAssociation.getFileId(), msHTTPElement);
// 替换文件的Id和name
apiCommonService.replaceApiFileInfo(updateFiles, newFileMetadata);
// 如果有需要更新的文件则更新步骤详情
if (CollectionUtils.isNotEmpty(updateFiles)) {
apiScenarioStepBlob.setContent(ApiDataUtils.toJSONString(msTestElement).getBytes());
apiScenarioStepBlobMapper.updateByPrimaryKeySelective(apiScenarioStepBlob);
}
}
}
@ -2852,6 +2899,7 @@ public class ApiScenarioService extends MoveNodeService {
/**
* 获取场景前置的总等待时间
*
* @param scenarioConfig
* @return
*/

View File

@ -175,6 +175,7 @@ public class ApiScenarioControllerTests extends BaseTest {
private ApiScenarioReportService scenarioReportService;
private static String fileMetadataId;
private static String fileMetadataStepId;
private static String localFileId;
private static ApiScenario addApiScenario;
private static List<ApiScenarioStepRequest> addApiScenarioSteps;
@ -627,6 +628,7 @@ public class ApiScenarioControllerTests extends BaseTest {
msHttpElement.setBody(ApiDebugControllerTests.addBodyLinkFile(msHttpElement.getBody(), fileMetadataId));
steptDetailMap.put(steps.get(0).getId(), getMsHttpElementStr(msHttpElement));
steptDetailMap.put(steps.get(1).getId(), getMsHttpElementStr(msHttpElement));
fileMetadataStepId = steps.get(0).getId();
request.setSteps(steps);
request.setStepDetails(steptDetailMap);
@ -682,14 +684,12 @@ public class ApiScenarioControllerTests extends BaseTest {
private List<ApiFile> getApiFiles(String fileId) {
ApiScenarioStepBlobExample example = new ApiScenarioStepBlobExample();
example.createCriteria().andScenarioIdEqualTo(addApiScenario.getId());
List<ApiScenarioStepBlob> apiScenarioStepBlobs = apiScenarioStepBlobMapper.selectByExampleWithBLOBs(example);
ApiScenarioStepBlob apiScenarioStepBlob = apiScenarioStepBlobMapper.selectByPrimaryKey(fileMetadataStepId);
List<ApiFile> apiFiles = new ArrayList<>();
for (ApiScenarioStepBlob apiScenarioStepBlob : apiScenarioStepBlobs) {
try {
AbstractMsTestElement msTestElement = ApiDataUtils.parseObject(new String(apiScenarioStepBlob.getContent()), AbstractMsTestElement.class);
apiFiles.addAll(apiCommonService.getApiFilesByFileId(fileId, msTestElement));
} catch (Exception e) {
}
try {
AbstractMsTestElement msTestElement = ApiDataUtils.parseObject(new String(apiScenarioStepBlob.getContent()), AbstractMsTestElement.class);
apiFiles.addAll(apiCommonService.getApiFilesByFileId(fileId, msTestElement));
} catch (Exception e) {
}
return apiFiles;
}

View File

@ -7,4 +7,5 @@ public class FileAssociationSource {
private String sourceId;
private String sourceNum;
private String sourceName;
private String redirectId;
}

View File

@ -13,6 +13,9 @@ public class FileAssociationResponse {
@Schema(description = "资源Id")
private String sourceId;
@Schema(description = "前端跳转Id")
private String redirectId;
@Schema(description = "资源编号")
private String sourceNum;

View File

@ -4,12 +4,12 @@
<select id="selectNameBySourceTableAndId"
resultType="io.metersphere.project.dto.filemanagement.FileAssociationSource">
${querySql}
WHERE id = #{sourceId}
WHERE t.id = #{sourceId}
</select>
<select id="selectAssociationSourceBySourceTableAndIdList"
resultType="io.metersphere.project.dto.filemanagement.FileAssociationSource">
${querySql}
WHERE id IN
WHERE t.id IN
<foreach collection="idList" item="id" open="(" separator="," close=")">
#{id}
</foreach>

View File

@ -77,6 +77,7 @@ public class FileAssociationService {
response.setFileId(item.getFileId());
response.setSourceName(sourceIdNameMap.get(item.getSourceId()).getSourceName());
response.setSourceNum(sourceIdNameMap.get(item.getSourceId()).getSourceNum());
response.setRedirectId(sourceIdNameMap.get(item.getSourceId()).getRedirectId());
response.setSourceType(item.getSourceType());
response.setFileVersion(fileIdMap.get(item.getFileId()).getFileVersion());
responseList.add(response);