refactor(测试计划): 修改测试计划统计查询方法,增加定时任务的查询
This commit is contained in:
parent
051b3acbdd
commit
63168b9067
|
@ -19,10 +19,8 @@ import io.metersphere.api.parser.step.StepParser;
|
||||||
import io.metersphere.api.parser.step.StepParserFactory;
|
import io.metersphere.api.parser.step.StepParserFactory;
|
||||||
import io.metersphere.api.service.ApiCommonService;
|
import io.metersphere.api.service.ApiCommonService;
|
||||||
import io.metersphere.api.service.ApiFileResourceService;
|
import io.metersphere.api.service.ApiFileResourceService;
|
||||||
import io.metersphere.api.service.definition.ApiDefinitionModuleService;
|
|
||||||
import io.metersphere.api.service.definition.ApiDefinitionService;
|
import io.metersphere.api.service.definition.ApiDefinitionService;
|
||||||
import io.metersphere.api.service.definition.ApiTestCaseService;
|
import io.metersphere.api.service.definition.ApiTestCaseService;
|
||||||
import io.metersphere.api.service.queue.ApiExecutionSetService;
|
|
||||||
import io.metersphere.api.utils.ApiDataUtils;
|
import io.metersphere.api.utils.ApiDataUtils;
|
||||||
import io.metersphere.api.utils.ApiScenarioBatchOperationUtils;
|
import io.metersphere.api.utils.ApiScenarioBatchOperationUtils;
|
||||||
import io.metersphere.functional.domain.FunctionalCaseTestExample;
|
import io.metersphere.functional.domain.FunctionalCaseTestExample;
|
||||||
|
@ -65,6 +63,7 @@ import io.metersphere.system.service.OperationHistoryService;
|
||||||
import io.metersphere.system.service.UserLoginService;
|
import io.metersphere.system.service.UserLoginService;
|
||||||
import io.metersphere.system.uid.IDGenerator;
|
import io.metersphere.system.uid.IDGenerator;
|
||||||
import io.metersphere.system.uid.NumGenerator;
|
import io.metersphere.system.uid.NumGenerator;
|
||||||
|
import io.metersphere.system.utils.ScheduleUtils;
|
||||||
import io.metersphere.system.utils.ServiceUtils;
|
import io.metersphere.system.utils.ServiceUtils;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import jakarta.validation.constraints.NotBlank;
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
@ -79,10 +78,6 @@ import org.apache.ibatis.session.ExecutorType;
|
||||||
import org.apache.ibatis.session.SqlSession;
|
import org.apache.ibatis.session.SqlSession;
|
||||||
import org.apache.ibatis.session.SqlSessionFactory;
|
import org.apache.ibatis.session.SqlSessionFactory;
|
||||||
import org.mybatis.spring.SqlSessionUtils;
|
import org.mybatis.spring.SqlSessionUtils;
|
||||||
import org.quartz.CronExpression;
|
|
||||||
import org.quartz.CronScheduleBuilder;
|
|
||||||
import org.quartz.CronTrigger;
|
|
||||||
import org.quartz.TriggerBuilder;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@ -242,7 +237,7 @@ public class ApiScenarioService extends MoveNodeService {
|
||||||
}
|
}
|
||||||
item.setScheduleConfig(request);
|
item.setScheduleConfig(request);
|
||||||
if (schedule.getEnable()) {
|
if (schedule.getEnable()) {
|
||||||
item.setNextTriggerTime(getNextTriggerTime(schedule.getValue()));
|
item.setNextTriggerTime(ScheduleUtils.getNextTriggerTime(schedule.getValue()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (MapUtils.isNotEmpty(reportMap) && reportMap.containsKey(item.getLastReportId())) {
|
if (MapUtils.isNotEmpty(reportMap) && reportMap.containsKey(item.getLastReportId())) {
|
||||||
|
@ -251,22 +246,6 @@ public class ApiScenarioService extends MoveNodeService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取下次执行时间(getFireTimeAfter,也可以下下次...)
|
|
||||||
*
|
|
||||||
* @param cron cron表达式
|
|
||||||
* @return 下次执行时间
|
|
||||||
*/
|
|
||||||
private static Long getNextTriggerTime(String cron) {
|
|
||||||
if (!CronExpression.isValidExpression(cron)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity("Calculate Date").withSchedule(CronScheduleBuilder.cronSchedule(cron)).build();
|
|
||||||
Date time0 = trigger.getStartTime();
|
|
||||||
Date time1 = trigger.getFireTimeAfter(time0);
|
|
||||||
return time1 == null ? 0 : time1.getTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Set<String> extractUserIds(List<ApiScenarioDTO> list) {
|
private Set<String> extractUserIds(List<ApiScenarioDTO> list) {
|
||||||
return list.stream()
|
return list.stream()
|
||||||
.flatMap(apiScenario -> Stream.of(apiScenario.getUpdateUser(), apiScenario.getDeleteUser(), apiScenario.getCreateUser()))
|
.flatMap(apiScenario -> Stream.of(apiScenario.getUpdateUser(), apiScenario.getDeleteUser(), apiScenario.getCreateUser()))
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
package io.metersphere.system.utils;
|
||||||
|
|
||||||
|
|
||||||
|
import org.quartz.CronExpression;
|
||||||
|
import org.quartz.CronScheduleBuilder;
|
||||||
|
import org.quartz.CronTrigger;
|
||||||
|
import org.quartz.TriggerBuilder;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
public class ScheduleUtils {
|
||||||
|
/**
|
||||||
|
* 获取下次执行时间(getFireTimeAfter,也可以下下次...)
|
||||||
|
*
|
||||||
|
* @param cron cron表达式
|
||||||
|
* @return 下次执行时间
|
||||||
|
*/
|
||||||
|
public static Long getNextTriggerTime(String cron) {
|
||||||
|
if (!CronExpression.isValidExpression(cron)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity("Calculate Date").withSchedule(CronScheduleBuilder.cronSchedule(cron)).build();
|
||||||
|
Date time0 = trigger.getStartTime();
|
||||||
|
Date time1 = trigger.getFireTimeAfter(time0);
|
||||||
|
return time1 == null ? 0 : time1.getTime();
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ package io.metersphere.plan.dto.response;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
|
||||||
import io.metersphere.plan.serializer.CustomRateSerializer;
|
import io.metersphere.plan.serializer.CustomRateSerializer;
|
||||||
|
import io.metersphere.system.dto.request.schedule.BaseScheduleConfigRequest;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
|
@ -55,4 +56,8 @@ public class TestPlanStatisticsResponse {
|
||||||
private Integer apiScenarioCount = 0;
|
private Integer apiScenarioCount = 0;
|
||||||
@Schema(description = "缺陷数量")
|
@Schema(description = "缺陷数量")
|
||||||
private Integer bugCount = 0;
|
private Integer bugCount = 0;
|
||||||
|
@Schema(description = "定时任务配置")
|
||||||
|
private BaseScheduleConfigRequest scheduleConfig;
|
||||||
|
@Schema(description = "定时任务下一次执行时间")
|
||||||
|
private Long nextTriggerTime;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,13 @@ import io.metersphere.plan.mapper.ExtTestPlanFunctionalCaseMapper;
|
||||||
import io.metersphere.plan.mapper.TestPlanConfigMapper;
|
import io.metersphere.plan.mapper.TestPlanConfigMapper;
|
||||||
import io.metersphere.plan.utils.RateCalculateUtils;
|
import io.metersphere.plan.utils.RateCalculateUtils;
|
||||||
import io.metersphere.sdk.constants.ExecStatus;
|
import io.metersphere.sdk.constants.ExecStatus;
|
||||||
|
import io.metersphere.sdk.constants.ScheduleResourceType;
|
||||||
|
import io.metersphere.sdk.util.JSON;
|
||||||
|
import io.metersphere.system.domain.Schedule;
|
||||||
|
import io.metersphere.system.domain.ScheduleExample;
|
||||||
|
import io.metersphere.system.dto.request.schedule.BaseScheduleConfigRequest;
|
||||||
|
import io.metersphere.system.mapper.ScheduleMapper;
|
||||||
|
import io.metersphere.system.utils.ScheduleUtils;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import org.apache.commons.collections4.CollectionUtils;
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
@ -29,6 +36,8 @@ public class TestPlanStatisticsService {
|
||||||
private ExtTestPlanFunctionalCaseMapper extTestPlanFunctionalCaseMapper;
|
private ExtTestPlanFunctionalCaseMapper extTestPlanFunctionalCaseMapper;
|
||||||
@Resource
|
@Resource
|
||||||
private ExtTestPlanBugMapper extTestPlanBugMapper;
|
private ExtTestPlanBugMapper extTestPlanBugMapper;
|
||||||
|
@Resource
|
||||||
|
private ScheduleMapper scheduleMapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 计划的用例统计数据
|
* 计划的用例统计数据
|
||||||
|
@ -83,6 +92,13 @@ public class TestPlanStatisticsService {
|
||||||
// 计划-功能用例的关联数据
|
// 计划-功能用例的关联数据
|
||||||
List<TestPlanFunctionalCase> planFunctionalCases = extTestPlanFunctionalCaseMapper.getPlanFunctionalCaseByIds(planIds);
|
List<TestPlanFunctionalCase> planFunctionalCases = extTestPlanFunctionalCaseMapper.getPlanFunctionalCaseByIds(planIds);
|
||||||
Map<String, List<TestPlanFunctionalCase>> planFunctionalCaseMap = planFunctionalCases.stream().collect(Collectors.groupingBy(TestPlanFunctionalCase::getTestPlanId));
|
Map<String, List<TestPlanFunctionalCase>> planFunctionalCaseMap = planFunctionalCases.stream().collect(Collectors.groupingBy(TestPlanFunctionalCase::getTestPlanId));
|
||||||
|
|
||||||
|
//查询定时任务
|
||||||
|
ScheduleExample scheduleExample = new ScheduleExample();
|
||||||
|
scheduleExample.createCriteria().andResourceIdIn(planIds).andResourceTypeEqualTo(ScheduleResourceType.TEST_PLAN.name());
|
||||||
|
List<Schedule> schedules = scheduleMapper.selectByExample(scheduleExample);
|
||||||
|
Map<String, Schedule> scheduleMap = schedules.stream().collect(Collectors.toMap(Schedule::getResourceId, t -> t));
|
||||||
|
|
||||||
// TODO: 计划-接口用例的关联数据
|
// TODO: 计划-接口用例的关联数据
|
||||||
planIds.forEach(planId -> {
|
planIds.forEach(planId -> {
|
||||||
TestPlanStatisticsResponse statisticsResponse = new TestPlanStatisticsResponse();
|
TestPlanStatisticsResponse statisticsResponse = new TestPlanStatisticsResponse();
|
||||||
|
@ -115,6 +131,23 @@ public class TestPlanStatisticsService {
|
||||||
statisticsResponse.setPassRate(RateCalculateUtils.divWithPrecision(statisticsResponse.getSuccessCount(), statisticsResponse.getCaseTotal(), 2));
|
statisticsResponse.setPassRate(RateCalculateUtils.divWithPrecision(statisticsResponse.getSuccessCount(), statisticsResponse.getCaseTotal(), 2));
|
||||||
statisticsResponse.setExecuteRate(RateCalculateUtils.divWithPrecision(statisticsResponse.getCaseTotal() - statisticsResponse.getPendingCount(), statisticsResponse.getCaseTotal(), 2));
|
statisticsResponse.setExecuteRate(RateCalculateUtils.divWithPrecision(statisticsResponse.getCaseTotal() - statisticsResponse.getPendingCount(), statisticsResponse.getCaseTotal(), 2));
|
||||||
planStatisticsResponses.add(statisticsResponse);
|
planStatisticsResponses.add(statisticsResponse);
|
||||||
|
|
||||||
|
//定时任务
|
||||||
|
if (scheduleMap.containsKey(planId)) {
|
||||||
|
Schedule schedule = scheduleMap.get(planId);
|
||||||
|
BaseScheduleConfigRequest request = new BaseScheduleConfigRequest();
|
||||||
|
request.setEnable(schedule.getEnable());
|
||||||
|
request.setCron(schedule.getValue());
|
||||||
|
request.setResourceId(planId);
|
||||||
|
if (schedule.getConfig() != null) {
|
||||||
|
request.setRunConfig(JSON.parseObject(schedule.getConfig(), Map.class));
|
||||||
|
}
|
||||||
|
statisticsResponse.setScheduleConfig(request);
|
||||||
|
if (schedule.getEnable()) {
|
||||||
|
statisticsResponse.setNextTriggerTime(ScheduleUtils.getNextTriggerTime(schedule.getValue()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
return planStatisticsResponses;
|
return planStatisticsResponses;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import io.metersphere.plan.domain.*;
|
||||||
import io.metersphere.plan.dto.request.*;
|
import io.metersphere.plan.dto.request.*;
|
||||||
import io.metersphere.plan.dto.response.TestPlanOperationResponse;
|
import io.metersphere.plan.dto.response.TestPlanOperationResponse;
|
||||||
import io.metersphere.plan.dto.response.TestPlanResponse;
|
import io.metersphere.plan.dto.response.TestPlanResponse;
|
||||||
|
import io.metersphere.plan.dto.response.TestPlanStatisticsResponse;
|
||||||
import io.metersphere.plan.mapper.ExtTestPlanMapper;
|
import io.metersphere.plan.mapper.ExtTestPlanMapper;
|
||||||
import io.metersphere.plan.mapper.TestPlanMapper;
|
import io.metersphere.plan.mapper.TestPlanMapper;
|
||||||
import io.metersphere.plan.mapper.TestPlanReportMapper;
|
import io.metersphere.plan.mapper.TestPlanReportMapper;
|
||||||
|
@ -1365,10 +1366,23 @@ public class TestPlanTests extends BaseTest {
|
||||||
this.requestGet(String.format(URL_POST_TEST_PLAN_SCHEDULE_DELETE, groupTestPlanId7)).andExpect(status().is5xxServerError());
|
this.requestGet(String.format(URL_POST_TEST_PLAN_SCHEDULE_DELETE, groupTestPlanId7)).andExpect(status().is5xxServerError());
|
||||||
//恢复
|
//恢复
|
||||||
testPlanTestService.resetProjectModule(project, PROJECT_MODULE);
|
testPlanTestService.resetProjectModule(project, PROJECT_MODULE);
|
||||||
|
|
||||||
|
//正是测试
|
||||||
MvcResult result = this.requestPostAndReturn(URL_POST_TEST_PLAN_SCHEDULE, request);
|
MvcResult result = this.requestPostAndReturn(URL_POST_TEST_PLAN_SCHEDULE, request);
|
||||||
ResultHolder resultHolder = JSON.parseObject(result.getResponse().getContentAsString(StandardCharsets.UTF_8), ResultHolder.class);
|
ResultHolder resultHolder = JSON.parseObject(result.getResponse().getContentAsString(StandardCharsets.UTF_8), ResultHolder.class);
|
||||||
String scheduleId = resultHolder.getData().toString();
|
String scheduleId = resultHolder.getData().toString();
|
||||||
testPlanTestService.checkSchedule(scheduleId, groupTestPlanId7, request.isEnable());
|
testPlanTestService.checkSchedule(scheduleId, groupTestPlanId7, request.isEnable());
|
||||||
|
//检查统计接口查询的是否正确
|
||||||
|
List<TestPlanStatisticsResponse> statisticsResponses = JSON.parseArray(
|
||||||
|
JSON.toJSONString(
|
||||||
|
JSON.parseObject(
|
||||||
|
this.requestPostAndReturn(URL_POST_TEST_PLAN_STATISTICS, List.of(groupTestPlanId7))
|
||||||
|
.getResponse().getContentAsString(), ResultHolder.class).getData()),
|
||||||
|
TestPlanStatisticsResponse.class);
|
||||||
|
Assertions.assertTrue(statisticsResponses.size() == 1);
|
||||||
|
Assertions.assertTrue(statisticsResponses.getFirst().getNextTriggerTime() > 0);
|
||||||
|
Assertions.assertTrue(statisticsResponses.getFirst().getScheduleConfig().isEnable());
|
||||||
|
|
||||||
|
|
||||||
//增加日志检查
|
//增加日志检查
|
||||||
LOG_CHECK_LIST.add(
|
LOG_CHECK_LIST.add(
|
||||||
|
@ -1383,6 +1397,17 @@ public class TestPlanTests extends BaseTest {
|
||||||
//检查两个scheduleId是否相同
|
//检查两个scheduleId是否相同
|
||||||
Assertions.assertEquals(scheduleId, newScheduleId);
|
Assertions.assertEquals(scheduleId, newScheduleId);
|
||||||
testPlanTestService.checkSchedule(newScheduleId, groupTestPlanId7, request.isEnable());
|
testPlanTestService.checkSchedule(newScheduleId, groupTestPlanId7, request.isEnable());
|
||||||
|
//检查统计接口查询的是否正确
|
||||||
|
statisticsResponses = JSON.parseArray(
|
||||||
|
JSON.toJSONString(
|
||||||
|
JSON.parseObject(
|
||||||
|
this.requestPostAndReturn(URL_POST_TEST_PLAN_STATISTICS, List.of(groupTestPlanId7))
|
||||||
|
.getResponse().getContentAsString(), ResultHolder.class).getData()),
|
||||||
|
TestPlanStatisticsResponse.class);
|
||||||
|
Assertions.assertTrue(statisticsResponses.size() == 1);
|
||||||
|
Assertions.assertTrue(statisticsResponses.getFirst().getNextTriggerTime() == null);
|
||||||
|
Assertions.assertFalse(statisticsResponses.getFirst().getScheduleConfig().isEnable());
|
||||||
|
|
||||||
|
|
||||||
//测试各种corn表达式用于校验正则的准确性
|
//测试各种corn表达式用于校验正则的准确性
|
||||||
String[] cornStrArr = new String[]{
|
String[] cornStrArr = new String[]{
|
||||||
|
@ -1451,6 +1476,16 @@ public class TestPlanTests extends BaseTest {
|
||||||
//测试删除
|
//测试删除
|
||||||
this.requestGetWithOk(String.format(URL_POST_TEST_PLAN_SCHEDULE_DELETE, groupTestPlanId7));
|
this.requestGetWithOk(String.format(URL_POST_TEST_PLAN_SCHEDULE_DELETE, groupTestPlanId7));
|
||||||
testPlanTestService.checkScheduleIsRemove(groupTestPlanId7);
|
testPlanTestService.checkScheduleIsRemove(groupTestPlanId7);
|
||||||
|
//检查统计接口查询的是否正确
|
||||||
|
statisticsResponses = JSON.parseArray(
|
||||||
|
JSON.toJSONString(
|
||||||
|
JSON.parseObject(
|
||||||
|
this.requestPostAndReturn(URL_POST_TEST_PLAN_STATISTICS, List.of(groupTestPlanId7))
|
||||||
|
.getResponse().getContentAsString(), ResultHolder.class).getData()),
|
||||||
|
TestPlanStatisticsResponse.class);
|
||||||
|
Assertions.assertTrue(statisticsResponses.size() == 1);
|
||||||
|
Assertions.assertTrue(statisticsResponses.getFirst().getNextTriggerTime() == null);
|
||||||
|
Assertions.assertTrue(statisticsResponses.getFirst().getScheduleConfig() == null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
Loading…
Reference in New Issue