feat(测试计划): 补充计划列表详情通过率, 执行进度统计逻辑

This commit is contained in:
song-cc-rock 2024-05-11 18:08:35 +08:00 committed by Craftsman
parent 1dae94ad32
commit 177d5afda1
21 changed files with 278 additions and 57 deletions

View File

@ -1,9 +1,25 @@
package io.metersphere.sdk.constants;
public enum FunctionalCaseExecuteResult {
UN_EXECUTED,
PASSED,
FAILED,
/**
* 未执行
*/
PENDING,
/**
* 通过
*/
SUCCESS,
/**
* 失败
*/
ERROR,
/**
* 阻塞
*/
BLOCKED,
SKIPPED
/**
* 误报
*/
FAKE_ERROR
}

View File

@ -177,19 +177,6 @@ public class BugCommonService {
bugFollowerMapper.deleteByExample(followerExample);
}
/**
* 获取状态集合
* @param projectId 项目ID
* @return 处理人集合
*/
public Map<String, String> getAllHandlerMap(String projectId) {
// 缺陷表头处理人选项
List<SelectOption> headerOptions = getHeaderHandlerOption(projectId);
List<SelectOption> localOptions = getLocalHandlerOption(projectId);
List<SelectOption> allHandleOption = ListUtils.union(headerOptions, localOptions).stream().distinct().toList();
return allHandleOption.stream().collect(Collectors.toMap(SelectOption::getValue, SelectOption::getText));
}
/**
* 获取状态选项
* @param projectId 项目ID

View File

@ -1245,12 +1245,18 @@ public class BugService {
List<String> ids = bugs.stream().map(BugDTO::getId).toList();
List<BugCustomFieldDTO> customFields = extBugCustomFieldMapper.getBugAllCustomFields(ids, projectId);
Map<String, List<BugCustomFieldDTO>> customFieldMap = customFields.stream().collect(Collectors.groupingBy(BugCustomFieldDTO::getBugId));
Map<String, String> allHandlerMap = bugCommonService.getAllHandlerMap(projectId);
// MS处理人会与第三方的值冲突, 分开查询
List<SelectOption> headerOptions = bugCommonService.getHeaderHandlerOption(projectId);
Map<String, String> headerHandleUserMap = headerOptions.stream().collect(Collectors.toMap(SelectOption::getValue, SelectOption::getText));
List<SelectOption> localOptions = bugCommonService.getLocalHandlerOption(projectId);
Map<String, String> localHandleUserMap = localOptions.stream().collect(Collectors.toMap(SelectOption::getValue, SelectOption::getText));
Map<String, String> allStatusMap = bugCommonService.getAllStatusMap(projectId);
bugs.forEach(bug -> {
bug.setCustomFields(customFieldMap.get(bug.getId()));
// 解析处理人, 状态
bug.setHandleUserName(allHandlerMap.get(bug.getHandleUser()));
bug.setHandleUserName(headerHandleUserMap.containsKey(bug.getHandleUser()) ?
headerHandleUserMap.get(bug.getHandleUser()) : localHandleUserMap.get(bug.getHandleUser()));
bug.setStatusName(allStatusMap.get(bug.getStatus()));
});
return bugs;

View File

@ -589,6 +589,12 @@ public class BugControllerTests extends BaseTest {
MultiValueMap<String, Object> addParam = getMultiPartParam(addRequest, file);
this.requestMultipartWithOkAndReturn(BUG_ADD, addParam);
BugPageRequest bugPageRequest = new BugPageRequest();
bugPageRequest.setCurrent(1);
bugPageRequest.setPageSize(10);
bugPageRequest.setProjectId("default-project-for-bug");
this.requestPostWithOk(BUG_PAGE, bugPageRequest);
// 更新Jira缺陷
BugEditRequest updateRequest = buildJiraBugRequest(true);
updateRequest.setUnLinkRefIds(List.of(getAddJiraAssociateFile().getId()));

View File

@ -282,7 +282,7 @@ public class FunctionalCaseService {
functionalCase.setReviewStatus(FunctionalCaseReviewStatus.UN_REVIEWED.name());
functionalCase.setPos(getNextOrder(request.getProjectId()));
functionalCase.setRefId(caseId);
functionalCase.setLastExecuteResult(FunctionalCaseExecuteResult.UN_EXECUTED.name());
functionalCase.setLastExecuteResult(FunctionalCaseExecuteResult.PENDING.name());
functionalCase.setLatest(true);
functionalCase.setCreateUser(userId);
functionalCase.setUpdateUser(userId);
@ -808,7 +808,7 @@ public class FunctionalCaseService {
functional.setName(getCopyName(functionalCase.getName(), num, functional.getNum()));
functional.setReviewStatus(FunctionalCaseReviewStatus.UN_REVIEWED.name());
functional.setPos(nextOrder.get());
functional.setLastExecuteResult(FunctionalCaseExecuteResult.UN_EXECUTED.name());
functional.setLastExecuteResult(FunctionalCaseExecuteResult.PENDING.name());
functional.setCreateUser(userId);
functional.setUpdateUser(userId);
functional.setCreateTime(System.currentTimeMillis());
@ -1125,7 +1125,7 @@ public class FunctionalCaseService {
functionalCase.setPos(nextOrder);
functionalCase.setVersionId(request.getVersionId());
functionalCase.setRefId(caseId);
functionalCase.setLastExecuteResult(FunctionalCaseExecuteResult.UN_EXECUTED.name());
functionalCase.setLastExecuteResult(FunctionalCaseExecuteResult.PENDING.name());
functionalCase.setLatest(true);
functionalCase.setCreateUser(userId);
functionalCase.setUpdateUser(userId);

View File

@ -23,13 +23,13 @@ import io.metersphere.system.log.constants.OperationLogType;
import io.metersphere.system.uid.IDGenerator;
import jakarta.annotation.Resource;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.*;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.apache.commons.lang3.StringUtils;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
@ -763,7 +763,7 @@ public class FunctionalCaseModuleControllerTests extends BaseTest {
functionalCase.setPos(500L);
functionalCase.setVersionId("12335");
functionalCase.setRefId(functionalCase.getId());
functionalCase.setLastExecuteResult(FunctionalCaseExecuteResult.UN_EXECUTED.name());
functionalCase.setLastExecuteResult(FunctionalCaseExecuteResult.PENDING.name());
functionalCase.setPublicCase(false);
functionalCase.setLatest(true);
functionalCase.setCreateUser("gyq");

View File

@ -450,7 +450,7 @@ public class FunctionalTestCaseControllerTests extends BaseTest {
functionalCase.setPos(500L);
functionalCase.setVersionId("12335");
functionalCase.setRefId(functionalCase.getId());
functionalCase.setLastExecuteResult(FunctionalCaseExecuteResult.UN_EXECUTED.name());
functionalCase.setLastExecuteResult(FunctionalCaseExecuteResult.PENDING.name());
functionalCase.setPublicCase(false);
functionalCase.setLatest(true);
functionalCase.setCreateUser("gyq");

View File

@ -5,9 +5,11 @@ import io.metersphere.plan.domain.TestPlan;
import io.metersphere.plan.dto.request.*;
import io.metersphere.plan.dto.response.TestPlanDetailResponse;
import io.metersphere.plan.dto.response.TestPlanResponse;
import io.metersphere.plan.dto.response.TestPlanStatisticsResponse;
import io.metersphere.plan.service.TestPlanLogService;
import io.metersphere.plan.service.TestPlanManagementService;
import io.metersphere.plan.service.TestPlanService;
import io.metersphere.plan.service.TestPlanStatisticsService;
import io.metersphere.sdk.constants.HttpMethodConstants;
import io.metersphere.sdk.constants.PermissionConstants;
import io.metersphere.system.log.annotation.Log;
@ -35,6 +37,8 @@ public class TestPlanController {
private TestPlanService testPlanService;
@Resource
private TestPlanManagementService testPlanManagementService;
@Resource
private TestPlanStatisticsService testPlanStatisticsService;
@PostMapping("/page")
@ -46,6 +50,12 @@ public class TestPlanController {
return testPlanManagementService.page(request);
}
@PostMapping("/statistics")
@Operation(summary = "测试计划-获取计划详情统计{通过率, 执行进度}")
@RequiresPermissions(PermissionConstants.TEST_PLAN_READ)
public List<TestPlanStatisticsResponse> selectTestPlanMetricById(@RequestBody List<String> ids) {
return testPlanStatisticsService.calculateRate(ids);
}
@PostMapping("/module/count")
@Operation(summary = "测试计划-模块统计")

View File

@ -11,13 +11,11 @@ import java.util.List;
* @author wx
*/
@Data
public class TestPlanDetailResponse implements Serializable {
public class TestPlanDetailResponse extends TestPlanStatisticsResponse implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@Schema(description = "测试计划ID")
private String id;
@Schema(description = "测试计划组Id")
private String groupId;
@Schema(description = "测试计划组名称")
@ -38,9 +36,6 @@ public class TestPlanDetailResponse implements Serializable {
@Schema(description = "是否允许重复添加用例")
private Boolean repeatCase;
@Schema(description = "测试计划通过阈值;0-100")
private Double passThreshold;
@Schema(description = "是否开启测试规划")
private Boolean testPlanning;

View File

@ -6,9 +6,7 @@ import lombok.Data;
import java.util.List;
@Data
public class TestPlanResponse {
@Schema(description = "测试计划ID")
private String id;
public class TestPlanResponse extends TestPlanStatisticsResponse {
@Schema(description = "项目ID")
private String projectId;
@Schema(description = "测试计划编号")

View File

@ -0,0 +1,51 @@
package io.metersphere.plan.dto.response;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 测试计划统计详情
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class TestPlanStatisticsResponse {
@Schema(description = "测试计划ID")
private String id;
@Schema(description = "测试计划通过阈值{0-100}")
private Double passThreshold;
@Schema(description = "测试计划: 通过率 {成功用例/全部用例}")
private Double passRate;
@Schema(description = "测试计划: 执行进度||测试进度 {已执行用例/全部用例}")
private Double executeRate;
/**
* 执行进度中的用例数量统计 (暂定)
*/
@Schema(description = "成功用例数量")
private Integer successCount = 0;
@Schema(description = "失败用例数量")
private Integer errorCount = 0;
@Schema(description = "误报用例数量")
private Integer fakeErrorCount = 0;
@Schema(description = "阻塞用例数量")
private Integer blockCount = 0;
@Schema(description = "未执行用例数量")
private Integer pendingCount = 0;
/**
* 用例数中用例数量统计 (暂定)
*/
@Schema(description = "用例总数")
private Integer caseTotal = 0;
@Schema(description = "功能用例数量")
private Integer functionalCaseCount = 0;
@Schema(description = "接口用例数量")
private Integer apiCaseCount = 0;
@Schema(description = "接口场景数量")
private Integer apiScenarioCount = 0;
}

View File

@ -3,6 +3,7 @@ package io.metersphere.plan.mapper;
import io.metersphere.functional.dto.FunctionalCaseModuleCountDTO;
import io.metersphere.functional.dto.FunctionalCaseModuleDTO;
import io.metersphere.functional.dto.ProjectOptionDTO;
import io.metersphere.plan.domain.TestPlanFunctionalCase;
import io.metersphere.plan.dto.AssociationNode;
import io.metersphere.plan.dto.ResourceSelectParam;
import io.metersphere.plan.dto.request.BasePlanCaseBatchRequest;
@ -38,4 +39,11 @@ public interface ExtTestPlanFunctionalCaseMapper {
long caseCount(@Param("request") TestPlanCaseRequest request, @Param("deleted") boolean deleted);
List<String> getIds(@Param("request") BasePlanCaseBatchRequest request, @Param("deleted") boolean deleted);
/**
* 获取计划下的功能用例集合
* @param planIds 测试计划ID集合
* @return 计划功能用例集合
*/
List<TestPlanFunctionalCase> getPlanFunctionalCaseByIds(@Param("planIds") List<String> planIds);
}

View File

@ -465,4 +465,18 @@
AND functional_case.deleted = #{deleted}
<include refid="queryWhereCondition"/>
</select>
<select id="getPlanFunctionalCaseByIds" resultType="io.metersphere.plan.domain.TestPlanFunctionalCase">
select tpfc.test_plan_id testPlanId, tpfc.functional_case_id functionalCaseId, tpfc.last_exec_result lastExecResult
from test_plan_functional_case tpfc join functional_case fc on fc.id = tpfc.functional_case_id
<where>
fc.deleted = false
<if test="planIds != null and planIds.size() > 0">
and tpfc.test_plan_id IN
<foreach collection="planIds" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</if>
</where>
</select>
</mapper>

View File

@ -37,7 +37,7 @@ public class TestPlanBatchArchivedService extends TestPlanBaseUtilsService {
* @param plans
*/
private int batchArchivedGroup(Map<String, List<TestPlan>> plans, TestPlanBatchProcessRequest request, String userId) {
//TODO 批量移动计划组
//TODO 批量归档计划组
return 0;
}

View File

@ -8,6 +8,7 @@ import io.metersphere.plan.dto.TestPlanBugCaseDTO;
import io.metersphere.plan.dto.request.TestPlanBugPageRequest;
import io.metersphere.plan.dto.response.TestPlanBugPageResponse;
import io.metersphere.plan.mapper.ExtTestPlanBugMapper;
import io.metersphere.plugin.platform.dto.SelectOption;
import io.metersphere.system.dto.sdk.OptionDTO;
import io.metersphere.system.mapper.BaseUserMapper;
import jakarta.annotation.Resource;
@ -86,11 +87,17 @@ public class TestPlanBugService extends TestPlanResourceService {
* @param bugList 缺陷集合
*/
private void parseCustomField(List<TestPlanBugPageResponse> bugList, String projectId) {
Map<String, String> allHandlerMap = bugCommonService.getAllHandlerMap(projectId);
// MS处理人会与第三方的值冲突, 分开查询
List<SelectOption> headerOptions = bugCommonService.getHeaderHandlerOption(projectId);
Map<String, String> headerHandleUserMap = headerOptions.stream().collect(Collectors.toMap(SelectOption::getValue, SelectOption::getText));
List<SelectOption> localOptions = bugCommonService.getLocalHandlerOption(projectId);
Map<String, String> localHandleUserMap = localOptions.stream().collect(Collectors.toMap(SelectOption::getValue, SelectOption::getText));
Map<String, String> allStatusMap = bugCommonService.getAllStatusMap(projectId);
bugList.forEach(bug -> {
// 解析处理人, 状态
bug.setHandleUser(allHandlerMap.get(bug.getHandleUser()));
bug.setHandleUser(headerHandleUserMap.containsKey(bug.getHandleUser()) ?
headerHandleUserMap.get(bug.getHandleUser()) : localHandleUserMap.get(bug.getHandleUser()));
bug.setStatus(allStatusMap.get(bug.getStatus()));
});
}

View File

@ -22,7 +22,6 @@ import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@ -38,6 +37,8 @@ public class TestPlanManagementService {
private ExtTestPlanModuleMapper extTestPlanModuleMapper;
@Resource
private TestPlanModuleService testPlanModuleService;
@Resource
private TestPlanStatisticsService testPlanStatisticsService;
public Map<String, Long> moduleCount(TestPlanTableRequest request) {
//查出每个模块节点下的资源数量 不需要按照模块进行筛选
@ -80,23 +81,14 @@ public class TestPlanManagementService {
if (collect.containsKey(item.getId())) {
//存在子节点
List<TestPlanResponse> list = collect.get(item.getId());
calculateData(list);
testPlanStatisticsService.calculateCaseCount(list);
item.setChildren(list);
item.setChildrenCount(list.size());
}
calculateData(Arrays.asList(item));
testPlanStatisticsService.calculateCaseCount(List.of(item));
});
}
/**
* 计算各种指标
*
* @param list
*/
private void calculateData(List<TestPlanResponse> list) {
//TODO 查询计划下面关联的用例数量用于各种计算 什么通过率 进度 用例数
}
public void checkModuleIsOpen(String resourceId, String resourceType, List<String> moduleMenus) {
Project project;

View File

@ -52,6 +52,8 @@ public class TestPlanService extends TestPlanBaseUtilsService {
@Resource
private TestPlanBatchArchivedService testPlanBatchArchivedService;
@Resource
private TestPlanStatisticsService testPlanStatisticsService;
@Resource
private TestPlanCaseService testPlanCaseService;
@ -151,7 +153,6 @@ public class TestPlanService extends TestPlanBaseUtilsService {
/**
* 计划组删除的相关逻辑(待定)
*
* @param testPlanGroupIds 计划组ID集合
*/
private void deleteGroupByList(List<String> testPlanGroupIds) {
@ -423,8 +424,8 @@ public class TestPlanService extends TestPlanBaseUtilsService {
/**
* 获取单个测试计划或测试计划组详情用于编辑
*
* @param id
* @return
* @param id 计划ID
* @return 计划的详情数据
*/
public TestPlanDetailResponse detail(String id) {
TestPlan testPlan = testPlanMapper.selectByPrimaryKey(id);
@ -443,6 +444,7 @@ public class TestPlanService extends TestPlanBaseUtilsService {
response.setPlannedStartTime(testPlan.getPlannedStartTime());
response.setPlannedEndTime(testPlan.getPlannedEndTime());
getOtherConfig(response, testPlan);
testPlanStatisticsService.calculateCaseCount(List.of(response));
}
return response;
}

View File

@ -0,0 +1,110 @@
package io.metersphere.plan.service;
import io.metersphere.plan.domain.TestPlanFunctionalCase;
import io.metersphere.plan.dto.response.TestPlanStatisticsResponse;
import io.metersphere.plan.mapper.ExtTestPlanFunctionalCaseMapper;
import io.metersphere.sdk.constants.FunctionalCaseExecuteResult;
import jakarta.annotation.Resource;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 测试计划统计相关业务
*/
@Service
public class TestPlanStatisticsService {
@Resource
private ExtTestPlanFunctionalCaseMapper extTestPlanFunctionalCaseMapper;
/**
* 计划的用例统计数据
*
* @param plans 计划集合
*/
public void calculateCaseCount(List<? extends TestPlanStatisticsResponse> plans) {
// TODO 计算计划下各种维度的用例统计数目 (待定)
/*
* 1. 查询计划下的用例数据集合(目前只有功能用例)
* 2. 根据执行结果统计(结果小数保留两位)
*/
List<String> planIds = plans.stream().map(TestPlanStatisticsResponse::getId).toList();
// 计划-功能用例的关联数据
List<TestPlanFunctionalCase> planFunctionalCases = extTestPlanFunctionalCaseMapper.getPlanFunctionalCaseByIds(planIds);
Map<String, List<TestPlanFunctionalCase>> planFunctionalCaseMap = planFunctionalCases.stream().collect(Collectors.groupingBy(TestPlanFunctionalCase::getTestPlanId));
// TODO: 计划-接口用例的关联数据
plans.forEach(plan -> {
// 功能用例统计开始
List<TestPlanFunctionalCase> functionalCases = planFunctionalCaseMap.get(plan.getId());
plan.setFunctionalCaseCount(CollectionUtils.isNotEmpty(functionalCases) ? functionalCases.size() : 0);
// TODO: 接口用例统计开始
// FIXME: CaseTotal后续会补充接口用例及场景的统计数据
plan.setCaseTotal(plan.getFunctionalCaseCount());
});
}
/**
* 计划的{通过率, 执行进度}统计数据
*
* @param planIds 计划ID集合
*/
public List<TestPlanStatisticsResponse> calculateRate(List<String> planIds) {
List<TestPlanStatisticsResponse> planStatisticsResponses = new ArrayList<>();
// TODO 计算计划下的用例通过率, 执行进度 (待定)
/*
* 1. 查询计划下的用例数据集合(目前只有功能用例)
* 2. 根据执行结果统计(结果小数保留两位)
*/
DecimalFormat rateFormat = new DecimalFormat("#0.00");
rateFormat.setMinimumFractionDigits(2);
rateFormat.setMaximumFractionDigits(2);
// 计划-功能用例的关联数据
List<TestPlanFunctionalCase> planFunctionalCases = extTestPlanFunctionalCaseMapper.getPlanFunctionalCaseByIds(planIds);
Map<String, List<TestPlanFunctionalCase>> planFunctionalCaseMap = planFunctionalCases.stream().collect(Collectors.groupingBy(TestPlanFunctionalCase::getTestPlanId));
// TODO: 计划-接口用例的关联数据
planIds.forEach(planId -> {
TestPlanStatisticsResponse statisticsResponse = new TestPlanStatisticsResponse();
int success = 0, error = 0, fakeError = 0, block = 0, pending = 0;
// 功能用例统计开始
List<TestPlanFunctionalCase> functionalCases = planFunctionalCaseMap.get(planId);
statisticsResponse.setFunctionalCaseCount(CollectionUtils.isNotEmpty(functionalCases) ? functionalCases.size() : 0);
Map<String, List<TestPlanFunctionalCase>> functionalCaseResultMap = CollectionUtils.isEmpty(functionalCases) ? new HashMap<>(16) : functionalCases.stream().collect(Collectors.groupingBy(TestPlanFunctionalCase::getLastExecResult));
success += functionalCaseResultMap.containsKey(FunctionalCaseExecuteResult.SUCCESS.name()) ? functionalCaseResultMap.get(FunctionalCaseExecuteResult.SUCCESS.name()).size() : 0;
error += functionalCaseResultMap.containsKey(FunctionalCaseExecuteResult.ERROR.name()) ? functionalCaseResultMap.get(FunctionalCaseExecuteResult.ERROR.name()).size() : 0;
fakeError += functionalCaseResultMap.containsKey(FunctionalCaseExecuteResult.FAKE_ERROR.name()) ? functionalCaseResultMap.get(FunctionalCaseExecuteResult.FAKE_ERROR.name()).size() : 0;
block += functionalCaseResultMap.containsKey(FunctionalCaseExecuteResult.BLOCKED.name()) ? functionalCaseResultMap.get(FunctionalCaseExecuteResult.BLOCKED.name()).size() : 0;
pending += functionalCaseResultMap.containsKey(FunctionalCaseExecuteResult.PENDING.name()) ? functionalCaseResultMap.get(FunctionalCaseExecuteResult.PENDING.name()).size() : 0;
// TODO: 接口用例统计开始
// 用例数据汇总
statisticsResponse.setSuccessCount(success);
statisticsResponse.setErrorCount(error);
statisticsResponse.setFakeErrorCount(fakeError);
statisticsResponse.setBlockCount(block);
statisticsResponse.setPendingCount(pending);
// FIXME: CaseTotal后续会补充接口用例及场景的统计数据
statisticsResponse.setCaseTotal(statisticsResponse.getFunctionalCaseCount());
// 通过率 {通过用例数/总用例数} && 执行进度 {非未执行的用例数/总用例数}
double passRate = (statisticsResponse.getSuccessCount() == 0 || statisticsResponse.getCaseTotal() == 0) ? 0.00 :
Double.parseDouble(rateFormat.format((double) statisticsResponse.getSuccessCount() * 100 / (double) statisticsResponse.getCaseTotal()));
double executeRate = (statisticsResponse.getPendingCount().equals(statisticsResponse.getCaseTotal()) || statisticsResponse.getCaseTotal() == 0) ? 0.00 :
Double.parseDouble(rateFormat.format((double) (statisticsResponse.getCaseTotal() - statisticsResponse.getPendingCount()) * 100 / (double) statisticsResponse.getCaseTotal()));
// V2旧逻辑, 如果算出的结果(99.999%)由于精度问题四舍五入为100%, 且计算数量小于总数, 实际值设为99.99%
statisticsResponse.setPassRate((passRate == 100 && statisticsResponse.getSuccessCount() < statisticsResponse.getCaseTotal()) ? 99.99 : passRate);
statisticsResponse.setExecuteRate((executeRate == 100 && statisticsResponse.getPendingCount() > 0) ? 99.99 : executeRate);
planStatisticsResponses.add(statisticsResponse);
});
return planStatisticsResponses;
}
}

View File

@ -101,6 +101,7 @@ public class TestPlanTests extends BaseTest {
private static final String URL_GET_TEST_PLAN_DELETE = "/test-plan/delete/%s";
private static final String URL_POST_TEST_PLAN_PAGE = "/test-plan/page";
private static final String URL_POST_TEST_PLAN_STATISTICS = "/test-plan/statistics";
private static final String URL_POST_TEST_PLAN_MODULE_COUNT = "/test-plan/module/count";
private static final String URL_POST_TEST_PLAN_ADD = "/test-plan/add";
private static final String URL_POST_TEST_PLAN_UPDATE = "/test-plan/update";
@ -1842,4 +1843,10 @@ public class TestPlanTests extends BaseTest {
this.requestPostWithOkAndReturn(URL_POST_RESOURCE_CASE_ASSOCIATION, request);
}
@Test
@Order(306)
public void testStatistics() throws Exception {
this.requestPostWithOk(URL_POST_TEST_PLAN_STATISTICS, List.of("wx_test_plan_id_7"));
}
}

View File

@ -19,9 +19,9 @@ import io.metersphere.system.uid.IDGenerator;
import io.metersphere.system.uid.NumGenerator;
import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.Assertions;
import org.springframework.stereotype.Service;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.List;
@ -138,7 +138,7 @@ public class TestPlanTestService {
functionalCase.setReviewStatus("UN_REVIEWED");
functionalCase.setPos((long) (i * 64));
functionalCase.setRefId(functionalCase.getId());
functionalCase.setLastExecuteResult(FunctionalCaseExecuteResult.UN_EXECUTED.name());
functionalCase.setLastExecuteResult(FunctionalCaseExecuteResult.PENDING.name());
functionalCase.setLatest(true);
functionalCase.setCreateUser("admin");
functionalCase.setCreateTime(System.currentTimeMillis());

View File

@ -9,8 +9,20 @@ VALUES
('wx_test_plan_id_7', 30000, '123', 'NONE', '1', '测试组4下计划', 'COMPLETED', 'TEST_PLAN', NULL, 1714980158000, 'WX', 1714980158000, 'WX', 1714980158000, 1714980158000, 1714980158000, 1714980158000, '11');
INSERT INTO `test_plan_functional_case`(`id`, `test_plan_id`, `functional_case_id`, `create_time`, `create_user`, `execute_user`, `last_exec_time`, `last_exec_result`, `pos`)
VALUES ('wx_tpfc_1', 'wx_test_plan_id_4', 'wx_fc_1', 1714980158000, 'admin', NULL, NULL, NULL, 1);
INSERT INTO `test_plan_functional_case`(`id`, `test_plan_id`, `functional_case_id`, `create_time`, `create_user`, `execute_user`, `last_exec_time`, `last_exec_result`, `pos`) VALUES
('wx_tpfc_1', 'wx_test_plan_id_4', 'wx_fc_1', 1714980158000, 'admin', NULL, NULL, NULL, 1),
('oasis_1', 'wx_test_plan_id_7', 'oasis_fc_1', 1714980158000, 'admin', NULL, NULL, 'UN_EXECUTED', 1),
('oasis_2', 'wx_test_plan_id_7', 'oasis_fc_2', 1714980158000, 'admin', NULL, NULL, 'PASSED', 1),
('oasis_3', 'wx_test_plan_id_7', 'oasis_fc_3', 1714980158000, 'admin', NULL, NULL, 'FAILED', 1),
('oasis_4', 'wx_test_plan_id_7', 'oasis_fc_4', 1714980158000, 'admin', NULL, NULL, 'BLOCKED', 1),
('oasis_5', 'wx_test_plan_id_7', 'oasis_fc_5', 1714980158000, 'admin', NULL, NULL, 'FAKE_ERROR', 1);
INSERT INTO functional_case(id, num, module_id, project_id, template_id, name, review_status, tags, case_edit_type, pos, version_id, ref_id, last_execute_result, deleted, public_case, latest, create_user, update_user, delete_user, create_time, update_time, delete_time) VALUES
('oasis_fc_1', 10001, 'TEST_MODULE_ID', 'project-associate-case-test', '100001', '测试', 'UN_REVIEWED', NULL, 'STEP', 0, 'v1.0.0', 'v1.0.0', 'PENDING', b'0', b'0', b'1', 'admin', 'admin', '', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL),
('oasis_fc_2', 10002, 'TEST_MODULE_ID', 'project-associate-case-test', '100001', '测试', 'UN_REVIEWED', NULL, 'STEP', 0, 'v1.0.0', 'v1.0.0', 'SUCCESS', b'0', b'0', b'1', 'admin', 'admin', '', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL),
('oasis_fc_3', 10003, 'TEST_MODULE_ID', 'project-associate-case-test', '100001', '测试', 'UN_REVIEWED', NULL, 'STEP', 0, 'v1.0.0', 'v1.0.0', 'ERROR', b'0', b'0', b'1', 'admin', 'admin', '', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL),
('oasis_fc_4', 10004, 'TEST_MODULE_ID', 'project-associate-case-test', '100001', '测试', 'UN_REVIEWED', NULL, 'STEP', 0, 'v1.0.0', 'v1.0.0', 'BLOCKED', b'0', b'0', b'1', 'admin', 'admin', '', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL),
('oasis_fc_5', 10005, 'TEST_MODULE_ID', 'project-associate-case-test', '100001', '测试', 'UN_REVIEWED', NULL, 'STEP', 0, 'v1.0.0', 'v1.0.0', 'FAKE_ERROR', b'0', b'0', b'1', 'admin', 'admin', '', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, NULL);
INSERT INTO `test_plan_module`(`id`, `project_id`, `name`, `parent_id`, `pos`, `create_time`, `update_time`, `create_user`, `update_user`)