feat(接口测试): 接口场景的定时任务功能开发
This commit is contained in:
parent
0d85df85c7
commit
f546b29ade
|
@ -1,7 +1,9 @@
|
|||
excel.parse.error=Excel解析失败
|
||||
id.not_blank=ID不能为空
|
||||
permission.system_user.invite=邀请用户
|
||||
role.not.global.system=角色不是全局系统角色
|
||||
role.not.contains.member=角色不包含系统成员角色
|
||||
schedule.cron.error=Cron表达式错误
|
||||
user.not.login=未获取到登录用户
|
||||
user.not.empty=用户不呢为空
|
||||
user.not.exist=用户不存在
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
excel.parse.error=Excel parse error
|
||||
id.not_blank=Id must not be blank
|
||||
permission.system_user.invite=Invite user
|
||||
role.not.global.system=Role is not global system role
|
||||
role.not.contains.member=Role not contains member
|
||||
schedule.cron.error=Cron is error
|
||||
user.not.login=User not login
|
||||
user.not.exist=User not exist
|
||||
personal.no.permission=No permission
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
excel.parse.error=Excel解析失败
|
||||
id.not_blank=ID不能为空
|
||||
permission.system_user.invite=邀请用户
|
||||
role.not.global.system=角色不是全局系统角色
|
||||
role.not.contains.member=角色不包含系统成员角色
|
||||
schedule.cron.error=Cron表达式错误
|
||||
user.not.login=未获取到登录用户
|
||||
user.not.empty=用户不呢为空
|
||||
user.not.exist=用户不存在
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
excel.parse.error=Excel解析失敗
|
||||
id.not_blank=ID不能為空
|
||||
permission.system_user.invite=邀請用戶
|
||||
role.not.global.system=角色不是為全局系統角色
|
||||
role.not.contains.member=角色不包含系統成員角色
|
||||
schedule.cron.error=Cron表達式錯誤
|
||||
user.not.login=未獲取到登錄用戶
|
||||
user.not.empty=用戶不呢為空
|
||||
user.not.exist=用戶不存在
|
||||
|
|
|
@ -6,11 +6,14 @@ import io.metersphere.api.constants.ApiResource;
|
|||
import io.metersphere.api.domain.ApiScenario;
|
||||
import io.metersphere.api.dto.scenario.*;
|
||||
import io.metersphere.api.service.ApiValidateService;
|
||||
import io.metersphere.api.service.definition.ApiScenarioNoticeService;
|
||||
import io.metersphere.api.service.scenario.ApiScenarioLogService;
|
||||
import io.metersphere.api.service.scenario.ApiScenarioService;
|
||||
import io.metersphere.sdk.constants.PermissionConstants;
|
||||
import io.metersphere.system.log.annotation.Log;
|
||||
import io.metersphere.system.log.constants.OperationLogType;
|
||||
import io.metersphere.system.notice.annotation.SendNotice;
|
||||
import io.metersphere.system.notice.constants.NoticeConstants;
|
||||
import io.metersphere.system.security.CheckOwner;
|
||||
import io.metersphere.system.utils.PageUtils;
|
||||
import io.metersphere.system.utils.Pager;
|
||||
|
@ -159,4 +162,19 @@ public class ApiScenarioController {
|
|||
apiScenarioService.updatePriority(id, priority, SessionUtils.getUserId());
|
||||
}
|
||||
|
||||
@PostMapping(value = "/schedule-config")
|
||||
@Operation(summary = "接口测试-接口场景管理-定时任务配置")
|
||||
@RequiresPermissions(PermissionConstants.PROJECT_API_SCENARIO_EXECUTE)
|
||||
@Log(type = OperationLogType.UPDATE, expression = "#msClass.scheduleLog(#request.getScenarioId())", msClass = ApiScenarioLogService.class)
|
||||
@CheckOwner(resourceId = "#request.getScenarioId()", resourceType = "api_scenario")
|
||||
@SendNotice(taskType = NoticeConstants.TaskType.SCHEDULE_TASK, event = NoticeConstants.Event.UPDATE, target = "#targetClass.getScheduleNotice(#request)", targetClass = ApiScenarioNoticeService.class)
|
||||
public String scheduleConfig(@Validated @RequestBody ApiScenarioScheduleConfigRequest request) {
|
||||
/*
|
||||
TODO to Chen Jianxing:
|
||||
request.configMap 中需要补充场景的执行信息,比如环境、资源池、是否失败停止等配置。
|
||||
在触发定时任务的APIScenarioScheduleJob中会用到
|
||||
*/
|
||||
apiValidateService.validateApiMenuInProject(request.getScenarioId(), ApiResource.API_SCENARIO.name());
|
||||
return apiScenarioService.scheduleConfig(request, SessionUtils.getUserId());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
package io.metersphere.api.dto.scenario;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
public class ApiScenarioScheduleConfigRequest {
|
||||
|
||||
@NotBlank(message = "{api_scenario.id.not_blank}")
|
||||
@Schema(description = "场景ID")
|
||||
private String scenarioId;
|
||||
|
||||
@Schema(description = "启用/禁用")
|
||||
private boolean enable;
|
||||
|
||||
@Schema(description = "Cron表达式")
|
||||
@NotBlank
|
||||
private String cron;
|
||||
|
||||
@Schema(description = "定时任务配置 (如果配置不更改,不需要传入这个参数。 如果要清空配置,传入一个空数据{} )")
|
||||
Map<String, Object> configMap;
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package io.metersphere.api.job;
|
||||
|
||||
import io.metersphere.system.schedule.BaseScheduleJob;
|
||||
import org.quartz.JobExecutionContext;
|
||||
import org.quartz.JobKey;
|
||||
import org.quartz.TriggerKey;
|
||||
|
||||
public class ApiScenarioScheduleJob extends BaseScheduleJob {
|
||||
@Override
|
||||
protected void businessExecute(JobExecutionContext context) {
|
||||
/*
|
||||
TODO to Chen Jianxing:
|
||||
这里需要补充执行逻辑
|
||||
记得执行信息(环境、资源池、是否失败停止等配置)在jobDataMap里面
|
||||
*/
|
||||
}
|
||||
|
||||
public static JobKey getJobKey(String scenarioId) {
|
||||
return new JobKey(scenarioId, ApiScenarioScheduleJob.class.getName());
|
||||
}
|
||||
|
||||
public static TriggerKey getTriggerKey(String scenarioId) {
|
||||
return new TriggerKey(scenarioId, ApiScenarioScheduleJob.class.getName());
|
||||
}
|
||||
}
|
|
@ -3,9 +3,14 @@ package io.metersphere.api.service.definition;
|
|||
import io.metersphere.api.domain.ApiScenario;
|
||||
import io.metersphere.api.domain.ApiScenarioExample;
|
||||
import io.metersphere.api.dto.scenario.ApiScenarioBatchRequest;
|
||||
import io.metersphere.api.dto.scenario.ApiScenarioScheduleConfigRequest;
|
||||
import io.metersphere.api.job.ApiScenarioScheduleJob;
|
||||
import io.metersphere.api.mapper.ApiScenarioMapper;
|
||||
import io.metersphere.api.service.scenario.ApiScenarioService;
|
||||
import io.metersphere.sdk.util.SubListUtils;
|
||||
import io.metersphere.system.domain.Schedule;
|
||||
import io.metersphere.system.domain.ScheduleExample;
|
||||
import io.metersphere.system.mapper.ScheduleMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
@ -21,6 +26,14 @@ public class ApiScenarioNoticeService {
|
|||
@Resource
|
||||
private ApiScenarioMapper apiScenarioMapper;
|
||||
|
||||
@Resource
|
||||
private ScheduleMapper scheduleMapper;
|
||||
|
||||
public List<Schedule> getScheduleNotice(ApiScenarioScheduleConfigRequest request) {
|
||||
ScheduleExample example = new ScheduleExample();
|
||||
example.createCriteria().andResourceIdEqualTo(request.getScenarioId()).andJobEqualTo(ApiScenarioScheduleJob.class.getName());
|
||||
return scheduleMapper.selectByExample(example);
|
||||
}
|
||||
|
||||
public List<ApiScenario> getBatchOptionScenarios(ApiScenarioBatchRequest request) {
|
||||
List<String> ids = apiScenarioService.doSelectIds(request, false);
|
||||
|
|
|
@ -133,6 +133,20 @@ public class ApiScenarioLogService {
|
|||
return dto;
|
||||
}
|
||||
|
||||
public LogDTO scheduleLog(String id) {
|
||||
ApiScenario apiScenario = apiScenarioMapper.selectByPrimaryKey(id);
|
||||
Project project = projectMapper.selectByPrimaryKey(apiScenario.getProjectId());
|
||||
LogDTO dto = LogDTOBuilder.builder()
|
||||
.projectId(project.getId())
|
||||
.organizationId(project.getOrganizationId())
|
||||
.type(OperationLogType.UPDATE.name())
|
||||
.module(OperationLogModule.API_SCENARIO)
|
||||
.sourceId(apiScenario.getId())
|
||||
.content(Translator.get("api_automation_schedule") + ":" + apiScenario.getName())
|
||||
.build().getLogDTO();
|
||||
return dto;
|
||||
}
|
||||
|
||||
public LogDTO updateLog(String id) {
|
||||
ApiScenario apiScenario = apiScenarioMapper.selectByPrimaryKey(id);
|
||||
// todo 记录完整的场景信息
|
||||
|
|
|
@ -9,6 +9,7 @@ import io.metersphere.api.dto.debug.ApiResourceRunRequest;
|
|||
import io.metersphere.api.dto.request.MsScenario;
|
||||
import io.metersphere.api.dto.response.ApiScenarioBatchOperationResponse;
|
||||
import io.metersphere.api.dto.scenario.*;
|
||||
import io.metersphere.api.job.ApiScenarioScheduleJob;
|
||||
import io.metersphere.api.mapper.*;
|
||||
import io.metersphere.api.parser.step.StepParser;
|
||||
import io.metersphere.api.parser.step.StepParserFactory;
|
||||
|
@ -23,10 +24,7 @@ import io.metersphere.project.mapper.ExtBaseProjectVersionMapper;
|
|||
import io.metersphere.project.service.FileAssociationService;
|
||||
import io.metersphere.project.service.FileMetadataService;
|
||||
import io.metersphere.project.service.ProjectService;
|
||||
import io.metersphere.sdk.constants.ApiExecuteRunMode;
|
||||
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.Environment;
|
||||
import io.metersphere.sdk.domain.EnvironmentExample;
|
||||
import io.metersphere.sdk.domain.EnvironmentGroup;
|
||||
|
@ -40,8 +38,10 @@ import io.metersphere.sdk.mapper.EnvironmentGroupMapper;
|
|||
import io.metersphere.sdk.mapper.EnvironmentMapper;
|
||||
import io.metersphere.sdk.util.*;
|
||||
import io.metersphere.system.dto.LogInsertModule;
|
||||
import io.metersphere.system.dto.request.ScheduleConfig;
|
||||
import io.metersphere.system.log.constants.OperationLogModule;
|
||||
import io.metersphere.system.log.constants.OperationLogType;
|
||||
import io.metersphere.system.schedule.ScheduleService;
|
||||
import io.metersphere.system.service.UserLoginService;
|
||||
import io.metersphere.system.uid.IDGenerator;
|
||||
import io.metersphere.system.uid.NumGenerator;
|
||||
|
@ -121,6 +121,9 @@ public class ApiScenarioService {
|
|||
private ApiScenarioCsvMapper apiScenarioCsvMapper;
|
||||
@Resource
|
||||
private ApiScenarioCsvStepMapper apiScenarioCsvStepMapper;
|
||||
@Resource
|
||||
private ScheduleService scheduleService;
|
||||
|
||||
public static final String PRIORITY = "Priority";
|
||||
public static final String STATUS = "Status";
|
||||
public static final String TAGS = "Tags";
|
||||
|
@ -836,6 +839,9 @@ public class ApiScenarioService {
|
|||
apiFileResourceService.deleteByResourceId(scenarioDir, scenario.getId(), scenario.getProjectId(), operator, OperationLogModule.API_DEBUG);
|
||||
}catch (Exception ignore){}
|
||||
|
||||
//删除定时任务
|
||||
scheduleService.deleteByResourceId(scenario.getId(), ApiScenarioScheduleJob.class.getName());
|
||||
|
||||
//todo wang xiao gang: 删除csv相关东西
|
||||
}
|
||||
|
||||
|
@ -858,13 +864,14 @@ public class ApiScenarioService {
|
|||
blobExample.createCriteria().andScenarioIdIn(scenarioIdList);
|
||||
apiScenarioStepBlobMapper.deleteByExample(blobExample);
|
||||
|
||||
//删除文件
|
||||
scenarioList.forEach(scenario -> {
|
||||
//删除文件
|
||||
String scenarioDir = DefaultRepositoryDir.getApiDebugDir(scenario.getProjectId(), scenario.getId());
|
||||
try {
|
||||
apiFileResourceService.deleteByResourceId(scenarioDir, scenario.getId(), scenario.getProjectId(), operator, OperationLogModule.API_DEBUG);
|
||||
}catch (Exception ignore){}
|
||||
|
||||
//删除定时任务
|
||||
scheduleService.deleteByResourceId(scenario.getId(), ApiScenarioScheduleJob.class.getName());
|
||||
});
|
||||
|
||||
//todo wang xiao gang: 删除csv相关东西
|
||||
|
@ -884,6 +891,9 @@ public class ApiScenarioService {
|
|||
apiScenario.setId(id);
|
||||
apiScenario.setDeleted(true);
|
||||
apiScenarioMapper.updateByPrimaryKeySelective(apiScenario);
|
||||
|
||||
//删除定时任务
|
||||
scheduleService.deleteByResourceId(id, ApiScenarioScheduleJob.class.getName());
|
||||
}
|
||||
|
||||
private void checkAddExist(ApiScenarioAddRequest apiScenario) {
|
||||
|
@ -1574,6 +1584,9 @@ public class ApiScenarioService {
|
|||
|
||||
for (ApiScenario scenario : apiScenarioList) {
|
||||
response.addSuccessData(scenario.getId(), scenario.getNum(), scenario.getName());
|
||||
|
||||
//删除定时任务
|
||||
scheduleService.deleteByResourceId(scenario.getId(), ApiScenarioScheduleJob.class.getName());
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
@ -1592,4 +1605,24 @@ public class ApiScenarioService {
|
|||
}
|
||||
|
||||
|
||||
public String scheduleConfig(ApiScenarioScheduleConfigRequest scheduleRequest, String operator) {
|
||||
ApiScenario apiScenario = apiScenarioMapper.selectByPrimaryKey(scheduleRequest.getScenarioId());
|
||||
ScheduleConfig scheduleConfig = ScheduleConfig.builder()
|
||||
.resourceId(apiScenario.getId())
|
||||
.key(apiScenario.getId())
|
||||
.projectId(apiScenario.getProjectId())
|
||||
.name(apiScenario.getName())
|
||||
.enable(scheduleRequest.isEnable())
|
||||
.cron(scheduleRequest.getCron())
|
||||
.resourceType(ScheduleResourceType.API_SCENARIO.name())
|
||||
.configMap(scheduleRequest.getConfigMap())
|
||||
.build();
|
||||
|
||||
return scheduleService.scheduleConfig(
|
||||
scheduleConfig,
|
||||
ApiScenarioScheduleJob.getJobKey(apiScenario.getId()),
|
||||
ApiScenarioScheduleJob.getTriggerKey(apiScenario.getId()),
|
||||
ApiScenarioScheduleJob.class,
|
||||
operator);
|
||||
}
|
||||
}
|
|
@ -98,6 +98,8 @@ public class ApiScenarioControllerTests extends BaseTest {
|
|||
@Resource
|
||||
private ApiScenarioStepBlobMapper apiScenarioStepBlobMapper;
|
||||
@Resource
|
||||
private ApiFileResourceMapper apiFileResourceMapper;
|
||||
@Resource
|
||||
private ApiScenarioBlobMapper apiScenarioBlobMapper;
|
||||
@Resource
|
||||
private ExtBaseProjectVersionMapper extBaseProjectVersionMapper;
|
||||
|
@ -1122,7 +1124,7 @@ public class ApiScenarioControllerTests extends BaseTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Order(21)
|
||||
@Order(22)
|
||||
void batchMove() throws Exception {
|
||||
String testUrl = "/batch-operation/move";
|
||||
if (CollectionUtils.isEmpty(BATCH_OPERATION_SCENARIO_ID)) {
|
||||
|
@ -1252,14 +1254,155 @@ public class ApiScenarioControllerTests extends BaseTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Order(22)
|
||||
@Order(23)
|
||||
void scheduleTest() throws Exception {
|
||||
String testUrl = "/schedule-config";
|
||||
|
||||
if (CollectionUtils.isEmpty(BATCH_OPERATION_SCENARIO_ID)) {
|
||||
this.batchCreateScenarios();
|
||||
}
|
||||
|
||||
//使用最后一个场景ID用于做定时任务的测试
|
||||
String scenarioId = BATCH_OPERATION_SCENARIO_ID.getLast();
|
||||
ApiScenarioScheduleConfigRequest request = new ApiScenarioScheduleConfigRequest();
|
||||
|
||||
request.setScenarioId(scenarioId);
|
||||
request.setEnable(true);
|
||||
request.setCron("0 0 0 * * ?");
|
||||
|
||||
//先测试一下没有开启模块时接口能否使用
|
||||
apiScenarioBatchOperationTestService.removeApiModule(DEFAULT_PROJECT_ID);
|
||||
this.requestPost(testUrl, request).andExpect(status().is5xxServerError());
|
||||
//恢复
|
||||
apiScenarioBatchOperationTestService.resetProjectModule(DEFAULT_PROJECT_ID);
|
||||
MvcResult result = this.requestPostAndReturn(testUrl, request);
|
||||
ResultHolder resultHolder = JSON.parseObject(result.getResponse().getContentAsString(StandardCharsets.UTF_8), ResultHolder.class);
|
||||
String scheduleId = resultHolder.getData().toString();
|
||||
apiScenarioBatchOperationTestService.checkSchedule(scheduleId, scenarioId, request.isEnable());
|
||||
|
||||
//增加日志检查
|
||||
LOG_CHECK_LIST.add(
|
||||
new CheckLogModel(scenarioId, OperationLogType.UPDATE, "/api/scenario/schedule-config")
|
||||
);
|
||||
|
||||
//关闭
|
||||
request.setEnable(false);
|
||||
result = this.requestPostAndReturn(testUrl, request);
|
||||
resultHolder = JSON.parseObject(result.getResponse().getContentAsString(StandardCharsets.UTF_8), ResultHolder.class);
|
||||
String newScheduleId = resultHolder.getData().toString();
|
||||
//检查两个scheduleId是否相同
|
||||
Assertions.assertEquals(scheduleId, newScheduleId);
|
||||
apiScenarioBatchOperationTestService.checkSchedule(newScheduleId, scenarioId, request.isEnable());
|
||||
|
||||
//配置configMap
|
||||
request.setEnable(true);
|
||||
request.setConfigMap(new HashMap<>() {{
|
||||
this.put("envId", "testEnv");
|
||||
this.put("resourcePoolId", "testResourcePool");
|
||||
}});
|
||||
result = this.requestPostAndReturn(testUrl, request);
|
||||
resultHolder = JSON.parseObject(result.getResponse().getContentAsString(StandardCharsets.UTF_8), ResultHolder.class);
|
||||
newScheduleId = resultHolder.getData().toString();
|
||||
apiScenarioBatchOperationTestService.checkSchedule(newScheduleId, scenarioId, request.isEnable());
|
||||
|
||||
//清空configMap
|
||||
request.setConfigMap(new HashMap<>());
|
||||
request.setEnable(false);
|
||||
result = this.requestPostAndReturn(testUrl, request);
|
||||
resultHolder = JSON.parseObject(result.getResponse().getContentAsString(StandardCharsets.UTF_8), ResultHolder.class);
|
||||
newScheduleId = resultHolder.getData().toString();
|
||||
apiScenarioBatchOperationTestService.checkSchedule(newScheduleId, scenarioId, request.isEnable());
|
||||
|
||||
//测试各种corn表达式用于校验正则的准确性
|
||||
String[] cornStrArr = new String[]{
|
||||
"0 0 12 * * ?", //每天中午12点触发
|
||||
"0 15 10 ? * *", //每天上午10:15触发
|
||||
"0 15 10 * * ?", //每天上午10:15触发
|
||||
"0 15 10 * * ? *",//每天上午10:15触发
|
||||
"0 15 10 * * ? 2048",//2008年的每天上午10:15触发
|
||||
"0 * 10 * * ?",//每天上午10:00至10:59期间的每1分钟触发
|
||||
"0 0/5 10 * * ?",//每天上午10:00至10:55期间的每5分钟触发
|
||||
"0 0/5 10,16 * * ?",//每天上午10:00至10:55期间和下午4:00至4:55期间的每5分钟触发
|
||||
"0 0-5 10 * * ?",//每天上午10:00至10:05期间的每1分钟触发
|
||||
"0 10,14,18 15 ? 3 WED",//每年三月的星期三的下午2:10和2:18触发
|
||||
"0 10 15 ? * MON-FRI",//每个周一、周二、周三、周四、周五的下午3:10触发
|
||||
"0 15 10 15 * ?",//每月15日上午10:15触发
|
||||
"0 15 10 L * ?", //每月最后一日的上午10:15触发
|
||||
"0 15 10 ? * 6L", //每月的最后一个星期五上午10:15触发
|
||||
"0 15 10 ? * 6L 2024-2026", //从2024年至2026年每月的最后一个星期五上午10:15触发
|
||||
"0 15 10 ? * 6#3", //每月的第三个星期五上午10:15触发
|
||||
};
|
||||
|
||||
//每种corn表达式开启、关闭都测试一遍,检查是否能正常开关定时任务
|
||||
for (String corn : cornStrArr) {
|
||||
request = new ApiScenarioScheduleConfigRequest();
|
||||
request.setScenarioId(scenarioId);
|
||||
request.setEnable(true);
|
||||
request.setCron(corn);
|
||||
result = this.requestPostAndReturn(testUrl, request);
|
||||
resultHolder = JSON.parseObject(result.getResponse().getContentAsString(StandardCharsets.UTF_8), ResultHolder.class);
|
||||
scheduleId = resultHolder.getData().toString();
|
||||
apiScenarioBatchOperationTestService.checkSchedule(scheduleId, scenarioId, request.isEnable());
|
||||
|
||||
request = new ApiScenarioScheduleConfigRequest();
|
||||
request.setScenarioId(scenarioId);
|
||||
request.setEnable(false);
|
||||
request.setCron(corn);
|
||||
result = this.requestPostAndReturn(testUrl, request);
|
||||
resultHolder = JSON.parseObject(result.getResponse().getContentAsString(StandardCharsets.UTF_8), ResultHolder.class);
|
||||
scheduleId = resultHolder.getData().toString();
|
||||
apiScenarioBatchOperationTestService.checkSchedule(scheduleId, scenarioId, request.isEnable());
|
||||
}
|
||||
|
||||
|
||||
//校验权限
|
||||
this.requestPostPermissionTest(PermissionConstants.PROJECT_API_SCENARIO_EXECUTE, testUrl, request);
|
||||
|
||||
//反例:scenarioId不存在
|
||||
request = new ApiScenarioScheduleConfigRequest();
|
||||
request.setCron("0 0 0 * * ?");
|
||||
this.requestPost(testUrl, request).andExpect(status().isBadRequest());
|
||||
request.setScenarioId(IDGenerator.nextStr());
|
||||
this.requestPost(testUrl, request).andExpect(status().is5xxServerError());
|
||||
|
||||
//反例:不配置cron表达式
|
||||
request = new ApiScenarioScheduleConfigRequest();
|
||||
request.setScenarioId(scenarioId);
|
||||
this.requestPost(testUrl, request).andExpect(status().isBadRequest());
|
||||
|
||||
//反例:配置错误的cron表达式,测试是否会关闭定时任务
|
||||
request = new ApiScenarioScheduleConfigRequest();
|
||||
request.setScenarioId(scenarioId);
|
||||
request.setEnable(true);
|
||||
request.setCron(IDGenerator.nextStr());
|
||||
this.requestPost(testUrl, request).andExpect(status().is5xxServerError());
|
||||
|
||||
}
|
||||
|
||||
//30开始是关于删除和恢复的
|
||||
@Test
|
||||
@Order(31)
|
||||
void batchRemoveToGc() throws Exception {
|
||||
String testUrl = "/batch-operation/delete-gc";
|
||||
if (CollectionUtils.isEmpty(BATCH_OPERATION_SCENARIO_ID)) {
|
||||
this.batchCopy();
|
||||
this.scheduleTest();
|
||||
}
|
||||
|
||||
//使用最后一个场景ID用于做定时任务的测试
|
||||
String scenarioId = BATCH_OPERATION_SCENARIO_ID.getLast();
|
||||
|
||||
//本次测试涉及到的场景ID
|
||||
List<String> operationScenarioIds = new ArrayList<>(BATCH_OPERATION_SCENARIO_ID.subList(200, 500));
|
||||
//给最后一个场景创建定时任务,测试batchToGc时,是否会删除定时任务
|
||||
ApiScenarioScheduleConfigRequest scheduleRequest = new ApiScenarioScheduleConfigRequest();
|
||||
scheduleRequest.setScenarioId(scenarioId);
|
||||
scheduleRequest.setEnable(true);
|
||||
scheduleRequest.setCron("0 0 0 * * ?");
|
||||
MvcResult scheduleResult = this.requestPostAndReturn("/schedule-config", scheduleRequest);
|
||||
ResultHolder scheduleResultHolder = JSON.parseObject(scheduleResult.getResponse().getContentAsString(StandardCharsets.UTF_8), ResultHolder.class);
|
||||
String scheduleId = scheduleResultHolder.getData().toString();
|
||||
apiScenarioBatchOperationTestService.checkSchedule(scheduleId, scenarioId, scheduleRequest.isEnable());
|
||||
|
||||
|
||||
/*
|
||||
正例测试
|
||||
|
@ -1283,7 +1426,8 @@ public class ApiScenarioControllerTests extends BaseTest {
|
|||
ApiScenarioBatchOperationResponse resultResponse = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), ApiScenarioBatchOperationResponse.class);
|
||||
//检查返回值
|
||||
Assertions.assertEquals(resultResponse.getSuccess(), 300);
|
||||
|
||||
//检查定时任务是否删除
|
||||
apiScenarioBatchOperationTestService.checkScheduleIsRemove(scenarioId);
|
||||
//数据库级别的检查
|
||||
apiScenarioBatchOperationTestService.checkBatchGCOperation
|
||||
(BATCH_OPERATION_SCENARIO_ID.subList(200, 500), true);
|
||||
|
@ -1320,7 +1464,7 @@ public class ApiScenarioControllerTests extends BaseTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Order(23)
|
||||
@Order(32)
|
||||
//todo
|
||||
void batchRecoverToGc() throws Exception {
|
||||
String testUrl = "/batch-operation/recover-gc";
|
||||
|
@ -1393,7 +1537,7 @@ public class ApiScenarioControllerTests extends BaseTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@Order(24)
|
||||
@Order(33)
|
||||
//todo
|
||||
void batchDelete() throws Exception {
|
||||
String testUrl = "/batch-operation/delete";
|
||||
|
@ -1467,9 +1611,8 @@ public class ApiScenarioControllerTests extends BaseTest {
|
|||
|
||||
}
|
||||
|
||||
//30开始是关于删除和恢复的
|
||||
@Test
|
||||
@Order(30)
|
||||
@Order(34)
|
||||
void recover() throws Exception {
|
||||
if (CollectionUtils.isEmpty(BATCH_OPERATION_SCENARIO_ID)) {
|
||||
this.batchCreateScenarios();
|
||||
|
@ -1651,6 +1794,7 @@ public class ApiScenarioControllerTests extends BaseTest {
|
|||
apiFileResource.setResourceType("API_SCENARIO");
|
||||
apiFileResource.setCreateTime(System.currentTimeMillis());
|
||||
apiFileResource.setProjectId(apiScenario.getProjectId());
|
||||
apiFileResourceMapper.insertSelective(apiFileResource);
|
||||
}
|
||||
apiScenarioMapper.insertSelective(apiScenario);
|
||||
BATCH_OPERATION_SCENARIO_ID.add(apiScenario.getId());
|
||||
|
|
|
@ -2,13 +2,16 @@ package io.metersphere.api.service;
|
|||
|
||||
import io.metersphere.api.domain.*;
|
||||
import io.metersphere.api.dto.scenario.ApiScenarioBatchCopyMoveRequest;
|
||||
import io.metersphere.api.job.ApiScenarioScheduleJob;
|
||||
import io.metersphere.api.mapper.*;
|
||||
import io.metersphere.project.domain.Project;
|
||||
import io.metersphere.project.mapper.ProjectMapper;
|
||||
import io.metersphere.sdk.util.JSON;
|
||||
import io.metersphere.system.mapper.ExtScheduleMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.quartz.Scheduler;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
|
@ -30,6 +33,11 @@ public class ApiScenarioBatchOperationTestService {
|
|||
private ApiScenarioStepBlobMapper apiScenarioStepBlobMapper;
|
||||
@Resource
|
||||
private ApiFileResourceMapper apiFileResourceMapper;
|
||||
@Resource
|
||||
private ExtScheduleMapper extScheduleMapper;
|
||||
|
||||
@Resource
|
||||
private Scheduler scheduler;
|
||||
|
||||
@Resource
|
||||
private ProjectMapper projectMapper;
|
||||
|
@ -189,4 +197,20 @@ public class ApiScenarioBatchOperationTestService {
|
|||
sourceFileExample.createCriteria().andResourceIdIn(deleteScenarioIds);
|
||||
Assertions.assertEquals(apiFileResourceMapper.countByExample(sourceFileExample), 0);
|
||||
}
|
||||
|
||||
/*
|
||||
校验定时任务是否成功开启:
|
||||
1.schedule表中存在数据,且开启状态符合isEnable
|
||||
2.开启状态下: qrtz_triggers 和 qrtz_cron_triggers 表存在对应的数据
|
||||
3.关闭状态下: qrtz_triggers 和 qrtz_cron_triggers 表不存在对应的数据
|
||||
*/
|
||||
public void checkSchedule(String scheduleId, String resourceId, boolean isEnable) throws Exception {
|
||||
Assertions.assertEquals(extScheduleMapper.countByIdAndEnable(scheduleId, isEnable), 1L);
|
||||
Assertions.assertEquals(scheduler.checkExists(ApiScenarioScheduleJob.getJobKey(resourceId)), isEnable);
|
||||
}
|
||||
|
||||
public void checkScheduleIsRemove(String resourceId) throws Exception {
|
||||
Assertions.assertEquals(extScheduleMapper.countByResourceId(resourceId), 0L);
|
||||
Assertions.assertEquals(scheduler.checkExists(ApiScenarioScheduleJob.getJobKey(resourceId)), false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ public class EnvironmentService {
|
|||
private static final String USERNAME = "user";
|
||||
private static final String PASSWORD = "password";
|
||||
private static final String PATH = "/project/environment/import";
|
||||
private static final String MOCK_EVN_SOCKET = "/api/mock/";
|
||||
private static final String MOCK_EVN_SOCKET = "/mock-server/";
|
||||
|
||||
public List<OptionDTO> getDriverOptions(String organizationId) {
|
||||
return jdbcDriverPluginService.getJdbcDriverOption(organizationId);
|
||||
|
|
|
@ -19,7 +19,8 @@ import org.springframework.web.bind.annotation.*;
|
|||
@RestController
|
||||
@Tag(name = "个人中心")
|
||||
@RequestMapping("/personal")
|
||||
public class PersonalCenterController {
|
||||
public class
|
||||
PersonalCenterController {
|
||||
|
||||
@Resource
|
||||
private UserService userService;
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
package io.metersphere.system.dto.request;
|
||||
|
||||
import io.metersphere.sdk.constants.ScheduleType;
|
||||
import io.metersphere.sdk.util.JSON;
|
||||
import io.metersphere.system.domain.Schedule;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
public class ScheduleConfig {
|
||||
|
||||
private String resourceId;
|
||||
|
||||
private String key;
|
||||
|
||||
private String projectId;
|
||||
|
||||
private String name;
|
||||
|
||||
private Boolean enable;
|
||||
|
||||
private String cron;
|
||||
|
||||
private String resourceType;
|
||||
|
||||
Map<String, Object> configMap;
|
||||
|
||||
public Schedule genCronSchedule(Schedule schedule) {
|
||||
if (schedule == null) {
|
||||
schedule = new Schedule();
|
||||
}
|
||||
schedule.setName(this.getName());
|
||||
schedule.setResourceId(this.getResourceId());
|
||||
schedule.setType(ScheduleType.CRON.name());
|
||||
schedule.setKey(this.getKey());
|
||||
schedule.setEnable(this.getEnable());
|
||||
schedule.setProjectId(this.getProjectId());
|
||||
schedule.setValue(this.getCron());
|
||||
schedule.setResourceType(this.getResourceType());
|
||||
//配置数据为null,意味着不更改; 如果要清空配置,需要传入空对象
|
||||
if (configMap != null) {
|
||||
schedule.setConfig(JSON.toJSONString(configMap));
|
||||
}
|
||||
return schedule;
|
||||
}
|
||||
}
|
|
@ -22,4 +22,11 @@ public interface ExtScheduleMapper {
|
|||
|
||||
List<ApiScenario> getApiScenarioListByIds(@Param("ids") List<String> ids);
|
||||
|
||||
long countByResourceId(String resourceId);
|
||||
|
||||
long countByIdAndEnable(@Param("id") String scheduleId, @Param("enable") boolean isEnable);
|
||||
|
||||
long countQuartzTriggersByResourceId(String scheduleId);
|
||||
|
||||
long countQuartzCronTriggersByResourceId(String scheduleId);
|
||||
}
|
||||
|
|
|
@ -70,6 +70,28 @@
|
|||
#{id}
|
||||
</foreach>
|
||||
</select>
|
||||
<select id="countByIdAndEnable" resultType="java.lang.Long">
|
||||
select count(*)
|
||||
from schedule
|
||||
where id = #{id}
|
||||
and enable = #{enable}
|
||||
</select>
|
||||
<select id="countQuartzTriggersByResourceId" resultType="java.lang.Long">
|
||||
SELECT *
|
||||
FROM QRTZ_TRIGGERS
|
||||
WHERE TRIGGER_NAME = #{0}
|
||||
|
||||
</select>
|
||||
<select id="countQuartzCronTriggersByResourceId" resultType="java.lang.Long">
|
||||
SELECT *
|
||||
FROM QRTZ_CRON_TRIGGERS
|
||||
WHERE TRIGGER_NAME = #{0}
|
||||
</select>
|
||||
<select id="countByResourceId" resultType="java.lang.Long">
|
||||
SELECT count(*)
|
||||
FROM schedule
|
||||
WHERE resource_id = #{0}
|
||||
</select>
|
||||
|
||||
|
||||
</mapper>
|
|
@ -1,20 +1,25 @@
|
|||
package io.metersphere.system.schedule;
|
||||
|
||||
import io.metersphere.sdk.exception.MSException;
|
||||
import io.metersphere.sdk.util.JSON;
|
||||
import io.metersphere.system.domain.Schedule;
|
||||
import io.metersphere.system.domain.ScheduleExample;
|
||||
import io.metersphere.system.dto.request.ScheduleConfig;
|
||||
import io.metersphere.system.mapper.ScheduleMapper;
|
||||
import io.metersphere.system.uid.IDGenerator;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.quartz.JobDataMap;
|
||||
import org.quartz.JobKey;
|
||||
import org.quartz.SchedulerException;
|
||||
import org.quartz.TriggerKey;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public class ScheduleService {
|
||||
|
@ -100,4 +105,44 @@ public class ScheduleService {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String scheduleConfig(ScheduleConfig scheduleConfig, JobKey jobKey, TriggerKey triggerKey, Class clazz, String operator) {
|
||||
Schedule schedule;
|
||||
ScheduleExample example = new ScheduleExample();
|
||||
example.createCriteria().andResourceIdEqualTo(scheduleConfig.getResourceId()).andJobEqualTo(clazz.getName());
|
||||
List<Schedule> scheduleList = scheduleMapper.selectByExample(example);
|
||||
if (CollectionUtils.isNotEmpty(scheduleList)) {
|
||||
schedule = scheduleConfig.genCronSchedule(scheduleList.getFirst());
|
||||
schedule.setUpdateTime(System.currentTimeMillis());
|
||||
schedule.setJob(clazz.getName());
|
||||
scheduleMapper.updateByExampleSelective(schedule, example);
|
||||
} else {
|
||||
schedule = scheduleConfig.genCronSchedule(null);
|
||||
schedule.setJob(clazz.getName());
|
||||
schedule.setId(IDGenerator.nextStr());
|
||||
schedule.setCreateUser(operator);
|
||||
schedule.setCreateTime(System.currentTimeMillis());
|
||||
schedule.setUpdateTime(System.currentTimeMillis());
|
||||
scheduleMapper.insert(schedule);
|
||||
}
|
||||
|
||||
JobDataMap jobDataMap = scheduleManager.getDefaultJobDataMap(schedule, scheduleConfig.getCron(), operator);
|
||||
if (StringUtils.isNotEmpty(schedule.getConfig())) {
|
||||
Map<String, Object> configMap = JSON.parseObject(schedule.getConfig(), Map.class);
|
||||
jobDataMap.putAll(configMap);
|
||||
}
|
||||
|
||||
/*
|
||||
scheduleManager.modifyCronJobTime方法如同它的方法名所说,只能修改定时任务的触发时间。
|
||||
如果定时任务的配置数据jobData发生了变化,上面方法是无法更新配置数据的。
|
||||
所以,如果配置数据发生了变化,做法就是先删除运行中的定时任务,再重新添加定时任务。
|
||||
|
||||
以上的更新逻辑配合 enable 开关,可以简化为下列写法:
|
||||
*/
|
||||
scheduleManager.removeJob(jobKey, triggerKey);
|
||||
if (BooleanUtils.isTrue(schedule.getEnable())) {
|
||||
scheduleManager.addCronJob(jobKey, triggerKey, clazz, scheduleConfig.getCron(), jobDataMap);
|
||||
}
|
||||
return schedule.getId();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,8 @@
|
|||
"id": "SYSTEM_USER:READ+UPDATE"
|
||||
},
|
||||
{
|
||||
"id": "SYSTEM_USER:READ+INVITE"
|
||||
"id": "SYSTEM_USER:READ+INVITE",
|
||||
"name": "permission.system_user.invite"
|
||||
},
|
||||
{
|
||||
"id": "SYSTEM_USER:READ+DELETE"
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
package io.metersphere.system.controller;
|
||||
|
||||
import io.metersphere.sdk.constants.ScheduleResourceType;
|
||||
import io.metersphere.sdk.constants.ScheduleType;
|
||||
import io.metersphere.system.base.BaseTest;
|
||||
import io.metersphere.system.domain.Schedule;
|
||||
import io.metersphere.system.dto.request.ScheduleConfig;
|
||||
import io.metersphere.system.job.ApiScenarioScheduleJob;
|
||||
import io.metersphere.system.schedule.ScheduleService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.junit.jupiter.api.MethodOrderer;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestMethodOrder;
|
||||
import org.quartz.JobKey;
|
||||
import org.quartz.TriggerKey;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
@AutoConfigureMockMvc
|
||||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||
public class ScheduleControllerTests extends BaseTest {
|
||||
|
||||
@Resource
|
||||
private ScheduleService scheduleService;
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
Schedule schedule = new Schedule();
|
||||
schedule.setName("test-schedule");
|
||||
schedule.setResourceId("test-resource-id");
|
||||
schedule.setEnable(true);
|
||||
schedule.setValue("0 0/1 * * * ?");
|
||||
schedule.setKey("test-resource-id");
|
||||
schedule.setCreateUser("admin");
|
||||
schedule.setProjectId(DEFAULT_PROJECT_ID);
|
||||
schedule.setConfig("{}");
|
||||
schedule.setJob(ApiScenarioScheduleJob.class.getName());
|
||||
schedule.setType(ScheduleType.CRON.name());
|
||||
schedule.setResourceType(ScheduleResourceType.API_IMPORT.name());
|
||||
|
||||
scheduleService.addSchedule(schedule);
|
||||
scheduleService.getSchedule(schedule.getId());
|
||||
scheduleService.editSchedule(schedule);
|
||||
scheduleService.getScheduleByResource(schedule.getResourceId(), schedule.getJob());
|
||||
scheduleService.deleteByResourceId(schedule.getResourceId(), schedule.getJob());
|
||||
schedule = new Schedule();
|
||||
schedule.setName("test-schedule-1");
|
||||
schedule.setResourceId("test-resource-id-1");
|
||||
schedule.setEnable(true);
|
||||
schedule.setValue("0 0/1 * * * ?");
|
||||
schedule.setKey("test-resource-id-1");
|
||||
schedule.setCreateUser("admin");
|
||||
schedule.setProjectId(DEFAULT_PROJECT_ID);
|
||||
schedule.setConfig("{}");
|
||||
schedule.setJob(ApiScenarioScheduleJob.class.getName());
|
||||
schedule.setType(ScheduleType.CRON.name());
|
||||
schedule.setResourceType(ScheduleResourceType.API_SCENARIO.name());
|
||||
scheduleService.addSchedule(schedule);
|
||||
scheduleService.deleteByResourceIds(List.of(schedule.getResourceId()), schedule.getJob());
|
||||
schedule = new Schedule();
|
||||
schedule.setName("test-schedule-2");
|
||||
schedule.setResourceId("test-resource-id-2");
|
||||
schedule.setEnable(true);
|
||||
schedule.setValue("0 0/1 * * * ?");
|
||||
schedule.setKey("test-resource-id-2");
|
||||
schedule.setCreateUser("admin");
|
||||
schedule.setProjectId(DEFAULT_PROJECT_ID);
|
||||
schedule.setConfig("{}");
|
||||
schedule.setJob("test-job");
|
||||
schedule.setType(ScheduleType.CRON.name());
|
||||
schedule.setResourceType(ScheduleResourceType.API_SCENARIO.name());
|
||||
scheduleService.addSchedule(schedule);
|
||||
scheduleService.addOrUpdateCronJob(schedule,
|
||||
new JobKey(schedule.getResourceId(), ApiScenarioScheduleJob.class.getName()),
|
||||
new TriggerKey(schedule.getResourceId(), ApiScenarioScheduleJob.class.getName()),
|
||||
ApiScenarioScheduleJob.class);
|
||||
scheduleService.deleteByProjectId(schedule.getProjectId());
|
||||
|
||||
ScheduleConfig scheduleConfig = ScheduleConfig.builder()
|
||||
.resourceId("test-resource-id-3")
|
||||
.key("test-resource-id-3")
|
||||
.projectId(DEFAULT_PROJECT_ID)
|
||||
.name("test-schedule-3")
|
||||
.enable(true)
|
||||
.cron("0 0/1 * * * ?")
|
||||
.resourceType(ScheduleResourceType.API_SCENARIO.name())
|
||||
.configMap(new HashMap<>() {{
|
||||
this.put("envId", "testEnv");
|
||||
this.put("resourcePoolId", "testResourcePool");
|
||||
}})
|
||||
.build();
|
||||
|
||||
scheduleService.scheduleConfig(
|
||||
scheduleConfig,
|
||||
new JobKey(scheduleConfig.getResourceId(), ApiScenarioScheduleJob.class.getName()),
|
||||
new TriggerKey(scheduleConfig.getResourceId(), ApiScenarioScheduleJob.class.getName()),
|
||||
ApiScenarioScheduleJob.class,
|
||||
"admin");
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package io.metersphere.system.job;
|
||||
|
||||
import io.metersphere.system.schedule.BaseScheduleJob;
|
||||
import org.quartz.JobExecutionContext;
|
||||
import org.quartz.JobKey;
|
||||
import org.quartz.TriggerKey;
|
||||
|
||||
public class ApiScenarioScheduleJob extends BaseScheduleJob {
|
||||
@Override
|
||||
protected void businessExecute(JobExecutionContext context) {
|
||||
/*
|
||||
TODO to Chen Jianxing:
|
||||
这里需要补充执行逻辑
|
||||
记得执行信息(环境、资源池、是否失败停止等配置)在jobDataMap里面
|
||||
*/
|
||||
}
|
||||
|
||||
public static JobKey getJobKey(String scenarioId) {
|
||||
return new JobKey(scenarioId, ApiScenarioScheduleJob.class.getName());
|
||||
}
|
||||
|
||||
public static TriggerKey getTriggerKey(String scenarioId) {
|
||||
return new TriggerKey(scenarioId, ApiScenarioScheduleJob.class.getName());
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@ import io.metersphere.sdk.constants.ModuleConstants;
|
|||
import io.metersphere.sdk.constants.TestPlanConstants;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
@ -58,6 +59,7 @@ public class TestPlanCreateRequest {
|
|||
|
||||
@Schema(description = "测试计划通过阈值;0-100", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@Max(value = 100, message = "{test_plan.pass_threshold.max}")
|
||||
@Min(value = 0)
|
||||
private double passThreshold = 100;
|
||||
@Schema(description = "测试计划类型")
|
||||
private String type = TestPlanConstants.TEST_PLAN_TYPE_PLAN;
|
||||
|
|
|
@ -619,7 +619,7 @@ public class TestPlanTests extends BaseTest {
|
|||
3.group_id
|
||||
3.1 group_id不存在
|
||||
3.2 group_id对应的测试计划type不是group
|
||||
4.参数校验:passThreshold大于100
|
||||
4.参数校验:passThreshold大于100 、 小于0
|
||||
5.重名校验
|
||||
*/
|
||||
request.setName(null);
|
||||
|
@ -633,6 +633,8 @@ public class TestPlanTests extends BaseTest {
|
|||
request.setGroupId(TestPlanConstants.TEST_PLAN_DEFAULT_GROUP_ID);
|
||||
request.setPassThreshold(100.111);
|
||||
this.requestPost(URL_POST_TEST_PLAN_ADD, request).andExpect(status().isBadRequest());
|
||||
request.setPassThreshold(-0.12);
|
||||
this.requestPost(URL_POST_TEST_PLAN_ADD, request).andExpect(status().isBadRequest());
|
||||
|
||||
//测试权限
|
||||
request.setProjectId(DEFAULT_PROJECT_ID);
|
||||
|
|
Loading…
Reference in New Issue