|
- package controller
-
- import (
- "bytes"
- "encoding/json"
- "net/http"
- "net/http/httptest"
- "testing"
-
- "github.com/QuantumNous/new-api/common"
- "github.com/QuantumNous/new-api/model"
- "github.com/QuantumNous/new-api/setting"
- "github.com/glebarez/sqlite"
- "github.com/gin-gonic/gin"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
- "gorm.io/gorm"
- )
-
- func setupWechatControllerDB(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 := model.DB
- origLogDB := model.LOG_DB
- model.DB = db
- model.LOG_DB = db
- common.UsingSQLite = true
- common.RedisEnabled = false
- common.QuotaPerUnit = 500 * 1000.0
-
- require.NoError(t, db.AutoMigrate(&model.User{}, &model.TopUp{}, &model.Log{}))
-
- t.Cleanup(func() {
- model.DB = origDB
- model.LOG_DB = origLogDB
- sqlDB.Close()
- })
- return db
- }
-
- func createTestUser(t *testing.T, db *gorm.DB, id int, username string) *model.User {
- user := &model.User{
- Id: id,
- Username: username,
- Password: "hashed",
- Quota: 100000,
- Role: common.RoleCommonUser,
- Status: common.UserStatusEnabled,
- AffCode: "aff_test_" + username,
- }
- require.NoError(t, db.Create(user).Error)
- return user
- }
-
- func newTestContextWithUser(userID int) (*gin.Context, *httptest.ResponseRecorder) {
- gin.SetMode(gin.TestMode)
- w := httptest.NewRecorder()
- c, _ := gin.CreateTestContext(w)
- c.Set("id", userID)
- return c, w
- }
-
- func setupWechatTestRouter() *gin.Engine {
- gin.SetMode(gin.TestMode)
- r := gin.New()
- r.POST("/api/user/wechat/pay/amount", RequestWechatPayAmount)
- r.POST("/api/user/wechat/pay", RequestWechatPay)
- r.GET("/api/user/wechat/pay/status", WechatPayStatus)
- return r
- }
-
- // ========== RequestWechatPayAmount 测试 ==========
-
- func TestRequestWechatPayAmount_Success(t *testing.T) {
- // 此测试验证 getWechatPayMoney 核心计算逻辑
- // RequestWechatPayAmount 依赖 GetUserGroup 等完整数据库,在集成测试中覆盖
- origUnitPrice := setting.WechatPayUnitPrice
- setting.WechatPayUnitPrice = 7.0
- t.Cleanup(func() {
- setting.WechatPayUnitPrice = origUnitPrice
- })
-
- // 测试不同金额的计算
- result := getWechatPayMoney(10.0, "default")
- assert.InDelta(t, 70.0, result, 0.01)
-
- result = getWechatPayMoney(1.0, "default")
- assert.InDelta(t, 7.0, result, 0.01)
-
- result = getWechatPayMoney(100.0, "default")
- assert.InDelta(t, 700.0, result, 0.01)
- }
-
- func TestRequestWechatPayAmount_BelowMinTopup(t *testing.T) {
- setupWechatControllerDB(t)
-
- origMinTopUp := setting.WechatPayMinTopUp
- setting.WechatPayMinTopUp = 5
- t.Cleanup(func() {
- setting.WechatPayMinTopUp = origMinTopUp
- })
-
- body, _ := json.Marshal(WechatPayRequest{Amount: 3})
- req := httptest.NewRequest(http.MethodPost, "/api/user/wechat/pay/amount", bytes.NewReader(body))
- req.Header.Set("Content-Type", "application/json")
-
- w := httptest.NewRecorder()
- c, _ := gin.CreateTestContext(w)
- c.Request = req
- c.Set("id", 400)
-
- RequestWechatPayAmount(c)
-
- var resp map[string]interface{}
- require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
- assert.Equal(t, "error", resp["message"])
- }
-
- func TestRequestWechatPayAmount_InvalidBody(t *testing.T) {
- setupWechatControllerDB(t)
-
- req := httptest.NewRequest(http.MethodPost, "/api/user/wechat/pay/amount", bytes.NewReader([]byte("invalid")))
- req.Header.Set("Content-Type", "application/json")
-
- w := httptest.NewRecorder()
- c, _ := gin.CreateTestContext(w)
- c.Request = req
- c.Set("id", 400)
-
- RequestWechatPayAmount(c)
-
- var resp map[string]interface{}
- require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
- assert.Equal(t, "error", resp["message"])
- assert.Equal(t, "参数错误", resp["data"])
- }
-
- // ========== RequestWechatPay 测试 ==========
-
- func TestRequestWechatPay_NotConfigured(t *testing.T) {
- setupWechatControllerDB(t)
-
- // 确保微信支付未配置
- origAppID := setting.WechatPayAppID
- setting.WechatPayAppID = ""
- t.Cleanup(func() {
- setting.WechatPayAppID = origAppID
- })
-
- body, _ := json.Marshal(WechatPayRequest{Amount: 10})
- req := httptest.NewRequest(http.MethodPost, "/api/user/wechat/pay", bytes.NewReader(body))
- req.Header.Set("Content-Type", "application/json")
-
- w := httptest.NewRecorder()
- c, _ := gin.CreateTestContext(w)
- c.Request = req
- c.Set("id", 400)
-
- RequestWechatPay(c)
-
- var resp map[string]interface{}
- require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
- assert.Equal(t, "error", resp["message"])
- assert.Equal(t, "微信支付未配置", resp["data"])
- }
-
- func TestRequestWechatPay_ExceedsMaxAmount(t *testing.T) {
- setupWechatControllerDB(t)
-
- body, _ := json.Marshal(WechatPayRequest{Amount: 20000})
- req := httptest.NewRequest(http.MethodPost, "/api/user/wechat/pay", bytes.NewReader(body))
- req.Header.Set("Content-Type", "application/json")
-
- w := httptest.NewRecorder()
- c, _ := gin.CreateTestContext(w)
- c.Request = req
- c.Set("id", 400)
-
- RequestWechatPay(c)
-
- var resp map[string]interface{}
- require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
- assert.Equal(t, "error", resp["message"])
- assert.Contains(t, resp["data"], "不能大于 10000")
- }
-
- func TestRequestWechatPay_BelowMinAmount(t *testing.T) {
- setupWechatControllerDB(t)
-
- origMinTopUp := setting.WechatPayMinTopUp
- setting.WechatPayMinTopUp = 5
- t.Cleanup(func() {
- setting.WechatPayMinTopUp = origMinTopUp
- })
-
- body, _ := json.Marshal(WechatPayRequest{Amount: 3})
- req := httptest.NewRequest(http.MethodPost, "/api/user/wechat/pay", bytes.NewReader(body))
- req.Header.Set("Content-Type", "application/json")
-
- w := httptest.NewRecorder()
- c, _ := gin.CreateTestContext(w)
- c.Request = req
- c.Set("id", 400)
-
- RequestWechatPay(c)
-
- var resp map[string]interface{}
- require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
- assert.Equal(t, "error", resp["message"])
- }
-
- // ========== WechatPayStatus 测试 ==========
-
- func TestWechatPayStatus_Success(t *testing.T) {
- db := setupWechatControllerDB(t)
- user := createTestUser(t, db, 401, "wx_status_test")
-
- // 创建订单
- topUp := &model.TopUp{
- UserId: user.Id,
- Amount: 10,
- Money: 70.0,
- TradeNo: "wx_status_trade_001",
- PaymentMethod: PaymentMethodWechatPay,
- CreateTime: common.GetTimestamp(),
- Status: common.TopUpStatusSuccess,
- }
- require.NoError(t, db.Create(topUp).Error)
-
- req := httptest.NewRequest(http.MethodGet, "/api/user/wechat/pay/status?trade_no=wx_status_trade_001", nil)
- w := httptest.NewRecorder()
- c, _ := gin.CreateTestContext(w)
- c.Request = req
- c.Set("id", user.Id)
-
- WechatPayStatus(c)
-
- assert.Equal(t, 200, w.Code)
- var resp map[string]interface{}
- require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
- assert.Equal(t, "success", resp["message"])
-
- data := resp["data"].(map[string]interface{})
- assert.Equal(t, common.TopUpStatusSuccess, data["status"])
- }
-
- func TestWechatPayStatus_OrderNotFound(t *testing.T) {
- setupWechatControllerDB(t)
-
- req := httptest.NewRequest(http.MethodGet, "/api/user/wechat/pay/status?trade_no=nonexistent", nil)
- w := httptest.NewRecorder()
- c, _ := gin.CreateTestContext(w)
- c.Request = req
- c.Set("id", 401)
-
- WechatPayStatus(c)
-
- var resp map[string]interface{}
- require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
- assert.Equal(t, "error", resp["message"])
- assert.Equal(t, "订单不存在", resp["data"])
- }
-
- func TestWechatPayStatus_WrongUser(t *testing.T) {
- db := setupWechatControllerDB(t)
- createTestUser(t, db, 402, "wx_status_owner")
- // 用户 403 尝试查询 402 的订单
- createTestUser(t, db, 403, "wx_status_other")
-
- topUp := &model.TopUp{
- UserId: 402,
- Amount: 10,
- Money: 70.0,
- TradeNo: "wx_status_trade_wrong_user",
- PaymentMethod: PaymentMethodWechatPay,
- CreateTime: common.GetTimestamp(),
- Status: common.TopUpStatusPending,
- }
- require.NoError(t, db.Create(topUp).Error)
-
- req := httptest.NewRequest(http.MethodGet, "/api/user/wechat/pay/status?trade_no=wx_status_trade_wrong_user", nil)
- w := httptest.NewRecorder()
- c, _ := gin.CreateTestContext(w)
- c.Request = req
- c.Set("id", 403) // 不同用户
-
- WechatPayStatus(c)
-
- var resp map[string]interface{}
- require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
- assert.Equal(t, "error", resp["message"])
- assert.Equal(t, "订单不存在", resp["data"]) // 不泄露订单存在信息
- }
-
- func TestWechatPayStatus_MissingTradeNo(t *testing.T) {
- setupWechatControllerDB(t)
-
- req := httptest.NewRequest(http.MethodGet, "/api/user/wechat/pay/status", nil)
- w := httptest.NewRecorder()
- c, _ := gin.CreateTestContext(w)
- c.Request = req
- c.Set("id", 401)
-
- WechatPayStatus(c)
-
- var resp map[string]interface{}
- require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
- assert.Equal(t, "error", resp["message"])
- assert.Equal(t, "参数错误", resp["data"])
- }
-
- // ========== getWechatPayMoney 测试 ==========
-
- func TestGetWechatPayMoney(t *testing.T) {
- origUnitPrice := setting.WechatPayUnitPrice
- setting.WechatPayUnitPrice = 7.0
- t.Cleanup(func() {
- setting.WechatPayUnitPrice = origUnitPrice
- })
-
- tests := []struct {
- name string
- amount float64
- group string
- expected float64
- }{
- {"标准计算", 10, "default", 70.0},
- {"最小金额", 1, "default", 7.0},
- }
-
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- result := getWechatPayMoney(tt.amount, tt.group)
- assert.InDelta(t, tt.expected, result, 0.01)
- })
- }
- }
-
- // ========== getWechatMinTopup 测试 ==========
-
- func TestGetWechatMinTopup(t *testing.T) {
- origMinTopUp := setting.WechatPayMinTopUp
- setting.WechatPayMinTopUp = 1
- t.Cleanup(func() {
- setting.WechatPayMinTopUp = origMinTopUp
- })
-
- result := getWechatMinTopup()
- assert.Equal(t, int64(1), result)
- }
-
- // ========== ResetWechatPayClient 测试 ==========
-
- func TestResetWechatPayClient(t *testing.T) {
- // 确保重置后客户端为 nil
- wechatPayClient = nil
- ResetWechatPayClient()
- assert.Nil(t, wechatPayClient)
- }
-
- func TestResetWechatPayClient_CallbackRegistered(t *testing.T) {
- // 验证 init() 注册了回调
- assert.NotNil(t, setting.OnWechatPayConfigChanged)
- }
-
- // ========== PaymentMethodWechatPay 常量测试 ==========
-
- func TestPaymentMethodWechatPay(t *testing.T) {
- assert.Equal(t, "wechat_pay", PaymentMethodWechatPay)
- }
|