test(pay): 补 fake 三态/MockChannelEnabled 装配/devmock 端点/gateway currency_mismatch/限流 bucket 回收

This commit is contained in:
wangjia
2026-07-11 08:39:57 +08:00
parent 1f87b0fcbf
commit 9076b5e0b8
5 changed files with 353 additions and 0 deletions
+60
View File
@@ -221,3 +221,63 @@ func TestCreateOrderCurrencyFromChannelCapability(t *testing.T) {
t.Fatalf("order = %s/%d want USDT/29990000", o.Currency, o.AmountMinor)
}
}
// cnyOnlyProvider 是本测试文件专用的最小 stub provider(结算币种固定 CNY),
// 陪 fake(结算 USDT)凑出"两个结算币种不同的方法"场景,用来直测 RetryOrder 换渠道
// 触发 gateway.ErrCurrencyMismatch 的分支(gateway.go:177 附近)。不复用
// internal/provider/provider_test.go 的 stubProvider——那个类型未导出且在
// package provider_test 里,gateway_test 包拿不到。
type cnyOnlyProvider struct{}
func (cnyOnlyProvider) Method() string { return "cny-only" }
func (cnyOnlyProvider) Capabilities() provider.Capabilities {
return provider.Capabilities{
RenderTypes: []provider.RenderType{provider.RenderQR},
SettleCurrencies: []string{"CNY"},
}
}
func (cnyOnlyProvider) Create(_ context.Context, req provider.CreateRequest) (*provider.Session, error) {
return &provider.Session{ProviderRef: "CNYONLY-" + req.OutTradeNo, RenderType: provider.RenderQR, Payload: map[string]any{}}, nil
}
func (cnyOnlyProvider) VerifyCallback(context.Context, provider.CallbackInput) (*provider.PaidEvent, error) {
return nil, errors.New("cnyOnlyProvider: not implemented")
}
func (cnyOnlyProvider) Query(context.Context, provider.QueryRequest) (*provider.PaidEvent, error) {
return &provider.PaidEvent{Status: provider.PaidPending}, nil
}
// TestRetryOrderCurrencyMismatch 直测 gateway.go:177 附近的触发条件:换到一个结算
// 币种与订单不同的渠道 retry,必须报 ErrCurrencyMismatch(而不是静默换币重定价——
// P3 范围之外,见 RetryOrder 注释)。此前只在 handler 层经 HTTP 间接测到 409。
func TestRetryOrderCurrencyMismatch(t *testing.T) {
db := model.OpenTestDB(t)
orders := store.NewOrderStore(db)
refunds := store.NewRefundStore(db)
subs := store.NewSubscriptionStore(db)
chargebacks := store.NewChargebackStore(db)
preg := provider.NewRegistry()
preg.Register(fake.New()) // "fake" 结算 USDT
preg.Register(cnyOnlyProvider{}) // "cny-only" 结算 CNY
areg := accounts.New([]config.AccountConfig{
{AccountID: "fake-a1", Channel: "fake", Region: "global", Enabled: true, Weight: 1},
{AccountID: "cny-a1", Channel: "cny-only", Region: "global", Enabled: true, Weight: 1},
})
picker := accounts.NewRouter(areg, nil, nil)
g := gateway.New(orders, refunds, preg, picker, stubResolver{}, &spyEnqueuer{}, "global", subs, chargebacks)
ctx := context.Background()
// 用 cny-only 下单(结算 CNY),再 retry 到 fake(结算 USDT)→ 币种不符。
res, err := g.CreateOrder(ctx, gateway.CreateOrderInput{SKU: "pro_year", Method: "cny-only"})
if err != nil {
t.Fatalf("create: %v", err)
}
_, err = g.RetryOrder(ctx, res.OrderNo, "fake", nil)
if !errors.Is(err, gateway.ErrCurrencyMismatch) {
t.Fatalf("RetryOrder 换到结算币种不同的渠道应报 ErrCurrencyMismatch, got %v", err)
}
}