package payment import ( "testing" "hfb_sys/backend/internal/model" "gorm.io/driver/sqlite" "gorm.io/gorm" "gorm.io/gorm/logger" ) // setupPaymentTestDB 创建测试数据库 func setupPaymentTestDB(t *testing.T) *gorm.DB { 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{}, &model.User{}, ); err != nil { t.Fatalf("数据库迁移失败: %v", err) } return db } // TestCanReusePaymentWithSameProvider 测试相同渠道可复用 func TestCanReusePaymentWithSameProvider(t *testing.T) { payment := model.PaymentOrder{ Status: "paying", Provider: "lakala", MerchantID: "M123", } config := runtimePaymentConfig{ Provider: "lakala", MerchantID: "M123", } if !canReuseOrderPayment(payment, config) { t.Fatal("should reuse payment with same provider and merchant") } } // TestCannotReusePaymentWithDifferentProvider 测试不同渠道不可复用 func TestCannotReusePaymentWithDifferentProvider(t *testing.T) { payment := model.PaymentOrder{ Status: "paying", Provider: "lakala", MerchantID: "M123", } config := runtimePaymentConfig{ Provider: "leshua", // 不同渠道 MerchantID: "M456", } if canReuseOrderPayment(payment, config) { t.Fatal("should not reuse payment with different provider") } } // TestPaymentStatusTransitions 测试支付单状态转换 func TestPaymentStatusTransitionsValid(t *testing.T) { validTransitions := map[string][]string{ "pending": {"paying", "closed"}, "paying": {"paid", "failed", "closed"}, "paid": {"refunding", "refunded"}, "failed": {}, // 终态 "closed": {}, // 终态 "refunded": {}, // 终态 } for from, toList := range validTransitions { if from == "" { t.Fatal("payment status should not be empty") } for _, to := range toList { if to == "" { t.Fatal("transition target should not be empty") } } } } // TestRefundBizTypeCoverage 测试退款业务类型覆盖 func TestRefundBizTypeCoverage(t *testing.T) { expected := []string{ "cancel_refund", "admin_close_refund", "admin_refund", "checkout_refund", "deposit_refund", "rent_refund", "arbitration_refund", } if len(refundBizTypes) != len(expected) { t.Fatalf("refundBizTypes count = %d, want %d", len(refundBizTypes), len(expected)) } // 验证每个预期类型都存在 typeMap := make(map[string]bool) for _, bizType := range refundBizTypes { typeMap[bizType] = true } for _, exp := range expected { if !typeMap[exp] { t.Fatalf("missing refund biz type: %s", exp) } } } // TestPaymentAmountValidation 测试支付金额验证 func TestPaymentAmountMustBePositive(t *testing.T) { testCases := []struct { name string amount int64 wantValid bool }{ {"正数金额", 10000, true}, {"零金额", 0, false}, {"负数金额", -1000, false}, {"最小金额", 1, true}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { isValid := tc.amount > 0 if isValid != tc.wantValid { t.Fatalf("amount %d validation = %v, want %v", tc.amount, isValid, tc.wantValid) } }) } } // TestPaymentProviderValidation 测试支付渠道验证 func TestPaymentProviderMustNotBeEmpty(t *testing.T) { testCases := []struct { name string provider string wantValid bool }{ {"lakala 渠道", "lakala", true}, {"leshua 渠道", "leshua", true}, {"mock 渠道", "mock", true}, {"空渠道", "", false}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { isValid := tc.provider != "" if isValid != tc.wantValid { t.Fatalf("provider %q validation = %v, want %v", tc.provider, isValid, tc.wantValid) } }) } } // TestRuntimeConfigIsMockMode 测试运行时配置 mock 模式 func TestRuntimeConfigIsMockModeTrue(t *testing.T) { config := runtimePaymentConfig{ Provider: "mock", } if !config.isMockMode() { t.Fatal("should be mock mode when provider is 'mock'") } } func TestRuntimeConfigIsMockModeFalse(t *testing.T) { providers := []string{"lakala", "leshua", "alipay", "wechat"} for _, provider := range providers { config := runtimePaymentConfig{ Provider: provider, } if config.isMockMode() { t.Fatalf("should not be mock mode when provider is %q", provider) } } } // TestChannelSourceValidation 测试渠道来源验证 func TestChannelSourceMustBeValid(t *testing.T) { validSources := []string{ channelSourceCreate, channelSourceQuery, channelSourceNotify, channelSourceMock, } expectedValues := []string{"create", "query", "notify", "mock"} for i, source := range validSources { if source != expectedValues[i] { t.Fatalf("channelSource[%d] = %q, want %q", i, source, expectedValues[i]) } } } // TestPaymentOrderFields 测试支付单字段完整性 func TestPaymentOrderRequiredFields(t *testing.T) { payment := model.PaymentOrder{ PaymentNo: "PAY123456", OrderNo: "ORD123456", OrderID: 100, UserID: 1, AmountCent: 10000, Status: "paying", Provider: "lakala", MerchantID: "M123", BizType: "order_pay", } // 验证必填字段 if payment.PaymentNo == "" { t.Fatal("PaymentNo should not be empty") } if payment.OrderNo == "" { t.Fatal("OrderNo should not be empty") } if payment.OrderID == 0 { t.Fatal("OrderID should not be zero") } if payment.UserID == 0 { t.Fatal("UserID should not be zero") } if payment.AmountCent <= 0 { t.Fatal("AmountCent should be positive") } if payment.Status == "" { t.Fatal("Status should not be empty") } if payment.Provider == "" { t.Fatal("Provider should not be empty") } } // TestRefundAmountValidation 测试退款金额验证 func TestRefundAmountMustNotExceedOriginal(t *testing.T) { testCases := []struct { name string originalAmount int64 refundAmount int64 wantValid bool }{ {"部分退款", 10000, 5000, true}, {"全额退款", 10000, 10000, true}, {"超额退款", 10000, 15000, false}, {"零退款", 10000, 0, false}, {"负数退款", 10000, -1000, false}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { isValid := tc.refundAmount > 0 && tc.refundAmount <= tc.originalAmount if isValid != tc.wantValid { t.Fatalf("refund %d from %d validation = %v, want %v", tc.refundAmount, tc.originalAmount, isValid, tc.wantValid) } }) } } // TestWalletRechargeEnabled 测试钱包充值开关 func TestWalletRechargeEnabledInDevelopment(t *testing.T) { svc := NewService(nil, "development") if !svc.walletRechargeEnabled { t.Fatal("wallet recharge should be enabled in development") } } func TestWalletRechargeDisabledInProduction(t *testing.T) { svc := NewService(nil, "production") if svc.walletRechargeEnabled { t.Fatal("wallet recharge should be disabled in production") } } func TestWalletRechargeEnabledInTestEnv(t *testing.T) { svc := NewService(nil, "test") if !svc.walletRechargeEnabled { t.Fatal("wallet recharge should be enabled in test env") } } // TestMinWalletRechargeAmountIsReasonable 测试最小充值金额合理性 func TestMinWalletRechargeAmountIsReasonable(t *testing.T) { if MinWalletRechargeAmount <= 0 { t.Fatal("MinWalletRechargeAmount should be positive") } if MinWalletRechargeAmount > 1.0 { t.Fatalf("MinWalletRechargeAmount = %.2f, seems too high for minimum", MinWalletRechargeAmount) } } // TestPaymentNotifyResultStructure 测试支付回调结果结构 func TestNotifyResultHasRequiredFields(t *testing.T) { result := NotifyResult{ OK: true, Message: "payment success", } if result.Message == "" { t.Fatal("NotifyResult.Message should not be empty") } }