fix(接口测试): 接口定义用例数统计和模块显示不正确

This commit is contained in:
AgAngle 2024-05-17 14:51:39 +08:00 committed by Craftsman
parent 1248028981
commit 5f6299b8d5
10 changed files with 87 additions and 71 deletions

View File

@ -3,7 +3,6 @@ package io.metersphere.api.controller.definition;
import com.fasterxml.jackson.databind.node.TextNode;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import io.metersphere.api.constants.ApiResourceType;
import io.metersphere.api.domain.ApiDefinition;
import io.metersphere.api.dto.ReferenceDTO;
import io.metersphere.api.dto.ReferenceRequest;

View File

@ -45,6 +45,9 @@ public class ApiDefinitionDTO extends ApiDefinition{
@Schema(description = "是否关注")
private Boolean follow;
@Schema(description = "模块名称")
private String moduleName;
@Schema(description = "自定义字段集合")
private List<ApiDefinitionCustomFieldDTO> customFields;

View File

@ -9,6 +9,7 @@ import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
@ -81,6 +82,10 @@ public class CsvVariable {
return StringUtils.isNotBlank(name) && file != null;
}
public boolean isEnable() {
return BooleanUtils.isTrue(enable);
}
public enum CsvEncodingType implements ValueEnum {
UTF8("UTF-8"), UFT16("UTF-16"), ISO885915("ISO-8859-15"), US_ASCII("US-ASCII"), GBK("GBK");
private String value;

View File

@ -2,6 +2,7 @@ package io.metersphere.api.mapper;
import io.metersphere.api.domain.ApiDefinition;
import io.metersphere.api.domain.ApiDefinitionCustomField;
import io.metersphere.api.domain.ApiTestCase;
import io.metersphere.api.dto.ApiDefinitionExecuteInfo;
import io.metersphere.api.dto.ReferenceDTO;
import io.metersphere.api.dto.ReferenceRequest;
@ -23,7 +24,7 @@ public interface ExtApiDefinitionMapper {
List<ApiDefinitionDTO> listDoc(@Param("request") ApiDefinitionDocRequest request);
List<ApiCaseComputeDTO> selectApiCaseByIdsAndStatusIsNotTrash(@Param("ids") List<String> ids, @Param("projectId") String projectId);
List<ApiTestCase> selectNotInTrashCaseIdsByApiIds(@Param("apiIds") List<String> apiIds);
Long getPos(@Param("projectId") String projectId);

View File

@ -46,27 +46,11 @@
<include refid="queryDocWhereCondition"/>
</select>
<select id="selectApiCaseByIdsAndStatusIsNotTrash"
resultType="io.metersphere.api.dto.definition.ApiCaseComputeDTO">
select
t1.api_definition_id apiDefinitionId,
count( t1.id ) caseTotal,
SUM( CASE WHEN t2.`status` = 'SUCCESS' THEN 1 ELSE 0 END ) AS `success`,
SUM( CASE WHEN t2.`status` = 'ERROR' THEN 1 ELSE 0 END ) AS `error`,
SUM( CASE WHEN t2.`status` = 'FAKE_ERROR' THEN 1 ELSE 0 END ) AS `fakeError`,
CONCAT( FORMAT( SUM( IF ( t2.`status` = 'SUCCESS', 1, 0 ))/ COUNT( t1.id )* 100, 2 ), '%' ) casePassRate
FROM
api_test_case t1
LEFT JOIN api_test_case_record t3 on t1.id = t3.api_test_case_id
LEFT JOIN api_report t2 ON t2.id = t3.api_report_id
WHERE
t1.project_id = #{projectId} and t1.deleted = 0
GROUP BY
t1.api_definition_id
HAVING
t1.api_definition_id IN
<foreach collection="ids" item="value" separator="," open="(" close=")">
<select id="selectNotInTrashCaseIdsByApiIds" resultType="io.metersphere.api.domain.ApiTestCase">
select atc.id, atc.api_definition_id
FROM api_test_case atc
WHERE atc.deleted = 0 and atc.api_definition_id IN
<foreach collection="apiIds" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</select>

View File

@ -1,5 +1,6 @@
package io.metersphere.api.mapper;
import io.metersphere.api.domain.ApiDefinitionModule;
import io.metersphere.api.dto.debug.ApiTreeNode;
import io.metersphere.api.dto.definition.ApiModuleRequest;
import io.metersphere.project.dto.ModuleCountDTO;
@ -36,4 +37,6 @@ public interface ExtApiDefinitionModuleMapper {
List<BaseTreeNode> selectBaseByIds(@Param("ids") List<String> ids);
List<String> getModuleIdsByParentIds(@Param("parentIds") List<String> parentIds);
List<ApiDefinitionModule> getNameInfoByIds(@Param("ids") List<String> ids);
}

View File

@ -123,6 +123,14 @@
#{parentId}
</foreach>
</select>
<select id="getNameInfoByIds" resultType="io.metersphere.api.domain.ApiDefinitionModule">
SELECT id, name
FROM api_definition_module
WHERE id IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</select>
<sql id="api_request">
<where>

View File

@ -47,7 +47,7 @@ public class MsCsvChildPreConverter extends AbstractJmeterElementConverter<Abstr
}
private static void addCsvDataSet(HashTree tree, String shareMode, CsvVariable csvVariable) {
if (!csvVariable.isValid()) {
if (!csvVariable.isValid() || !csvVariable.isEnable()) {
return;
}
// 执行机执行文件存放的缓存目录

View File

@ -25,10 +25,7 @@ import io.metersphere.project.mapper.ExtBaseProjectVersionMapper;
import io.metersphere.project.service.EnvironmentService;
import io.metersphere.project.service.MoveNodeService;
import io.metersphere.project.service.ProjectService;
import io.metersphere.sdk.constants.ApiFileResourceType;
import io.metersphere.sdk.constants.ApplicationNumScope;
import io.metersphere.sdk.constants.DefaultRepositoryDir;
import io.metersphere.sdk.constants.ModuleConstants;
import io.metersphere.sdk.constants.*;
import io.metersphere.sdk.domain.OperationLogBlob;
import io.metersphere.sdk.dto.api.task.TaskRequestDTO;
import io.metersphere.sdk.exception.MSException;
@ -79,6 +76,9 @@ public class ApiDefinitionService extends MoveNodeService {
@Resource
private ExtApiDefinitionMapper extApiDefinitionMapper;
@Resource
private ExtApiDefinitionModuleMapper extApiDefinitionModuleMapper;
@Resource
private ApiDefinitionFollowerMapper apiDefinitionFollowerMapper;
@ -117,8 +117,6 @@ public class ApiDefinitionService extends MoveNodeService {
@Resource
private ApiDefinitionLogService apiDefinitionLogService;
@Resource
private ApiDefinitionImportUtilService apiDefinitionImportUtilService;
@Resource
private ApiDefinitionMockService apiDefinitionMockService;
@ -136,9 +134,7 @@ public class ApiDefinitionService extends MoveNodeService {
public List<ApiDefinitionDTO> getApiDefinitionPage(ApiDefinitionPageRequest request, String userId) {
CustomFieldUtils.setBaseQueryRequestCustomMultipleFields(request, userId);
List<ApiDefinitionDTO> list = extApiDefinitionMapper.list(request);
if (!CollectionUtils.isEmpty(list)) {
processApiDefinitions(list, request.getProjectId());
}
processApiDefinitions(list);
return list;
}
@ -437,45 +433,42 @@ public class ApiDefinitionService extends MoveNodeService {
}
}
private void processApiDefinitions(List<ApiDefinitionDTO> list, String projectId) {
private void processApiDefinitions(List<ApiDefinitionDTO> list) {
if (CollectionUtils.isEmpty(list)) {
return;
}
Set<String> userIds = extractUserIds(list);
Map<String, String> userMap = userLoginService.getUserNameMap(new ArrayList<>(userIds));
/* List<String> apiDefinitionIds = list.stream().map(ApiDefinitionDTO::getId).toList();
List<ApiCaseComputeDTO> apiCaseComputeList = extApiDefinitionMapper.selectApiCaseByIdsAndStatusIsNotTrash(apiDefinitionIds, projectId);
Map<String, ApiCaseComputeDTO> resultMap = apiCaseComputeList.stream().collect(Collectors.toMap(ApiCaseComputeDTO::getApiDefinitionId, Function.identity()));
List<ApiDefinitionCustomFieldDTO> customFields = extApiDefinitionCustomFieldMapper.getApiCustomFields(apiDefinitionIds, projectId);
Map<String, List<ApiDefinitionCustomFieldDTO>> customFieldMap = customFields.stream().collect(Collectors.groupingBy(ApiDefinitionCustomFieldDTO::getApiId));
*/
List<String> apiDefinitionIds = list.stream().map(ApiDefinitionDTO::getId).toList();
List<ApiTestCase> apiCaseList = extApiDefinitionMapper.selectNotInTrashCaseIdsByApiIds(apiDefinitionIds);
Map<String, List<ApiTestCase>> apiCaseMap = apiCaseList.stream().
collect(Collectors.groupingBy(ApiTestCase::getApiDefinitionId));
List<String> moduleIds = list.stream().map(ApiDefinitionDTO::getModuleId).toList();
List<ApiDefinitionModule> modules = extApiDefinitionModuleMapper.getNameInfoByIds(moduleIds);
Map<String, String> moduleNameMap = modules.stream()
.collect(Collectors.toMap(ApiDefinitionModule::getId, ApiDefinitionModule::getName));
list.forEach(item -> {
// Convert User IDs to Names
item.setCreateUserName(userMap.get(item.getCreateUser()));
item.setDeleteUserName(userMap.get(item.getDeleteUser()));
item.setUpdateUserName(userMap.get(item.getUpdateUser()));
// Custom Fields
/*item.setCustomFields(customFieldMap.get(item.getId()));
// Calculate API Case Metrics
ApiCaseComputeDTO apiCaseComputeDTO = resultMap.get(item.getId());
if (apiCaseComputeDTO != null) {
item.setCaseTotal(apiCaseComputeDTO.getCaseTotal());
item.setCasePassRate(apiCaseComputeDTO.getCasePassRate());
// 状态优先级 未执行未通过误报FAKE_ERROR通过
if ((apiCaseComputeDTO.getError() + apiCaseComputeDTO.getFakeError() + apiCaseComputeDTO.getSuccess()) < apiCaseComputeDTO.getCaseTotal()) {
item.setCaseStatus(ApiReportStatus.PENDING.name());
} else if (apiCaseComputeDTO.getError() > 0) {
item.setCaseStatus(ApiReportStatus.ERROR.name());
} else if (apiCaseComputeDTO.getFakeError() > 0) {
item.setCaseStatus(ApiReportStatus.FAKE_ERROR.name());
} else {
item.setCaseStatus(ApiReportStatus.SUCCESS.name());
}
List<ApiTestCase> apiTestCases = apiCaseMap.get(item.getId());
if (apiTestCases != null) {
item.setCaseTotal(apiTestCases.size());
} else {
item.setCaseTotal(0);
item.setCasePassRate("-");
item.setCaseStatus("-");
}*/
}
if (moduleNameMap.get(item.getModuleId()) == null) {
item.setModuleName(Translator.get("api_unplanned_request"));
} else {
item.setModuleName(moduleNameMap.get(item.getModuleId()));
}
});
}

View File

@ -135,6 +135,9 @@ public class ApiDefinitionControllerTests extends BaseTest {
@Resource
private ExtApiTestCaseMapper extApiTestCaseMapper;
@Resource
private ApiTestCaseMapper apiTestCaseMapper;
@Resource
private ApiDefinitionModuleMapper apiDefinitionModuleMapper;
@ -879,14 +882,35 @@ public class ApiDefinitionControllerTests extends BaseTest {
@Order(9)
@Sql(scripts = {"/dml/init_api_definition.sql"}, config = @SqlConfig(encoding = "utf-8", transactionMode = SqlConfig.TransactionMode.ISOLATED))
public void getPage() throws Exception {
doApiDefinitionPage("All", PAGE);
doApiDefinitionPage("KEYWORD", PAGE);
assertPateDate(doApiDefinitionPage("All", PAGE));
assertPateDate(doApiDefinitionPage("KEYWORD", PAGE));
//doApiDefinitionPage("FILTER", PAGE);
doApiDefinitionPage("COMBINE", PAGE);
doApiDefinitionPage("DELETED", PAGE);
assertPateDate(doApiDefinitionPage("COMBINE", PAGE));
assertPateDate(doApiDefinitionPage("DELETED", PAGE));
}
private void doApiDefinitionPage(String search, String url) throws Exception {
private void assertPateDate(Pager pageData) {
List<ApiDefinitionDTO> apiDefinitions = ApiDataUtils.parseArray(JSON.toJSONString(pageData.getList()), ApiDefinitionDTO.class);
if (CollectionUtils.isNotEmpty(apiDefinitions)) {
ApiDefinitionDTO apiDefinitionDTO = apiDefinitions.get(0);
// 判断用例数是否正确
ApiTestCaseExample example = new ApiTestCaseExample();
example.createCriteria()
.andApiDefinitionIdEqualTo(apiDefinitionDTO.getId())
.andDeletedEqualTo(false);
List<ApiTestCase> apiTestCases = apiTestCaseMapper.selectByExample(example);
Assertions.assertEquals(apiDefinitionDTO.getCaseTotal(), apiTestCases.size());
// 判断模块名是否正确
ApiDefinitionModule apiDefinitionModule = apiDefinitionModuleMapper.selectByPrimaryKey(apiDefinitionDTO.getModuleId());
if (apiDefinitionModule == null) {
Assertions.assertEquals(apiDefinitionDTO.getModuleName(), Translator.get("api_unplanned_request"));
} else {
Assertions.assertEquals(apiDefinitionDTO.getModuleName(), apiDefinitionModule.getName());
}
}
}
private Pager doApiDefinitionPage(String search, String url) throws Exception {
ApiDefinitionPageRequest request = new ApiDefinitionPageRequest();
request.setProjectId(DEFAULT_PROJECT_ID);
request.setCurrent(1);
@ -905,19 +929,15 @@ public class ApiDefinitionControllerTests extends BaseTest {
}
MvcResult mvcResult = this.requestPostWithOkAndReturn(url, request);
// 获取返回值
String returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
ResultHolder resultHolder = JSON.parseObject(returnData, ResultHolder.class);
// 返回请求正常
Assertions.assertNotNull(resultHolder);
Pager<?> pageData = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), Pager.class);
Pager pageData = getResultData(mvcResult, Pager.class);
// 返回值不为空
Assertions.assertNotNull(pageData);
// 返回值的页码和当前页码相同
Assertions.assertEquals(pageData.getCurrent(), request.getCurrent());
// 返回的数据量不超过规定要返回的数据量相同
Assertions.assertTrue(JSON.parseArray(JSON.toJSONString(pageData.getList())).size() <= request.getPageSize());
return pageData;
}
private void configureAllSearch(ApiDefinitionPageRequest request) {