完善本地质量门禁与资金链路测试
This commit is contained in:
@@ -4,6 +4,8 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -694,6 +696,75 @@ func TestConfirmPaidRollsBackOrderWhenPaymentUpdateFails(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestConcurrentPaidNotificationsAreIdempotent(t *testing.T) {
|
||||
db := database.NewTestDBWithName("payment-concurrent")
|
||||
if err := database.MigrateRentalTransactionTestSchema(db); err != nil {
|
||||
t.Fatalf("数据库迁移失败: %v", err)
|
||||
}
|
||||
sqlDB, err := db.DB()
|
||||
if err != nil {
|
||||
t.Fatalf("get sqlite db: %v", err)
|
||||
}
|
||||
sqlDB.SetMaxOpenConns(8)
|
||||
orderRepo := ordermodule.NewRepository(db)
|
||||
repo := NewRepository(db, nil, orderRepo)
|
||||
fixture := createPayableOrderFixture(t, db, "CONCURRENT", true)
|
||||
params := map[string]string{
|
||||
"third_order_id": fixture.Payment.ThirdOrderID,
|
||||
"status": "paid",
|
||||
"amount": "1000",
|
||||
"provider_order_id": "PROVIDER-CONCURRENT",
|
||||
}
|
||||
|
||||
const callers = 8
|
||||
results := make(chan error, callers)
|
||||
var group sync.WaitGroup
|
||||
for i := 0; i < callers; i++ {
|
||||
group.Add(1)
|
||||
go func() {
|
||||
defer group.Done()
|
||||
var callErr error
|
||||
for attempt := 0; attempt < 50; attempt++ {
|
||||
_, callErr = repo.HandleNotify(t.Context(), "mock", params, "status=paid", "application/x-www-form-urlencoded", "")
|
||||
if callErr == nil || !strings.Contains(callErr.Error(), "locked") {
|
||||
break
|
||||
}
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
}
|
||||
results <- callErr
|
||||
}()
|
||||
}
|
||||
group.Wait()
|
||||
close(results)
|
||||
for callErr := range results {
|
||||
if callErr != nil {
|
||||
t.Fatalf("concurrent HandleNotify() error = %v", callErr)
|
||||
}
|
||||
}
|
||||
|
||||
var latestPayment model.PaymentOrder
|
||||
if err := db.First(&latestPayment, fixture.Payment.ID).Error; err != nil {
|
||||
t.Fatalf("find payment: %v", err)
|
||||
}
|
||||
if latestPayment.Status != "paid" {
|
||||
t.Fatalf("payment status = %q, want paid", latestPayment.Status)
|
||||
}
|
||||
var latestOrder model.RentalOrder
|
||||
if err := db.First(&latestOrder, fixture.Order.ID).Error; err != nil {
|
||||
t.Fatalf("find order: %v", err)
|
||||
}
|
||||
if latestOrder.Status != "pending_handoff" || latestOrder.HandoffStatus != "pending_owner" {
|
||||
t.Fatalf("order status = %q/%q, want pending_handoff/pending_owner", latestOrder.Status, latestOrder.HandoffStatus)
|
||||
}
|
||||
var paidCount int64
|
||||
if err := db.Model(&model.PaymentOrder{}).Where("id = ? AND status = 'paid'", fixture.Payment.ID).Count(&paidCount).Error; err != nil {
|
||||
t.Fatalf("count paid payment: %v", err)
|
||||
}
|
||||
if paidCount != 1 {
|
||||
t.Fatalf("paid payment rows = %d, want 1", paidCount)
|
||||
}
|
||||
}
|
||||
|
||||
// TestQueryClosedPaymentReturnsLocalStatus 验证关闭状态不再依赖渠道配置或继续轮询渠道。
|
||||
func TestQueryClosedPaymentReturnsLocalStatus(t *testing.T) {
|
||||
db := setupPaymentTestDB(t)
|
||||
|
||||
Reference in New Issue
Block a user