Commit 7c165fb1f6f2602b1929648b87fb40e1f61357e0
1 parent
3a1694de
1. 完善 PayNotifyServiceImpl 的单元测试
Showing
5 changed files
with
193 additions
and
9 deletions
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/vo/PayNotifyTaskPageReqVO.java
| ... | ... | @@ -21,13 +21,13 @@ public class PayNotifyTaskPageReqVO extends PageParam { |
| 21 | 21 | private Long appId; |
| 22 | 22 | |
| 23 | 23 | @Schema(description = "通知类型", example = "2") |
| 24 | - private Byte type; | |
| 24 | + private Integer type; | |
| 25 | 25 | |
| 26 | 26 | @Schema(description = "数据编号", example = "6722") |
| 27 | 27 | private Long dataId; |
| 28 | 28 | |
| 29 | 29 | @Schema(description = "通知状态", example = "1") |
| 30 | - private Byte status; | |
| 30 | + private Integer status; | |
| 31 | 31 | |
| 32 | 32 | @Schema(description = "商户订单编号", example = "26697") |
| 33 | 33 | private String merchantOrderId; | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/notify/PayNotifyServiceImpl.java
| ... | ... | @@ -25,6 +25,7 @@ import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyStatusEnum; |
| 25 | 25 | import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum; |
| 26 | 26 | import cn.iocoder.yudao.module.pay.service.order.PayOrderService; |
| 27 | 27 | import cn.iocoder.yudao.module.pay.service.refund.PayRefundService; |
| 28 | +import com.google.common.annotations.VisibleForTesting; | |
| 28 | 29 | import lombok.extern.slf4j.Slf4j; |
| 29 | 30 | import org.springframework.context.annotation.Lazy; |
| 30 | 31 | import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; |
| ... | ... | @@ -177,7 +178,7 @@ public class PayNotifyServiceImpl implements PayNotifyService { |
| 177 | 178 | }); |
| 178 | 179 | } |
| 179 | 180 | |
| 180 | - @Transactional | |
| 181 | + @Transactional(rollbackFor = Exception.class) | |
| 181 | 182 | public void executeNotify0(PayNotifyTaskDO task) { |
| 182 | 183 | // 发起回调 |
| 183 | 184 | CommonResult<?> invokeResult = null; |
| ... | ... | @@ -237,7 +238,8 @@ public class PayNotifyServiceImpl implements PayNotifyService { |
| 237 | 238 | * @param invokeException 通知异常 |
| 238 | 239 | * @return 最终任务的状态 |
| 239 | 240 | */ |
| 240 | - private Integer processNotifyResult(PayNotifyTaskDO task, CommonResult<?> invokeResult, Throwable invokeException) { | |
| 241 | + @VisibleForTesting | |
| 242 | + Integer processNotifyResult(PayNotifyTaskDO task, CommonResult<?> invokeResult, Throwable invokeException) { | |
| 241 | 243 | // 设置通用的更新 PayNotifyTaskDO 的字段 |
| 242 | 244 | PayNotifyTaskDO updateTask = new PayNotifyTaskDO() |
| 243 | 245 | .setId(task.getId()) | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/notify/PayNotifyServiceTest.java
| 1 | 1 | package cn.iocoder.yudao.module.pay.service.notify; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.extra.spring.SpringUtil; |
| 4 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | |
| 5 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 4 | 6 | import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; |
| 7 | +import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskPageReqVO; | |
| 8 | +import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyLogDO; | |
| 5 | 9 | import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyTaskDO; |
| 6 | 10 | import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; |
| 7 | 11 | import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO; |
| ... | ... | @@ -20,14 +24,17 @@ import org.redisson.api.RLock; |
| 20 | 24 | import org.redisson.api.RedissonClient; |
| 21 | 25 | import org.springframework.boot.test.mock.mockito.MockBean; |
| 22 | 26 | import org.springframework.context.annotation.Import; |
| 23 | -import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; | |
| 24 | 27 | |
| 25 | 28 | import javax.annotation.Resource; |
| 26 | - | |
| 27 | 29 | import java.time.Duration; |
| 30 | +import java.util.List; | |
| 28 | 31 | |
| 29 | -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.addTime; | |
| 32 | +import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; | |
| 33 | +import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*; | |
| 34 | +import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; | |
| 35 | +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; | |
| 30 | 36 | import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; |
| 37 | +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; | |
| 31 | 38 | import static org.assertj.core.api.Assertions.assertThat; |
| 32 | 39 | import static org.junit.jupiter.api.Assertions.*; |
| 33 | 40 | import static org.mockito.ArgumentMatchers.anyString; |
| ... | ... | @@ -167,7 +174,167 @@ public class PayNotifyServiceTest extends BaseDbUnitTest { |
| 167 | 174 | |
| 168 | 175 | // 调用 |
| 169 | 176 | notifyService.executeNotify0(task); |
| 177 | + // 断言,task | |
| 178 | + PayNotifyTaskDO dbTask = notifyTaskMapper.selectById(task.getId()); | |
| 179 | + assertNotEquals(task.getNextNotifyTime(), dbTask.getNextNotifyTime()); | |
| 180 | + assertNotEquals(task.getLastExecuteTime(), dbTask.getNextNotifyTime()); | |
| 181 | + assertEquals(dbTask.getNotifyTimes(), 1); | |
| 182 | + assertEquals(dbTask.getStatus(), PayNotifyStatusEnum.REQUEST_FAILURE.getStatus()); | |
| 183 | + // 断言,log | |
| 184 | + PayNotifyLogDO dbLog = notifyLogMapper.selectOne(null); | |
| 185 | + assertEquals(dbLog.getTaskId(), task.getId()); | |
| 186 | + assertEquals(dbLog.getNotifyTimes(), 1); | |
| 187 | + assertTrue(dbLog.getResponse().contains("未知的通知任务类型:")); | |
| 188 | + assertEquals(dbLog.getStatus(), PayNotifyStatusEnum.REQUEST_FAILURE.getStatus()); | |
| 189 | + } | |
| 190 | + | |
| 191 | + @Test | |
| 192 | + public void testProcessNotifyResult_success() { | |
| 193 | + // mock 数据(task) | |
| 194 | + PayNotifyTaskDO task = randomPojo(PayNotifyTaskDO.class, | |
| 195 | + o -> o.setNotifyTimes(0).setMaxNotifyTimes(9)); | |
| 196 | + notifyTaskMapper.insert(task); | |
| 197 | + // 准备参数 | |
| 198 | + CommonResult<?> invokeResult = CommonResult.success(randomString()); | |
| 199 | + | |
| 200 | + // 调用 | |
| 201 | + notifyService.processNotifyResult(task, invokeResult, null); | |
| 202 | + // 断言 | |
| 203 | + PayNotifyTaskDO dbTask = notifyTaskMapper.selectById(task.getId()); | |
| 204 | + assertEquals(task.getNextNotifyTime(), dbTask.getNextNotifyTime()); | |
| 205 | + assertNotEquals(task.getLastExecuteTime(), dbTask.getNextNotifyTime()); | |
| 206 | + assertEquals(dbTask.getNotifyTimes(), 1); | |
| 207 | + assertEquals(dbTask.getStatus(), PayNotifyStatusEnum.SUCCESS.getStatus()); | |
| 208 | + } | |
| 209 | + | |
| 210 | + @Test | |
| 211 | + public void testProcessNotifyResult_failure() { | |
| 212 | + // mock 数据(task) | |
| 213 | + PayNotifyTaskDO task = randomPojo(PayNotifyTaskDO.class, | |
| 214 | + o -> o.setNotifyTimes(8).setMaxNotifyTimes(9)); | |
| 215 | + notifyTaskMapper.insert(task); | |
| 216 | + // 准备参数 | |
| 217 | + CommonResult<?> invokeResult = CommonResult.error(BAD_REQUEST); | |
| 218 | + | |
| 219 | + // 调用 | |
| 220 | + notifyService.processNotifyResult(task, invokeResult, null); | |
| 221 | + // 断言 | |
| 222 | + PayNotifyTaskDO dbTask = notifyTaskMapper.selectById(task.getId()); | |
| 223 | + assertEquals(task.getNextNotifyTime(), dbTask.getNextNotifyTime()); | |
| 224 | + assertNotEquals(task.getLastExecuteTime(), dbTask.getNextNotifyTime()); | |
| 225 | + assertEquals(dbTask.getNotifyTimes(), 9); | |
| 226 | + assertEquals(dbTask.getStatus(), PayNotifyStatusEnum.FAILURE.getStatus()); | |
| 227 | + } | |
| 228 | + | |
| 229 | + @Test | |
| 230 | + public void testProcessNotifyResult_requestFailure() { | |
| 231 | + // mock 数据(task) | |
| 232 | + PayNotifyTaskDO task = randomPojo(PayNotifyTaskDO.class, | |
| 233 | + o -> o.setNotifyTimes(0).setMaxNotifyTimes(9)); | |
| 234 | + notifyTaskMapper.insert(task); | |
| 235 | + // 准备参数 | |
| 236 | + CommonResult<?> invokeResult = CommonResult.error(BAD_REQUEST); | |
| 237 | + | |
| 238 | + // 调用 | |
| 239 | + notifyService.processNotifyResult(task, invokeResult, null); | |
| 240 | + // 断言 | |
| 241 | + PayNotifyTaskDO dbTask = notifyTaskMapper.selectById(task.getId()); | |
| 242 | + assertNotEquals(task.getNextNotifyTime(), dbTask.getNextNotifyTime()); | |
| 243 | + assertNotEquals(task.getLastExecuteTime(), dbTask.getNextNotifyTime()); | |
| 244 | + assertEquals(dbTask.getNotifyTimes(), 1); | |
| 245 | + assertEquals(dbTask.getStatus(), PayNotifyStatusEnum.REQUEST_SUCCESS.getStatus()); | |
| 246 | + } | |
| 170 | 247 | |
| 248 | + @Test | |
| 249 | + public void testProcessNotifyResult_requestSuccess() { | |
| 250 | + // mock 数据(task) | |
| 251 | + PayNotifyTaskDO task = randomPojo(PayNotifyTaskDO.class, | |
| 252 | + o -> o.setNotifyTimes(0).setMaxNotifyTimes(9)); | |
| 253 | + notifyTaskMapper.insert(task); | |
| 254 | + // 准备参数 | |
| 255 | + CommonResult<?> invokeResult = CommonResult.error(BAD_REQUEST); | |
| 256 | + RuntimeException invokeException = new RuntimeException(); | |
| 257 | + | |
| 258 | + // 调用 | |
| 259 | + notifyService.processNotifyResult(task, invokeResult, invokeException); | |
| 260 | + // 断言 | |
| 261 | + PayNotifyTaskDO dbTask = notifyTaskMapper.selectById(task.getId()); | |
| 262 | + assertNotEquals(task.getNextNotifyTime(), dbTask.getNextNotifyTime()); | |
| 263 | + assertNotEquals(task.getLastExecuteTime(), dbTask.getNextNotifyTime()); | |
| 264 | + assertEquals(dbTask.getNotifyTimes(), 1); | |
| 265 | + assertEquals(dbTask.getStatus(), PayNotifyStatusEnum.REQUEST_FAILURE.getStatus()); | |
| 266 | + } | |
| 267 | + | |
| 268 | + @Test | |
| 269 | + public void testGetNotifyTask() { | |
| 270 | + // mock 数据(task) | |
| 271 | + PayNotifyTaskDO task = randomPojo(PayNotifyTaskDO.class); | |
| 272 | + notifyTaskMapper.insert(task); | |
| 273 | + // 准备参数 | |
| 274 | + Long id = task.getId(); | |
| 275 | + | |
| 276 | + // 调用 | |
| 277 | + PayNotifyTaskDO dbTask = notifyService.getNotifyTask(id); | |
| 278 | + // 断言 | |
| 279 | + assertPojoEquals(dbTask, task); | |
| 280 | + } | |
| 281 | + | |
| 282 | + @Test | |
| 283 | + public void testGetNotifyTaskPage() { | |
| 284 | + // mock 数据 | |
| 285 | + PayNotifyTaskDO dbTask = randomPojo(PayNotifyTaskDO.class, o -> { // 等会查询到 | |
| 286 | + o.setAppId(1L); | |
| 287 | + o.setType(PayNotifyTypeEnum.REFUND.getType()); | |
| 288 | + o.setDataId(100L); | |
| 289 | + o.setStatus(PayNotifyStatusEnum.SUCCESS.getStatus()); | |
| 290 | + o.setMerchantOrderId("P110"); | |
| 291 | + o.setCreateTime(buildTime(2023, 2, 3)); | |
| 292 | + }); | |
| 293 | + notifyTaskMapper.insert(dbTask); | |
| 294 | + // 测试 appId 不匹配 | |
| 295 | + notifyTaskMapper.insert(cloneIgnoreId(dbTask, o -> o.setAppId(2L))); | |
| 296 | + // 测试 type 不匹配 | |
| 297 | + notifyTaskMapper.insert(cloneIgnoreId(dbTask, o -> o.setType(PayNotifyTypeEnum.ORDER.getType()))); | |
| 298 | + // 测试 dataId 不匹配 | |
| 299 | + notifyTaskMapper.insert(cloneIgnoreId(dbTask, o -> o.setDataId(200L))); | |
| 300 | + // 测试 status 不匹配 | |
| 301 | + notifyTaskMapper.insert(cloneIgnoreId(dbTask, o -> o.setStatus(PayNotifyStatusEnum.FAILURE.getStatus()))); | |
| 302 | + // 测试 merchantOrderId 不匹配 | |
| 303 | + notifyTaskMapper.insert(cloneIgnoreId(dbTask, o -> o.setMerchantOrderId(randomString()))); | |
| 304 | + // 测试 createTime 不匹配 | |
| 305 | + notifyTaskMapper.insert(cloneIgnoreId(dbTask, o -> o.setCreateTime(buildTime(2023, 1, 1)))); | |
| 306 | + // 准备参数 | |
| 307 | + PayNotifyTaskPageReqVO reqVO = new PayNotifyTaskPageReqVO(); | |
| 308 | + reqVO.setAppId(1L); | |
| 309 | + reqVO.setType(PayNotifyTypeEnum.REFUND.getType()); | |
| 310 | + reqVO.setDataId(100L); | |
| 311 | + reqVO.setStatus(PayNotifyStatusEnum.SUCCESS.getStatus()); | |
| 312 | + reqVO.setMerchantOrderId("P110"); | |
| 313 | + reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); | |
| 314 | + | |
| 315 | + // 调用 | |
| 316 | + PageResult<PayNotifyTaskDO> pageResult = notifyService.getNotifyTaskPage(reqVO); | |
| 317 | + // 断言 | |
| 318 | + assertEquals(1, pageResult.getTotal()); | |
| 319 | + assertEquals(1, pageResult.getList().size()); | |
| 320 | + assertPojoEquals(dbTask, pageResult.getList().get(0)); | |
| 321 | + } | |
| 322 | + | |
| 323 | + @Test | |
| 324 | + public void testGetNotifyLogList() { | |
| 325 | + // mock 数据 | |
| 326 | + PayNotifyLogDO dbLog = randomPojo(PayNotifyLogDO.class); | |
| 327 | + notifyLogMapper.insert(dbLog); | |
| 328 | + PayNotifyLogDO dbLog02 = randomPojo(PayNotifyLogDO.class); | |
| 329 | + notifyLogMapper.insert(dbLog02); | |
| 330 | + // 准备参数 | |
| 331 | + Long taskId = dbLog.getTaskId(); | |
| 332 | + | |
| 333 | + // 调用 | |
| 334 | + List<PayNotifyLogDO> logList = notifyService.getNotifyLogList(taskId); | |
| 335 | + // 断言 | |
| 336 | + assertEquals(logList.size(), 1); | |
| 337 | + assertPojoEquals(dbLog, logList.get(0)); | |
| 171 | 338 | } |
| 172 | 339 | |
| 173 | 340 | private void mockLock(Long id) { | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/test/resources/sql/clean.sql
yudao-module-pay/yudao-module-pay-biz/src/test/resources/sql/create_tables.sql
| ... | ... | @@ -129,4 +129,18 @@ CREATE TABLE IF NOT EXISTS `pay_notify_task` ( |
| 129 | 129 | `deleted` bit(1) NOT NULL DEFAULT FALSE, |
| 130 | 130 | `tenant_id` bigint(20) NOT NULL DEFAULT 0, |
| 131 | 131 | PRIMARY KEY ("id") |
| 132 | -) COMMENT = '支付通知'; | |
| 132 | +) COMMENT = '支付通知任务'; | |
| 133 | + | |
| 134 | +CREATE TABLE IF NOT EXISTS `pay_notify_log` ( | |
| 135 | + "id" number NOT NULL GENERATED BY DEFAULT AS IDENTITY, | |
| 136 | + `task_id` bigint(20) NOT NULL, | |
| 137 | + `notify_times` int NOT NULL, | |
| 138 | + `response` varchar(1024) NOT NULL, | |
| 139 | + `status` tinyint(4) NOT NULL, | |
| 140 | + `creator` varchar(64) NULL DEFAULT '', | |
| 141 | + `create_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP, | |
| 142 | + `updater` varchar(64) NULL DEFAULT '', | |
| 143 | + `update_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, | |
| 144 | + `deleted` bit(1) NOT NULL DEFAULT FALSE, | |
| 145 | + PRIMARY KEY ("id") | |
| 146 | +) COMMENT = '支付通知日志'; | ... | ... |