|
- package model
-
- import (
- "testing"
-
- "github.com/QuantumNous/new-api/common"
- "github.com/glebarez/sqlite"
- "github.com/shopspring/decimal"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
- "gorm.io/gorm"
- )
-
- func setupWechatTopUpDB(t *testing.T) *gorm.DB {
- t.Helper()
- db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
- require.NoError(t, err)
- sqlDB, _ := db.DB()
- sqlDB.SetMaxOpenConns(1)
-
- origDB := DB
- origLogDB := LOG_DB
- DB = db
- LOG_DB = db
- common.UsingSQLite = true
- common.RedisEnabled = false
- common.QuotaPerUnit = 500 * 1000.0
-
- require.NoError(t, db.AutoMigrate(&User{}, &TopUp{}, &Log{}))
-
- t.Cleanup(func() {
- DB = origDB
- LOG_DB = origLogDB
- sqlDB.Close()
- })
- return db
- }
-
- func TestRechargeWechat_Success(t *testing.T) {
- db := setupWechatTopUpDB(t)
-
- // 创建测试用户
- user := User{
- Id: 200,
- Username: "wechat_test_user",
- Password: "hashed_password",
- Quota: 100000,
- AffCode: "aff_wx_200",
- }
- require.NoError(t, db.Create(&user).Error)
-
- // 创建 pending 状态的充值订单
- expectedQuota := int(decimal.NewFromFloat(10.0).Mul(decimal.NewFromFloat(common.QuotaPerUnit)).IntPart())
- topUp := &TopUp{
- UserId: 200,
- Amount: 10,
- Money: 10.0, // 10 元
- TradeNo: "wx_test_trade_001",
- PaymentMethod: "wechat_pay",
- CreateTime: common.GetTimestamp(),
- Status: common.TopUpStatusPending,
- }
- require.NoError(t, db.Create(topUp).Error)
-
- // 执行充值
- err := RechargeWechat("wx_test_trade_001")
- require.NoError(t, err)
-
- // 验证用户额度已增加
- var updatedUser User
- require.NoError(t, db.First(&updatedUser, 200).Error)
- assert.Equal(t, 100000+expectedQuota, updatedUser.Quota)
-
- // 验证订单状态已更新
- var updatedTopUp TopUp
- require.NoError(t, db.Where("trade_no = ?", "wx_test_trade_001").First(&updatedTopUp).Error)
- assert.Equal(t, common.TopUpStatusSuccess, updatedTopUp.Status)
- assert.NotZero(t, updatedTopUp.CompleteTime)
- }
-
- func TestRechargeWechat_OrderNotFound(t *testing.T) {
- setupWechatTopUpDB(t)
-
- err := RechargeWechat("non_existent_trade_no")
- assert.Error(t, err)
- assert.Contains(t, err.Error(), "充值订单不存在")
- }
-
- func TestRechargeWechat_EmptyTradeNo(t *testing.T) {
- setupWechatTopUpDB(t)
-
- err := RechargeWechat("")
- assert.Error(t, err)
- assert.Contains(t, err.Error(), "未提供支付单号")
- }
-
- func TestRechargeWechat_AlreadyCompleted(t *testing.T) {
- db := setupWechatTopUpDB(t)
-
- // 创建测试用户
- user := User{
- Id: 201,
- Username: "wechat_test_user_2",
- Password: "hashed_password",
- Quota: 100000,
- AffCode: "aff_wx_201",
- }
- require.NoError(t, db.Create(&user).Error)
-
- // 创建已完成的充值订单
- topUp := &TopUp{
- UserId: 201,
- Amount: 10,
- Money: 10.0,
- TradeNo: "wx_test_trade_completed",
- PaymentMethod: "wechat_pay",
- CreateTime: common.GetTimestamp(),
- CompleteTime: common.GetTimestamp(),
- Status: common.TopUpStatusSuccess,
- }
- require.NoError(t, db.Create(topUp).Error)
-
- // 再次充值应幂等返回成功
- err := RechargeWechat("wx_test_trade_completed")
- require.NoError(t, err)
-
- // 验证额度未变化
- var updatedUser User
- require.NoError(t, db.First(&updatedUser, 201).Error)
- assert.Equal(t, 100000, updatedUser.Quota) // 额度不变
- }
-
- func TestRechargeWechat_InvalidStatus(t *testing.T) {
- db := setupWechatTopUpDB(t)
-
- // 创建测试用户
- user := User{
- Id: 202,
- Username: "wechat_test_user_3",
- Password: "hashed_password",
- Quota: 100000,
- AffCode: "aff_wx_202",
- }
- require.NoError(t, db.Create(&user).Error)
-
- // 创建 expired 状态的订单
- topUp := &TopUp{
- UserId: 202,
- Amount: 10,
- Money: 10.0,
- TradeNo: "wx_test_trade_expired",
- PaymentMethod: "wechat_pay",
- CreateTime: common.GetTimestamp(),
- Status: common.TopUpStatusExpired,
- }
- require.NoError(t, db.Create(topUp).Error)
-
- // 应返回状态错误
- err := RechargeWechat("wx_test_trade_expired")
- assert.Error(t, err)
- assert.Contains(t, err.Error(), "充值订单状态错误")
-
- // 验证额度未变化
- var updatedUser User
- require.NoError(t, db.First(&updatedUser, 202).Error)
- assert.Equal(t, 100000, updatedUser.Quota)
- }
-
- func TestRechargeWechat_ConcurrentSafety(t *testing.T) {
- db := setupWechatTopUpDB(t)
-
- // 创建测试用户
- user := User{
- Id: 203,
- Username: "wechat_concurrent_user",
- Password: "hashed_password",
- Quota: 100000,
- AffCode: "aff_wx_203",
- }
- require.NoError(t, db.Create(&user).Error)
-
- expectedQuota := int(decimal.NewFromFloat(10.0).Mul(decimal.NewFromFloat(common.QuotaPerUnit)).IntPart())
-
- // 创建 pending 订单
- topUp := &TopUp{
- UserId: 203,
- Amount: 10,
- Money: 10.0,
- TradeNo: "wx_concurrent_trade",
- PaymentMethod: "wechat_pay",
- CreateTime: common.GetTimestamp(),
- Status: common.TopUpStatusPending,
- }
- require.NoError(t, db.Create(topUp).Error)
-
- // 并发调用 RechargeWechat,只有第一个应该成功充值
- done := make(chan error, 2)
- go func() { done <- RechargeWechat("wx_concurrent_trade") }()
- go func() { done <- RechargeWechat("wx_concurrent_trade") }()
-
- err1 := <-done
- err2 := <-done
-
- // 至少一个成功,不能两个都失败
- require.True(t, err1 == nil || err2 == nil, "至少一个并发请求应成功")
-
- // 验证额度只增加了一次(因为 SQLite 单连接,实际上是序列化的)
- var updatedUser User
- require.NoError(t, db.First(&updatedUser, 203).Error)
- assert.Equal(t, 100000+expectedQuota, updatedUser.Quota, "额度应只增加一次")
- }
-
- func TestRechargeWechat_QuotaCalculation(t *testing.T) {
- db := setupWechatTopUpDB(t)
-
- testCases := []struct {
- name string
- money float64
- initialQuota int
- expectedAdd int
- userId int
- }{
- {"整数金额", 7.0, 0, int(decimal.NewFromFloat(7.0).Mul(decimal.NewFromFloat(common.QuotaPerUnit)).IntPart()), 310},
- {"小数金额", 7.5, 0, int(decimal.NewFromFloat(7.5).Mul(decimal.NewFromFloat(common.QuotaPerUnit)).IntPart()), 311},
- {"最小金额", 0.01, 0, int(decimal.NewFromFloat(0.01).Mul(decimal.NewFromFloat(common.QuotaPerUnit)).IntPart()), 312},
- }
-
- for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
- userId := tc.userId
- tradeNo := "wx_quota_test_" + tc.name
-
- // 创建用户
- user := User{
- Id: userId,
- Username: "quota_test_" + tc.name,
- Password: "hashed",
- Quota: tc.initialQuota,
- AffCode: "aff_" + tc.name,
- }
- require.NoError(t, db.Create(&user).Error)
-
- // 创建订单
- topUp := &TopUp{
- UserId: userId,
- Amount: 1,
- Money: tc.money,
- TradeNo: tradeNo,
- PaymentMethod: "wechat_pay",
- CreateTime: common.GetTimestamp(),
- Status: common.TopUpStatusPending,
- }
- require.NoError(t, db.Create(topUp).Error)
-
- err := RechargeWechat(tradeNo)
- require.NoError(t, err)
-
- var updatedUser User
- require.NoError(t, db.First(&updatedUser, userId).Error)
- assert.Equal(t, tc.initialQuota+tc.expectedAdd, updatedUser.Quota, tc.name)
- })
- }
- }
|