Просмотр исходного кода

test(统一支付): 补齐收银台终态与退款金额闸门覆盖

alphaH 1 неделя назад
Родитель
Сommit
28f4899b61

+ 4 - 2
java/src/main/java/com/payment/platform/module/payment/unified/service/UnifiedCashierService.java

@@ -14,9 +14,11 @@ import org.springframework.stereotype.Service;
 import java.time.OffsetDateTime;
 
 /**
- * 收银台(免登录顾客页)的下单与轮询快照。
+ * 后台收银台(租户登录身份)的下单与轮询快照。
  *
- * <p>下单复用订单主流程({@link UnifiedSource#CASHIER});请求侧的默认值
+ * <p>超管可以没有归属租户通过守卫:创建腿由
+ * {@link UnifiedMerchantService#resolveForTenant} 拒绝,快照腿按 {@code tenant_id = null}
+ * 查询不会命中。下单复用订单主流程({@link UnifiedSource#CASHIER});请求侧的默认值
  * ({@code outTradeNo} 必填、未给过期时间默认 300 秒)在控制器与服务层各兜一次。
  *
  * <p>快照按 <b>{@code order_no + tenant_id}</b> 定位:少了租户条件就是跨租户读,

+ 19 - 0
java/src/test/java/com/payment/platform/module/payment/unified/controller/UnifiedCashierControllerTest.java

@@ -279,6 +279,25 @@ class UnifiedCashierControllerTest {
         verify(orderService, never()).syncFromUpstream(any());
     }
 
+    /**
+     * 真正的 FINAL 成员(3/4/5/6)同样直接返回:它们既不在 PENDING 里,也不在
+     * 「还能再变」的成功态里;收银台没有任何理由再打一次上游。
+     *
+     * <p>与上面的 {@code trade_status=2} 用例合起来,两个方向都钉死:把早返回分支
+     * 删掉、改成 {@code PENDING.contains}(反向)、或改成只看 {@code FINAL},必有一条红。
+     */
+    @Test
+    void serviceSnapshot_finalStatuses_returnWithoutUpstreamCall() {
+        for (String status : List.of("3", "4", "5", "6")) {
+            UnifiedOrderEntity finalRow = order(status);
+            finalRow.setLastQueryTime(OffsetDateTime.now().minusMinutes(5));
+            when(orderMapper.selectOne(any())).thenReturn(finalRow);
+
+            assertSame(finalRow, service.snapshot("UO1", 2L), "status=" + status);
+        }
+        verify(orderService, never()).syncFromUpstream(any());
+    }
+
     @Test
     void serviceSnapshot_recentQueryWithin3s_returnsWithoutUpstreamCall() {
         UnifiedOrderEntity recent = order("0");

+ 24 - 0
java/src/test/java/com/payment/platform/module/payment/unified/service/UnifiedRefundServiceTest.java

@@ -884,6 +884,30 @@ class UnifiedRefundServiceTest {
         verifyNoInteractions(orderService, client, tx);
     }
 
+    @Test
+    void zeroRefundAmount_isRejectedBeforeAnyLookup() {
+        assertNonPositiveAmountRejected(0L);
+    }
+
+    @Test
+    void negativeRefundAmount_isRejectedBeforeAnyLookup() {
+        assertNonPositiveAmountRejected(-1L);
+    }
+
+    /**
+     * 金额闸门必须在任何查单/取商户/调上游之前:非法金额不消耗幂等键,
+     * 也不能让调用方从后续 404 文案里探到订单是否存在。
+     */
+    private void assertNonPositiveAmountRejected(long amount) {
+        BusinessException e = assertThrows(BusinessException.class,
+                () -> service.createRefund(command(amount, "R1"), TENANT, null,
+                        UnifiedSource.OPENAPI));
+
+        assertEquals(400, e.getCode(), "amount=" + amount + " 实测消息: " + e.getMessage());
+        assertEquals("退款金额必须大于 0", e.getMessage(), "amount=" + amount);
+        verifyNoInteractions(orderService, merchantService, client, tx, refundMapper);
+    }
+
     // ────────────────────────────── 查询语义(Task 13 依赖) ──────────────────────────────
 
     @Test