修复退款幂等与补偿重试
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
package refundretry
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"hfb_sys/backend/internal/model"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
)
|
||||
|
||||
func setupRefundRetryTestDB(t *testing.T) *gorm.DB {
|
||||
t.Helper()
|
||||
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
|
||||
Logger: logger.Default.LogMode(logger.Silent),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("创建测试数据库失败: %v", err)
|
||||
}
|
||||
if err := db.AutoMigrate(&model.PaymentOrder{}, &model.RentalOrder{}); err != nil {
|
||||
t.Fatalf("数据库迁移失败: %v", err)
|
||||
}
|
||||
return db
|
||||
}
|
||||
|
||||
func TestRetryBackoffCapsAtOneHour(t *testing.T) {
|
||||
cases := []struct {
|
||||
retryCount int
|
||||
want time.Duration
|
||||
}{
|
||||
{retryCount: 1, want: 5 * time.Minute},
|
||||
{retryCount: 2, want: 10 * time.Minute},
|
||||
{retryCount: 3, want: 20 * time.Minute},
|
||||
{retryCount: 4, want: 40 * time.Minute},
|
||||
{retryCount: 5, want: time.Hour},
|
||||
{retryCount: 10, want: time.Hour},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
if got := retryBackoff(tc.retryCount); got != tc.want {
|
||||
t.Fatalf("retryBackoff(%d) = %s, want %s", tc.retryCount, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarkRetryFailedStopsAtMaxCount(t *testing.T) {
|
||||
db := setupRefundRetryTestDB(t)
|
||||
job := New(db, nil, zap.NewNop(), nil)
|
||||
now := time.Date(2026, 6, 14, 12, 0, 0, 0, time.UTC)
|
||||
row := model.PaymentOrder{
|
||||
PaymentNo: "PAY202606140101",
|
||||
OrderID: 1,
|
||||
OrderNo: "ORD202606140101",
|
||||
UserID: 2,
|
||||
Provider: "mock",
|
||||
ThirdOrderID: "REF202606140101",
|
||||
BizType: "admin_refund",
|
||||
Status: "failed",
|
||||
RetryCount: maxRetryCount - 1,
|
||||
}
|
||||
if err := db.Create(&row).Error; err != nil {
|
||||
t.Fatalf("创建退款单失败: %v", err)
|
||||
}
|
||||
|
||||
if err := job.markRetryFailed(t.Context(), row, now); err != nil {
|
||||
t.Fatalf("markRetryFailed() error = %v", err)
|
||||
}
|
||||
var latest model.PaymentOrder
|
||||
if err := db.First(&latest, row.ID).Error; err != nil {
|
||||
t.Fatalf("查询退款单失败: %v", err)
|
||||
}
|
||||
if latest.RetryCount != maxRetryCount {
|
||||
t.Fatalf("RetryCount = %d, want %d", latest.RetryCount, maxRetryCount)
|
||||
}
|
||||
if latest.LastRetryAt == nil || !latest.LastRetryAt.Equal(now) {
|
||||
t.Fatalf("LastRetryAt = %v, want %v", latest.LastRetryAt, now)
|
||||
}
|
||||
if latest.NextRetryAt != nil {
|
||||
t.Fatalf("NextRetryAt = %v, want nil after max retry", latest.NextRetryAt)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResetRetryClearsRetryFields(t *testing.T) {
|
||||
db := setupRefundRetryTestDB(t)
|
||||
job := New(db, nil, zap.NewNop(), nil)
|
||||
now := time.Date(2026, 6, 14, 12, 0, 0, 0, time.UTC)
|
||||
next := now.Add(time.Hour)
|
||||
row := model.PaymentOrder{
|
||||
PaymentNo: "PAY202606140102",
|
||||
OrderID: 1,
|
||||
OrderNo: "ORD202606140102",
|
||||
UserID: 2,
|
||||
Provider: "mock",
|
||||
ThirdOrderID: "REF202606140102",
|
||||
BizType: "admin_refund",
|
||||
Status: "refunded",
|
||||
RetryCount: 3,
|
||||
LastRetryAt: &now,
|
||||
NextRetryAt: &next,
|
||||
}
|
||||
if err := db.Create(&row).Error; err != nil {
|
||||
t.Fatalf("创建退款单失败: %v", err)
|
||||
}
|
||||
|
||||
if err := job.resetRetry(t.Context(), row.ID); err != nil {
|
||||
t.Fatalf("resetRetry() error = %v", err)
|
||||
}
|
||||
var latest model.PaymentOrder
|
||||
if err := db.First(&latest, row.ID).Error; err != nil {
|
||||
t.Fatalf("查询退款单失败: %v", err)
|
||||
}
|
||||
if latest.RetryCount != 0 || latest.LastRetryAt != nil || latest.NextRetryAt != nil {
|
||||
t.Fatalf("retry fields = count:%d last:%v next:%v, want zero/nil", latest.RetryCount, latest.LastRetryAt, latest.NextRetryAt)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user