package repository import ( "backend/pkg/permission/domain" "backend/pkg/permission/domain/entity" domainRepo "backend/pkg/permission/domain/repository" "context" "fmt" "github.com/alicebob/miniredis/v2" "github.com/zeromicro/go-zero/core/stores/redis" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/zeromicro/go-zero/core/stores/cache" "go.mongodb.org/mongo-driver/v2/bson" mgo "backend/pkg/library/mongo" ) func setupUserRoleRepo(db string) (domainRepo.UserRoleRepository, func(), error) { h, p, tearDown, err := startMongoContainer() if err != nil { return nil, nil, err } s, _ := miniredis.Run() conf := &mgo.Conf{ Schema: mongoSchema, Host: fmt.Sprintf("%s:%s", h, p), Database: db, MaxStaleness: 300, MaxPoolSize: 100, MinPoolSize: 100, MaxConnIdleTime: 300, Compressors: []string{}, EnableStandardReadWriteSplitMode: false, ConnectTimeoutMs: 3000, } cacheConf := cache.CacheConf{ cache.NodeConf{ RedisConf: redis.RedisConf{ Host: s.Addr(), Type: redis.NodeType, }, Weight: 100, }, } cacheOpts := []cache.Option{ cache.WithExpiry(1000 * time.Microsecond), cache.WithNotFoundExpiry(1000 * time.Microsecond), } param := UserRoleRepositoryParam{ Conf: conf, CacheConf: cacheConf, CacheOpts: cacheOpts, } repo := NewUserRoleRepository(param) _, _ = repo.Index20251009004UP(context.Background()) return repo, tearDown, nil } func TestUserRoleRepository_CreateAndGet(t *testing.T) { repo, tearDown, err := setupUserRoleRepo("testDB") defer tearDown() assert.NoError(t, err) ctx := context.Background() tests := []struct { name string userRole *entity.UserRole wantErr bool }{ { name: "成功創建使用者角色", userRole: &entity.UserRole{ ID: bson.NewObjectID(), Brand: "brand1", UID: "user123", RoleID: "ROLE0000000001", Status: domain.RecordActive, }, wantErr: false, }, { name: "創建另一個使用者角色", userRole: &entity.UserRole{ ID: bson.NewObjectID(), Brand: "brand2", UID: "user456", RoleID: "ROLE0000000002", Status: domain.RecordActive, }, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := repo.Create(ctx, tt.userRole) if tt.wantErr { assert.Error(t, err) } else { assert.NoError(t, err) // 驗證可以找到創建的使用者角色 retrieved, err := repo.Get(ctx, tt.userRole.UID) assert.NoError(t, err) assert.Equal(t, tt.userRole.UID, retrieved.UID) assert.Equal(t, tt.userRole.RoleID, retrieved.RoleID) assert.Equal(t, tt.userRole.Brand, retrieved.Brand) } }) } } func TestUserRoleRepository_Get(t *testing.T) { repo, tearDown, err := setupUserRoleRepo("testDB") defer tearDown() assert.NoError(t, err) ctx := context.Background() // 準備測試數據 testUserRole := &entity.UserRole{ ID: bson.NewObjectID(), Brand: "brand1", UID: "user123", RoleID: "ROLE0000000001", Status: domain.RecordActive, } err = repo.Create(ctx, testUserRole) require.NoError(t, err) tests := []struct { name string uid string wantErr error check func(*testing.T, *entity.UserRole) }{ { name: "找到存在的使用者角色", uid: "user123", wantErr: nil, check: func(t *testing.T, ur *entity.UserRole) { assert.Equal(t, "ROLE0000000001", ur.RoleID) assert.Equal(t, "brand1", ur.Brand) }, }, { name: "不存在的使用者", uid: "user999", wantErr: ErrNotFound, check: nil, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ur, err := repo.Get(ctx, tt.uid) if tt.wantErr != nil { assert.ErrorIs(t, err, tt.wantErr) assert.Nil(t, ur) } else { assert.NoError(t, err) assert.NotNil(t, ur) if tt.check != nil { tt.check(t, ur) } } }) } } func TestUserRoleRepository_Update(t *testing.T) { repo, tearDown, err := setupUserRoleRepo("testDB") defer tearDown() assert.NoError(t, err) ctx := context.Background() // 準備測試數據 testUserRole := &entity.UserRole{ ID: bson.NewObjectID(), Brand: "brand1", UID: "user123", RoleID: "ROLE0000000001", Status: domain.RecordActive, } err = repo.Create(ctx, testUserRole) require.NoError(t, err) tests := []struct { name string uid string newRoleID string wantErr error check func(*testing.T, *entity.UserRole) }{ { name: "成功更新角色", uid: "user123", newRoleID: "ROLE0000000002", wantErr: nil, check: func(t *testing.T, ur *entity.UserRole) { assert.Equal(t, "ROLE0000000002", ur.RoleID) }, }, { name: "更新不存在的使用者", uid: "user999", newRoleID: "ROLE0000000003", wantErr: ErrNotFound, check: nil, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ur, err := repo.Update(ctx, tt.uid, tt.newRoleID) if tt.wantErr != nil { assert.ErrorIs(t, err, tt.wantErr) assert.Nil(t, ur) } else { assert.NoError(t, err) assert.NotNil(t, ur) if tt.check != nil { tt.check(t, ur) } // 驗證更新 retrieved, err := repo.Get(ctx, tt.uid) assert.NoError(t, err) assert.Equal(t, tt.newRoleID, retrieved.RoleID) } }) } } func TestUserRoleRepository_Delete(t *testing.T) { repo, tearDown, err := setupUserRoleRepo("testDB") defer tearDown() assert.NoError(t, err) ctx := context.Background() // 準備測試數據 testUserRole := &entity.UserRole{ ID: bson.NewObjectID(), Brand: "brand1", UID: "user123", RoleID: "ROLE0000000001", Status: domain.RecordActive, } err = repo.Create(ctx, testUserRole) require.NoError(t, err) t.Run("刪除使用者角色", func(t *testing.T) { err := repo.Delete(ctx, testUserRole.UID) assert.NoError(t, err) // 驗證已被刪除 _, err = repo.Get(ctx, testUserRole.UID) assert.ErrorIs(t, err, ErrNotFound) }) t.Run("刪除不存在的使用者", func(t *testing.T) { err := repo.Delete(ctx, "user999") assert.NoError(t, err) // 刪除不存在的記錄不應該報錯 }) } func TestUserRoleRepository_GetByRoleID(t *testing.T) { repo, tearDown, err := setupUserRoleRepo("testDB") defer tearDown() assert.NoError(t, err) ctx := context.Background() // 準備測試數據 testUserRoles := []*entity.UserRole{ { ID: bson.NewObjectID(), Brand: "brand1", UID: "user1", RoleID: "ROLE0000000001", Status: domain.RecordActive, }, { ID: bson.NewObjectID(), Brand: "brand1", UID: "user2", RoleID: "ROLE0000000001", Status: domain.RecordActive, }, { ID: bson.NewObjectID(), Brand: "brand2", UID: "user3", RoleID: "ROLE0000000002", Status: domain.RecordActive, }, } for _, ur := range testUserRoles { err := repo.Create(ctx, ur) require.NoError(t, err) } tests := []struct { name string roleID string wantCount int }{ { name: "找到多個使用者", roleID: "ROLE0000000001", wantCount: 2, }, { name: "找到單一使用者", roleID: "ROLE0000000002", wantCount: 1, }, { name: "不存在的角色", roleID: "ROLE9999999999", wantCount: 0, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { userRoles, err := repo.GetByRoleID(ctx, tt.roleID) assert.NoError(t, err) assert.Len(t, userRoles, tt.wantCount) for _, ur := range userRoles { assert.Equal(t, tt.roleID, ur.RoleID) } }) } } func TestUserRoleRepository_List(t *testing.T) { repo, tearDown, err := setupUserRoleRepo("testDB") defer tearDown() assert.NoError(t, err) ctx := context.Background() // 準備測試數據 testUserRoles := []*entity.UserRole{ { ID: bson.NewObjectID(), Brand: "brand1", UID: "user1", RoleID: "ROLE0000000001", Status: domain.RecordActive, }, { ID: bson.NewObjectID(), Brand: "brand1", UID: "user2", RoleID: "ROLE0000000002", Status: domain.RecordActive, }, { ID: bson.NewObjectID(), Brand: "brand2", UID: "user3", RoleID: "ROLE0000000001", Status: domain.RecordInactive, }, } for _, ur := range testUserRoles { err := repo.Create(ctx, ur) require.NoError(t, err) } tests := []struct { name string filter domainRepo.UserRoleFilter wantCount int }{ { name: "列出所有使用者角色", filter: domainRepo.UserRoleFilter{}, wantCount: 3, }, { name: "按 Brand 過濾", filter: domainRepo.UserRoleFilter{ Brand: "brand1", }, wantCount: 2, }, { name: "按 RoleID 過濾", filter: domainRepo.UserRoleFilter{ RoleID: "ROLE0000000001", }, wantCount: 2, }, { name: "組合過濾", filter: domainRepo.UserRoleFilter{ Brand: "brand1", RoleID: "ROLE0000000001", }, wantCount: 1, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { userRoles, err := repo.List(ctx, tt.filter) assert.NoError(t, err) assert.Len(t, userRoles, tt.wantCount) }) } } func TestUserRoleRepository_CountByRoleID(t *testing.T) { repo, tearDown, err := setupUserRoleRepo("testDB") defer tearDown() assert.NoError(t, err) ctx := context.Background() // 準備測試數據 testUserRoles := []*entity.UserRole{ {ID: bson.NewObjectID(), Brand: "brand1", UID: "user1", RoleID: "ROLE0000000001", Status: domain.RecordActive}, {ID: bson.NewObjectID(), Brand: "brand1", UID: "user2", RoleID: "ROLE0000000001", Status: domain.RecordActive}, {ID: bson.NewObjectID(), Brand: "brand1", UID: "user3", RoleID: "ROLE0000000001", Status: domain.RecordActive}, {ID: bson.NewObjectID(), Brand: "brand2", UID: "user4", RoleID: "ROLE0000000002", Status: domain.RecordActive}, {ID: bson.NewObjectID(), Brand: "brand2", UID: "user5", RoleID: "ROLE0000000002", Status: domain.RecordActive}, } for _, ur := range testUserRoles { err := repo.Create(ctx, ur) require.NoError(t, err) } tests := []struct { name string roleIDs []string wantCount map[string]int }{ { name: "統計多個角色", roleIDs: []string{"ROLE0000000001", "ROLE0000000002"}, wantCount: map[string]int{ "ROLE0000000001": 3, "ROLE0000000002": 2, }, }, { name: "統計單一角色", roleIDs: []string{"ROLE0000000001"}, wantCount: map[string]int{ "ROLE0000000001": 3, }, }, { name: "統計不存在的角色", roleIDs: []string{"ROLE9999999999"}, wantCount: map[string]int{ "ROLE9999999999": 0, }, }, { name: "混合存在和不存在的角色", roleIDs: []string{"ROLE0000000001", "ROLE9999999999"}, wantCount: map[string]int{ "ROLE0000000001": 3, "ROLE9999999999": 0, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { counts, err := repo.CountByRoleID(ctx, tt.roleIDs) assert.NoError(t, err) assert.Equal(t, tt.wantCount, counts) }) } } func TestUserRoleRepository_Exists(t *testing.T) { repo, tearDown, err := setupUserRoleRepo("testDB") defer tearDown() assert.NoError(t, err) ctx := context.Background() // 準備測試數據 testUserRole := &entity.UserRole{ ID: bson.NewObjectID(), Brand: "brand1", UID: "user123", RoleID: "ROLE0000000001", Status: domain.RecordActive, } err = repo.Create(ctx, testUserRole) require.NoError(t, err) tests := []struct { name string uid string exists bool }{ { name: "存在的使用者角色", uid: "user123", exists: true, }, { name: "不存在的使用者", uid: "user999", exists: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { exists, err := repo.Exists(ctx, tt.uid) assert.NoError(t, err) assert.Equal(t, tt.exists, exists) }) } }