Commit d5fb438f59bf608ada5bc26b933e76a38fdaa64c

Authored by 芋道源码
Committed by Gitee
2 parents 97e842a5 a7a2725a

!20 Job 模块单元测试

Merge pull request !20 from neilz/feature/ut-job
sql/quartz.sql
@@ -178,3 +178,11 @@ CREATE INDEX IDX_QRTZ_FT_T_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIG @@ -178,3 +178,11 @@ CREATE INDEX IDX_QRTZ_FT_T_G ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_NAME,TRIG
178 CREATE INDEX IDX_QRTZ_FT_TG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_GROUP); 178 CREATE INDEX IDX_QRTZ_FT_TG ON QRTZ_FIRED_TRIGGERS(SCHED_NAME,TRIGGER_GROUP);
179 179
180 commit; 180 commit;
  181 +
  182 +-- 初始化默认任务 用户 Session 超时 Job
  183 +INSERT INTO QRTZ_JOB_DETAILS VALUES ('schedulerName', 'sysUserSessionTimeoutJob', 'DEFAULT', NULL, 'cn.iocoder.dashboard.framework.quartz.core.handler.JobHandlerInvoker', '0', '1', '1', '0', 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000027400064A4F425F49447372000E6A6176612E6C616E672E4C6F6E673B8BE490CC8F23DF0200014A000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B020000787000000000000000047400104A4F425F48414E444C45525F4E414D457400187379735573657253657373696F6E54696D656F75744A6F627800);
  184 +commit;
  185 +INSERT INTO QRTZ_TRIGGERS VALUES ('schedulerName', 'sysUserSessionTimeoutJob', 'DEFAULT', 'sysUserSessionTimeoutJob', 'DEFAULT', NULL, 1615706340000, 1615706280000, 5, 'WAITING', 'CRON', 1615706125000, 0, NULL, 0, 0xACED0005737200156F72672E71756172747A2E4A6F62446174614D61709FB083E8BFA9B0CB020000787200266F72672E71756172747A2E7574696C732E537472696E674B65794469727479466C61674D61708208E8C3FBC55D280200015A0013616C6C6F77735472616E7369656E74446174617872001D6F72672E71756172747A2E7574696C732E4469727479466C61674D617013E62EAD28760ACE0200025A000564697274794C00036D617074000F4C6A6176612F7574696C2F4D61703B787001737200116A6176612E7574696C2E486173684D61700507DAC1C31660D103000246000A6C6F6164466163746F724900097468726573686F6C6478703F4000000000000C770800000010000000037400114A4F425F48414E444C45525F504152414D707400124A4F425F52455452595F494E54455256414C737200116A6176612E6C616E672E496E746567657212E2A0A4F781873802000149000576616C7565787200106A6176612E6C616E672E4E756D62657286AC951D0B94E08B0200007870000007D074000F4A4F425F52455452595F434F554E547371007E0009000000037800);
  186 +commit;
  187 +INSERT INTO QRTZ_CRON_TRIGGERS VALUES ('schedulerName', 'sysUserSessionTimeoutJob', 'DEFAULT', '0 * * * * ? *', 'Asia/Shanghai');
  188 +commit;
