|
- package region_sync
-
- import (
- "encoding/json"
- "fmt"
- "net/http"
- "net/http/httptest"
- "testing"
- "time"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
- "github.com/glebarez/sqlite"
- "gorm.io/gorm"
-
- "github.com/QuantumNous/new-api/model"
- "github.com/QuantumNous/new-api/setting/system_setting"
- )
-
- // setupTestDB 设置测试数据库
- func setupTestDB(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)
-
- // 迁移模型
- err = db.AutoMigrate(&model.PendingSyncRecord{})
- require.NoError(t, err)
-
- return db
- }
-
- func TestSyncManager_NewSyncManager(t *testing.T) {
- manager := NewSyncManager()
- assert.NotNil(t, manager.client)
- }
-
- func TestSyncManager_RunBatchSync_Empty(t *testing.T) {
- // 保存原始设置
- settings := system_setting.GetRegionSyncSettings()
- settings.Enabled = false
-
- manager := NewSyncManager()
- count := manager.RunBatchSync()
- assert.Equal(t, 0, count)
- }
-
- func TestSyncManager_RunBatchSync_WithRecords(t *testing.T) {
- origDB := model.DB
- defer func() {
- model.DB = origDB
- }()
-
- // 设置测试数据库
- db := setupTestDB(t)
- model.DB = db
-
- // 插入一些待同步记录
- record1 := &model.PendingSyncRecord{
- Id: 1,
- UserId: 100,
- RemoteUserId: 100,
- RequestId: "req-001",
- Quota: 100,
- Status: model.PendingSyncStatusSettled,
- CreatedAt: time.Now().Unix(),
- }
- record2 := &model.PendingSyncRecord{
- Id: 2,
- UserId: 100,
- RemoteUserId: 100,
- RequestId: "req-002",
- Quota: 200,
- Status: model.PendingSyncStatusSettled,
- CreatedAt: time.Now().Unix(),
- }
-
- require.NoError(t, db.Create(record1).Error)
- require.NoError(t, db.Create(record2).Error)
-
- manager := NewSyncManager()
- count := manager.RunBatchSync()
-
- // 因为没有真正的服务器,返回 0
- assert.Equal(t, 0, count)
- }
-
- func TestSyncManager_QueryMasterQuota(t *testing.T) {
- origDB := model.DB
- defer func() {
- model.DB = origDB
- }()
-
- manager := NewSyncManager()
- quota, err := manager.QueryMasterQuota(100)
-
- // 在测试环境中应该返回错误(因为没有启用配置)
- require.Error(t, err)
- assert.Equal(t, 0, quota)
- }
-
- func TestSyncManager_RunCleanup(t *testing.T) {
- origDB := model.DB
- defer func() {
- model.DB = origDB
- }()
-
- // 设置测试数据库
- db := setupTestDB(t)
- model.DB = db
-
- // 插入一些已同步的旧记录
- oldRecord := &model.PendingSyncRecord{
- Id: 1,
- UserId: 100,
- RemoteUserId: 100,
- RequestId: "req-001",
- Quota: 100,
- Status: model.PendingSyncStatusSynced,
- CreatedAt: time.Now().Add(-40 * 24 * time.Hour).Unix(),
- }
- require.NoError(t, db.Create(oldRecord).Error)
-
- manager := NewSyncManager()
- deletedCount := manager.RunCleanup()
- assert.GreaterOrEqual(t, deletedCount, 0)
- }
-
- func TestSyncManager_RunBatchSync_MultipleRecords(t *testing.T) {
- origDB := model.DB
- defer func() {
- model.DB = origDB
- }()
-
- // 设置测试数据库
- db := setupTestDB(t)
- model.DB = db
-
- // 插入待同步记录
- for i := 1; i <= 3; i++ {
- record := &model.PendingSyncRecord{
- Id: i,
- UserId: 100,
- RemoteUserId: 100,
- RequestId: fmt.Sprintf("req-batch-%d", i),
- Quota: i * 100,
- Status: model.PendingSyncStatusSettled,
- CreatedAt: time.Now().Unix(),
- }
- require.NoError(t, db.Create(record).Error)
- }
-
- manager := NewSyncManager()
- // 执行批量同步
- count := manager.RunBatchSync()
-
- // 因为没有真正的服务器,返回 0
- assert.Equal(t, 0, count)
- }
-
- func TestSyncManager_StartSyncWorkers(t *testing.T) {
- manager := NewSyncManager()
-
- // 这个测试只是验证不会 panic
- // 实际的协程测试需要更复杂的设置
- manager.StartSyncWorkers()
-
- // 等待一小段时间让协程启动
- time.Sleep(100 * time.Millisecond)
-
- // 停止管理器
- manager.Stop()
- }
-
- // ---------------------------------------------------------------------------
- // 阶段 3 补充:SyncManager 端到端测试
- // ---------------------------------------------------------------------------
-
- func TestRunBatchSync_WithMockServer(t *testing.T) {
- origDB := model.DB
- defer func() { model.DB = origDB }()
-
- db := setupTestDB(t)
- model.DB = db
-
- now := time.Now().Unix()
-
- settings := system_setting.GetRegionSyncSettings()
- origEnabled := settings.Enabled
- origBatchSize := settings.SyncBatchSize
- origMaxRetry := settings.MaxRetryCount
- defer func() {
- settings.Enabled = origEnabled
- settings.SyncBatchSize = origBatchSize
- settings.MaxRetryCount = origMaxRetry
- }()
-
- settings.Enabled = true
- settings.IsMaster = true // 跳过 FetchConfig
- settings.SyncBatchSize = 100
- settings.MaxRetryCount = 3
-
- t.Run("AllSuccess", func(t *testing.T) {
- require.NoError(t, db.Exec("DELETE FROM pending_sync_records").Error)
- require.NoError(t, db.Create(&model.PendingSyncRecord{
- UserId: 100, RemoteUserId: 200, RequestId: "req-batch-ok-1",
- Quota: 100, Status: model.PendingSyncStatusSettled, CreatedAt: now,
- }).Error)
- require.NoError(t, db.Create(&model.PendingSyncRecord{
- UserId: 101, RemoteUserId: 201, RequestId: "req-batch-ok-2",
- Quota: 200, Status: model.PendingSyncStatusSettled, CreatedAt: now + 1,
- }).Error)
-
- server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- w.Header().Set("Content-Type", "application/json")
- json.NewEncoder(w).Encode(BatchDeductResponse{
- Success: true,
- Results: []DeductResult{
- {UserId: 200, Success: true, DeductedQuota: 100, RemainingQuota: 999},
- {UserId: 201, Success: true, DeductedQuota: 200, RemainingQuota: 888},
- },
- })
- }))
- defer server.Close()
-
- manager := &SyncManager{client: NewSyncClient(server.URL, "test-key")}
- count := manager.RunBatchSync()
- assert.Equal(t, 2, count)
-
- var syncedCount int64
- db.Model(&model.PendingSyncRecord{}).Where("status = ?", model.PendingSyncStatusSynced).Count(&syncedCount)
- assert.Equal(t, int64(2), syncedCount)
- })
-
- t.Run("PartialSuccess", func(t *testing.T) {
- require.NoError(t, db.Exec("DELETE FROM pending_sync_records").Error)
- require.NoError(t, db.Create(&model.PendingSyncRecord{
- UserId: 100, RemoteUserId: 200, RequestId: "req-part-1",
- Quota: 100, Status: model.PendingSyncStatusSettled, CreatedAt: now,
- }).Error)
- require.NoError(t, db.Create(&model.PendingSyncRecord{
- UserId: 101, RemoteUserId: 201, RequestId: "req-part-2",
- Quota: 200, Status: model.PendingSyncStatusSettled, CreatedAt: now + 1,
- }).Error)
-
- server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- w.Header().Set("Content-Type", "application/json")
- json.NewEncoder(w).Encode(BatchDeductResponse{
- Success: true,
- Results: []DeductResult{
- {UserId: 200, Success: true, DeductedQuota: 100},
- {UserId: 201, Success: false, Error: "insufficient quota"},
- },
- })
- }))
- defer server.Close()
-
- manager := &SyncManager{client: NewSyncClient(server.URL, "test-key")}
- count := manager.RunBatchSync()
- assert.Equal(t, 1, count)
-
- var syncedCount, pendingCount int64
- db.Model(&model.PendingSyncRecord{}).Where("status = ?", model.PendingSyncStatusSynced).Count(&syncedCount)
- db.Model(&model.PendingSyncRecord{}).Where("status = ?", model.PendingSyncStatusSettled).Count(&pendingCount)
- assert.Equal(t, int64(1), syncedCount)
- assert.Equal(t, int64(1), pendingCount)
- })
-
- t.Run("ServerError", func(t *testing.T) {
- require.NoError(t, db.Exec("DELETE FROM pending_sync_records").Error)
- require.NoError(t, db.Create(&model.PendingSyncRecord{
- UserId: 100, RemoteUserId: 200, RequestId: "req-srv-err",
- Quota: 100, Status: model.PendingSyncStatusSettled, CreatedAt: now,
- }).Error)
-
- server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- w.Header().Set("Content-Type", "application/json")
- w.WriteHeader(500)
- json.NewEncoder(w).Encode(BatchDeductResponse{Success: false, Error: "internal error"})
- }))
- defer server.Close()
-
- manager := &SyncManager{client: NewSyncClient(server.URL, "test-key")}
- count := manager.RunBatchSync()
- assert.Equal(t, 0, count)
- })
- }
-
- func TestRunBatchSync_RetryExhaustion(t *testing.T) {
- origDB := model.DB
- defer func() { model.DB = origDB }()
-
- db := setupTestDB(t)
- model.DB = db
-
- now := time.Now().Unix()
- require.NoError(t, db.Create(&model.PendingSyncRecord{
- UserId: 100, RemoteUserId: 200, RequestId: "req-retry-exh",
- Quota: 100, Status: model.PendingSyncStatusFailed, RetryCount: 2, CreatedAt: now,
- }).Error)
-
- settings := system_setting.GetRegionSyncSettings()
- origEnabled := settings.Enabled
- origBatchSize := settings.SyncBatchSize
- origMaxRetry := settings.MaxRetryCount
- defer func() {
- settings.Enabled = origEnabled
- settings.SyncBatchSize = origBatchSize
- settings.MaxRetryCount = origMaxRetry
- }()
- settings.Enabled = true
- settings.IsMaster = true
- settings.SyncBatchSize = 100
- settings.MaxRetryCount = 3
-
- manager := &SyncManager{client: NewSyncClient("http://127.0.0.1:0", "key")}
- count := manager.RunBatchSync()
- assert.Equal(t, 0, count)
-
- var record model.PendingSyncRecord
- require.NoError(t, db.Where("request_id = ?", "req-retry-exh").First(&record).Error)
- assert.Equal(t, model.PendingSyncStatusFailed, record.Status)
- assert.Equal(t, 3, record.RetryCount)
- }
|