package com.tourguide.modules.order.service; import com.tourguide.modules.order.domain.RefundStatus; 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()); } 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; } }