src/main/java/cn/iocoder/dashboard/modules/infra/controller/job/vo/job/InfJobBaseVO.java
@@ -34,4 +34,5 @@ public class InfJobBaseVO { @@ -34,4 +34,5 @@ public class InfJobBaseVO {
34 @ApiModelProperty(value = "监控超时时间", example = "1000") 34 @ApiModelProperty(value = "监控超时时间", example = "1000")
35 private Integer monitorTimeout; 35 private Integer monitorTimeout;
36 36
  37 +
37 } 38 }
src/test/java/cn/iocoder/dashboard/modules/infra/service/job/InfJobLogServiceTest.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.infra.service.job;
  2 +
  3 +import static cn.hutool.core.util.RandomUtil.randomEle;
  4 +import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals;
  5 +import static cn.iocoder.dashboard.util.RandomUtils.randomLongId;
  6 +import static cn.iocoder.dashboard.util.RandomUtils.randomPojo;
  7 +import static cn.iocoder.dashboard.util.RandomUtils.randomString;
  8 +import static cn.iocoder.dashboard.util.date.DateUtils.buildTime;
  9 +import static org.junit.jupiter.api.Assertions.assertEquals;
  10 +import static org.junit.jupiter.api.Assertions.assertNotNull;
  11 +
  12 +import java.util.ArrayList;
  13 +import java.util.List;
  14 +
  15 +import javax.annotation.Resource;
  16 +
  17 +import org.junit.jupiter.api.Test;
  18 +import org.springframework.context.annotation.Import;
  19 +
  20 +import cn.iocoder.dashboard.BaseDbUnitTest;
  21 +import cn.iocoder.dashboard.common.pojo.PageResult;
  22 +import cn.iocoder.dashboard.modules.infra.controller.job.vo.log.InfJobLogExportReqVO;
  23 +import cn.iocoder.dashboard.modules.infra.controller.job.vo.log.InfJobLogPageReqVO;
  24 +import cn.iocoder.dashboard.modules.infra.dal.dataobject.job.InfJobLogDO;
  25 +import cn.iocoder.dashboard.modules.infra.dal.mysql.job.InfJobLogMapper;
  26 +import cn.iocoder.dashboard.modules.infra.enums.job.InfJobLogStatusEnum;
  27 +import cn.iocoder.dashboard.modules.infra.service.job.impl.InfJobLogServiceImpl;
  28 +import cn.iocoder.dashboard.util.object.ObjectUtils;
  29 +
  30 +/**
  31 + * {@link InfJobLogServiceImpl} 的单元测试
  32 + *
  33 + * @author neilz
  34 + */
  35 +@Import(InfJobLogServiceImpl.class)
  36 +public class InfJobLogServiceTest extends BaseDbUnitTest {
  37 +
  38 + @Resource
  39 + private InfJobLogServiceImpl jobLogService;
  40 + @Resource
  41 + private InfJobLogMapper jobLogMapper;
  42 +
  43 + @Test
  44 + public void testCreateJobLog_success() {
  45 + // 准备参数
  46 + InfJobLogDO reqVO = randomPojo(InfJobLogDO.class, o -> {
  47 + o.setExecuteIndex(1);
  48 + });
  49 + // 调用
  50 + Long jobLogId = jobLogService.createJobLog(reqVO.getJobId(), reqVO.getBeginTime(), reqVO.getHandlerName(), reqVO.getHandlerParam(), reqVO.getExecuteIndex());
  51 + // 断言
  52 + assertNotNull(jobLogId);
  53 + // 校验记录的属性是否正确
  54 + InfJobLogDO job = jobLogMapper.selectById(jobLogId);
  55 + assertEquals(InfJobLogStatusEnum.RUNNING.getStatus(), job.getStatus());
  56 + }
  57 +
  58 + @Test
  59 + public void testUpdateJobLogResultAsync_success() {
  60 + // 准备参数
  61 + InfJobLogDO reqVO = randomPojo(InfJobLogDO.class, o -> {
  62 + o.setExecuteIndex(1);
  63 + });
  64 + InfJobLogDO log = InfJobLogDO.builder().jobId(reqVO.getJobId()).handlerName(reqVO.getHandlerName()).handlerParam(reqVO.getHandlerParam()).executeIndex(reqVO.getExecuteIndex())
  65 + .beginTime(reqVO.getBeginTime()).status(InfJobLogStatusEnum.RUNNING.getStatus()).build();
  66 + jobLogMapper.insert(log);
  67 + // 调用
  68 + jobLogService.updateJobLogResultAsync(log.getId(), reqVO.getBeginTime(), reqVO.getDuration(), true,reqVO.getResult());
  69 + // 校验记录的属性是否正确
  70 + InfJobLogDO job = jobLogMapper.selectById(log.getId());
  71 + assertEquals(InfJobLogStatusEnum.SUCCESS.getStatus(), job.getStatus());
  72 +
  73 + // 调用
  74 + jobLogService.updateJobLogResultAsync(log.getId(), reqVO.getBeginTime(), reqVO.getDuration(), false,reqVO.getResult());
  75 + // 校验记录的属性是否正确
  76 + InfJobLogDO job2 = jobLogMapper.selectById(log.getId());
  77 + assertEquals(InfJobLogStatusEnum.FAILURE.getStatus(), job2.getStatus());
  78 + }
  79 +
  80 + @Test
  81 + public void testGetJobLogListByIds_success() {
  82 + // mock 数据
  83 + InfJobLogDO dbJobLog = randomPojo(InfJobLogDO.class, o -> {
  84 + o.setExecuteIndex(1);
  85 + o.setStatus(randomEle(InfJobLogStatusEnum.values()).getStatus()); // 保证 status 的范围
  86 + });
  87 + InfJobLogDO cloneJobLog = ObjectUtils.clone(dbJobLog, o -> o.setHandlerName(randomString()));
  88 + jobLogMapper.insert(dbJobLog);
  89 + // 测试 handlerName 不匹配
  90 + jobLogMapper.insert(cloneJobLog);
  91 + // 准备参数
  92 + ArrayList ids = new ArrayList<>();
  93 + ids.add(dbJobLog.getId());
  94 + ids.add(cloneJobLog.getId());
  95 + // 调用
  96 + List<InfJobLogDO> list = jobLogService.getJobLogList(ids);
  97 + // 断言
  98 + assertEquals(2, list.size());
  99 + assertPojoEquals(dbJobLog, list.get(0));
  100 + }
  101 +
  102 + @Test
  103 + public void testGetJobPage_success() {
  104 + // mock 数据
  105 + InfJobLogDO dbJobLog = randomPojo(InfJobLogDO.class, o -> {
  106 + o.setExecuteIndex(1);
  107 + o.setHandlerName("handlerName 单元测试");
  108 + o.setStatus(InfJobLogStatusEnum.SUCCESS.getStatus());
  109 + o.setBeginTime(buildTime(2021, 1, 8));
  110 + o.setEndTime(buildTime(2021, 1, 8));
  111 + });
  112 + jobLogMapper.insert(dbJobLog);
  113 + // 测试 jobId 不匹配
  114 + jobLogMapper.insert(ObjectUtils.clone(dbJobLog, o -> o.setJobId(randomLongId())));
  115 + // 测试 handlerName 不匹配
  116 + jobLogMapper.insert(ObjectUtils.clone(dbJobLog, o -> o.setHandlerName(randomString())));
  117 + // 测试 beginTime 不匹配
  118 + jobLogMapper.insert(ObjectUtils.clone(dbJobLog, o -> o.setBeginTime(buildTime(2021, 1, 7))));
  119 + // 测试 endTime 不匹配
  120 + jobLogMapper.insert(ObjectUtils.clone(dbJobLog, o -> o.setEndTime(buildTime(2021, 1, 9))));
  121 + // 测试 status 不匹配
  122 + jobLogMapper.insert(ObjectUtils.clone(dbJobLog, o -> o.setStatus(InfJobLogStatusEnum.FAILURE.getStatus())));
  123 + // 准备参数
  124 + InfJobLogPageReqVO reqVo = new InfJobLogPageReqVO();
  125 + reqVo.setJobId(dbJobLog.getJobId());
  126 + reqVo.setHandlerName("单元");
  127 + reqVo.setBeginTime(dbJobLog.getBeginTime());
  128 + reqVo.setEndTime(dbJobLog.getEndTime());
  129 + reqVo.setStatus(InfJobLogStatusEnum.SUCCESS.getStatus());
  130 + // 调用
  131 + PageResult<InfJobLogDO> pageResult = jobLogService.getJobLogPage(reqVo);
  132 + // 断言
  133 + assertEquals(1, pageResult.getTotal());
  134 + assertEquals(1, pageResult.getList().size());
  135 + assertPojoEquals(dbJobLog, pageResult.getList().get(0));
  136 + }
  137 +
  138 + @Test
  139 + public void testGetJobListForExport_success() {
  140 + // mock 数据
  141 + InfJobLogDO dbJobLog = randomPojo(InfJobLogDO.class, o -> {
  142 + o.setExecuteIndex(1);
  143 + o.setHandlerName("handlerName 单元测试");
  144 + o.setStatus(InfJobLogStatusEnum.SUCCESS.getStatus());
  145 + o.setBeginTime(buildTime(2021, 1, 8));
  146 + o.setEndTime(buildTime(2021, 1, 8));
  147 + });
  148 + jobLogMapper.insert(dbJobLog);
  149 + // 测试 jobId 不匹配
  150 + jobLogMapper.insert(ObjectUtils.clone(dbJobLog, o -> o.setJobId(randomLongId())));
  151 + // 测试 handlerName 不匹配
  152 + jobLogMapper.insert(ObjectUtils.clone(dbJobLog, o -> o.setHandlerName(randomString())));
  153 + // 测试 beginTime 不匹配
  154 + jobLogMapper.insert(ObjectUtils.clone(dbJobLog, o -> o.setBeginTime(buildTime(2021, 1, 7))));
  155 + // 测试 endTime 不匹配
  156 + jobLogMapper.insert(ObjectUtils.clone(dbJobLog, o -> o.setEndTime(buildTime(2021, 1, 9))));
  157 + // 测试 status 不匹配
  158 + jobLogMapper.insert(ObjectUtils.clone(dbJobLog, o -> o.setStatus(InfJobLogStatusEnum.FAILURE.getStatus())));
  159 + // 准备参数
  160 + InfJobLogExportReqVO reqVo = new InfJobLogExportReqVO();
  161 + reqVo.setJobId(dbJobLog.getJobId());
  162 + reqVo.setHandlerName("单元");
  163 + reqVo.setBeginTime(dbJobLog.getBeginTime());
  164 + reqVo.setEndTime(dbJobLog.getEndTime());
  165 + reqVo.setStatus(InfJobLogStatusEnum.SUCCESS.getStatus());
  166 + // 调用
  167 + List<InfJobLogDO> list = jobLogService.getJobLogList(reqVo);
  168 + // 断言
  169 + assertEquals(1, list.size());
  170 + assertPojoEquals(dbJobLog, list.get(0));
  171 + }
  172 +
  173 +}
