package integration import ( "context" "testing" "time" "github.com/redis/go-redis/v9" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/testcontainers/testcontainers-go" testcontainers_postgres "github.com/testcontainers/testcontainers-go/modules/postgres" testcontainers_redis "github.com/testcontainers/testcontainers-go/modules/redis" "github.com/testcontainers/testcontainers-go/wait" "gorm.io/driver/postgres" "gorm.io/gorm" "gorm.io/gorm/logger" "github.com/break/junhong_cmp_fiber/internal/model" accountService "github.com/break/junhong_cmp_fiber/internal/service/account" postgresStore "github.com/break/junhong_cmp_fiber/internal/store/postgres" "github.com/break/junhong_cmp_fiber/pkg/constants" "github.com/break/junhong_cmp_fiber/pkg/middleware" ) // TestAccountRoleAssociation_AssignRoles 测试账号角色分配功能 func TestAccountRoleAssociation_AssignRoles(t *testing.T) { ctx := context.Background() // 启动 PostgreSQL 容器 pgContainer, err := testcontainers_postgres.Run(ctx, "postgres:14-alpine", testcontainers_postgres.WithDatabase("testdb"), testcontainers_postgres.WithUsername("postgres"), testcontainers_postgres.WithPassword("password"), testcontainers.WithWaitStrategy( wait.ForLog("database system is ready to accept connections"). WithOccurrence(2). WithStartupTimeout(30*time.Second), ), ) require.NoError(t, err, "启动 PostgreSQL 容器失败") defer func() { _ = pgContainer.Terminate(ctx) }() pgConnStr, err := pgContainer.ConnectionString(ctx, "sslmode=disable") require.NoError(t, err) // 启动 Redis 容器 redisContainer, err := testcontainers_redis.Run(ctx, "redis:6-alpine", ) require.NoError(t, err, "启动 Redis 容器失败") defer func() { _ = redisContainer.Terminate(ctx) }() redisHost, _ := redisContainer.Host(ctx) redisPort, _ := redisContainer.MappedPort(ctx, "6379") // 连接数据库 db, err := gorm.Open(postgres.Open(pgConnStr), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), }) require.NoError(t, err) // 自动迁移 err = db.AutoMigrate( &model.Account{}, &model.Role{}, &model.AccountRole{}, ) require.NoError(t, err) // 连接 Redis redisClient := redis.NewClient(&redis.Options{ Addr: redisHost + ":" + redisPort.Port(), }) // 初始化 Store 和 Service accountStore := postgresStore.NewAccountStore(db, redisClient) roleStore := postgresStore.NewRoleStore(db) accountRoleStore := postgresStore.NewAccountRoleStore(db) accService := accountService.New(accountStore, roleStore, accountRoleStore) // 创建测试用户上下文 userCtx := middleware.SetUserContext(ctx, 1, constants.UserTypeSuperAdmin, 0) t.Run("成功分配单个角色", func(t *testing.T) { // 创建测试账号 account := &model.Account{ Username: "single_role_test", Phone: "13800000100", Password: "hashedpassword", UserType: constants.UserTypePlatform, Status: constants.StatusEnabled, } db.Create(account) // 创建测试角色 role := &model.Role{ RoleName: "单角色测试", RoleType: constants.RoleTypeSuper, Status: constants.StatusEnabled, } db.Create(role) // 分配角色 ars, err := accService.AssignRoles(userCtx, account.ID, []uint{role.ID}) require.NoError(t, err) assert.Len(t, ars, 1) assert.Equal(t, account.ID, ars[0].AccountID) assert.Equal(t, role.ID, ars[0].RoleID) }) t.Run("成功分配多个角色", func(t *testing.T) { // 创建测试账号 account := &model.Account{ Username: "multi_role_test", Phone: "13800000101", Password: "hashedpassword", UserType: constants.UserTypePlatform, Status: constants.StatusEnabled, } db.Create(account) // 创建多个测试角色 roles := make([]*model.Role, 3) roleIDs := make([]uint, 3) for i := 0; i < 3; i++ { roles[i] = &model.Role{ RoleName: "多角色测试_" + string(rune('A'+i)), RoleType: constants.RoleTypeSuper, Status: constants.StatusEnabled, } db.Create(roles[i]) roleIDs[i] = roles[i].ID } // 分配角色 ars, err := accService.AssignRoles(userCtx, account.ID, roleIDs) require.NoError(t, err) assert.Len(t, ars, 3) }) t.Run("获取账号的角色列表", func(t *testing.T) { // 创建测试账号 account := &model.Account{ Username: "get_roles_test", Phone: "13800000102", Password: "hashedpassword", UserType: constants.UserTypePlatform, Status: constants.StatusEnabled, } db.Create(account) // 创建并分配角色 role := &model.Role{ RoleName: "获取角色列表测试", RoleType: constants.RoleTypeSuper, Status: constants.StatusEnabled, } db.Create(role) _, err := accService.AssignRoles(userCtx, account.ID, []uint{role.ID}) require.NoError(t, err) // 获取角色列表 roles, err := accService.GetRoles(userCtx, account.ID) require.NoError(t, err) assert.Len(t, roles, 1) assert.Equal(t, role.ID, roles[0].ID) }) t.Run("移除账号的角色", func(t *testing.T) { // 创建测试账号 account := &model.Account{ Username: "remove_role_test", Phone: "13800000103", Password: "hashedpassword", UserType: constants.UserTypePlatform, Status: constants.StatusEnabled, } db.Create(account) // 创建并分配角色 role := &model.Role{ RoleName: "移除角色测试", RoleType: constants.RoleTypeSuper, Status: constants.StatusEnabled, } db.Create(role) _, err := accService.AssignRoles(userCtx, account.ID, []uint{role.ID}) require.NoError(t, err) // 移除角色 err = accService.RemoveRole(userCtx, account.ID, role.ID) require.NoError(t, err) // 验证角色已被软删除 var ar model.AccountRole err = db.Unscoped().Where("account_id = ? AND role_id = ?", account.ID, role.ID).First(&ar).Error require.NoError(t, err) assert.NotNil(t, ar.DeletedAt) }) t.Run("重复分配角色不会创建重复记录", func(t *testing.T) { // 创建测试账号 account := &model.Account{ Username: "duplicate_role_test", Phone: "13800000104", Password: "hashedpassword", UserType: constants.UserTypePlatform, Status: constants.StatusEnabled, } db.Create(account) // 创建测试角色 role := &model.Role{ RoleName: "重复分配测试", RoleType: constants.RoleTypeSuper, Status: constants.StatusEnabled, } db.Create(role) // 第一次分配 _, err := accService.AssignRoles(userCtx, account.ID, []uint{role.ID}) require.NoError(t, err) // 第二次分配相同角色 _, err = accService.AssignRoles(userCtx, account.ID, []uint{role.ID}) require.NoError(t, err) // 验证只有一条记录 var count int64 db.Model(&model.AccountRole{}).Where("account_id = ? AND role_id = ?", account.ID, role.ID).Count(&count) assert.Equal(t, int64(1), count) }) t.Run("账号不存在时分配角色失败", func(t *testing.T) { role := &model.Role{ RoleName: "账号不存在测试", RoleType: constants.RoleTypeSuper, Status: constants.StatusEnabled, } db.Create(role) _, err := accService.AssignRoles(userCtx, 99999, []uint{role.ID}) assert.Error(t, err) }) t.Run("角色不存在时分配失败", func(t *testing.T) { account := &model.Account{ Username: "role_not_exist_test", Phone: "13800000105", Password: "hashedpassword", UserType: constants.UserTypePlatform, Status: constants.StatusEnabled, } db.Create(account) _, err := accService.AssignRoles(userCtx, account.ID, []uint{99999}) assert.Error(t, err) }) } // TestAccountRoleAssociation_SoftDelete 测试软删除对账号角色关联的影响 func TestAccountRoleAssociation_SoftDelete(t *testing.T) { ctx := context.Background() // 启动容器 pgContainer, err := testcontainers_postgres.Run(ctx, "postgres:14-alpine", testcontainers_postgres.WithDatabase("testdb"), testcontainers_postgres.WithUsername("postgres"), testcontainers_postgres.WithPassword("password"), testcontainers.WithWaitStrategy( wait.ForLog("database system is ready to accept connections"). WithOccurrence(2). WithStartupTimeout(30*time.Second), ), ) require.NoError(t, err) defer func() { _ = pgContainer.Terminate(ctx) }() pgConnStr, _ := pgContainer.ConnectionString(ctx, "sslmode=disable") redisContainer, err := testcontainers_redis.Run(ctx, "redis:6-alpine", ) require.NoError(t, err) defer func() { _ = redisContainer.Terminate(ctx) }() redisHost, _ := redisContainer.Host(ctx) redisPort, _ := redisContainer.MappedPort(ctx, "6379") // 设置环境 db, _ := gorm.Open(postgres.Open(pgConnStr), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), }) _ = db.AutoMigrate(&model.Account{}, &model.Role{}, &model.AccountRole{}) redisClient := redis.NewClient(&redis.Options{ Addr: redisHost + ":" + redisPort.Port(), }) accountStore := postgresStore.NewAccountStore(db, redisClient) roleStore := postgresStore.NewRoleStore(db) accountRoleStore := postgresStore.NewAccountRoleStore(db) accService := accountService.New(accountStore, roleStore, accountRoleStore) userCtx := middleware.SetUserContext(ctx, 1, constants.UserTypeSuperAdmin, 0) t.Run("软删除角色后重新分配可以恢复", func(t *testing.T) { // 创建测试数据 account := &model.Account{ Username: "restore_role_test", Phone: "13800000200", Password: "hashedpassword", UserType: constants.UserTypePlatform, Status: constants.StatusEnabled, } db.Create(account) role := &model.Role{ RoleName: "恢复角色测试", RoleType: constants.RoleTypeSuper, Status: constants.StatusEnabled, } db.Create(role) // 分配角色 _, err := accService.AssignRoles(userCtx, account.ID, []uint{role.ID}) require.NoError(t, err) // 移除角色 err = accService.RemoveRole(userCtx, account.ID, role.ID) require.NoError(t, err) // 重新分配角色 ars, err := accService.AssignRoles(userCtx, account.ID, []uint{role.ID}) require.NoError(t, err) assert.Len(t, ars, 1) // 验证关联已恢复 roles, err := accService.GetRoles(userCtx, account.ID) require.NoError(t, err) assert.Len(t, roles, 1) }) }