完成自动化测试套件实施(W1-W11)
W1-W3: 基线修复与测试基础设施搭建 - 修复 Jenkins JDK 21 兼容性,统一 E2E 目录,修复 storageState 冲突 - 搭建后端测试基类 BaseContractTest + Testcontainers PostgreSQL - 创建 TestDataFactory 链式构造,完善 Vitest 基座与 Playwright fixtures - 建立 docker-compose.test.yml 与测试数据隔离方案 W4-W5: 单元测试补齐(阶段 2) - 补齐 gym-member/gym-groupCourse/gym-checkIn/gym-payment 核心模块单元测试 - 补齐 gym-coach/manage-sys 模块单元测试 - 前端 utils/composables/stores 单元测试,37 文件 502 项测试 - JaCoCo 覆盖率门禁从 30% 调整至 55%,21 模块全部通过 W6-W7: 集成与契约测试(阶段 3) - Repository 集成测试:会员/团课/签到/支付关键表,Testcontainers 100% 通过 - Handler 集成测试:WebTestClient 覆盖正向/异常/权限路径 - 网关集成测试:JWT/RBAC/签名/限流/重试 - Flyway 迁移测试:验证迁移脚本可重复执行 - OpenAPI 契约测试:覆盖 ≥80% P0 接口,202 项契约测试 0 失败 - 跨模块契约测试:会员-支付-团课数据一致性 W8-W9: E2E 与用户旅程测试(阶段 4) - 管理员 Web 核心流程 E2E:用户/角色/菜单/字典/配置 - 小程序会员端核心页面 E2E:购卡/预约/签到 - 5 条 P0 用户旅程全链路自动化,60 条 journey 测试 0 失败 W10: 变异测试与质量门禁(阶段 5) - 后端 PIT 配置:pitest-maven 1.19.1 + JUnit 5,覆盖率阈值 55%/变异阈值 45% - P0 模块基线:manage-sys 48%,gym-member 30%,gym-payment 36% - 前端 StrykerJS 配置:utils/stores 变异测试,dateFormat.ts 70.83% - Jenkins 质量门禁:JaCoCo/PIT/E2E 统一检查,不达标阻断构建 W11: 持续运行与改进(阶段 6) - 测试指标收集脚本 scripts/collect-test-metrics.py + HTML 看板生成器 - Flaky Test 治理 SOP:检测→隔离→根因分析→修复→验证闭环 - 测试资产定期评审流程:月度/季度/事件驱动三级机制 - 快速参考指南 docs/testing/quick-reference.md - 累计 10 份测试文档,7 个里程碑全部达成
This commit was merged in pull request #54.
This commit is contained in:
@@ -71,5 +71,10 @@
|
||||
<version>1.0.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
+655
@@ -0,0 +1,655 @@
|
||||
package cn.novalon.gym.manage.payment.handler;
|
||||
|
||||
import cn.novalon.gym.manage.payment.dto.ApiResponse;
|
||||
import cn.novalon.gym.manage.payment.dto.CreatePaymentRequest;
|
||||
import cn.novalon.gym.manage.payment.dto.PaymentResponse;
|
||||
import cn.novalon.gym.manage.payment.service.PaymentService;
|
||||
import cn.novalon.gym.manage.sys.util.AuthUtil;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.mock.web.reactive.function.server.MockServerRequest;
|
||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class PaymentHandlerTest {
|
||||
|
||||
@Mock
|
||||
private PaymentService paymentService;
|
||||
|
||||
@Mock
|
||||
private AuthUtil authUtil;
|
||||
|
||||
private PaymentHandler paymentHandler;
|
||||
|
||||
private static final Long MEMBER_ID = 10001L;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
paymentHandler = new PaymentHandler(paymentService, authUtil);
|
||||
}
|
||||
|
||||
// ==================== createPayment ====================
|
||||
|
||||
@Test
|
||||
void createPayment_shouldReturnOk() {
|
||||
CreatePaymentRequest createReq = new CreatePaymentRequest();
|
||||
createReq.setOrderType("MEMBER_CARD");
|
||||
createReq.setGoodsDesc("会员卡");
|
||||
createReq.setTransAmt("100");
|
||||
|
||||
PaymentResponse paymentResp = PaymentResponse.builder()
|
||||
.orderId("PAY2026073101")
|
||||
.status("PENDING")
|
||||
.build();
|
||||
|
||||
when(authUtil.getMemberIdOrThrow(any())).thenReturn(MEMBER_ID);
|
||||
when(paymentService.alipayAppPay(eq(MEMBER_ID), any(CreatePaymentRequest.class)))
|
||||
.thenReturn(Mono.just(paymentResp));
|
||||
|
||||
MockServerRequest request = MockServerRequest.builder()
|
||||
.body(Mono.just(createReq));
|
||||
|
||||
Mono<ServerResponse> result = paymentHandler.createPayment(request);
|
||||
|
||||
ServerResponse response = result.block();
|
||||
assertThat(response.statusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
verify(paymentService).alipayAppPay(eq(MEMBER_ID), any(CreatePaymentRequest.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createPayment_shouldReturnErrorWhenServiceReturnsFail() {
|
||||
CreatePaymentRequest createReq = new CreatePaymentRequest();
|
||||
createReq.setOrderType("MEMBER_CARD");
|
||||
createReq.setGoodsDesc("会员卡");
|
||||
createReq.setTransAmt("100");
|
||||
|
||||
PaymentResponse paymentResp = PaymentResponse.builder()
|
||||
.status("FAIL")
|
||||
.errorMsg("余额不足")
|
||||
.build();
|
||||
|
||||
when(authUtil.getMemberIdOrThrow(any())).thenReturn(MEMBER_ID);
|
||||
when(paymentService.alipayAppPay(eq(MEMBER_ID), any(CreatePaymentRequest.class)))
|
||||
.thenReturn(Mono.just(paymentResp));
|
||||
|
||||
MockServerRequest request = MockServerRequest.builder()
|
||||
.body(Mono.just(createReq));
|
||||
|
||||
Mono<ServerResponse> result = paymentHandler.createPayment(request);
|
||||
|
||||
ServerResponse response = result.block();
|
||||
assertThat(response.statusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
verify(paymentService).alipayAppPay(eq(MEMBER_ID), any(CreatePaymentRequest.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createPayment_shouldHandleException() {
|
||||
CreatePaymentRequest createReq = new CreatePaymentRequest();
|
||||
|
||||
when(authUtil.getMemberIdOrThrow(any())).thenReturn(MEMBER_ID);
|
||||
when(paymentService.alipayAppPay(eq(MEMBER_ID), any(CreatePaymentRequest.class)))
|
||||
.thenReturn(Mono.error(new RuntimeException("服务异常")));
|
||||
|
||||
MockServerRequest request = MockServerRequest.builder()
|
||||
.body(Mono.just(createReq));
|
||||
|
||||
Mono<ServerResponse> result = paymentHandler.createPayment(request);
|
||||
|
||||
ServerResponse response = result.block();
|
||||
assertThat(response.statusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
verify(paymentService).alipayAppPay(eq(MEMBER_ID), any(CreatePaymentRequest.class));
|
||||
}
|
||||
|
||||
// ==================== createQrCodePayment ====================
|
||||
|
||||
@Test
|
||||
void createQrCodePayment_shouldReturnOk() {
|
||||
CreatePaymentRequest createReq = new CreatePaymentRequest();
|
||||
createReq.setOrderType("MEMBER_CARD");
|
||||
createReq.setGoodsDesc("会员卡");
|
||||
createReq.setTransAmt("100");
|
||||
|
||||
PaymentResponse paymentResp = PaymentResponse.builder()
|
||||
.orderId("PAY2026073102")
|
||||
.status("PENDING")
|
||||
.qrCode("https://qr.alipay.com/test")
|
||||
.build();
|
||||
|
||||
when(authUtil.getMemberIdOrThrow(any())).thenReturn(MEMBER_ID);
|
||||
when(paymentService.alipayQrCodePay(eq(MEMBER_ID), any(CreatePaymentRequest.class)))
|
||||
.thenReturn(Mono.just(paymentResp));
|
||||
|
||||
MockServerRequest request = MockServerRequest.builder()
|
||||
.body(Mono.just(createReq));
|
||||
|
||||
Mono<ServerResponse> result = paymentHandler.createQrCodePayment(request);
|
||||
|
||||
ServerResponse response = result.block();
|
||||
assertThat(response.statusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
verify(paymentService).alipayQrCodePay(eq(MEMBER_ID), any(CreatePaymentRequest.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createQrCodePayment_shouldReturnErrorWhenServiceReturnsFail() {
|
||||
CreatePaymentRequest createReq = new CreatePaymentRequest();
|
||||
createReq.setOrderType("MEMBER_CARD");
|
||||
createReq.setGoodsDesc("会员卡");
|
||||
createReq.setTransAmt("100");
|
||||
|
||||
PaymentResponse paymentResp = PaymentResponse.builder()
|
||||
.status("FAIL")
|
||||
.errorMsg("下单失败")
|
||||
.build();
|
||||
|
||||
when(authUtil.getMemberIdOrThrow(any())).thenReturn(MEMBER_ID);
|
||||
when(paymentService.alipayQrCodePay(eq(MEMBER_ID), any(CreatePaymentRequest.class)))
|
||||
.thenReturn(Mono.just(paymentResp));
|
||||
|
||||
MockServerRequest request = MockServerRequest.builder()
|
||||
.body(Mono.just(createReq));
|
||||
|
||||
Mono<ServerResponse> result = paymentHandler.createQrCodePayment(request);
|
||||
|
||||
ServerResponse response = result.block();
|
||||
assertThat(response.statusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
verify(paymentService).alipayQrCodePay(eq(MEMBER_ID), any(CreatePaymentRequest.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createQrCodePayment_shouldHandleException() {
|
||||
CreatePaymentRequest createReq = new CreatePaymentRequest();
|
||||
|
||||
when(authUtil.getMemberIdOrThrow(any())).thenReturn(MEMBER_ID);
|
||||
when(paymentService.alipayQrCodePay(eq(MEMBER_ID), any(CreatePaymentRequest.class)))
|
||||
.thenReturn(Mono.error(new RuntimeException("服务异常")));
|
||||
|
||||
MockServerRequest request = MockServerRequest.builder()
|
||||
.body(Mono.just(createReq));
|
||||
|
||||
Mono<ServerResponse> result = paymentHandler.createQrCodePayment(request);
|
||||
|
||||
ServerResponse response = result.block();
|
||||
assertThat(response.statusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
verify(paymentService).alipayQrCodePay(eq(MEMBER_ID), any(CreatePaymentRequest.class));
|
||||
}
|
||||
|
||||
// ==================== getPaymentStatus ====================
|
||||
|
||||
@Test
|
||||
void getPaymentStatus_shouldReturnOk() {
|
||||
PaymentResponse paymentResp = PaymentResponse.builder()
|
||||
.orderId("PAY2026073101")
|
||||
.status("SUCCESS")
|
||||
.build();
|
||||
|
||||
when(authUtil.getMemberIdOrThrow(any())).thenReturn(MEMBER_ID);
|
||||
when(paymentService.getPaymentStatus(MEMBER_ID, "PAY2026073101"))
|
||||
.thenReturn(Mono.just(paymentResp));
|
||||
|
||||
MockServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("orderId", "PAY2026073101")
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> result = paymentHandler.getPaymentStatus(request);
|
||||
|
||||
ServerResponse response = result.block();
|
||||
assertThat(response.statusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
verify(paymentService).getPaymentStatus(MEMBER_ID, "PAY2026073101");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getPaymentStatus_shouldHandleException() {
|
||||
when(authUtil.getMemberIdOrThrow(any())).thenReturn(MEMBER_ID);
|
||||
when(paymentService.getPaymentStatus(MEMBER_ID, "PAY2026073101"))
|
||||
.thenReturn(Mono.error(new RuntimeException("查询失败")));
|
||||
|
||||
MockServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("orderId", "PAY2026073101")
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> result = paymentHandler.getPaymentStatus(request);
|
||||
|
||||
ServerResponse response = result.block();
|
||||
assertThat(response.statusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
verify(paymentService).getPaymentStatus(MEMBER_ID, "PAY2026073101");
|
||||
}
|
||||
|
||||
// ==================== huifuNotify ====================
|
||||
|
||||
@Test
|
||||
void huifuNotify_shouldReturnOk() {
|
||||
when(authUtil.getMemberIdOrThrow(any())).thenReturn(MEMBER_ID);
|
||||
when(paymentService.handleAlipayNotify(anyMap()))
|
||||
.thenReturn(Mono.just("RECV_ORD_ID_test123"));
|
||||
|
||||
MockServerRequest request = MockServerRequest.builder()
|
||||
.queryParam("resp_data", "{\"req_seq_id\":\"test123\",\"trans_stat\":\"S\"}")
|
||||
.queryParam("sign", "testSign")
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> result = paymentHandler.huifuNotify(request);
|
||||
|
||||
ServerResponse response = result.block();
|
||||
assertThat(response.statusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
verify(paymentService).handleAlipayNotify(anyMap());
|
||||
}
|
||||
|
||||
@Test
|
||||
void huifuNotify_shouldHandleException() {
|
||||
when(authUtil.getMemberIdOrThrow(any())).thenReturn(MEMBER_ID);
|
||||
when(paymentService.handleAlipayNotify(anyMap()))
|
||||
.thenReturn(Mono.error(new RuntimeException("通知处理失败")));
|
||||
|
||||
MockServerRequest request = MockServerRequest.builder()
|
||||
.queryParam("resp_data", "{\"req_seq_id\":\"test123\",\"trans_stat\":\"S\"}")
|
||||
.queryParam("sign", "testSign")
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> result = paymentHandler.huifuNotify(request);
|
||||
|
||||
ServerResponse response = result.block();
|
||||
assertThat(response.statusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
verify(paymentService).handleAlipayNotify(anyMap());
|
||||
}
|
||||
|
||||
// ==================== refundPayment ====================
|
||||
|
||||
@Test
|
||||
void refundPayment_shouldReturnOkWhenRefundSuccess() {
|
||||
Map<String, Object> body = Map.of("refundAmt", "50");
|
||||
|
||||
when(authUtil.getMemberIdOrThrow(any())).thenReturn(MEMBER_ID);
|
||||
when(paymentService.refund(MEMBER_ID, "PAY2026073101", "50"))
|
||||
.thenReturn(Mono.just(true));
|
||||
|
||||
MockServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("orderId", "PAY2026073101")
|
||||
.body(Mono.just(body));
|
||||
|
||||
Mono<ServerResponse> result = paymentHandler.refundPayment(request);
|
||||
|
||||
ServerResponse response = result.block();
|
||||
assertThat(response.statusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
verify(paymentService).refund(MEMBER_ID, "PAY2026073101", "50");
|
||||
}
|
||||
|
||||
@Test
|
||||
void refundPayment_shouldReturnErrorWhenRefundFails() {
|
||||
Map<String, Object> body = Map.of("refundAmt", "50");
|
||||
|
||||
when(authUtil.getMemberIdOrThrow(any())).thenReturn(MEMBER_ID);
|
||||
when(paymentService.refund(MEMBER_ID, "PAY2026073101", "50"))
|
||||
.thenReturn(Mono.just(false));
|
||||
|
||||
MockServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("orderId", "PAY2026073101")
|
||||
.body(Mono.just(body));
|
||||
|
||||
Mono<ServerResponse> result = paymentHandler.refundPayment(request);
|
||||
|
||||
ServerResponse response = result.block();
|
||||
assertThat(response.statusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
verify(paymentService).refund(MEMBER_ID, "PAY2026073101", "50");
|
||||
}
|
||||
|
||||
@Test
|
||||
void refundPayment_shouldHandleException() {
|
||||
Map<String, Object> body = Map.of("refundAmt", "50");
|
||||
|
||||
when(authUtil.getMemberIdOrThrow(any())).thenReturn(MEMBER_ID);
|
||||
when(paymentService.refund(MEMBER_ID, "PAY2026073101", "50"))
|
||||
.thenReturn(Mono.error(new RuntimeException("退款异常")));
|
||||
|
||||
MockServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("orderId", "PAY2026073101")
|
||||
.body(Mono.just(body));
|
||||
|
||||
Mono<ServerResponse> result = paymentHandler.refundPayment(request);
|
||||
|
||||
ServerResponse response = result.block();
|
||||
assertThat(response.statusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
verify(paymentService).refund(MEMBER_ID, "PAY2026073101", "50");
|
||||
}
|
||||
|
||||
// ==================== getAllPaymentOrders ====================
|
||||
|
||||
@Test
|
||||
void getAllPaymentOrders_shouldReturnOk() {
|
||||
PaymentResponse order1 = PaymentResponse.builder().orderId("PAY001").status("SUCCESS").build();
|
||||
PaymentResponse order2 = PaymentResponse.builder().orderId("PAY002").status("PENDING").build();
|
||||
|
||||
when(authUtil.getMemberIdOrThrow(any())).thenReturn(MEMBER_ID);
|
||||
when(paymentService.getAllPaymentOrders(MEMBER_ID))
|
||||
.thenReturn(Flux.just(order1, order2));
|
||||
|
||||
MockServerRequest request = MockServerRequest.builder().build();
|
||||
|
||||
Mono<ServerResponse> result = paymentHandler.getAllPaymentOrders(request);
|
||||
|
||||
ServerResponse response = result.block();
|
||||
assertThat(response.statusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
verify(paymentService).getAllPaymentOrders(MEMBER_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAllPaymentOrders_shouldReturnEmptyList() {
|
||||
when(authUtil.getMemberIdOrThrow(any())).thenReturn(MEMBER_ID);
|
||||
when(paymentService.getAllPaymentOrders(MEMBER_ID))
|
||||
.thenReturn(Flux.empty());
|
||||
|
||||
MockServerRequest request = MockServerRequest.builder().build();
|
||||
|
||||
Mono<ServerResponse> result = paymentHandler.getAllPaymentOrders(request);
|
||||
|
||||
ServerResponse response = result.block();
|
||||
assertThat(response.statusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
verify(paymentService).getAllPaymentOrders(MEMBER_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAllPaymentOrders_shouldHandleException() {
|
||||
when(authUtil.getMemberIdOrThrow(any())).thenReturn(MEMBER_ID);
|
||||
when(paymentService.getAllPaymentOrders(MEMBER_ID))
|
||||
.thenReturn(Flux.error(new RuntimeException("查询异常")));
|
||||
|
||||
MockServerRequest request = MockServerRequest.builder().build();
|
||||
|
||||
Mono<ServerResponse> result = paymentHandler.getAllPaymentOrders(request);
|
||||
|
||||
ServerResponse response = result.block();
|
||||
assertThat(response.statusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
verify(paymentService).getAllPaymentOrders(MEMBER_ID);
|
||||
}
|
||||
|
||||
// ==================== queryHuifuTradeList ====================
|
||||
|
||||
@Test
|
||||
void queryHuifuTradeList_shouldReturnOk() {
|
||||
Map<String, Object> resultMap = Map.of("total_count", 5, "trade_list", List.of());
|
||||
|
||||
when(authUtil.getMemberIdOrThrow(any())).thenReturn(MEMBER_ID);
|
||||
when(paymentService.queryHuifuTradeList(MEMBER_ID, "20260701", "20260731"))
|
||||
.thenReturn(Mono.just(resultMap));
|
||||
|
||||
MockServerRequest request = MockServerRequest.builder()
|
||||
.queryParam("beginDate", "20260701")
|
||||
.queryParam("endDate", "20260731")
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> result = paymentHandler.queryHuifuTradeList(request);
|
||||
|
||||
ServerResponse response = result.block();
|
||||
assertThat(response.statusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
verify(paymentService).queryHuifuTradeList(MEMBER_ID, "20260701", "20260731");
|
||||
}
|
||||
|
||||
@Test
|
||||
void queryHuifuTradeList_shouldReturnOkWithNullDates() {
|
||||
Map<String, Object> resultMap = Map.of("total_count", 0, "trade_list", List.of());
|
||||
|
||||
when(authUtil.getMemberIdOrThrow(any())).thenReturn(MEMBER_ID);
|
||||
when(paymentService.queryHuifuTradeList(MEMBER_ID, null, null))
|
||||
.thenReturn(Mono.just(resultMap));
|
||||
|
||||
MockServerRequest request = MockServerRequest.builder().build();
|
||||
|
||||
Mono<ServerResponse> result = paymentHandler.queryHuifuTradeList(request);
|
||||
|
||||
ServerResponse response = result.block();
|
||||
assertThat(response.statusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
verify(paymentService).queryHuifuTradeList(MEMBER_ID, null, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void queryHuifuTradeList_shouldHandleException() {
|
||||
when(authUtil.getMemberIdOrThrow(any())).thenReturn(MEMBER_ID);
|
||||
when(paymentService.queryHuifuTradeList(MEMBER_ID, null, null))
|
||||
.thenReturn(Mono.error(new RuntimeException("查询异常")));
|
||||
|
||||
MockServerRequest request = MockServerRequest.builder().build();
|
||||
|
||||
Mono<ServerResponse> result = paymentHandler.queryHuifuTradeList(request);
|
||||
|
||||
ServerResponse response = result.block();
|
||||
assertThat(response.statusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
verify(paymentService).queryHuifuTradeList(MEMBER_ID, null, null);
|
||||
}
|
||||
|
||||
// ==================== queryHuifuTradeByOrderId ====================
|
||||
|
||||
@Test
|
||||
void queryHuifuTradeByOrderId_shouldReturnOk() {
|
||||
Map<String, Object> resultMap = Map.of("success", true, "trade_info", Map.of());
|
||||
|
||||
when(authUtil.getMemberIdOrThrow(any())).thenReturn(MEMBER_ID);
|
||||
when(paymentService.queryHuifuTradeByOrderId(MEMBER_ID, "PAY2026073101", null))
|
||||
.thenReturn(Mono.just(resultMap));
|
||||
|
||||
MockServerRequest request = MockServerRequest.builder()
|
||||
.queryParam("outOrdId", "PAY2026073101")
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> result = paymentHandler.queryHuifuTradeByOrderId(request);
|
||||
|
||||
ServerResponse response = result.block();
|
||||
assertThat(response.statusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
verify(paymentService).queryHuifuTradeByOrderId(MEMBER_ID, "PAY2026073101", null);
|
||||
}
|
||||
|
||||
@Test
|
||||
void queryHuifuTradeByOrderId_shouldReturnOkWithHfSeqId() {
|
||||
Map<String, Object> resultMap = Map.of("success", true, "trade_info", Map.of());
|
||||
|
||||
when(authUtil.getMemberIdOrThrow(any())).thenReturn(MEMBER_ID);
|
||||
when(paymentService.queryHuifuTradeByOrderId(MEMBER_ID, "PAY2026073101", "HF2026073101"))
|
||||
.thenReturn(Mono.just(resultMap));
|
||||
|
||||
MockServerRequest request = MockServerRequest.builder()
|
||||
.queryParam("outOrdId", "PAY2026073101")
|
||||
.queryParam("hfSeqId", "HF2026073101")
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> result = paymentHandler.queryHuifuTradeByOrderId(request);
|
||||
|
||||
ServerResponse response = result.block();
|
||||
assertThat(response.statusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
verify(paymentService).queryHuifuTradeByOrderId(MEMBER_ID, "PAY2026073101", "HF2026073101");
|
||||
}
|
||||
|
||||
@Test
|
||||
void queryHuifuTradeByOrderId_shouldReturnErrorWhenOutOrdIdEmpty() {
|
||||
when(authUtil.getMemberIdOrThrow(any())).thenReturn(MEMBER_ID);
|
||||
|
||||
MockServerRequest request = MockServerRequest.builder().build();
|
||||
|
||||
Mono<ServerResponse> result = paymentHandler.queryHuifuTradeByOrderId(request);
|
||||
|
||||
ServerResponse response = result.block();
|
||||
assertThat(response.statusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
verify(paymentService, never()).queryHuifuTradeByOrderId(anyLong(), anyString(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void queryHuifuTradeByOrderId_shouldHandleException() {
|
||||
when(authUtil.getMemberIdOrThrow(any())).thenReturn(MEMBER_ID);
|
||||
when(paymentService.queryHuifuTradeByOrderId(MEMBER_ID, "PAY2026073101", null))
|
||||
.thenReturn(Mono.error(new RuntimeException("查询异常")));
|
||||
|
||||
MockServerRequest request = MockServerRequest.builder()
|
||||
.queryParam("outOrdId", "PAY2026073101")
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> result = paymentHandler.queryHuifuTradeByOrderId(request);
|
||||
|
||||
ServerResponse response = result.block();
|
||||
assertThat(response.statusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
verify(paymentService).queryHuifuTradeByOrderId(MEMBER_ID, "PAY2026073101", null);
|
||||
}
|
||||
|
||||
// ==================== getPendingOrder ====================
|
||||
|
||||
@Test
|
||||
void getPendingOrder_shouldReturnOk() {
|
||||
PaymentResponse paymentResp = PaymentResponse.builder()
|
||||
.orderId("PAY2026073101")
|
||||
.status("PENDING")
|
||||
.build();
|
||||
|
||||
when(paymentService.getPendingOrder(10002L, "MEMBER_CARD"))
|
||||
.thenReturn(Mono.just(paymentResp));
|
||||
|
||||
MockServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("memberId", "10002")
|
||||
.queryParam("orderType", "MEMBER_CARD")
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> result = paymentHandler.getPendingOrder(request);
|
||||
|
||||
ServerResponse response = result.block();
|
||||
assertThat(response.statusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
verify(paymentService).getPendingOrder(10002L, "MEMBER_CARD");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getPendingOrder_shouldReturnOkWhenOrderNotFound() {
|
||||
when(paymentService.getPendingOrder(10002L, "MEMBER_CARD"))
|
||||
.thenReturn(Mono.empty());
|
||||
|
||||
MockServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("memberId", "10002")
|
||||
.queryParam("orderType", "MEMBER_CARD")
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> result = paymentHandler.getPendingOrder(request);
|
||||
|
||||
ServerResponse response = result.block();
|
||||
assertThat(response.statusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
verify(paymentService).getPendingOrder(10002L, "MEMBER_CARD");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getPendingOrder_shouldReturnErrorWhenOrderTypeEmpty() {
|
||||
MockServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("memberId", "10002")
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> result = paymentHandler.getPendingOrder(request);
|
||||
|
||||
ServerResponse response = result.block();
|
||||
assertThat(response.statusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
verify(paymentService, never()).getPendingOrder(anyLong(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getPendingOrder_shouldHandleException() {
|
||||
when(paymentService.getPendingOrder(10002L, "MEMBER_CARD"))
|
||||
.thenReturn(Mono.error(new RuntimeException("查询异常")));
|
||||
|
||||
MockServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("memberId", "10002")
|
||||
.queryParam("orderType", "MEMBER_CARD")
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> result = paymentHandler.getPendingOrder(request);
|
||||
|
||||
ServerResponse response = result.block();
|
||||
assertThat(response.statusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
verify(paymentService).getPendingOrder(10002L, "MEMBER_CARD");
|
||||
}
|
||||
|
||||
// ==================== closeOrder ====================
|
||||
|
||||
@Test
|
||||
void closeOrder_shouldReturnOkWhenCloseSuccess() {
|
||||
when(authUtil.getMemberIdOrThrow(any())).thenReturn(MEMBER_ID);
|
||||
when(paymentService.closeOrder(MEMBER_ID, "PAY2026073101"))
|
||||
.thenReturn(Mono.just(true));
|
||||
|
||||
MockServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("orderId", "PAY2026073101")
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> result = paymentHandler.closeOrder(request);
|
||||
|
||||
ServerResponse response = result.block();
|
||||
assertThat(response.statusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
verify(paymentService).closeOrder(MEMBER_ID, "PAY2026073101");
|
||||
}
|
||||
|
||||
@Test
|
||||
void closeOrder_shouldReturnErrorWhenCloseFails() {
|
||||
when(authUtil.getMemberIdOrThrow(any())).thenReturn(MEMBER_ID);
|
||||
when(paymentService.closeOrder(MEMBER_ID, "PAY2026073101"))
|
||||
.thenReturn(Mono.just(false));
|
||||
|
||||
MockServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("orderId", "PAY2026073101")
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> result = paymentHandler.closeOrder(request);
|
||||
|
||||
ServerResponse response = result.block();
|
||||
assertThat(response.statusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
verify(paymentService).closeOrder(MEMBER_ID, "PAY2026073101");
|
||||
}
|
||||
|
||||
@Test
|
||||
void closeOrder_shouldHandleException() {
|
||||
when(authUtil.getMemberIdOrThrow(any())).thenReturn(MEMBER_ID);
|
||||
when(paymentService.closeOrder(MEMBER_ID, "PAY2026073101"))
|
||||
.thenReturn(Mono.error(new RuntimeException("关闭异常")));
|
||||
|
||||
MockServerRequest request = MockServerRequest.builder()
|
||||
.pathVariable("orderId", "PAY2026073101")
|
||||
.build();
|
||||
|
||||
Mono<ServerResponse> result = paymentHandler.closeOrder(request);
|
||||
|
||||
ServerResponse response = result.block();
|
||||
assertThat(response.statusCode()).isEqualTo(HttpStatus.OK);
|
||||
|
||||
verify(paymentService).closeOrder(MEMBER_ID, "PAY2026073101");
|
||||
}
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
package cn.novalon.gym.manage.payment.handler;
|
||||
|
||||
import cn.novalon.gym.manage.payment.entity.PaymentOrder;
|
||||
import cn.novalon.gym.manage.payment.repository.PaymentOrderRepository;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatCode;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class PaymentScheduledHandlerTest {
|
||||
|
||||
@Mock
|
||||
private PaymentOrderRepository paymentOrderRepository;
|
||||
|
||||
private PaymentScheduledHandler paymentScheduledHandler;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
paymentScheduledHandler = new PaymentScheduledHandler(paymentOrderRepository);
|
||||
}
|
||||
|
||||
// ==================== closeExpiredOrders ====================
|
||||
|
||||
@Test
|
||||
void closeExpiredOrders_shouldCloseExpiredOrdersSuccessfully() throws InterruptedException {
|
||||
// given
|
||||
PaymentOrder order1 = PaymentOrder.builder()
|
||||
.id(1L)
|
||||
.orderNo("PAY20260731001")
|
||||
.expireTime(LocalDateTime.now().minusMinutes(5))
|
||||
.build();
|
||||
PaymentOrder order2 = PaymentOrder.builder()
|
||||
.id(2L)
|
||||
.orderNo("PAY20260731002")
|
||||
.expireTime(LocalDateTime.now().minusMinutes(10))
|
||||
.build();
|
||||
|
||||
when(paymentOrderRepository.findExpiredOrders(any(LocalDateTime.class), anyInt()))
|
||||
.thenReturn(Flux.just(order1, order2));
|
||||
when(paymentOrderRepository.closeExpiredOrder(1L)).thenReturn(Mono.just(1));
|
||||
when(paymentOrderRepository.closeExpiredOrder(2L)).thenReturn(Mono.just(1));
|
||||
|
||||
// when
|
||||
paymentScheduledHandler.closeExpiredOrders();
|
||||
|
||||
// then - wait for async subscribe() to complete
|
||||
Thread.sleep(500);
|
||||
|
||||
verify(paymentOrderRepository).findExpiredOrders(any(LocalDateTime.class), eq(100));
|
||||
verify(paymentOrderRepository).closeExpiredOrder(1L);
|
||||
verify(paymentOrderRepository).closeExpiredOrder(2L);
|
||||
}
|
||||
|
||||
@Test
|
||||
void closeExpiredOrders_shouldDoNothingWhenNoExpiredOrders() throws InterruptedException {
|
||||
// given
|
||||
when(paymentOrderRepository.findExpiredOrders(any(LocalDateTime.class), anyInt()))
|
||||
.thenReturn(Flux.empty());
|
||||
|
||||
// when
|
||||
paymentScheduledHandler.closeExpiredOrders();
|
||||
|
||||
// then - wait for async subscribe() to complete
|
||||
Thread.sleep(500);
|
||||
|
||||
verify(paymentOrderRepository).findExpiredOrders(any(LocalDateTime.class), eq(100));
|
||||
verify(paymentOrderRepository, never()).closeExpiredOrder(anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
void closeExpiredOrders_shouldHandleErrorWhenCloseFails() throws InterruptedException {
|
||||
// given
|
||||
PaymentOrder order = PaymentOrder.builder()
|
||||
.id(1L)
|
||||
.orderNo("PAY20260731001")
|
||||
.expireTime(LocalDateTime.now().minusMinutes(5))
|
||||
.build();
|
||||
|
||||
when(paymentOrderRepository.findExpiredOrders(any(LocalDateTime.class), anyInt()))
|
||||
.thenReturn(Flux.just(order));
|
||||
when(paymentOrderRepository.closeExpiredOrder(1L))
|
||||
.thenReturn(Mono.error(new RuntimeException("关闭订单失败")));
|
||||
|
||||
// when & then - should not throw exception, error should be handled by onErrorResume
|
||||
assertThatCode(() -> {
|
||||
paymentScheduledHandler.closeExpiredOrders();
|
||||
Thread.sleep(500);
|
||||
}).doesNotThrowAnyException();
|
||||
|
||||
verify(paymentOrderRepository).findExpiredOrders(any(LocalDateTime.class), eq(100));
|
||||
verify(paymentOrderRepository).closeExpiredOrder(1L);
|
||||
}
|
||||
}
|
||||
+657
@@ -0,0 +1,657 @@
|
||||
package cn.novalon.gym.manage.payment.service.impl;
|
||||
|
||||
import cn.novalon.gym.manage.common.util.RedisUtil;
|
||||
import cn.novalon.gym.manage.payment.config.HuifuProperties;
|
||||
import cn.novalon.gym.manage.payment.dto.CreatePaymentRequest;
|
||||
import cn.novalon.gym.manage.payment.dto.PaymentResponse;
|
||||
import cn.novalon.gym.manage.payment.entity.PaymentOrder;
|
||||
import cn.novalon.gym.manage.payment.repository.PaymentOrderRepository;
|
||||
import cn.novalon.gym.manage.payment.service.PaymentNotifyService;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class PaymentServiceImplTest {
|
||||
|
||||
@Mock
|
||||
private HuifuProperties huifuProperties;
|
||||
|
||||
@Mock
|
||||
private PaymentOrderRepository paymentOrderRepository;
|
||||
|
||||
@Mock
|
||||
private RedisUtil redisUtil;
|
||||
|
||||
@Mock
|
||||
private PaymentNotifyService paymentNotifyService;
|
||||
|
||||
private PaymentServiceImpl paymentService;
|
||||
|
||||
private static final Long MEMBER_ID = 10001L;
|
||||
private static final String ORDER_ID = "PAY2026073100000001";
|
||||
private static final String ORDER_TYPE = "MEMBER_CARD";
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
HuifuProperties.MerchantConfig merchantConfig = mock(HuifuProperties.MerchantConfig.class);
|
||||
lenient().when(huifuProperties.getMerchant()).thenReturn(merchantConfig);
|
||||
lenient().when(merchantConfig.getHuifuId()).thenReturn("MOCK_HUIFU_ID");
|
||||
|
||||
HuifuProperties.NotifyConfig notifyConfig = mock(HuifuProperties.NotifyConfig.class);
|
||||
lenient().when(huifuProperties.getNotify()).thenReturn(notifyConfig);
|
||||
lenient().when(notifyConfig.getUrl()).thenReturn("http://mock.notify.url");
|
||||
|
||||
lenient().when(huifuProperties.isSdkInitialized()).thenReturn(false);
|
||||
|
||||
paymentService = new PaymentServiceImpl(huifuProperties, paymentOrderRepository, redisUtil, paymentNotifyService);
|
||||
}
|
||||
|
||||
private PaymentOrder createTestOrder(String status) {
|
||||
return PaymentOrder.builder()
|
||||
.orderNo(ORDER_ID)
|
||||
.memberId(MEMBER_ID)
|
||||
.orderType(ORDER_TYPE)
|
||||
.goodsDesc("会员卡")
|
||||
.transAmt(new BigDecimal("0.01"))
|
||||
.payStatus(status)
|
||||
.tradeType("ALIPAY")
|
||||
.huifuId("MOCK_HUIFU_ID")
|
||||
.reqSeqId("REQ123456")
|
||||
.reqDate("20260731")
|
||||
.remark("测试备注")
|
||||
.notifyUrl("http://mock.notify.url")
|
||||
.expireTime(LocalDateTime.now().plusMinutes(15))
|
||||
.build();
|
||||
}
|
||||
|
||||
private CreatePaymentRequest createTestRequest() {
|
||||
CreatePaymentRequest request = new CreatePaymentRequest();
|
||||
request.setOrderType(ORDER_TYPE);
|
||||
request.setGoodsDesc("会员卡");
|
||||
request.setTransAmt("100");
|
||||
request.setRemark("测试备注");
|
||||
return request;
|
||||
}
|
||||
|
||||
// ==================== createPayment ====================
|
||||
|
||||
@Test
|
||||
void createPayment_whenSdkNotInitialized_shouldReturnMockOrder() {
|
||||
CreatePaymentRequest request = createTestRequest();
|
||||
|
||||
when(paymentOrderRepository.save(any(PaymentOrder.class))).thenAnswer(invocation -> {
|
||||
PaymentOrder saved = invocation.getArgument(0);
|
||||
saved.setId(1L);
|
||||
return Mono.just(saved);
|
||||
});
|
||||
|
||||
Mono<PaymentResponse> result = paymentService.createPayment(MEMBER_ID, request);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.assertNext(response -> {
|
||||
assertThat(response.getStatus()).isEqualTo("PENDING");
|
||||
assertThat(response.getOrderId()).isNotNull();
|
||||
assertThat(response.getPayInfo()).contains("alipays://");
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(paymentOrderRepository).save(any(PaymentOrder.class));
|
||||
}
|
||||
|
||||
// ==================== alipayAppPay ====================
|
||||
|
||||
@Test
|
||||
void alipayAppPay_whenSdkNotInitialized_shouldReturnMockOrder() {
|
||||
CreatePaymentRequest request = createTestRequest();
|
||||
|
||||
when(paymentOrderRepository.save(any(PaymentOrder.class))).thenAnswer(invocation -> {
|
||||
PaymentOrder saved = invocation.getArgument(0);
|
||||
saved.setId(1L);
|
||||
return Mono.just(saved);
|
||||
});
|
||||
|
||||
Mono<PaymentResponse> result = paymentService.alipayAppPay(MEMBER_ID, request);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.assertNext(response -> {
|
||||
assertThat(response.getStatus()).isEqualTo("PENDING");
|
||||
assertThat(response.getOrderId()).isNotNull();
|
||||
assertThat(response.getPayInfo()).contains("alipays://");
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(paymentOrderRepository).save(any(PaymentOrder.class));
|
||||
verify(paymentOrderRepository, never()).findLatestPendingOrder(anyLong(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void alipayAppPay_whenHasPendingOrder_shouldReturnExistingOrder() {
|
||||
when(huifuProperties.isSdkInitialized()).thenReturn(true);
|
||||
|
||||
CreatePaymentRequest request = createTestRequest();
|
||||
PaymentOrder existingOrder = createTestOrder("PENDING");
|
||||
existingOrder.setPayInfo("alipays://existing");
|
||||
|
||||
when(paymentOrderRepository.findLatestPendingOrder(MEMBER_ID, ORDER_TYPE))
|
||||
.thenReturn(Mono.just(existingOrder));
|
||||
|
||||
Mono<PaymentResponse> result = paymentService.alipayAppPay(MEMBER_ID, request);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.assertNext(response -> {
|
||||
assertThat(response.getStatus()).isEqualTo("PENDING");
|
||||
assertThat(response.getOrderId()).isEqualTo(ORDER_ID);
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(paymentOrderRepository).findLatestPendingOrder(MEMBER_ID, ORDER_TYPE);
|
||||
verify(paymentOrderRepository, never()).save(any(PaymentOrder.class));
|
||||
}
|
||||
|
||||
// ==================== alipayQrCodePay ====================
|
||||
|
||||
@Test
|
||||
void alipayQrCodePay_whenSdkNotInitialized_shouldReturnMockOrder() {
|
||||
CreatePaymentRequest request = createTestRequest();
|
||||
|
||||
when(paymentOrderRepository.save(any(PaymentOrder.class))).thenAnswer(invocation -> {
|
||||
PaymentOrder saved = invocation.getArgument(0);
|
||||
saved.setId(1L);
|
||||
return Mono.just(saved);
|
||||
});
|
||||
|
||||
Mono<PaymentResponse> result = paymentService.alipayQrCodePay(MEMBER_ID, request);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.assertNext(response -> {
|
||||
assertThat(response.getStatus()).isEqualTo("PENDING");
|
||||
assertThat(response.getOrderId()).isNotNull();
|
||||
assertThat(response.getQrCode()).contains("https://qr.alipay.com/");
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(paymentOrderRepository).save(any(PaymentOrder.class));
|
||||
verify(paymentOrderRepository, never()).findLatestPendingOrder(anyLong(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void alipayQrCodePay_whenHasPendingOrder_shouldReturnExistingOrder() {
|
||||
when(huifuProperties.isSdkInitialized()).thenReturn(true);
|
||||
|
||||
CreatePaymentRequest request = createTestRequest();
|
||||
PaymentOrder existingOrder = createTestOrder("PENDING");
|
||||
existingOrder.setQrCode("https://qr.alipay.com/existing");
|
||||
|
||||
when(paymentOrderRepository.findLatestPendingOrder(MEMBER_ID, ORDER_TYPE))
|
||||
.thenReturn(Mono.just(existingOrder));
|
||||
|
||||
Mono<PaymentResponse> result = paymentService.alipayQrCodePay(MEMBER_ID, request);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.assertNext(response -> {
|
||||
assertThat(response.getStatus()).isEqualTo("PENDING");
|
||||
assertThat(response.getOrderId()).isEqualTo(ORDER_ID);
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(paymentOrderRepository).findLatestPendingOrder(MEMBER_ID, ORDER_TYPE);
|
||||
verify(paymentOrderRepository, never()).save(any(PaymentOrder.class));
|
||||
}
|
||||
|
||||
// ==================== getPaymentStatus ====================
|
||||
|
||||
@Test
|
||||
void getPaymentStatus_whenOrderNotFound_shouldReturnNotFound() {
|
||||
when(paymentOrderRepository.findByOrderNo(ORDER_ID)).thenReturn(Mono.empty());
|
||||
|
||||
Mono<PaymentResponse> result = paymentService.getPaymentStatus(MEMBER_ID, ORDER_ID);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.assertNext(response -> {
|
||||
assertThat(response.getStatus()).isEqualTo("NOT_FOUND");
|
||||
assertThat(response.getErrorMsg()).isEqualTo("订单不存在");
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getPaymentStatus_whenOrderNotBelongToMember_shouldReturnNotFound() {
|
||||
PaymentOrder order = createTestOrder("PENDING");
|
||||
order.setMemberId(99999L);
|
||||
|
||||
when(paymentOrderRepository.findByOrderNo(ORDER_ID)).thenReturn(Mono.just(order));
|
||||
|
||||
Mono<PaymentResponse> result = paymentService.getPaymentStatus(MEMBER_ID, ORDER_ID);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.assertNext(response -> {
|
||||
assertThat(response.getStatus()).isEqualTo("NOT_FOUND");
|
||||
assertThat(response.getErrorMsg()).isEqualTo("订单不存在");
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getPaymentStatus_whenOrderIsSuccess_shouldReturnDirectly() {
|
||||
PaymentOrder order = createTestOrder("SUCCESS");
|
||||
|
||||
when(paymentOrderRepository.findByOrderNo(ORDER_ID)).thenReturn(Mono.just(order));
|
||||
|
||||
Mono<PaymentResponse> result = paymentService.getPaymentStatus(MEMBER_ID, ORDER_ID);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.assertNext(response -> {
|
||||
assertThat(response.getStatus()).isEqualTo("SUCCESS");
|
||||
assertThat(response.getOrderId()).isEqualTo(ORDER_ID);
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(paymentOrderRepository, never()).save(any(PaymentOrder.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getPaymentStatus_whenOrderIsClosed_shouldReturnDirectly() {
|
||||
PaymentOrder order = createTestOrder("CLOSED");
|
||||
|
||||
when(paymentOrderRepository.findByOrderNo(ORDER_ID)).thenReturn(Mono.just(order));
|
||||
|
||||
Mono<PaymentResponse> result = paymentService.getPaymentStatus(MEMBER_ID, ORDER_ID);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.assertNext(response -> {
|
||||
assertThat(response.getStatus()).isEqualTo("CLOSED");
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(paymentOrderRepository, never()).save(any(PaymentOrder.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getPaymentStatus_whenOrderIsFail_shouldReturnDirectly() {
|
||||
PaymentOrder order = createTestOrder("FAIL");
|
||||
|
||||
when(paymentOrderRepository.findByOrderNo(ORDER_ID)).thenReturn(Mono.just(order));
|
||||
|
||||
Mono<PaymentResponse> result = paymentService.getPaymentStatus(MEMBER_ID, ORDER_ID);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.assertNext(response -> {
|
||||
assertThat(response.getStatus()).isEqualTo("FAIL");
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(paymentOrderRepository, never()).save(any(PaymentOrder.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getPaymentStatus_whenSdkNotInitialized_shouldReturnCurrentStatus() {
|
||||
PaymentOrder order = createTestOrder("PENDING");
|
||||
|
||||
when(paymentOrderRepository.findByOrderNo(ORDER_ID)).thenReturn(Mono.just(order));
|
||||
|
||||
Mono<PaymentResponse> result = paymentService.getPaymentStatus(MEMBER_ID, ORDER_ID);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.assertNext(response -> {
|
||||
assertThat(response.getStatus()).isEqualTo("PENDING");
|
||||
assertThat(response.getOrderId()).isEqualTo(ORDER_ID);
|
||||
})
|
||||
.verifyComplete();
|
||||
|
||||
verify(paymentOrderRepository, never()).save(any(PaymentOrder.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getPaymentStatus_whenQueryError_shouldReturnCurrentStatus() {
|
||||
when(huifuProperties.isSdkInitialized()).thenReturn(true);
|
||||
|
||||
PaymentOrder order = createTestOrder("PENDING");
|
||||
order.setHfSeqId("HF123");
|
||||
|
||||
when(paymentOrderRepository.findByOrderNo(ORDER_ID)).thenReturn(Mono.just(order));
|
||||
|
||||
Mono<PaymentResponse> result = paymentService.getPaymentStatus(MEMBER_ID, ORDER_ID);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.assertNext(response -> {
|
||||
assertThat(response.getStatus()).isEqualTo("PENDING");
|
||||
assertThat(response.getOrderId()).isEqualTo(ORDER_ID);
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
// ==================== handleAlipayNotify ====================
|
||||
|
||||
@Test
|
||||
void handleAlipayNotify_whenRespDataNull_shouldReturnFail() {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put("resp_data", null);
|
||||
params.put("sign", "testSign");
|
||||
|
||||
Mono<String> result = paymentService.handleAlipayNotify(params);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectNext("FAIL")
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void handleAlipayNotify_whenSignNull_shouldReturnFail() {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put("resp_data", "{\"req_seq_id\":\"REQ123\"}");
|
||||
params.put("sign", null);
|
||||
|
||||
Mono<String> result = paymentService.handleAlipayNotify(params);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectNext("FAIL")
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void handleAlipayNotify_whenAlreadyProcessed_shouldReturnRecvOrdId() {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put("resp_data", "{\"req_seq_id\":\"REQ123\",\"hf_seq_id\":\"HF123\",\"trans_stat\":\"S\"}");
|
||||
params.put("sign", "testSign");
|
||||
|
||||
when(redisUtil.hasKey("huifu:processed:trade:REQ123")).thenReturn(Mono.just(true));
|
||||
|
||||
Mono<String> result = paymentService.handleAlipayNotify(params);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectNext("RECV_ORD_ID_REQ123")
|
||||
.verifyComplete();
|
||||
|
||||
verify(paymentOrderRepository, never()).findByReqSeqId(anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void handleAlipayNotify_whenTradeSuccess_shouldProcessCorrectly() {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put("resp_data", "{\"req_seq_id\":\"REQ123\",\"hf_seq_id\":\"HF123\",\"trans_stat\":\"S\"}");
|
||||
params.put("sign", "testSign");
|
||||
|
||||
PaymentOrder order = createTestOrder("PENDING");
|
||||
|
||||
when(redisUtil.hasKey("huifu:processed:trade:REQ123")).thenReturn(Mono.just(false));
|
||||
when(paymentOrderRepository.findByReqSeqId("REQ123")).thenReturn(Mono.just(order));
|
||||
when(paymentOrderRepository.save(any(PaymentOrder.class))).thenReturn(Mono.just(order));
|
||||
when(redisUtil.setWithExpire("huifu:processed:trade:REQ123", "1", 86400)).thenReturn(Mono.just(true));
|
||||
when(paymentNotifyService.notifyPaymentStatus(order.getOrderNo(), "SUCCESS", MEMBER_ID)).thenReturn(Mono.empty());
|
||||
|
||||
Mono<String> result = paymentService.handleAlipayNotify(params);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectNext("RECV_ORD_ID_REQ123")
|
||||
.verifyComplete();
|
||||
|
||||
verify(paymentOrderRepository).save(any(PaymentOrder.class));
|
||||
verify(redisUtil).setWithExpire("huifu:processed:trade:REQ123", "1", 86400);
|
||||
verify(paymentNotifyService).notifyPaymentStatus(order.getOrderNo(), "SUCCESS", MEMBER_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
void handleAlipayNotify_whenTradeFailed_shouldProcessCorrectly() {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put("resp_data", "{\"req_seq_id\":\"REQ123\",\"hf_seq_id\":\"HF123\",\"trans_stat\":\"F\",\"resp_desc\":\"支付失败\"}");
|
||||
params.put("sign", "testSign");
|
||||
|
||||
PaymentOrder order = createTestOrder("PENDING");
|
||||
|
||||
when(redisUtil.hasKey("huifu:processed:trade:REQ123")).thenReturn(Mono.just(false));
|
||||
when(paymentOrderRepository.findByReqSeqId("REQ123")).thenReturn(Mono.just(order));
|
||||
when(paymentOrderRepository.save(any(PaymentOrder.class))).thenReturn(Mono.just(order));
|
||||
when(redisUtil.setWithExpire("huifu:processed:trade:REQ123", "1", 86400)).thenReturn(Mono.just(true));
|
||||
when(paymentNotifyService.notifyPaymentStatus(order.getOrderNo(), "FAIL", MEMBER_ID)).thenReturn(Mono.empty());
|
||||
|
||||
Mono<String> result = paymentService.handleAlipayNotify(params);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectNext("RECV_ORD_ID_REQ123")
|
||||
.verifyComplete();
|
||||
|
||||
verify(paymentOrderRepository).save(any(PaymentOrder.class));
|
||||
verify(redisUtil).setWithExpire("huifu:processed:trade:REQ123", "1", 86400);
|
||||
verify(paymentNotifyService).notifyPaymentStatus(order.getOrderNo(), "FAIL", MEMBER_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
void handleAlipayNotify_whenTradeProcessing_shouldReturnRecvOrdId() {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put("resp_data", "{\"req_seq_id\":\"REQ123\",\"hf_seq_id\":\"HF123\",\"trans_stat\":\"P\"}");
|
||||
params.put("sign", "testSign");
|
||||
|
||||
PaymentOrder order = createTestOrder("PENDING");
|
||||
|
||||
when(redisUtil.hasKey("huifu:processed:trade:REQ123")).thenReturn(Mono.just(false));
|
||||
when(paymentOrderRepository.findByReqSeqId("REQ123")).thenReturn(Mono.just(order));
|
||||
when(paymentNotifyService.notifyPaymentStatus(order.getOrderNo(), "PENDING", MEMBER_ID)).thenReturn(Mono.empty());
|
||||
|
||||
Mono<String> result = paymentService.handleAlipayNotify(params);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectNext("RECV_ORD_ID_REQ123")
|
||||
.verifyComplete();
|
||||
|
||||
verify(paymentOrderRepository, never()).save(any(PaymentOrder.class));
|
||||
verify(paymentNotifyService).notifyPaymentStatus(order.getOrderNo(), "PENDING", MEMBER_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
void handleAlipayNotify_whenOrderNotFound_shouldReturnFail() {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put("resp_data", "{\"req_seq_id\":\"REQ123\",\"hf_seq_id\":\"HF123\",\"trans_stat\":\"S\"}");
|
||||
params.put("sign", "testSign");
|
||||
|
||||
when(redisUtil.hasKey("huifu:processed:trade:REQ123")).thenReturn(Mono.just(false));
|
||||
when(paymentOrderRepository.findByReqSeqId("REQ123")).thenReturn(Mono.empty());
|
||||
|
||||
Mono<String> result = paymentService.handleAlipayNotify(params);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectNext("FAIL")
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void handleAlipayNotify_whenException_shouldReturnFail() {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
params.put("resp_data", "{\"req_seq_id\":\"REQ123\",\"hf_seq_id\":\"HF123\",\"trans_stat\":\"S\"}");
|
||||
params.put("sign", "testSign");
|
||||
|
||||
when(redisUtil.hasKey("huifu:processed:trade:REQ123")).thenReturn(Mono.just(false));
|
||||
when(paymentOrderRepository.findByReqSeqId("REQ123")).thenReturn(Mono.error(new RuntimeException("DB error")));
|
||||
|
||||
Mono<String> result = paymentService.handleAlipayNotify(params);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectNext("FAIL")
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
// ==================== refund ====================
|
||||
|
||||
@Test
|
||||
void refund_shouldReturnFalse() {
|
||||
Mono<Boolean> result = paymentService.refund(MEMBER_ID, ORDER_ID, "50");
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectNext(false)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
// ==================== getAllPaymentOrders ====================
|
||||
|
||||
@Test
|
||||
void getAllPaymentOrders_shouldReturnOrderList() {
|
||||
PaymentOrder order1 = createTestOrder("SUCCESS");
|
||||
order1.setOrderNo("PAY001");
|
||||
PaymentOrder order2 = createTestOrder("PENDING");
|
||||
order2.setOrderNo("PAY002");
|
||||
|
||||
when(paymentOrderRepository.findByMemberIdAndDeletedAtIsNull(MEMBER_ID))
|
||||
.thenReturn(Flux.just(order1, order2));
|
||||
|
||||
Flux<PaymentResponse> result = paymentService.getAllPaymentOrders(MEMBER_ID);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.assertNext(response -> {
|
||||
assertThat(response.getOrderId()).isEqualTo("PAY001");
|
||||
assertThat(response.getStatus()).isEqualTo("SUCCESS");
|
||||
})
|
||||
.assertNext(response -> {
|
||||
assertThat(response.getOrderId()).isEqualTo("PAY002");
|
||||
assertThat(response.getStatus()).isEqualTo("PENDING");
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAllPaymentOrders_whenNoOrders_shouldReturnEmpty() {
|
||||
when(paymentOrderRepository.findByMemberIdAndDeletedAtIsNull(MEMBER_ID))
|
||||
.thenReturn(Flux.empty());
|
||||
|
||||
Flux<PaymentResponse> result = paymentService.getAllPaymentOrders(MEMBER_ID);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
// ==================== getPendingOrder ====================
|
||||
|
||||
@Test
|
||||
void getPendingOrder_shouldReturnPendingOrder() {
|
||||
PaymentOrder order = createTestOrder("PENDING");
|
||||
|
||||
when(paymentOrderRepository.findLatestPendingOrder(MEMBER_ID, ORDER_TYPE))
|
||||
.thenReturn(Mono.just(order));
|
||||
|
||||
Mono<PaymentResponse> result = paymentService.getPendingOrder(MEMBER_ID, ORDER_TYPE);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.assertNext(response -> {
|
||||
assertThat(response.getOrderId()).isEqualTo(ORDER_ID);
|
||||
assertThat(response.getStatus()).isEqualTo("PENDING");
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getPendingOrder_whenNoPendingOrder_shouldReturnEmpty() {
|
||||
when(paymentOrderRepository.findLatestPendingOrder(MEMBER_ID, ORDER_TYPE))
|
||||
.thenReturn(Mono.empty());
|
||||
|
||||
Mono<PaymentResponse> result = paymentService.getPendingOrder(MEMBER_ID, ORDER_TYPE);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
// ==================== closeOrder ====================
|
||||
|
||||
@Test
|
||||
void closeOrder_shouldCloseSuccessfully() {
|
||||
PaymentOrder order = createTestOrder("PENDING");
|
||||
order.setId(1L);
|
||||
|
||||
when(paymentOrderRepository.findByOrderNo(ORDER_ID)).thenReturn(Mono.just(order));
|
||||
when(paymentOrderRepository.closeExpiredOrder(1L)).thenReturn(Mono.just(1));
|
||||
|
||||
Mono<Boolean> result = paymentService.closeOrder(MEMBER_ID, ORDER_ID);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectNext(true)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
void closeOrder_whenOrderNotBelongToMember_shouldReturnFalse() {
|
||||
PaymentOrder order = createTestOrder("PENDING");
|
||||
order.setMemberId(99999L);
|
||||
|
||||
when(paymentOrderRepository.findByOrderNo(ORDER_ID)).thenReturn(Mono.just(order));
|
||||
|
||||
Mono<Boolean> result = paymentService.closeOrder(MEMBER_ID, ORDER_ID);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectNext(false)
|
||||
.verifyComplete();
|
||||
|
||||
verify(paymentOrderRepository, never()).closeExpiredOrder(anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
void closeOrder_whenOrderNotPending_shouldReturnFalse() {
|
||||
PaymentOrder order = createTestOrder("SUCCESS");
|
||||
|
||||
when(paymentOrderRepository.findByOrderNo(ORDER_ID)).thenReturn(Mono.just(order));
|
||||
|
||||
Mono<Boolean> result = paymentService.closeOrder(MEMBER_ID, ORDER_ID);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectNext(false)
|
||||
.verifyComplete();
|
||||
|
||||
verify(paymentOrderRepository, never()).closeExpiredOrder(anyLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
void closeOrder_whenOrderNotFound_shouldReturnFalse() {
|
||||
when(paymentOrderRepository.findByOrderNo(ORDER_ID)).thenReturn(Mono.empty());
|
||||
|
||||
Mono<Boolean> result = paymentService.closeOrder(MEMBER_ID, ORDER_ID);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.expectNext(false)
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
// ==================== queryHuifuTradeList ====================
|
||||
|
||||
@Test
|
||||
void queryHuifuTradeList_shouldReturnResult() {
|
||||
Mono<Map<String, Object>> result = paymentService.queryHuifuTradeList(MEMBER_ID, "20260701", "20260731");
|
||||
|
||||
StepVerifier.create(result)
|
||||
.assertNext(map -> {
|
||||
assertThat(map).isNotNull();
|
||||
assertThat(map).containsKey("begin_date");
|
||||
assertThat(map).containsKey("end_date");
|
||||
assertThat(map).containsKey("huifu_ids");
|
||||
assertThat(map).containsKey("api_results");
|
||||
assertThat(map).containsKey("trade_list");
|
||||
assertThat(map).containsKey("total_count");
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
// ==================== queryHuifuTradeByOrderId ====================
|
||||
|
||||
@Test
|
||||
void queryHuifuTradeByOrderId_shouldReturnResult() {
|
||||
Mono<Map<String, Object>> result = paymentService.queryHuifuTradeByOrderId(MEMBER_ID, ORDER_ID, null);
|
||||
|
||||
StepVerifier.create(result)
|
||||
.assertNext(map -> {
|
||||
assertThat(map).isNotNull();
|
||||
assertThat(map).containsKey("out_ord_id");
|
||||
assertThat(map).containsKey("success");
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user