src/test/java/cn/iocoder/dashboard/modules/infra/service/job/InfJobServiceTest.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.infra.service.job;
  2 +
  3 +import static cn.hutool.core.util.RandomUtil.randomEle;
  4 +import static cn.iocoder.dashboard.modules.infra.enums.InfErrorCodeConstants.JOB_CHANGE_STATUS_EQUALS;
  5 +import static cn.iocoder.dashboard.modules.infra.enums.InfErrorCodeConstants.JOB_CHANGE_STATUS_INVALID;
  6 +import static cn.iocoder.dashboard.modules.infra.enums.InfErrorCodeConstants.JOB_CRON_EXPRESSION_VALID;
  7 +import static cn.iocoder.dashboard.modules.infra.enums.InfErrorCodeConstants.JOB_HANDLER_EXISTS;
  8 +import static cn.iocoder.dashboard.modules.infra.enums.InfErrorCodeConstants.JOB_NOT_EXISTS;
  9 +import static cn.iocoder.dashboard.modules.infra.enums.InfErrorCodeConstants.JOB_UPDATE_ONLY_NORMAL_STATUS;
  10 +import static cn.iocoder.dashboard.util.AssertUtils.assertPojoEquals;
  11 +import static cn.iocoder.dashboard.util.AssertUtils.assertServiceException;
  12 +import static cn.iocoder.dashboard.util.RandomUtils.randomPojo;
  13 +import static cn.iocoder.dashboard.util.RandomUtils.randomString;
  14 +import static org.junit.jupiter.api.Assertions.assertEquals;
  15 +import static org.junit.jupiter.api.Assertions.assertNotNull;
  16 +import static org.junit.jupiter.api.Assertions.assertNull;
  17 +
  18 +import java.util.ArrayList;
  19 +import java.util.List;
  20 +
  21 +import javax.annotation.Resource;
  22 +
  23 +import org.junit.jupiter.api.Test;
  24 +import org.quartz.SchedulerException;
  25 +import org.springframework.boot.test.mock.mockito.MockBean;
  26 +import org.springframework.context.annotation.Import;
  27 +
  28 +import cn.iocoder.dashboard.BaseDbUnitTest;
  29 +import cn.iocoder.dashboard.common.pojo.PageResult;
  30 +import cn.iocoder.dashboard.framework.quartz.core.scheduler.SchedulerManager;
  31 +import cn.iocoder.dashboard.modules.infra.controller.job.vo.job.InfJobCreateReqVO;
  32 +import cn.iocoder.dashboard.modules.infra.controller.job.vo.job.InfJobExportReqVO;
  33 +import cn.iocoder.dashboard.modules.infra.controller.job.vo.job.InfJobPageReqVO;
  34 +import cn.iocoder.dashboard.modules.infra.controller.job.vo.job.InfJobUpdateReqVO;
  35 +import cn.iocoder.dashboard.modules.infra.convert.job.InfJobConvert;
  36 +import cn.iocoder.dashboard.modules.infra.dal.dataobject.job.InfJobDO;
  37 +import cn.iocoder.dashboard.modules.infra.dal.mysql.job.InfJobMapper;
  38 +import cn.iocoder.dashboard.modules.infra.enums.job.InfJobStatusEnum;
  39 +import cn.iocoder.dashboard.modules.infra.service.job.impl.InfJobServiceImpl;
  40 +import cn.iocoder.dashboard.util.object.ObjectUtils;
  41 +
  42 +/**
  43 + * {@link InfJobServiceImpl} 的单元测试
  44 + *
  45 + * @author neilz
  46 + */
  47 +@Import(InfJobServiceImpl.class)
  48 +public class InfJobServiceTest extends BaseDbUnitTest {
  49 +
  50 + @Resource
  51 + private InfJobServiceImpl jobService;
  52 + @Resource
  53 + private InfJobMapper jobMapper;
  54 + @MockBean
  55 + private SchedulerManager schedulerManager;
  56 +
  57 + @Test
  58 + public void testCreateJob_cronExpressionValid() {
  59 + // 准备参数。Cron 表达式为 String 类型,默认随机字符串。
  60 + InfJobCreateReqVO reqVO = randomPojo(InfJobCreateReqVO.class);
  61 + // 调用,并断言异常
  62 + assertServiceException(() -> jobService.createJob(reqVO), JOB_CRON_EXPRESSION_VALID);
  63 + }
  64 +
  65 + @Test
  66 + public void testCreateJob_jobHandlerExists() throws SchedulerException {
  67 + // 准备参数 指定 Cron 表达式
  68 + InfJobCreateReqVO reqVO = randomPojo(InfJobCreateReqVO.class, o -> o.setCronExpression("0 0/1 * * * ? *"));
  69 + // 调用
  70 + jobService.createJob(reqVO);
  71 + // 调用,并断言异常
  72 + assertServiceException(() -> jobService.createJob(reqVO), JOB_HANDLER_EXISTS);
  73 + }
  74 +
  75 + @Test
  76 + public void testCreateJob_success() throws SchedulerException {
  77 + // 准备参数 指定 Cron 表达式
  78 + InfJobCreateReqVO reqVO = randomPojo(InfJobCreateReqVO.class, o -> o.setCronExpression("0 0/1 * * * ? *"));
  79 + // 调用
  80 + Long jobId = jobService.createJob(reqVO);
  81 + // 断言
  82 + assertNotNull(jobId);
  83 + // 校验记录的属性是否正确
  84 + InfJobDO job = jobMapper.selectById(jobId);
  85 + assertPojoEquals(reqVO, job);
  86 + assertEquals(InfJobStatusEnum.NORMAL.getStatus(), job.getStatus());
  87 + }
  88 +
  89 + @Test
  90 + public void testUpdateJob_jobNotExists(){
  91 + // 准备参数
  92 + InfJobUpdateReqVO reqVO = randomPojo(InfJobUpdateReqVO.class, o -> o.setCronExpression("0 0/1 * * * ? *"));
  93 + // 调用,并断言异常
  94 + assertServiceException(() -> jobService.updateJob(reqVO), JOB_NOT_EXISTS);
  95 + }
  96 +
  97 + @Test
  98 + public void testUpdateJob_onlyNormalStatus(){
  99 + // mock 数据
  100 + InfJobCreateReqVO createReqVO = randomPojo(InfJobCreateReqVO.class, o -> o.setCronExpression("0 0/1 * * * ? *"));
  101 + InfJobDO job = InfJobConvert.INSTANCE.convert(createReqVO);
  102 + job.setStatus(InfJobStatusEnum.INIT.getStatus());
  103 + fillJobMonitorTimeoutEmpty(job);
  104 + jobMapper.insert(job);
  105 + // 准备参数
  106 + InfJobUpdateReqVO updateReqVO = randomPojo(InfJobUpdateReqVO.class, o -> {
  107 + o.setId(job.getId());
  108 + o.setName(createReqVO.getName());
  109 + o.setCronExpression(createReqVO.getCronExpression());
  110 + });
  111 + // 调用,并断言异常
  112 + assertServiceException(() -> jobService.updateJob(updateReqVO), JOB_UPDATE_ONLY_NORMAL_STATUS);
  113 + }
  114 +
  115 + @Test
  116 + public void testUpdateJob_success() throws SchedulerException {
  117 + // mock 数据
  118 + InfJobCreateReqVO createReqVO = randomPojo(InfJobCreateReqVO.class, o -> o.setCronExpression("0 0/1 * * * ? *"));
  119 + InfJobDO job = InfJobConvert.INSTANCE.convert(createReqVO);
  120 + job.setStatus(InfJobStatusEnum.NORMAL.getStatus());
  121 + fillJobMonitorTimeoutEmpty(job);
  122 + jobMapper.insert(job);
  123 + schedulerManager.addJob(job.getId(), job.getHandlerName(), job.getHandlerParam(), job.getCronExpression(),
  124 + createReqVO.getRetryCount(), createReqVO.getRetryInterval());
  125 + // 准备参数
  126 + InfJobUpdateReqVO updateReqVO = randomPojo(InfJobUpdateReqVO.class, o -> {
  127 + o.setId(job.getId());
  128 + o.setName(createReqVO.getName());
  129 + o.setCronExpression(createReqVO.getCronExpression());
  130 + });
  131 + // 调用
  132 + jobService.updateJob(updateReqVO);
  133 + // 校验记录的属性是否正确
  134 + InfJobDO updateJob = jobMapper.selectById(updateReqVO.getId());
  135 + assertPojoEquals(updateReqVO, updateJob);
  136 + }
  137 +
  138 + @Test
  139 + public void testUpdateJobStatus_changeStatusInvalid() {
  140 + // 调用,并断言异常
  141 + assertServiceException(() -> jobService.updateJobStatus(1l, InfJobStatusEnum.INIT.getStatus()), JOB_CHANGE_STATUS_INVALID);
  142 + }
  143 +
  144 + @Test
  145 + public void testUpdateJobStatus_changeStatusEquals() {
  146 + // mock 数据
  147 + InfJobCreateReqVO createReqVO = randomPojo(InfJobCreateReqVO.class, o -> o.setCronExpression("0 0/1 * * * ? *"));
  148 + InfJobDO job = InfJobConvert.INSTANCE.convert(createReqVO);
  149 + job.setStatus(InfJobStatusEnum.NORMAL.getStatus());
  150 + fillJobMonitorTimeoutEmpty(job);
  151 + jobMapper.insert(job);
  152 + // 调用,并断言异常
  153 + assertServiceException(() -> jobService.updateJobStatus(job.getId(), job.getStatus()), JOB_CHANGE_STATUS_EQUALS);
  154 + }
  155 +
  156 + @Test
  157 + public void testUpdateJobStatus_success() throws SchedulerException {
  158 + // mock 数据
  159 + InfJobCreateReqVO createReqVO = randomPojo(InfJobCreateReqVO.class, o -> o.setCronExpression("0 0/1 * * * ? *"));
  160 + InfJobDO job = InfJobConvert.INSTANCE.convert(createReqVO);
  161 + job.setStatus(InfJobStatusEnum.NORMAL.getStatus());
  162 + fillJobMonitorTimeoutEmpty(job);
  163 + jobMapper.insert(job);
  164 + schedulerManager.addJob(job.getId(), job.getHandlerName(), job.getHandlerParam(), job.getCronExpression(),
  165 + createReqVO.getRetryCount(), createReqVO.getRetryInterval());
  166 + // 调用
  167 + jobService.updateJobStatus(job.getId(), InfJobStatusEnum.STOP.getStatus());
  168 + // 校验记录的属性是否正确
  169 + InfJobDO updateJob = jobMapper.selectById(job.getId());
  170 + assertEquals(InfJobStatusEnum.STOP.getStatus(), updateJob.getStatus());
  171 + }
  172 +
  173 + /**
  174 + * 页面"执行一次"按钮功能集成测试发现问题:
  175 + * inf_job 表初始化任务 sysUserSessionTimeoutJob 点击报错,是因为 Job 并没有添加到 Quartz 中;
  176 + * 没有走 createJob 中 scheduler.scheduleJob() 这一步,报错任务找不到。
  177 + * // FINISHED Quartz 相关表新增初始化任务 sysUserSessionTimeoutJob sql
  178 + */
  179 + @Test
  180 + public void testTriggerJob_success() throws SchedulerException {
  181 + /**
  182 + * TODO 不知道是否要将 Quartz 相关 SQL 引入来做单元测试
  183 + * 1、schedulerManager.addJob sysUserSessionTimeoutJob
  184 + * 2、schedulerManager.triggerJob
  185 + * 3、check inf_job_log
  186 + */
  187 + }
  188 +
  189 + @Test
  190 + public void testDeleteJob_success() throws SchedulerException {
  191 + // mock 数据
  192 + InfJobCreateReqVO createReqVO = randomPojo(InfJobCreateReqVO.class, o -> o.setCronExpression("0 0/1 * * * ? *"));
  193 + InfJobDO job = InfJobConvert.INSTANCE.convert(createReqVO);
  194 + job.setStatus(InfJobStatusEnum.NORMAL.getStatus());
  195 + fillJobMonitorTimeoutEmpty(job);
  196 + jobMapper.insert(job);
  197 + schedulerManager.addJob(job.getId(), job.getHandlerName(), job.getHandlerParam(), job.getCronExpression(),
  198 + createReqVO.getRetryCount(), createReqVO.getRetryInterval());
  199 + // 调用 UPDATE inf_job SET deleted=1 WHERE id=? AND deleted=0
  200 + jobService.deleteJob(job.getId());
  201 + // 校验数据不存在了 WHERE id=? AND deleted=0 查询为空正常
  202 + assertNull(jobMapper.selectById(job.getId()));
  203 + }
  204 +
  205 + @Test
  206 + public void testGetJobListByIds_success() {
  207 + // mock 数据
  208 + InfJobDO dbJob = randomPojo(InfJobDO.class, o -> {
  209 + o.setStatus(randomEle(InfJobStatusEnum.values()).getStatus()); // 保证 status 的范围
  210 + });
  211 + InfJobDO cloneJob = ObjectUtils.clone(dbJob, o -> o.setHandlerName(randomString()));
  212 + jobMapper.insert(dbJob);
  213 + // 测试 handlerName 不匹配
  214 + jobMapper.insert(cloneJob);
  215 + // 准备参数
  216 + ArrayList ids = new ArrayList<>();
  217 + ids.add(dbJob.getId());
  218 + ids.add(cloneJob.getId());
  219 + // 调用
  220 + List<InfJobDO> list = jobService.getJobList(ids);
  221 + // 断言
  222 + assertEquals(2, list.size());
  223 + assertPojoEquals(dbJob, list.get(0));
  224 + }
  225 +
  226 + @Test
  227 + public void testGetJobPage_success() {
  228 + // mock 数据
  229 + InfJobDO dbJob = randomPojo(InfJobDO.class, o -> {
  230 + o.setName("定时任务测试");
  231 + o.setHandlerName("handlerName 单元测试");
  232 + o.setStatus(InfJobStatusEnum.INIT.getStatus());
  233 + });
  234 + jobMapper.insert(dbJob);
  235 + // 测试 name 不匹配
  236 + jobMapper.insert(ObjectUtils.clone(dbJob, o -> o.setName("土豆")));
  237 + // 测试 status 不匹配
  238 + jobMapper.insert(ObjectUtils.clone(dbJob, o -> o.setStatus(InfJobStatusEnum.NORMAL.getStatus())));
  239 + // 测试 handlerName 不匹配
  240 + jobMapper.insert(ObjectUtils.clone(dbJob, o -> o.setHandlerName(randomString())));
  241 + // 准备参数
  242 + InfJobPageReqVO reqVo = new InfJobPageReqVO();
  243 + reqVo.setName("定时");
  244 + reqVo.setStatus(InfJobStatusEnum.INIT.getStatus());
  245 + reqVo.setHandlerName("单元");
  246 + // 调用
  247 + PageResult<InfJobDO> pageResult = jobService.getJobPage(reqVo);
  248 + // 断言
  249 + assertEquals(1, pageResult.getTotal());
  250 + assertEquals(1, pageResult.getList().size());
  251 + assertPojoEquals(dbJob, pageResult.getList().get(0));
  252 + }
  253 +
  254 + @Test
  255 + public void testGetJobListForExport_success() {
  256 + // mock 数据
  257 + InfJobDO dbJob = randomPojo(InfJobDO.class, o -> {
  258 + o.setName("定时任务测试");
  259 + o.setHandlerName("handlerName 单元测试");
  260 + o.setStatus(InfJobStatusEnum.INIT.getStatus());
  261 + });
  262 + jobMapper.insert(dbJob);
  263 + // 测试 name 不匹配
  264 + jobMapper.insert(ObjectUtils.clone(dbJob, o -> o.setName("土豆")));
  265 + // 测试 status 不匹配
  266 + jobMapper.insert(ObjectUtils.clone(dbJob, o -> o.setStatus(InfJobStatusEnum.NORMAL.getStatus())));
  267 + // 测试 handlerName 不匹配
  268 + jobMapper.insert(ObjectUtils.clone(dbJob, o -> o.setHandlerName(randomString())));
  269 + // 准备参数
  270 + InfJobExportReqVO reqVo = new InfJobExportReqVO();
  271 + reqVo.setName("定时");
  272 + reqVo.setStatus(InfJobStatusEnum.INIT.getStatus());
  273 + reqVo.setHandlerName("单元");
  274 + // 调用
  275 + List<InfJobDO> list = jobService.getJobList(reqVo);
  276 + // 断言
  277 + assertEquals(1, list.size());
  278 + assertPojoEquals(dbJob, list.get(0));
  279 + }
  280 +
  281 + private static void fillJobMonitorTimeoutEmpty(InfJobDO job) {
  282 + if (job.getMonitorTimeout() == null) {
  283 + job.setMonitorTimeout(0);
  284 + }
  285 + }
  286 +
  287 +}
