114 lines
4.0 KiB
Go
114 lines
4.0 KiB
Go
package handler_test
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"github.com/wangjia/pay/config"
|
|
"github.com/wangjia/pay/internal/accounts"
|
|
"github.com/wangjia/pay/internal/gateway"
|
|
"github.com/wangjia/pay/internal/handler"
|
|
"github.com/wangjia/pay/internal/model"
|
|
"github.com/wangjia/pay/internal/provider"
|
|
"github.com/wangjia/pay/internal/provider/fake"
|
|
"github.com/wangjia/pay/internal/store"
|
|
"github.com/wangjia/pay/internal/webhook"
|
|
)
|
|
|
|
// buildDevMockEngine 装配一个带 DevMockHandler 的最小 gin engine:与 main.go 的
|
|
// MOCK_CHANNEL_ENABLED 挂载点(POST /api/v2/dev/orders/:order_no/mark-paid)同形,
|
|
// 但走真实 webhook.Notifier + store.WebhookStore(而非 nopEnqueuer),这样才能断言
|
|
// mark-paid 触发的 webhook 出站 outbox 真的入队了一行——handler 包既有的
|
|
// buildEngine()/buildEngineWithStore() 都用 nopEnqueuer,查不到 outbox。
|
|
func buildDevMockEngine(t *testing.T) (*gin.Engine, *gateway.Gateway, *store.OrderStore, *store.WebhookStore) {
|
|
t.Helper()
|
|
gin.SetMode(gin.TestMode)
|
|
db := model.OpenTestDB(t)
|
|
orders := store.NewOrderStore(db)
|
|
refunds := store.NewRefundStore(db)
|
|
subs := store.NewSubscriptionStore(db)
|
|
chargebacks := store.NewChargebackStore(db)
|
|
webhookStore := store.NewWebhookStore(db)
|
|
|
|
preg := provider.NewRegistry()
|
|
preg.Register(fake.New())
|
|
areg := accounts.New([]config.AccountConfig{
|
|
{AccountID: "fake-a1", Channel: "fake", Region: "global", Enabled: true, Weight: 1},
|
|
})
|
|
picker := accounts.NewRouter(areg, nil, nil)
|
|
|
|
// bizConfig/orderPaid 只是 Notifier 投递(DeliverPending)才用得到;本测试只验证
|
|
// Enqueue 落 outbox 一行,不跑投递循环,给最简单的桩即可。
|
|
notifier := webhook.NewNotifier(webhookStore,
|
|
func(string) (config.BizSystemConfig, bool) { return config.BizSystemConfig{}, true },
|
|
func(string) (bool, error) { return true, nil },
|
|
)
|
|
g := gateway.New(orders, refunds, preg, picker, oneResolver{}, notifier, "global", subs, chargebacks)
|
|
|
|
devH := handler.NewDevMockHandler(db, g)
|
|
r := gin.New()
|
|
r.POST("/api/v2/dev/orders/:order_no/mark-paid", devH.MarkPaid)
|
|
return r, g, orders, webhookStore
|
|
}
|
|
|
|
// TestDevMockMarkPaid 覆盖 dev-only「模拟付款成功」端点的最小闭环:fake 渠道下单
|
|
// (带 biz_system,否则 Settle 不会入队 webhook,见 gateway/settle.go
|
|
// enqueuePaymentSucceeded)→ POST mark-paid → 200 + 订单翻 paid + webhook outbox
|
|
// 有一行 payment.succeeded 待投递。
|
|
func TestDevMockMarkPaid(t *testing.T) {
|
|
r, g, orders, webhookStore := buildDevMockEngine(t)
|
|
ctx := t.Context()
|
|
|
|
res, err := g.CreateOrder(ctx, gateway.CreateOrderInput{
|
|
SKU: "pro_year", Method: "fake", BizSystem: "pangolin", BizRef: "u-1",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("CreateOrder: %v", err)
|
|
}
|
|
|
|
req := httptest.NewRequest(http.MethodPost, "/api/v2/dev/orders/"+res.OrderNo+"/mark-paid", nil)
|
|
w := httptest.NewRecorder()
|
|
r.ServeHTTP(w, req)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("mark-paid code=%d body=%s", w.Code, w.Body.String())
|
|
}
|
|
|
|
o, err := orders.GetOrder(res.OrderNo)
|
|
if err != nil {
|
|
t.Fatalf("GetOrder: %v", err)
|
|
}
|
|
if o.Status != model.OrderPaidV2 {
|
|
t.Fatalf("订单状态 = %v, want paid", o.Status)
|
|
}
|
|
|
|
rows, err := webhookStore.ListUndelivered(10)
|
|
if err != nil {
|
|
t.Fatalf("ListUndelivered: %v", err)
|
|
}
|
|
found := false
|
|
for _, row := range rows {
|
|
if row.OutTradeNo == res.OrderNo && row.EventType == "payment.succeeded" {
|
|
found = true
|
|
}
|
|
}
|
|
if !found {
|
|
t.Fatalf("webhook outbox 应有一行 %s/payment.succeeded 待投递, rows=%+v", res.OrderNo, rows)
|
|
}
|
|
}
|
|
|
|
// TestDevMockMarkPaidOrderNotFound 覆盖找不到订单(未走 fake 渠道下单/order_no 错)
|
|
// 时的 404 分支,不误判成 500。
|
|
func TestDevMockMarkPaidOrderNotFound(t *testing.T) {
|
|
r, _, _, _ := buildDevMockEngine(t)
|
|
|
|
req := httptest.NewRequest(http.MethodPost, "/api/v2/dev/orders/GHOST-ORDER/mark-paid", nil)
|
|
w := httptest.NewRecorder()
|
|
r.ServeHTTP(w, req)
|
|
if w.Code != http.StatusNotFound {
|
|
t.Fatalf("未知订单 mark-paid code=%d, want 404, body=%s", w.Code, w.Body.String())
|
|
}
|
|
}
|