Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 

375 строки
10 KiB

  1. package controller
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "net/http"
  6. "net/http/httptest"
  7. "testing"
  8. "github.com/QuantumNous/new-api/common"
  9. "github.com/QuantumNous/new-api/model"
  10. "github.com/QuantumNous/new-api/setting"
  11. "github.com/glebarez/sqlite"
  12. "github.com/gin-gonic/gin"
  13. "github.com/stretchr/testify/assert"
  14. "github.com/stretchr/testify/require"
  15. "gorm.io/gorm"
  16. )
  17. func setupWechatControllerDB(t *testing.T) *gorm.DB {
  18. t.Helper()
  19. db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
  20. require.NoError(t, err)
  21. sqlDB, _ := db.DB()
  22. sqlDB.SetMaxOpenConns(1)
  23. origDB := model.DB
  24. origLogDB := model.LOG_DB
  25. model.DB = db
  26. model.LOG_DB = db
  27. common.UsingSQLite = true
  28. common.RedisEnabled = false
  29. common.QuotaPerUnit = 500 * 1000.0
  30. require.NoError(t, db.AutoMigrate(&model.User{}, &model.TopUp{}, &model.Log{}))
  31. t.Cleanup(func() {
  32. model.DB = origDB
  33. model.LOG_DB = origLogDB
  34. sqlDB.Close()
  35. })
  36. return db
  37. }
  38. func createTestUser(t *testing.T, db *gorm.DB, id int, username string) *model.User {
  39. user := &model.User{
  40. Id: id,
  41. Username: username,
  42. Password: "hashed",
  43. Quota: 100000,
  44. Role: common.RoleCommonUser,
  45. Status: common.UserStatusEnabled,
  46. AffCode: "aff_test_" + username,
  47. }
  48. require.NoError(t, db.Create(user).Error)
  49. return user
  50. }
  51. func newTestContextWithUser(userID int) (*gin.Context, *httptest.ResponseRecorder) {
  52. gin.SetMode(gin.TestMode)
  53. w := httptest.NewRecorder()
  54. c, _ := gin.CreateTestContext(w)
  55. c.Set("id", userID)
  56. return c, w
  57. }
  58. func setupWechatTestRouter() *gin.Engine {
  59. gin.SetMode(gin.TestMode)
  60. r := gin.New()
  61. r.POST("/api/user/wechat/pay/amount", RequestWechatPayAmount)
  62. r.POST("/api/user/wechat/pay", RequestWechatPay)
  63. r.GET("/api/user/wechat/pay/status", WechatPayStatus)
  64. return r
  65. }
  66. // ========== RequestWechatPayAmount 测试 ==========
  67. func TestRequestWechatPayAmount_Success(t *testing.T) {
  68. // 此测试验证 getWechatPayMoney 核心计算逻辑
  69. // RequestWechatPayAmount 依赖 GetUserGroup 等完整数据库,在集成测试中覆盖
  70. origUnitPrice := setting.WechatPayUnitPrice
  71. setting.WechatPayUnitPrice = 7.0
  72. t.Cleanup(func() {
  73. setting.WechatPayUnitPrice = origUnitPrice
  74. })
  75. // 测试不同金额的计算
  76. result := getWechatPayMoney(10.0, "default")
  77. assert.InDelta(t, 70.0, result, 0.01)
  78. result = getWechatPayMoney(1.0, "default")
  79. assert.InDelta(t, 7.0, result, 0.01)
  80. result = getWechatPayMoney(100.0, "default")
  81. assert.InDelta(t, 700.0, result, 0.01)
  82. }
  83. func TestRequestWechatPayAmount_BelowMinTopup(t *testing.T) {
  84. setupWechatControllerDB(t)
  85. origMinTopUp := setting.WechatPayMinTopUp
  86. setting.WechatPayMinTopUp = 5
  87. t.Cleanup(func() {
  88. setting.WechatPayMinTopUp = origMinTopUp
  89. })
  90. body, _ := json.Marshal(WechatPayRequest{Amount: 3})
  91. req := httptest.NewRequest(http.MethodPost, "/api/user/wechat/pay/amount", bytes.NewReader(body))
  92. req.Header.Set("Content-Type", "application/json")
  93. w := httptest.NewRecorder()
  94. c, _ := gin.CreateTestContext(w)
  95. c.Request = req
  96. c.Set("id", 400)
  97. RequestWechatPayAmount(c)
  98. var resp map[string]interface{}
  99. require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
  100. assert.Equal(t, "error", resp["message"])
  101. }
  102. func TestRequestWechatPayAmount_InvalidBody(t *testing.T) {
  103. setupWechatControllerDB(t)
  104. req := httptest.NewRequest(http.MethodPost, "/api/user/wechat/pay/amount", bytes.NewReader([]byte("invalid")))
  105. req.Header.Set("Content-Type", "application/json")
  106. w := httptest.NewRecorder()
  107. c, _ := gin.CreateTestContext(w)
  108. c.Request = req
  109. c.Set("id", 400)
  110. RequestWechatPayAmount(c)
  111. var resp map[string]interface{}
  112. require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
  113. assert.Equal(t, "error", resp["message"])
  114. assert.Equal(t, "参数错误", resp["data"])
  115. }
  116. // ========== RequestWechatPay 测试 ==========
  117. func TestRequestWechatPay_NotConfigured(t *testing.T) {
  118. setupWechatControllerDB(t)
  119. // 确保微信支付未配置
  120. origAppID := setting.WechatPayAppID
  121. setting.WechatPayAppID = ""
  122. t.Cleanup(func() {
  123. setting.WechatPayAppID = origAppID
  124. })
  125. body, _ := json.Marshal(WechatPayRequest{Amount: 10})
  126. req := httptest.NewRequest(http.MethodPost, "/api/user/wechat/pay", bytes.NewReader(body))
  127. req.Header.Set("Content-Type", "application/json")
  128. w := httptest.NewRecorder()
  129. c, _ := gin.CreateTestContext(w)
  130. c.Request = req
  131. c.Set("id", 400)
  132. RequestWechatPay(c)
  133. var resp map[string]interface{}
  134. require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
  135. assert.Equal(t, "error", resp["message"])
  136. assert.Equal(t, "微信支付未配置", resp["data"])
  137. }
  138. func TestRequestWechatPay_ExceedsMaxAmount(t *testing.T) {
  139. setupWechatControllerDB(t)
  140. body, _ := json.Marshal(WechatPayRequest{Amount: 20000})
  141. req := httptest.NewRequest(http.MethodPost, "/api/user/wechat/pay", bytes.NewReader(body))
  142. req.Header.Set("Content-Type", "application/json")
  143. w := httptest.NewRecorder()
  144. c, _ := gin.CreateTestContext(w)
  145. c.Request = req
  146. c.Set("id", 400)
  147. RequestWechatPay(c)
  148. var resp map[string]interface{}
  149. require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
  150. assert.Equal(t, "error", resp["message"])
  151. assert.Contains(t, resp["data"], "不能大于 10000")
  152. }
  153. func TestRequestWechatPay_BelowMinAmount(t *testing.T) {
  154. setupWechatControllerDB(t)
  155. origMinTopUp := setting.WechatPayMinTopUp
  156. setting.WechatPayMinTopUp = 5
  157. t.Cleanup(func() {
  158. setting.WechatPayMinTopUp = origMinTopUp
  159. })
  160. body, _ := json.Marshal(WechatPayRequest{Amount: 3})
  161. req := httptest.NewRequest(http.MethodPost, "/api/user/wechat/pay", bytes.NewReader(body))
  162. req.Header.Set("Content-Type", "application/json")
  163. w := httptest.NewRecorder()
  164. c, _ := gin.CreateTestContext(w)
  165. c.Request = req
  166. c.Set("id", 400)
  167. RequestWechatPay(c)
  168. var resp map[string]interface{}
  169. require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
  170. assert.Equal(t, "error", resp["message"])
  171. }
  172. // ========== WechatPayStatus 测试 ==========
  173. func TestWechatPayStatus_Success(t *testing.T) {
  174. db := setupWechatControllerDB(t)
  175. user := createTestUser(t, db, 401, "wx_status_test")
  176. // 创建订单
  177. topUp := &model.TopUp{
  178. UserId: user.Id,
  179. Amount: 10,
  180. Money: 70.0,
  181. TradeNo: "wx_status_trade_001",
  182. PaymentMethod: PaymentMethodWechatPay,
  183. CreateTime: common.GetTimestamp(),
  184. Status: common.TopUpStatusSuccess,
  185. }
  186. require.NoError(t, db.Create(topUp).Error)
  187. req := httptest.NewRequest(http.MethodGet, "/api/user/wechat/pay/status?trade_no=wx_status_trade_001", nil)
  188. w := httptest.NewRecorder()
  189. c, _ := gin.CreateTestContext(w)
  190. c.Request = req
  191. c.Set("id", user.Id)
  192. WechatPayStatus(c)
  193. assert.Equal(t, 200, w.Code)
  194. var resp map[string]interface{}
  195. require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
  196. assert.Equal(t, "success", resp["message"])
  197. data := resp["data"].(map[string]interface{})
  198. assert.Equal(t, common.TopUpStatusSuccess, data["status"])
  199. }
  200. func TestWechatPayStatus_OrderNotFound(t *testing.T) {
  201. setupWechatControllerDB(t)
  202. req := httptest.NewRequest(http.MethodGet, "/api/user/wechat/pay/status?trade_no=nonexistent", nil)
  203. w := httptest.NewRecorder()
  204. c, _ := gin.CreateTestContext(w)
  205. c.Request = req
  206. c.Set("id", 401)
  207. WechatPayStatus(c)
  208. var resp map[string]interface{}
  209. require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
  210. assert.Equal(t, "error", resp["message"])
  211. assert.Equal(t, "订单不存在", resp["data"])
  212. }
  213. func TestWechatPayStatus_WrongUser(t *testing.T) {
  214. db := setupWechatControllerDB(t)
  215. createTestUser(t, db, 402, "wx_status_owner")
  216. // 用户 403 尝试查询 402 的订单
  217. createTestUser(t, db, 403, "wx_status_other")
  218. topUp := &model.TopUp{
  219. UserId: 402,
  220. Amount: 10,
  221. Money: 70.0,
  222. TradeNo: "wx_status_trade_wrong_user",
  223. PaymentMethod: PaymentMethodWechatPay,
  224. CreateTime: common.GetTimestamp(),
  225. Status: common.TopUpStatusPending,
  226. }
  227. require.NoError(t, db.Create(topUp).Error)
  228. req := httptest.NewRequest(http.MethodGet, "/api/user/wechat/pay/status?trade_no=wx_status_trade_wrong_user", nil)
  229. w := httptest.NewRecorder()
  230. c, _ := gin.CreateTestContext(w)
  231. c.Request = req
  232. c.Set("id", 403) // 不同用户
  233. WechatPayStatus(c)
  234. var resp map[string]interface{}
  235. require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
  236. assert.Equal(t, "error", resp["message"])
  237. assert.Equal(t, "订单不存在", resp["data"]) // 不泄露订单存在信息
  238. }
  239. func TestWechatPayStatus_MissingTradeNo(t *testing.T) {
  240. setupWechatControllerDB(t)
  241. req := httptest.NewRequest(http.MethodGet, "/api/user/wechat/pay/status", nil)
  242. w := httptest.NewRecorder()
  243. c, _ := gin.CreateTestContext(w)
  244. c.Request = req
  245. c.Set("id", 401)
  246. WechatPayStatus(c)
  247. var resp map[string]interface{}
  248. require.NoError(t, json.Unmarshal(w.Body.Bytes(), &resp))
  249. assert.Equal(t, "error", resp["message"])
  250. assert.Equal(t, "参数错误", resp["data"])
  251. }
  252. // ========== getWechatPayMoney 测试 ==========
  253. func TestGetWechatPayMoney(t *testing.T) {
  254. origUnitPrice := setting.WechatPayUnitPrice
  255. setting.WechatPayUnitPrice = 7.0
  256. t.Cleanup(func() {
  257. setting.WechatPayUnitPrice = origUnitPrice
  258. })
  259. tests := []struct {
  260. name string
  261. amount float64
  262. group string
  263. expected float64
  264. }{
  265. {"标准计算", 10, "default", 70.0},
  266. {"最小金额", 1, "default", 7.0},
  267. }
  268. for _, tt := range tests {
  269. t.Run(tt.name, func(t *testing.T) {
  270. result := getWechatPayMoney(tt.amount, tt.group)
  271. assert.InDelta(t, tt.expected, result, 0.01)
  272. })
  273. }
  274. }
  275. // ========== getWechatMinTopup 测试 ==========
  276. func TestGetWechatMinTopup(t *testing.T) {
  277. origMinTopUp := setting.WechatPayMinTopUp
  278. setting.WechatPayMinTopUp = 1
  279. t.Cleanup(func() {
  280. setting.WechatPayMinTopUp = origMinTopUp
  281. })
  282. result := getWechatMinTopup()
  283. assert.Equal(t, int64(1), result)
  284. }
  285. // ========== ResetWechatPayClient 测试 ==========
  286. func TestResetWechatPayClient(t *testing.T) {
  287. // 确保重置后客户端为 nil
  288. wechatPayClient = nil
  289. ResetWechatPayClient()
  290. assert.Nil(t, wechatPayClient)
  291. }
  292. func TestResetWechatPayClient_CallbackRegistered(t *testing.T) {
  293. // 验证 init() 注册了回调
  294. assert.NotNil(t, setting.OnWechatPayConfigChanged)
  295. }
  296. // ========== PaymentMethodWechatPay 常量测试 ==========
  297. func TestPaymentMethodWechatPay(t *testing.T) {
  298. assert.Equal(t, "wechat_pay", PaymentMethodWechatPay)
  299. }