您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 

624 行
18 KiB

  1. package model
  2. import (
  3. "fmt"
  4. "testing"
  5. "time"
  6. "github.com/QuantumNous/new-api/common"
  7. "github.com/glebarez/sqlite"
  8. "github.com/stretchr/testify/assert"
  9. "github.com/stretchr/testify/require"
  10. "gorm.io/gorm"
  11. )
  12. func setupRegionSyncDB(t *testing.T) *gorm.DB {
  13. t.Helper()
  14. db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
  15. require.NoError(t, err)
  16. sqlDB, _ := db.DB()
  17. sqlDB.SetMaxOpenConns(1)
  18. origDB := DB
  19. origLogDB := LOG_DB
  20. DB = db
  21. LOG_DB = db
  22. common.UsingSQLite = true
  23. common.RedisEnabled = false
  24. require.NoError(t, db.AutoMigrate(&User{}))
  25. t.Cleanup(func() {
  26. DB = origDB
  27. LOG_DB = origLogDB
  28. sqlDB.Close()
  29. })
  30. return db
  31. }
  32. func TestUser_IsSyncedUser_Method(t *testing.T) {
  33. tests := []struct {
  34. name string
  35. user User
  36. want bool
  37. }{
  38. {"synced with normal id", User{Source: "synced", Id: 100}, true},
  39. {"synced with large id", User{Source: "synced", Id: 99999999}, true},
  40. {"synced with id 0", User{Source: "synced", Id: 0}, true},
  41. {"local user", User{Source: "local", Id: 100}, false},
  42. {"empty source", User{Source: "", Id: 100}, false},
  43. }
  44. for _, tt := range tests {
  45. t.Run(tt.name, func(t *testing.T) {
  46. assert.Equal(t, tt.want, tt.user.IsSyncedUser())
  47. })
  48. }
  49. }
  50. func TestUser_IsLocalUser_Method(t *testing.T) {
  51. tests := []struct {
  52. name string
  53. user User
  54. want bool
  55. }{
  56. {"local source", User{Source: "local", Id: 100}, true},
  57. {"empty source is local", User{Source: "", Id: 100}, true},
  58. {"synced source is not local", User{Source: "synced", Id: 100}, false},
  59. }
  60. for _, tt := range tests {
  61. t.Run(tt.name, func(t *testing.T) {
  62. assert.Equal(t, tt.want, tt.user.IsLocalUser())
  63. })
  64. }
  65. }
  66. func TestUser_SyncFields_CRUD(t *testing.T) {
  67. db := setupRegionSyncDB(t)
  68. user := User{
  69. Id: 100, Username: "synced_test", Password: "hashed_password",
  70. Source: common.UserSourceSynced, RemoteUserId: 100,
  71. SyncedQuota: 500000, LastSyncAt: 1700000000,
  72. }
  73. require.NoError(t, db.Create(&user).Error)
  74. var loaded User
  75. require.NoError(t, db.First(&loaded, 100).Error)
  76. assert.Equal(t, common.UserSourceSynced, loaded.Source)
  77. assert.Equal(t, 100, loaded.RemoteUserId)
  78. assert.Equal(t, 500000, loaded.SyncedQuota)
  79. assert.Equal(t, int64(1700000000), loaded.LastSyncAt)
  80. }
  81. func TestUser_DefaultSourceIsLocal(t *testing.T) {
  82. db := setupRegionSyncDB(t)
  83. user := User{
  84. Id: 101, Username: "default_source_test", Password: "hashed_password",
  85. }
  86. require.NoError(t, db.Create(&user).Error)
  87. var loaded User
  88. require.NoError(t, db.Where("username = ?", "default_source_test").First(&loaded).Error)
  89. assert.Equal(t, common.UserSourceLocal, loaded.Source)
  90. }
  91. // PendingSyncRecord tests
  92. func setupPendingSyncDB(t *testing.T) *gorm.DB {
  93. t.Helper()
  94. db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
  95. require.NoError(t, err)
  96. sqlDB, _ := db.DB()
  97. sqlDB.SetMaxOpenConns(1)
  98. origDB := DB
  99. origLogDB := LOG_DB
  100. DB = db
  101. LOG_DB = db
  102. common.UsingSQLite = true
  103. common.RedisEnabled = false
  104. require.NoError(t, db.AutoMigrate(&PendingSyncRecord{}))
  105. t.Cleanup(func() {
  106. DB = origDB
  107. LOG_DB = origLogDB
  108. sqlDB.Close()
  109. })
  110. return db
  111. }
  112. func TestPendingSyncRecord_Create(t *testing.T) {
  113. db := setupPendingSyncDB(t)
  114. userId := 100
  115. remoteUserId := 200
  116. requestId := "req-test-001"
  117. quota := 1000
  118. estimatedQuota := 500
  119. err := CreatePendingSyncRecord(userId, remoteUserId, requestId, quota, estimatedQuota)
  120. require.NoError(t, err)
  121. var record PendingSyncRecord
  122. require.NoError(t, db.Where("request_id = ?", requestId).First(&record).Error)
  123. assert.Equal(t, userId, record.UserId)
  124. assert.Equal(t, remoteUserId, record.RemoteUserId)
  125. assert.Equal(t, requestId, record.RequestId)
  126. assert.Equal(t, quota, record.Quota)
  127. assert.Equal(t, estimatedQuota, record.EstimatedQuota)
  128. assert.Equal(t, PendingSyncStatusPending, record.Status)
  129. assert.NotZero(t, record.CreatedAt)
  130. assert.Zero(t, record.SyncedAt)
  131. assert.Zero(t, record.RetryCount)
  132. }
  133. func TestPendingSyncRecord_RequestIdUnique(t *testing.T) {
  134. db := setupPendingSyncDB(t)
  135. requestId := "req-unique-001"
  136. // 创建第一条记录
  137. err := CreatePendingSyncRecord(100, 200, requestId, 1000, 500)
  138. require.NoError(t, err)
  139. // 尝试用相同的 requestId 创建第二条记录
  140. err = CreatePendingSyncRecord(101, 201, requestId, 2000, 600)
  141. assert.Error(t, err) // 应该失败,因为 requestId 唯一
  142. // 验证只有一条记录
  143. var count int64
  144. db.Model(&PendingSyncRecord{}).Where("request_id = ?", requestId).Count(&count)
  145. assert.Equal(t, int64(1), count)
  146. }
  147. func TestGetPendingSyncQuota(t *testing.T) {
  148. setupPendingSyncDB(t)
  149. userId := 100
  150. // 创建多条记录
  151. require.NoError(t, CreatePendingSyncRecord(userId, 200, "req-001", 1000, 500))
  152. require.NoError(t, CreatePendingSyncRecord(userId, 200, "req-002", 2000, 600))
  153. require.NoError(t, CreatePendingSyncRecord(userId, 200, "req-003", 500, 200))
  154. // 创建一个 failed 状态的记录,应该被计入
  155. require.NoError(t, CreatePendingSyncRecord(userId, 200, "req-004", 300, 100))
  156. DB.Model(&PendingSyncRecord{}).Where("request_id = ?", "req-004").Update("status", PendingSyncStatusFailed)
  157. // 创建一个 settled 状态的记录,应该被计入
  158. require.NoError(t, CreatePendingSyncRecord(userId, 200, "req-005", 400, 200))
  159. DB.Model(&PendingSyncRecord{}).Where("request_id = ?", "req-005").Update("status", PendingSyncStatusSettled)
  160. // 创建一个 synced 状态的记录,不应该被计入
  161. require.NoError(t, CreatePendingSyncRecord(userId, 200, "req-006", 9999, 0))
  162. DB.Model(&PendingSyncRecord{}).Where("request_id = ?", "req-006").Update("status", PendingSyncStatusSynced)
  163. total := GetPendingSyncQuota(userId)
  164. assert.Equal(t, 4200, total) // 1000 + 2000 + 500 + 300 + 400 = 4200
  165. }
  166. func TestGetPendingSyncQuota_Empty(t *testing.T) {
  167. setupPendingSyncDB(t)
  168. // 用户没有待同步记录
  169. total := GetPendingSyncQuota(99999)
  170. assert.Equal(t, 0, total)
  171. }
  172. // QuotaSyncLog tests
  173. func TestQuotaSyncLog_Create(t *testing.T) {
  174. db := setupRegionSyncDB(t)
  175. require.NoError(t, db.AutoMigrate(&QuotaSyncLog{}))
  176. log := QuotaSyncLog{
  177. UserId: 100,
  178. RemoteUserId: 100,
  179. SyncType: SyncTypeUserCreate,
  180. BeforeQuota: 0,
  181. AfterQuota: 500000,
  182. ChangeAmount: 500000,
  183. MasterQuota: 500000,
  184. PendingQuota: 0,
  185. Direction: SyncDirectionCnToOv,
  186. Status: SyncStatusSuccess,
  187. CreatedAt: 1700000000,
  188. SyncedAt: 1700000000,
  189. }
  190. require.NoError(t, db.Create(&log).Error)
  191. var loaded QuotaSyncLog
  192. require.NoError(t, db.First(&loaded, log.Id).Error)
  193. assert.Equal(t, SyncTypeUserCreate, loaded.SyncType)
  194. assert.Equal(t, 500000, loaded.ChangeAmount)
  195. assert.Equal(t, SyncDirectionCnToOv, loaded.Direction)
  196. }
  197. func TestQuotaSyncLog_QueryByUser(t *testing.T) {
  198. db := setupRegionSyncDB(t)
  199. require.NoError(t, db.AutoMigrate(&QuotaSyncLog{}))
  200. for i := 0; i < 3; i++ {
  201. log := QuotaSyncLog{
  202. UserId: 100, RemoteUserId: 100,
  203. SyncType: SyncTypeBatchSync, Direction: SyncDirectionOvToCn,
  204. Status: SyncStatusSuccess, ChangeAmount: 100 * (i + 1),
  205. CreatedAt: 1700000000 + int64(i),
  206. }
  207. require.NoError(t, db.Create(&log).Error)
  208. }
  209. var logs []QuotaSyncLog
  210. db.Where("user_id = ?", 100).Order("created_at desc").Find(&logs)
  211. assert.Len(t, logs, 3)
  212. }
  213. // ---------------------------------------------------------------------------
  214. // 阶段 1:Model 层缺失测试
  215. // ---------------------------------------------------------------------------
  216. func TestGetPendingRecordsForSync_Basic(t *testing.T) {
  217. db := setupPendingSyncDB(t)
  218. now := time.Now().Unix()
  219. // settled 记录会被同步
  220. require.NoError(t, db.Create(&PendingSyncRecord{
  221. UserId: 100, RemoteUserId: 200, RequestId: "req-rs-001",
  222. Quota: 100, Status: PendingSyncStatusSettled, CreatedAt: now - 100,
  223. }).Error)
  224. // failed 记录会被重试
  225. require.NoError(t, db.Create(&PendingSyncRecord{
  226. UserId: 101, RemoteUserId: 201, RequestId: "req-rs-002",
  227. Quota: 200, Status: PendingSyncStatusFailed, CreatedAt: now - 50,
  228. }).Error)
  229. // synced 记录不会被查到
  230. require.NoError(t, db.Create(&PendingSyncRecord{
  231. UserId: 102, RemoteUserId: 202, RequestId: "req-rs-003",
  232. Quota: 300, Status: PendingSyncStatusSynced, CreatedAt: now,
  233. }).Error)
  234. // pending 记录不会被同步(等待 Settle)
  235. require.NoError(t, db.Create(&PendingSyncRecord{
  236. UserId: 103, RemoteUserId: 203, RequestId: "req-rs-004",
  237. Quota: 400, Status: PendingSyncStatusPending, CreatedAt: now + 1,
  238. }).Error)
  239. records := GetPendingRecordsForSync(10, 3)
  240. assert.Len(t, records, 2) // 只查 settled + failed
  241. // 按 created_at 升序
  242. assert.Equal(t, "req-rs-001", records[0].RequestId)
  243. assert.Equal(t, "req-rs-002", records[1].RequestId)
  244. }
  245. func TestGetPendingRecordsForSync_MaxRetry(t *testing.T) {
  246. db := setupPendingSyncDB(t)
  247. now := time.Now().Unix()
  248. require.NoError(t, db.Create(&PendingSyncRecord{
  249. UserId: 100, RemoteUserId: 200, RequestId: "req-retry-001",
  250. Quota: 100, Status: PendingSyncStatusFailed, RetryCount: 3, CreatedAt: now,
  251. }).Error)
  252. require.NoError(t, db.Create(&PendingSyncRecord{
  253. UserId: 101, RemoteUserId: 201, RequestId: "req-retry-002",
  254. Quota: 200, Status: PendingSyncStatusSettled, RetryCount: 2, CreatedAt: now,
  255. }).Error)
  256. records := GetPendingRecordsForSync(10, 3)
  257. assert.Len(t, records, 1)
  258. assert.Equal(t, "req-retry-002", records[0].RequestId)
  259. }
  260. func TestGetPendingRecordsForSync_Limit(t *testing.T) {
  261. db := setupPendingSyncDB(t)
  262. now := time.Now().Unix()
  263. for i := 0; i < 5; i++ {
  264. require.NoError(t, db.Create(&PendingSyncRecord{
  265. UserId: 100, RemoteUserId: 200, RequestId: fmt.Sprintf("req-lim-%d", i),
  266. Quota: 100, Status: PendingSyncStatusSettled, CreatedAt: now + int64(i),
  267. }).Error)
  268. }
  269. records := GetPendingRecordsForSync(2, 3)
  270. assert.Len(t, records, 2)
  271. }
  272. func TestMarkRecordSynced(t *testing.T) {
  273. db := setupPendingSyncDB(t)
  274. require.NoError(t, db.Create(&PendingSyncRecord{
  275. UserId: 100, RemoteUserId: 200, RequestId: "req-sync-001",
  276. Quota: 100, Status: PendingSyncStatusPending, CreatedAt: time.Now().Unix(),
  277. }).Error)
  278. var record PendingSyncRecord
  279. require.NoError(t, db.Where("request_id = ?", "req-sync-001").First(&record).Error)
  280. require.NoError(t, MarkRecordSynced(record.Id))
  281. var updated PendingSyncRecord
  282. require.NoError(t, db.First(&updated, record.Id).Error)
  283. assert.Equal(t, PendingSyncStatusSynced, updated.Status)
  284. assert.NotZero(t, updated.SyncedAt)
  285. }
  286. func TestMarkRecordFailed(t *testing.T) {
  287. db := setupPendingSyncDB(t)
  288. require.NoError(t, db.Create(&PendingSyncRecord{
  289. UserId: 100, RemoteUserId: 200, RequestId: "req-fail-001",
  290. Quota: 100, Status: PendingSyncStatusPending, RetryCount: 0, CreatedAt: time.Now().Unix(),
  291. }).Error)
  292. var record PendingSyncRecord
  293. require.NoError(t, db.Where("request_id = ?", "req-fail-001").First(&record).Error)
  294. require.NoError(t, MarkRecordFailed(record.Id, "connection refused"))
  295. var updated PendingSyncRecord
  296. require.NoError(t, db.First(&updated, record.Id).Error)
  297. assert.Equal(t, PendingSyncStatusFailed, updated.Status)
  298. assert.Equal(t, 1, updated.RetryCount)
  299. assert.Equal(t, "connection refused", updated.ErrorMsg)
  300. }
  301. func TestCleanupOldSyncedRecords(t *testing.T) {
  302. db := setupPendingSyncDB(t)
  303. now := time.Now().Unix()
  304. // 40 天前已同步的记录
  305. require.NoError(t, db.Create(&PendingSyncRecord{
  306. UserId: 100, RemoteUserId: 200, RequestId: "req-old-001",
  307. Quota: 100, Status: PendingSyncStatusSynced, SyncedAt: now - 40*86400, CreatedAt: now - 45*86400,
  308. }).Error)
  309. // 5 天前已同步的记录(不应被清理)
  310. require.NoError(t, db.Create(&PendingSyncRecord{
  311. UserId: 100, RemoteUserId: 200, RequestId: "req-old-002",
  312. Quota: 200, Status: PendingSyncStatusSynced, SyncedAt: now - 5*86400, CreatedAt: now - 10*86400,
  313. }).Error)
  314. // pending 状态的记录(不应被清理)
  315. require.NoError(t, db.Create(&PendingSyncRecord{
  316. UserId: 100, RemoteUserId: 200, RequestId: "req-old-003",
  317. Quota: 300, Status: PendingSyncStatusPending, CreatedAt: now - 50*86400,
  318. }).Error)
  319. deleted := CleanupOldSyncedRecords(30)
  320. assert.Equal(t, int64(1), deleted)
  321. var count int64
  322. db.Model(&PendingSyncRecord{}).Count(&count)
  323. assert.Equal(t, int64(2), count)
  324. }
  325. func TestCleanupOldSyncedRecords_Empty(t *testing.T) {
  326. setupPendingSyncDB(t)
  327. deleted := CleanupOldSyncedRecords(30)
  328. assert.Equal(t, int64(0), deleted)
  329. }
  330. func TestGetPendingQuotaByUser(t *testing.T) {
  331. db := setupPendingSyncDB(t)
  332. now := time.Now().Unix()
  333. require.NoError(t, db.Create(&PendingSyncRecord{
  334. UserId: 100, RemoteUserId: 200, RequestId: "req-pq-001",
  335. Quota: 100, Status: PendingSyncStatusPending, CreatedAt: now,
  336. }).Error)
  337. require.NoError(t, db.Create(&PendingSyncRecord{
  338. UserId: 100, RemoteUserId: 200, RequestId: "req-pq-002",
  339. Quota: 200, Status: PendingSyncStatusFailed, CreatedAt: now,
  340. }).Error)
  341. require.NoError(t, db.Create(&PendingSyncRecord{
  342. UserId: 101, RemoteUserId: 201, RequestId: "req-pq-003",
  343. Quota: 500, Status: PendingSyncStatusPending, CreatedAt: now,
  344. }).Error)
  345. // settled 记录应计入
  346. require.NoError(t, db.Create(&PendingSyncRecord{
  347. UserId: 100, RemoteUserId: 200, RequestId: "req-pq-004",
  348. Quota: 150, Status: PendingSyncStatusSettled, CreatedAt: now,
  349. }).Error)
  350. // synced 记录不计入
  351. require.NoError(t, db.Create(&PendingSyncRecord{
  352. UserId: 100, RemoteUserId: 200, RequestId: "req-pq-005",
  353. Quota: 9999, Status: PendingSyncStatusSynced, CreatedAt: now,
  354. }).Error)
  355. m := GetPendingQuotaByUser()
  356. assert.Equal(t, 450, m[100]) // 100 + 200 + 150 (pending + failed + settled), synced 不计入
  357. assert.Equal(t, 500, m[101])
  358. _, exists := m[102]
  359. assert.False(t, exists)
  360. }
  361. // ---------------------------------------------------------------------------
  362. // Settled status tests
  363. // ---------------------------------------------------------------------------
  364. func TestSettlePendingSyncRecord(t *testing.T) {
  365. db := setupPendingSyncDB(t)
  366. now := time.Now().Unix()
  367. require.NoError(t, db.Create(&PendingSyncRecord{
  368. UserId: 100, RemoteUserId: 200, RequestId: "req-settle-1",
  369. Quota: 1000, EstimatedQuota: 1000, Status: PendingSyncStatusPending, CreatedAt: now,
  370. }).Error)
  371. found, err := SettlePendingSyncRecord("req-settle-1", 700)
  372. require.NoError(t, err)
  373. assert.True(t, found)
  374. var record PendingSyncRecord
  375. require.NoError(t, db.Where("request_id = ?", "req-settle-1").First(&record).Error)
  376. assert.Equal(t, PendingSyncStatusSettled, record.Status)
  377. assert.Equal(t, 700, record.Quota)
  378. }
  379. func TestSettlePendingSyncRecord_NotFound(t *testing.T) {
  380. setupPendingSyncDB(t)
  381. found, err := SettlePendingSyncRecord("nonexistent", 100)
  382. require.NoError(t, err)
  383. assert.False(t, found)
  384. }
  385. func TestCreateSettledSyncRecord(t *testing.T) {
  386. db := setupPendingSyncDB(t)
  387. err := CreateSettledSyncRecord(100, 200, "req-settled-direct", 500)
  388. require.NoError(t, err)
  389. var record PendingSyncRecord
  390. require.NoError(t, db.Where("request_id = ?", "req-settled-direct").First(&record).Error)
  391. assert.Equal(t, PendingSyncStatusSettled, record.Status)
  392. assert.Equal(t, 500, record.Quota)
  393. }
  394. func TestArchiveStalePendingRecords(t *testing.T) {
  395. db := setupPendingSyncDB(t)
  396. now := time.Now().Unix()
  397. // 超时的 pending 记录(应被归档)
  398. require.NoError(t, db.Create(&PendingSyncRecord{
  399. UserId: 100, RemoteUserId: 200, RequestId: "req-stale-1",
  400. Quota: 100, Status: PendingSyncStatusPending, CreatedAt: now - 600,
  401. }).Error)
  402. // 未超时的 pending 记录(不应被归档)
  403. require.NoError(t, db.Create(&PendingSyncRecord{
  404. UserId: 100, RemoteUserId: 200, RequestId: "req-stale-2",
  405. Quota: 200, Status: PendingSyncStatusPending, CreatedAt: now - 100,
  406. }).Error)
  407. archived := ArchiveStalePendingRecords(300) // 5分钟超时
  408. assert.Equal(t, int64(1), archived)
  409. var record PendingSyncRecord
  410. require.NoError(t, db.Where("request_id = ?", "req-stale-1").First(&record).Error)
  411. assert.Equal(t, PendingSyncStatusArchived, record.Status)
  412. }
  413. // ---------------------------------------------------------------------------
  414. // QuotaUpdateCallback tests
  415. // ---------------------------------------------------------------------------
  416. func TestSetQuotaUpdateCallback(t *testing.T) {
  417. // 测试设置回调函数
  418. called := false
  419. var callbackUserId int
  420. var callbackQuota int
  421. callback := func(userId int, quota int) {
  422. called = true
  423. callbackUserId = userId
  424. callbackQuota = quota
  425. }
  426. SetQuotaUpdateCallback(callback)
  427. // 清理:重置为 nil
  428. defer SetQuotaUpdateCallback(nil)
  429. // 验证回调被设置(通过调用测试)
  430. if quotaUpdateCallback != nil {
  431. quotaUpdateCallback(123, 500000)
  432. }
  433. assert.True(t, called)
  434. assert.Equal(t, 123, callbackUserId)
  435. assert.Equal(t, 500000, callbackQuota)
  436. }
  437. func TestIncreaseUserQuota_TriggersCallback(t *testing.T) {
  438. db := setupRegionSyncDB(t)
  439. // 创建测试用户
  440. user := User{
  441. Id: 1000,
  442. Username: "callback_test",
  443. Password: "hashed_password",
  444. Quota: 100000,
  445. }
  446. require.NoError(t, db.Create(&user).Error)
  447. // 设置回调
  448. callbackCalled := false
  449. var receivedUserId int
  450. var receivedQuota int
  451. SetQuotaUpdateCallback(func(userId int, quota int) {
  452. callbackCalled = true
  453. receivedUserId = userId
  454. receivedQuota = quota
  455. })
  456. defer SetQuotaUpdateCallback(nil) // 清理
  457. // 执行增加额度操作
  458. err := IncreaseUserQuota(1000, 50000, true)
  459. require.NoError(t, err)
  460. // 验证回调被触发
  461. assert.True(t, callbackCalled)
  462. assert.Equal(t, 1000, receivedUserId)
  463. assert.Equal(t, 150000, receivedQuota) // 原始 100000 + 增加 50000
  464. // 验证数据库已更新
  465. var updated User
  466. require.NoError(t, db.First(&updated, 1000).Error)
  467. assert.Equal(t, 150000, updated.Quota)
  468. }
  469. func TestIncreaseUserQuota_NoCallback(t *testing.T) {
  470. db := setupRegionSyncDB(t)
  471. // 创建测试用户
  472. user := User{
  473. Id: 1001,
  474. Username: "no_callback_test",
  475. Password: "hashed_password",
  476. Quota: 100000,
  477. }
  478. require.NoError(t, db.Create(&user).Error)
  479. // 确保没有回调
  480. SetQuotaUpdateCallback(nil)
  481. // 执行增加额度操作(不应 panic)
  482. err := IncreaseUserQuota(1001, 50000, true)
  483. require.NoError(t, err)
  484. // 验证数据库已更新
  485. var updated User
  486. require.NoError(t, db.First(&updated, 1001).Error)
  487. assert.Equal(t, 150000, updated.Quota)
  488. }
  489. func TestIncreaseUserQuota_CallbackError(t *testing.T) {
  490. db := setupRegionSyncDB(t)
  491. // 创建测试用户
  492. user := User{
  493. Id: 1002,
  494. Username: "callback_error_test",
  495. Password: "hashed_password",
  496. Quota: 100000,
  497. }
  498. require.NoError(t, db.Create(&user).Error)
  499. // 设置一个会 panic 的回调,验证不影响主流程
  500. SetQuotaUpdateCallback(func(userId int, quota int) {
  501. panic("callback error")
  502. })
  503. defer SetQuotaUpdateCallback(nil)
  504. // 执行增加额度操作(回调 panic 不应影响数据库更新)
  505. err := IncreaseUserQuota(1002, 50000, true)
  506. require.NoError(t, err)
  507. // 验证数据库已更新(即使回调失败)
  508. var updated User
  509. require.NoError(t, db.First(&updated, 1002).Error)
  510. assert.Equal(t, 150000, updated.Quota)
  511. }