mall + pay:

1. 完善 PayRefundServiceTest 的单元测试
This commit is contained in:
YunaiV 2023-07-23 23:20:52 +08:00
parent ebeb14be2f
commit 6aea461e8d
2 changed files with 266 additions and 5 deletions

View File

@ -212,7 +212,7 @@ public class PayRefundServiceImpl implements PayRefundService {
} }
} }
public void notifyRefundSuccess(PayChannelDO channel, PayRefundRespDTO notify) { private void notifyRefundSuccess(PayChannelDO channel, PayRefundRespDTO notify) {
// 1.1 查询 PayRefundDO // 1.1 查询 PayRefundDO
PayRefundDO refund = refundMapper.selectByAppIdAndNo( PayRefundDO refund = refundMapper.selectByAppIdAndNo(
channel.getAppId(), notify.getOutRefundNo()); channel.getAppId(), notify.getOutRefundNo());
@ -246,8 +246,7 @@ public class PayRefundServiceImpl implements PayRefundService {
.type(PayNotifyTypeEnum.REFUND.getType()).dataId(refund.getId()).build()); .type(PayNotifyTypeEnum.REFUND.getType()).dataId(refund.getId()).build());
} }
@Transactional(rollbackFor = Exception.class) private void notifyRefundFailure(PayChannelDO channel, PayRefundRespDTO notify) {
public void notifyRefundFailure(PayChannelDO channel, PayRefundRespDTO notify) {
// 1.1 查询 PayRefundDO // 1.1 查询 PayRefundDO
PayRefundDO refund = refundMapper.selectByAppIdAndNo( PayRefundDO refund = refundMapper.selectByAppIdAndNo(
channel.getAppId(), notify.getOutRefundNo()); channel.getAppId(), notify.getOutRefundNo());

View File

@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbAndRedisUnitTest; import cn.iocoder.yudao.framework.test.core.ut.BaseDbAndRedisUnitTest;
import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO; import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO;
import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundExportReqVO; import cn.iocoder.yudao.module.pay.controller.admin.refund.vo.PayRefundExportReqVO;
@ -17,12 +18,14 @@ import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO;
import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO; import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO;
import cn.iocoder.yudao.module.pay.dal.mysql.refund.PayRefundMapper; import cn.iocoder.yudao.module.pay.dal.mysql.refund.PayRefundMapper;
import cn.iocoder.yudao.module.pay.dal.redis.no.PayNoRedisDAO; import cn.iocoder.yudao.module.pay.dal.redis.no.PayNoRedisDAO;
import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum;
import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum;
import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum; import cn.iocoder.yudao.module.pay.enums.refund.PayRefundStatusEnum;
import cn.iocoder.yudao.module.pay.framework.pay.config.PayProperties; import cn.iocoder.yudao.module.pay.framework.pay.config.PayProperties;
import cn.iocoder.yudao.module.pay.service.app.PayAppService; import cn.iocoder.yudao.module.pay.service.app.PayAppService;
import cn.iocoder.yudao.module.pay.service.channel.PayChannelService; import cn.iocoder.yudao.module.pay.service.channel.PayChannelService;
import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService; import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService;
import cn.iocoder.yudao.module.pay.service.notify.dto.PayNotifyTaskCreateReqDTO;
import cn.iocoder.yudao.module.pay.service.order.PayOrderService; import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -35,6 +38,7 @@ import java.util.List;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime;
import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException; import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
@ -42,8 +46,7 @@ import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString; import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*; import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
@ -439,4 +442,263 @@ public class PayRefundServiceTest extends BaseDbAndRedisUnitTest {
} }
} }
@Test
public void testNotifyRefund() {
PayRefundServiceImpl payRefundServiceImpl = mock(PayRefundServiceImpl.class);
try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PayRefundServiceImpl.class)))
.thenReturn(payRefundServiceImpl);
// 准备参数
Long channelId = 10L;
PayRefundRespDTO refundRespDTO = randomPojo(PayRefundRespDTO.class);
// mock 方法channel
PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L));
when(channelService.validPayChannel(eq(10L))).thenReturn(channel);
// 调用
refundService.notifyRefund(channelId, refundRespDTO);
// 断言
verify(payRefundServiceImpl).notifyRefund(same(channel), same(refundRespDTO));
}
}
@Test
public void testNotifyRefundSuccess_notFound() {
// 准备参数
PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L).setAppId(1L));
PayRefundRespDTO refundRespDTO = randomPojo(PayRefundRespDTO.class,
o -> o.setStatus(PayRefundStatusRespEnum.SUCCESS.getStatus()).setOutRefundNo("R100"));
// 调用并断言异常
assertServiceException(() -> refundService.notifyRefund(channel, refundRespDTO),
REFUND_NOT_FOUND);
}
@Test
public void testNotifyRefundSuccess_isSuccess() {
// 准备参数
PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L).setAppId(1L));
PayRefundRespDTO refundRespDTO = randomPojo(PayRefundRespDTO.class,
o -> o.setStatus(PayRefundStatusRespEnum.SUCCESS.getStatus()).setOutRefundNo("R100"));
// mock 数据refund + 已支付
PayRefundDO refund = randomPojo(PayRefundDO.class, o -> o.setAppId(1L).setNo("R100")
.setStatus(PayRefundStatusEnum.SUCCESS.getStatus()));
refundMapper.insert(refund);
// 调用
refundService.notifyRefund(channel, refundRespDTO);
// 断言refund 没有更新因为已经退款成功
assertPojoEquals(refund, refundMapper.selectById(refund.getId()));
}
@Test
public void testNotifyRefundSuccess_failure() {
// 准备参数
PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L).setAppId(1L));
PayRefundRespDTO refundRespDTO = randomPojo(PayRefundRespDTO.class,
o -> o.setStatus(PayRefundStatusRespEnum.SUCCESS.getStatus()).setOutRefundNo("R100"));
// mock 数据refund + 已支付
PayRefundDO refund = randomPojo(PayRefundDO.class, o -> o.setAppId(1L).setNo("R100")
.setStatus(PayRefundStatusEnum.FAILURE.getStatus()));
refundMapper.insert(refund);
// 调用并断言异常
assertServiceException(() -> refundService.notifyRefund(channel, refundRespDTO),
REFUND_STATUS_IS_NOT_WAITING);
}
@Test
public void testNotifyRefundSuccess_updateOrderException() {
// 准备参数
PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L).setAppId(1L));
PayRefundRespDTO refundRespDTO = randomPojo(PayRefundRespDTO.class,
o -> o.setStatus(PayRefundStatusRespEnum.SUCCESS.getStatus()).setOutRefundNo("R100"));
// mock 数据refund + 已支付
PayRefundDO refund = randomPojo(PayRefundDO.class, o -> o.setAppId(1L).setNo("R100")
.setStatus(PayRefundStatusEnum.WAITING.getStatus())
.setOrderId(100L).setRefundPrice(23));
refundMapper.insert(refund);
// mock 方法order + 更新异常
doThrow(new RuntimeException()).when(orderService)
.updateOrderRefundPrice(eq(100L), eq(23));
// 调用并断言异常
assertThrows(RuntimeException.class, () -> refundService.notifyRefund(channel, refundRespDTO));
// 断言refund 没有更新因为事务回滚了
assertPojoEquals(refund, refundMapper.selectById(refund.getId()));
}
@Test
public void testNotifyRefundSuccess_success() {
// 准备参数
PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L).setAppId(1L));
PayRefundRespDTO refundRespDTO = randomPojo(PayRefundRespDTO.class,
o -> o.setStatus(PayRefundStatusRespEnum.SUCCESS.getStatus()).setOutRefundNo("R100"));
// mock 数据refund + 已支付
PayRefundDO refund = randomPojo(PayRefundDO.class, o -> o.setAppId(1L).setNo("R100")
.setStatus(PayRefundStatusEnum.WAITING.getStatus())
.setOrderId(100L).setRefundPrice(23));
refundMapper.insert(refund);
// 调用
refundService.notifyRefund(channel, refundRespDTO);
// 断言refund
refund.setSuccessTime(refundRespDTO.getSuccessTime())
.setChannelRefundNo(refundRespDTO.getChannelRefundNo())
.setStatus(PayRefundStatusEnum.SUCCESS.getStatus())
.setChannelNotifyData(toJsonString(refundRespDTO));
assertPojoEquals(refund, refundMapper.selectById(refund.getId()),
"updateTime", "updater");
// 断言调用
verify(orderService).updateOrderRefundPrice(eq(100L), eq(23));
verify(notifyService).createPayNotifyTask(eq(PayNotifyTaskCreateReqDTO.builder()
.type(PayNotifyTypeEnum.REFUND.getType()).dataId(refund.getId()).build()));
}
@Test
public void testNotifyRefundFailure_notFound() {
// 准备参数
PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L).setAppId(1L));
PayRefundRespDTO refundRespDTO = randomPojo(PayRefundRespDTO.class,
o -> o.setStatus(PayRefundStatusRespEnum.FAILURE.getStatus()).setOutRefundNo("R100"));
// 调用并断言异常
assertServiceException(() -> refundService.notifyRefund(channel, refundRespDTO),
REFUND_NOT_FOUND);
}
@Test
public void testNotifyRefundFailure_isFailure() {
// 准备参数
PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L).setAppId(1L));
PayRefundRespDTO refundRespDTO = randomPojo(PayRefundRespDTO.class,
o -> o.setStatus(PayRefundStatusRespEnum.FAILURE.getStatus()).setOutRefundNo("R100"));
// mock 数据refund + 退款失败
PayRefundDO refund = randomPojo(PayRefundDO.class, o -> o.setAppId(1L).setNo("R100")
.setStatus(PayRefundStatusEnum.FAILURE.getStatus()));
refundMapper.insert(refund);
// 调用
refundService.notifyRefund(channel, refundRespDTO);
// 断言refund 没有更新因为已经退款失败
assertPojoEquals(refund, refundMapper.selectById(refund.getId()));
}
@Test
public void testNotifyRefundFailure_isSuccess() {
// 准备参数
PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L).setAppId(1L));
PayRefundRespDTO refundRespDTO = randomPojo(PayRefundRespDTO.class,
o -> o.setStatus(PayRefundStatusRespEnum.FAILURE.getStatus()).setOutRefundNo("R100"));
// mock 数据refund + 已支付
PayRefundDO refund = randomPojo(PayRefundDO.class, o -> o.setAppId(1L).setNo("R100")
.setStatus(PayRefundStatusEnum.SUCCESS.getStatus()));
refundMapper.insert(refund);
// 调用并断言异常
assertServiceException(() -> refundService.notifyRefund(channel, refundRespDTO),
REFUND_STATUS_IS_NOT_WAITING);
}
@Test
public void testNotifyRefundFailure_success() {
// 准备参数
PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L).setAppId(1L));
PayRefundRespDTO refundRespDTO = randomPojo(PayRefundRespDTO.class,
o -> o.setStatus(PayRefundStatusRespEnum.FAILURE.getStatus()).setOutRefundNo("R100"));
// mock 数据refund + 已支付
PayRefundDO refund = randomPojo(PayRefundDO.class, o -> o.setAppId(1L).setNo("R100")
.setStatus(PayRefundStatusEnum.WAITING.getStatus())
.setOrderId(100L).setRefundPrice(23));
refundMapper.insert(refund);
// 调用
refundService.notifyRefund(channel, refundRespDTO);
// 断言refund
refund.setChannelRefundNo(refundRespDTO.getChannelRefundNo())
.setStatus(PayRefundStatusEnum.FAILURE.getStatus())
.setChannelNotifyData(toJsonString(refundRespDTO))
.setChannelErrorCode(refundRespDTO.getChannelErrorCode())
.setChannelErrorMsg(refundRespDTO.getChannelErrorMsg());
assertPojoEquals(refund, refundMapper.selectById(refund.getId()),
"updateTime", "updater");
// 断言调用
verify(notifyService).createPayNotifyTask(eq(PayNotifyTaskCreateReqDTO.builder()
.type(PayNotifyTypeEnum.REFUND.getType()).dataId(refund.getId()).build()));
}
@Test
public void testSyncRefund_notFound() {
// 准备参数
PayRefundDO refund = randomPojo(PayRefundDO.class, o -> o.setAppId(1L)
.setStatus(PayRefundStatusEnum.WAITING.getStatus()));
refundMapper.insert(refund);
// 调用
int count = refundService.syncRefund();
// 断言
assertEquals(count, 0);
}
@Test
public void testSyncRefund_waiting() {
assertEquals(testSyncRefund_waitingOrSuccessOrFailure(PayRefundStatusRespEnum.WAITING.getStatus()), 0);
}
@Test
public void testSyncRefund_success() {
assertEquals(testSyncRefund_waitingOrSuccessOrFailure(PayRefundStatusRespEnum.SUCCESS.getStatus()), 1);
}
@Test
public void testSyncRefund_failure() {
assertEquals(testSyncRefund_waitingOrSuccessOrFailure(PayRefundStatusRespEnum.FAILURE.getStatus()), 1);
}
private int testSyncRefund_waitingOrSuccessOrFailure(Integer status) {
PayRefundServiceImpl payRefundServiceImpl = mock(PayRefundServiceImpl.class);
try (MockedStatic<SpringUtil> springUtilMockedStatic = mockStatic(SpringUtil.class)) {
springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PayRefundServiceImpl.class)))
.thenReturn(payRefundServiceImpl);
// 准备参数
PayRefundDO refund = randomPojo(PayRefundDO.class, o -> o.setAppId(1L).setChannelId(10L)
.setStatus(PayRefundStatusEnum.WAITING.getStatus())
.setOrderNo("P110").setNo("R220"));
refundMapper.insert(refund);
// mock 方法client
PayClient client = mock(PayClient.class);
when(payClientFactory.getPayClient(eq(10L))).thenReturn(client);
// mock 方法client 返回指定状态
PayRefundRespDTO respDTO = randomPojo(PayRefundRespDTO.class, o -> o.setStatus(status));
when(client.getRefund(eq("P110"), eq("R220"))).thenReturn(respDTO);
// mock 方法channel
PayChannelDO channel = randomPojo(PayChannelDO.class, o -> o.setId(10L));
when(channelService.validPayChannel(eq(10L))).thenReturn(channel);
// 调用
return refundService.syncRefund();
}
}
@Test
public void testSyncRefund_exception() {
// 准备参数
PayRefundDO refund = randomPojo(PayRefundDO.class, o -> o.setAppId(1L).setChannelId(10L)
.setStatus(PayRefundStatusEnum.WAITING.getStatus())
.setOrderNo("P110").setNo("R220"));
refundMapper.insert(refund);
// mock 方法client
PayClient client = mock(PayClient.class);
when(payClientFactory.getPayClient(eq(10L))).thenReturn(client);
// mock 方法client 抛出异常
when(client.getRefund(eq("P110"), eq("R220"))).thenThrow(new RuntimeException());
// 调用
int count = refundService.syncRefund();
// 断言
assertEquals(count, 0);
}
} }