package com.tourguide.modules.order.service;
|
|
import com.tourguide.modules.order.domain.RefundStatus;
|
import com.tourguide.modules.order.domain.PaymentMethod;
|
import com.tourguide.modules.order.entity.TourOrder;
|
import com.tourguide.modules.order.mapper.TourOrderMapper;
|
import com.tourguide.modules.order.payment.PaymentRefundGateway;
|
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.Test;
|
|
import java.math.BigDecimal;
|
import java.time.LocalDateTime;
|
|
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.when;
|
|
class OrderRefundServiceTest {
|
private TourOrderMapper orderMapper;
|
private PaymentRefundGateway refundGateway;
|
private OrderRefundService refundService;
|
|
@BeforeEach
|
void setUp() {
|
orderMapper = mock(TourOrderMapper.class);
|
refundGateway = mock(PaymentRefundGateway.class);
|
refundService = new OrderRefundService(orderMapper, refundGateway);
|
}
|
|
@Test
|
void paidCancellationShouldSubmitRefundOnce() {
|
TourOrder order = paidOrder();
|
when(refundGateway.refund(org.mockito.ArgumentMatchers.eq(order),
|
org.mockito.ArgumentMatchers.eq(new BigDecimal("36.00"))))
|
.thenAnswer(invocation -> order.getRefundOrderNo());
|
|
refundService.requestRefund(order);
|
refundService.requestRefund(order);
|
|
assertThat(order.getRefundStatus()).isEqualTo(RefundStatus.REFUNDING.getCode());
|
assertThat(order.getRefundOrderNo()).startsWith("RF").hasSize(32);
|
assertThat(order.getRefundAmount()).isEqualByComparingTo("36.00");
|
assertThat(order.getRefundMethod()).isEqualTo("WECHAT");
|
verify(refundGateway, times(1)).refund(order, new BigDecimal("36.00"));
|
verify(orderMapper, times(1)).updateById(order);
|
}
|
|
@Test
|
void gatewayFailureShouldRecordRefundFailure() {
|
TourOrder order = paidOrder();
|
when(refundGateway.refund(org.mockito.ArgumentMatchers.eq(order),
|
org.mockito.ArgumentMatchers.any(BigDecimal.class)))
|
.thenThrow(new IllegalStateException("gateway unavailable"));
|
|
refundService.requestRefund(order);
|
|
assertThat(order.getRefundStatus()).isEqualTo(RefundStatus.REFUND_FAILED.getCode());
|
verify(orderMapper, times(2)).updateById(order);
|
}
|
|
@Test
|
void unpaidOrderShouldNotRequestRefund() {
|
TourOrder order = paidOrder();
|
order.setPaymentTime(null);
|
|
refundService.requestRefund(order);
|
|
verify(refundGateway, never()).refund(org.mockito.ArgumentMatchers.any(),
|
org.mockito.ArgumentMatchers.any());
|
}
|
|
@Test
|
void temporaryBypassRefundShouldCompleteWithoutCallingGateway() {
|
TourOrder order = paidOrder();
|
order.setPaymentMethod(PaymentMethod.TEMPORARY_BYPASS.getCode());
|
order.setPaymentOrderNo("TEMPMJ202608030001");
|
|
refundService.requestRefund(order);
|
|
assertThat(order.getRefundStatus()).isEqualTo(RefundStatus.REFUNDED.getCode());
|
assertThat(order.getRefundTime()).isNotNull();
|
assertThat(order.getRefundMethod()).isEqualTo(PaymentMethod.TEMPORARY_BYPASS.getCode());
|
verify(refundGateway, never()).refund(org.mockito.ArgumentMatchers.any(),
|
org.mockito.ArgumentMatchers.any());
|
verify(orderMapper).updateById(order);
|
}
|
|
private TourOrder paidOrder() {
|
TourOrder order = new TourOrder();
|
order.setId(99L);
|
order.setOrderNo("MJ202608030001");
|
order.setPaymentOrderNo("PY202608031700000000000000000001");
|
order.setPaymentMethod("WECHAT");
|
order.setAmount(new BigDecimal("40.00"));
|
order.setPaymentAmount(new BigDecimal("36.00"));
|
order.setPaymentTime(LocalDateTime.now());
|
order.setRefundStatus(RefundStatus.NONE.getCode());
|
return order;
|
}
|
}
|