src/test/resources/sql/clean.sql
1 -- inf 开头的 DB 1 -- inf 开头的 DB
2 DELETE FROM "inf_config"; 2 DELETE FROM "inf_config";
3 DELETE FROM "inf_file"; 3 DELETE FROM "inf_file";
  4 +DELETE FROM "inf_job";
  5 +DELETE FROM "inf_job_log";
4 6
5 -- sys 开头的 DB 7 -- sys 开头的 DB
6 DELETE FROM "sys_dept"; 8 DELETE FROM "sys_dept";
src/test/resources/sql/create_tables.sql
@@ -29,6 +29,44 @@ CREATE TABLE IF NOT EXISTS &quot;inf_file&quot; ( @@ -29,6 +29,44 @@ CREATE TABLE IF NOT EXISTS &quot;inf_file&quot; (
29 PRIMARY KEY ("id") 29 PRIMARY KEY ("id")
30 ) COMMENT '文件表'; 30 ) COMMENT '文件表';
31 31
  32 +CREATE TABLE IF NOT EXISTS "inf_job" (
  33 + "id" bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY COMMENT '任务编号',
  34 + "name" varchar(32) NOT NULL COMMENT '任务名称',
  35 + "status" tinyint(4) NOT NULL COMMENT '任务状态',
  36 + "handler_name" varchar(64) NOT NULL COMMENT '处理器的名字',
  37 + "handler_param" varchar(255) DEFAULT NULL COMMENT '处理器的参数',
  38 + "cron_expression" varchar(32) NOT NULL COMMENT 'CRON 表达式',
  39 + "retry_count" int(11) NOT NULL DEFAULT '0' COMMENT '重试次数',
  40 + "retry_interval" int(11) NOT NULL DEFAULT '0' COMMENT '重试间隔',
  41 + "monitor_timeout" int(11) NOT NULL DEFAULT '0' COMMENT '监控超时时间',
  42 + "creator" varchar(64) DEFAULT '' COMMENT '创建者',
  43 + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  44 + "updater" varchar(64) DEFAULT '' COMMENT '更新者',
  45 + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  46 + "deleted" bit NOT NULL DEFAULT FALSE COMMENT '是否删除',
  47 + PRIMARY KEY ("id")
  48 +) COMMENT='定时任务表';
  49 +
  50 +DROP TABLE IF EXISTS "inf_job_log";
  51 +CREATE TABLE "inf_job_log" (
  52 + "id" bigint(20) NOT NULL GENERATED BY DEFAULT AS IDENTITY COMMENT '日志编号',
  53 + "job_id" bigint(20) NOT NULL COMMENT '任务编号',
  54 + "handler_name" varchar(64) NOT NULL COMMENT '处理器的名字',
  55 + "handler_param" varchar(255) DEFAULT NULL COMMENT '处理器的参数',
  56 + "execute_index" tinyint(4) NOT NULL DEFAULT '1' COMMENT '第几次执行',
  57 + "begin_time" datetime NOT NULL COMMENT '开始执行时间',
  58 + "end_time" datetime DEFAULT NULL COMMENT '结束执行时间',
  59 + "duration" int(11) DEFAULT NULL COMMENT '执行时长',
  60 + "status" tinyint(4) NOT NULL COMMENT '任务状态',
  61 + "result" varchar(4000) DEFAULT '' COMMENT '结果数据',
  62 + "creator" varchar(64) DEFAULT '' COMMENT '创建者',
  63 + "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  64 + "updater" varchar(64) DEFAULT '' COMMENT '更新者',
  65 + "update_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  66 + "deleted" bit(1) NOT NULL DEFAULT FALSE COMMENT '是否删除',
  67 + PRIMARY KEY ("id")
  68 +)COMMENT='定时任务日志表';
  69 +
32 -- sys 开头的 DB 70 -- sys 开头的 DB
33 71
34 CREATE TABLE IF NOT EXISTS "sys_dept" ( 72 CREATE TABLE IF NOT EXISTS "sys_dept" (