|
- package user_migration
-
- import (
- "fmt"
- "testing"
-
- "github.com/QuantumNous/new-api/model"
- "github.com/glebarez/sqlite"
- "github.com/stretchr/testify/require"
- "gorm.io/gorm"
- )
-
- type fakeClient struct {
- listPages map[int][]RemoteUserSnapshot
- getUserResp *RemoteUserSnapshot
- getUserByID map[int]*RemoteUserSnapshot
- hasCopyResult bool
- hasCopyByID map[int]bool
- convertCalls []int
- convertReqs map[int]*ConvertRemoteUserRequest
- }
-
- func (f *fakeClient) ListUsers(page, pageSize int) (*ListRemoteUsersResponse, error) {
- users := f.listPages[page]
- return &ListRemoteUsersResponse{
- Success: true,
- Data: users,
- Total: len(users),
- }, nil
- }
-
- func (f *fakeClient) GetUser(userId int) (*RemoteUserSnapshot, error) {
- if snapshot, ok := f.getUserByID[userId]; ok {
- return snapshot, nil
- }
- if f.getUserResp != nil {
- return f.getUserResp, nil
- }
- return nil, fmt.Errorf("user %d not found", userId)
- }
-
- func (f *fakeClient) ConvertToSynced(userId int, req *ConvertRemoteUserRequest) error {
- f.convertCalls = append(f.convertCalls, userId)
- if f.convertReqs == nil {
- f.convertReqs = make(map[int]*ConvertRemoteUserRequest)
- }
- if req != nil {
- copyReq := *req
- f.convertReqs[userId] = ©Req
- }
- return nil
- }
-
- func (f *fakeClient) HasSyncedCopy(cnUserId int) (bool, error) {
- if value, ok := f.hasCopyByID[cnUserId]; ok {
- return value, nil
- }
- return f.hasCopyResult, nil
- }
-
- func setupMigrationServiceDB(t *testing.T) *gorm.DB {
- t.Helper()
-
- db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
- require.NoError(t, err)
-
- sqlDB, err := db.DB()
- require.NoError(t, err)
- sqlDB.SetMaxOpenConns(1)
-
- require.NoError(t, db.AutoMigrate(
- &model.User{},
- &model.CustomOAuthProvider{},
- &model.UserOAuthBinding{},
- &model.UserMigrationBatch{},
- &model.UserMigrationItem{},
- &model.MigrationQuotaGrant{},
- ))
-
- return db
- }
-
- func TestScanBatch_NoConflict_MarksReady(t *testing.T) {
- db := setupMigrationServiceDB(t)
- orig := model.DB
- model.DB = db
- defer func() {
- model.DB = orig
- }()
-
- batch := &model.UserMigrationBatch{Name: "wave-1", SourceRegion: "ov", TargetRegion: "cn", OperatorId: 1}
- require.NoError(t, model.CreateUserMigrationBatch(batch))
-
- client := &fakeClient{
- listPages: map[int][]RemoteUserSnapshot{
- 0: {{
- Id: 10000001,
- Username: "ov-bob",
- Email: "bob@example.com",
- Quota: 456,
- AffCode: "A002",
- }},
- },
- }
- svc := &Service{client: client}
- require.NoError(t, svc.ScanBatch(batch.Id))
-
- items, err := model.ListUserMigrationItemsByBatch(batch.Id)
- require.NoError(t, err)
- require.Len(t, items, 1)
- require.Equal(t, model.UserMigrationStrategyCreateNew, items[0].ResolutionStrategy)
- require.Equal(t, model.UserMigrationItemStatusReady, items[0].Status)
-
- var savedBatch model.UserMigrationBatch
- require.NoError(t, db.First(&savedBatch, batch.Id).Error)
- require.Equal(t, model.UserMigrationBatchStatusReady, savedBatch.Status)
- }
-
- func TestScanBatch_EmailConflict_NoCNSyncedCopy_RecommendsMerge(t *testing.T) {
- db := setupMigrationServiceDB(t)
- orig := model.DB
- model.DB = db
- defer func() {
- model.DB = orig
- }()
-
- require.NoError(t, db.Create(&model.User{
- Id: 1,
- Username: "cn-alice",
- Password: "hash",
- Email: "alice@example.com",
- AffCode: "C001",
- }).Error)
-
- batch := &model.UserMigrationBatch{Name: "wave-2", SourceRegion: "ov", TargetRegion: "cn", OperatorId: 1}
- require.NoError(t, model.CreateUserMigrationBatch(batch))
-
- client := &fakeClient{
- listPages: map[int][]RemoteUserSnapshot{
- 0: {{
- Id: 10000001,
- Username: "ov-alice",
- Email: "alice@example.com",
- Quota: 123,
- AffCode: "A001",
- }},
- },
- hasCopyResult: false,
- }
- svc := &Service{client: client}
- require.NoError(t, svc.ScanBatch(batch.Id))
-
- items, err := model.ListUserMigrationItemsByBatch(batch.Id)
- require.NoError(t, err)
- require.Len(t, items, 1)
- require.Equal(t, model.UserMigrationStrategyMergeExisting, items[0].ResolutionStrategy)
- require.Equal(t, model.UserMigrationItemStatusReady, items[0].Status)
- require.Equal(t, 1, items[0].TargetUserId)
- }
-
- func TestScanBatch_EmailConflict_CNHasSyncedCopy_MarksHardConflict(t *testing.T) {
- db := setupMigrationServiceDB(t)
- orig := model.DB
- model.DB = db
- defer func() {
- model.DB = orig
- }()
-
- require.NoError(t, db.Create(&model.User{
- Id: 456,
- Username: "cn-user456",
- Password: "hash",
- Email: "shared@example.com",
- AffCode: "C456",
- }).Error)
-
- batch := &model.UserMigrationBatch{Name: "wave-3", SourceRegion: "ov", TargetRegion: "cn", OperatorId: 1}
- require.NoError(t, model.CreateUserMigrationBatch(batch))
-
- client := &fakeClient{
- listPages: map[int][]RemoteUserSnapshot{
- 0: {{
- Id: 10000003,
- Username: "ov-x",
- Email: "shared@example.com",
- Quota: 50,
- AffCode: "OVX",
- }},
- },
- hasCopyResult: true,
- }
- svc := &Service{client: client}
- require.NoError(t, svc.ScanBatch(batch.Id))
-
- items, err := model.ListUserMigrationItemsByBatch(batch.Id)
- require.NoError(t, err)
- require.Len(t, items, 1)
- require.Equal(t, model.UserMigrationItemStatusConflict, items[0].Status)
- require.Contains(t, items[0].ConflictFlags, "cn_already_synced_to_ov")
- }
-
- func TestScanBatch_Paginated(t *testing.T) {
- db := setupMigrationServiceDB(t)
- orig := model.DB
- model.DB = db
- defer func() {
- model.DB = orig
- }()
-
- batch := &model.UserMigrationBatch{Name: "wave-big", SourceRegion: "ov", TargetRegion: "cn", OperatorId: 1}
- require.NoError(t, model.CreateUserMigrationBatch(batch))
-
- page0 := make([]RemoteUserSnapshot, scanPageSize)
- for i := range page0 {
- page0[i] = RemoteUserSnapshot{
- Id: 10000001 + i,
- Username: fmt.Sprintf("u%d", i),
- AffCode: fmt.Sprintf("A%d", i),
- }
- }
- page1 := []RemoteUserSnapshot{{
- Id: 10000001 + scanPageSize,
- Username: "last",
- AffCode: "LAST",
- }}
-
- client := &fakeClient{
- listPages: map[int][]RemoteUserSnapshot{
- 0: page0,
- 1: page1,
- },
- }
- svc := &Service{client: client}
- require.NoError(t, svc.ScanBatch(batch.Id))
-
- items, err := model.ListUserMigrationItemsByBatch(batch.Id)
- require.NoError(t, err)
- require.Len(t, items, scanPageSize+1)
- }
|