package integration import ( "context" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/testcontainers/testcontainers-go" testcontainers_postgres "github.com/testcontainers/testcontainers-go/modules/postgres" "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" roleService "github.com/break/junhong_cmp_fiber/internal/service/role" 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" ) // TestRolePermissionAssociation_AssignPermissions 测试角色权限分配功能 func TestRolePermissionAssociation_AssignPermissions(t *testing.T) { ctx := context.Background() // 启动 PostgreSQL 容器 pgContainer, err := testcontainers_postgres.RunContainer(ctx, testcontainers.WithImage("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) // 连接数据库 db, err := gorm.Open(postgres.Open(pgConnStr), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), }) require.NoError(t, err) // 自动迁移 err = db.AutoMigrate( &model.Role{}, &model.Permission{}, &model.RolePermission{}, ) require.NoError(t, err) // 初始化 Store 和 Service roleStore := postgresStore.NewRoleStore(db) permStore := postgresStore.NewPermissionStore(db) rolePermStore := postgresStore.NewRolePermissionStore(db) roleSvc := roleService.New(roleStore, permStore, rolePermStore) // 创建测试用户上下文 userCtx := middleware.SetUserContext(ctx, 1, constants.UserTypeSuperAdmin, 0) t.Run("成功分配单个权限", func(t *testing.T) { // 创建测试角色 role := &model.Role{ RoleName: "单权限测试角色", RoleType: constants.RoleTypeSuper, Status: constants.StatusEnabled, } db.Create(role) // 创建测试权限 perm := &model.Permission{ PermName: "单权限测试", PermCode: "single:perm:test", PermType: constants.PermissionTypeMenu, Status: constants.StatusEnabled, } db.Create(perm) // 分配权限 rps, err := roleSvc.AssignPermissions(userCtx, role.ID, []uint{perm.ID}) require.NoError(t, err) assert.Len(t, rps, 1) assert.Equal(t, role.ID, rps[0].RoleID) assert.Equal(t, perm.ID, rps[0].PermID) }) t.Run("成功分配多个权限", func(t *testing.T) { // 创建测试角色 role := &model.Role{ RoleName: "多权限测试角色", RoleType: constants.RoleTypeSuper, Status: constants.StatusEnabled, } db.Create(role) // 创建多个测试权限 permIDs := make([]uint, 3) for i := 0; i < 3; i++ { perm := &model.Permission{ PermName: "多权限测试_" + string(rune('A'+i)), PermCode: "multi:perm:test:" + string(rune('a'+i)), PermType: constants.PermissionTypeMenu, Status: constants.StatusEnabled, } db.Create(perm) permIDs[i] = perm.ID } // 分配权限 rps, err := roleSvc.AssignPermissions(userCtx, role.ID, permIDs) require.NoError(t, err) assert.Len(t, rps, 3) }) t.Run("获取角色的权限列表", func(t *testing.T) { // 创建测试角色 role := &model.Role{ RoleName: "获取权限列表测试角色", RoleType: constants.RoleTypeSuper, Status: constants.StatusEnabled, } db.Create(role) // 创建并分配权限 perm := &model.Permission{ PermName: "获取权限列表测试", PermCode: "get:perm:list:test", PermType: constants.PermissionTypeMenu, Status: constants.StatusEnabled, } db.Create(perm) _, err := roleSvc.AssignPermissions(userCtx, role.ID, []uint{perm.ID}) require.NoError(t, err) // 获取权限列表 perms, err := roleSvc.GetPermissions(userCtx, role.ID) require.NoError(t, err) assert.Len(t, perms, 1) assert.Equal(t, perm.ID, perms[0].ID) }) t.Run("移除角色的权限", func(t *testing.T) { // 创建测试角色 role := &model.Role{ RoleName: "移除权限测试角色", RoleType: constants.RoleTypeSuper, Status: constants.StatusEnabled, } db.Create(role) // 创建并分配权限 perm := &model.Permission{ PermName: "移除权限测试", PermCode: "remove:perm:test", PermType: constants.PermissionTypeMenu, Status: constants.StatusEnabled, } db.Create(perm) _, err := roleSvc.AssignPermissions(userCtx, role.ID, []uint{perm.ID}) require.NoError(t, err) // 移除权限 err = roleSvc.RemovePermission(userCtx, role.ID, perm.ID) require.NoError(t, err) // 验证权限已被软删除 var rp model.RolePermission err = db.Unscoped().Where("role_id = ? AND perm_id = ?", role.ID, perm.ID).First(&rp).Error require.NoError(t, err) assert.NotNil(t, rp.DeletedAt) }) t.Run("重复分配权限不会创建重复记录", func(t *testing.T) { // 创建测试角色 role := &model.Role{ RoleName: "重复权限测试角色", RoleType: constants.RoleTypeSuper, Status: constants.StatusEnabled, } db.Create(role) // 创建测试权限 perm := &model.Permission{ PermName: "重复权限测试", PermCode: "duplicate:perm:test", PermType: constants.PermissionTypeMenu, Status: constants.StatusEnabled, } db.Create(perm) // 第一次分配 _, err := roleSvc.AssignPermissions(userCtx, role.ID, []uint{perm.ID}) require.NoError(t, err) // 第二次分配相同权限 _, err = roleSvc.AssignPermissions(userCtx, role.ID, []uint{perm.ID}) require.NoError(t, err) // 验证只有一条记录 var count int64 db.Model(&model.RolePermission{}).Where("role_id = ? AND perm_id = ?", role.ID, perm.ID).Count(&count) assert.Equal(t, int64(1), count) }) t.Run("角色不存在时分配权限失败", func(t *testing.T) { perm := &model.Permission{ PermName: "角色不存在测试", PermCode: "role:not:exist:test", PermType: constants.PermissionTypeMenu, Status: constants.StatusEnabled, } db.Create(perm) _, err := roleSvc.AssignPermissions(userCtx, 99999, []uint{perm.ID}) assert.Error(t, err) }) t.Run("权限不存在时分配失败", func(t *testing.T) { role := &model.Role{ RoleName: "权限不存在测试角色", RoleType: constants.RoleTypeSuper, Status: constants.StatusEnabled, } db.Create(role) _, err := roleSvc.AssignPermissions(userCtx, role.ID, []uint{99999}) assert.Error(t, err) }) } // TestRolePermissionAssociation_SoftDelete 测试软删除对角色权限关联的影响 func TestRolePermissionAssociation_SoftDelete(t *testing.T) { ctx := context.Background() // 启动容器 pgContainer, err := testcontainers_postgres.RunContainer(ctx, testcontainers.WithImage("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") // 设置环境 db, _ := gorm.Open(postgres.Open(pgConnStr), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), }) _ = db.AutoMigrate(&model.Role{}, &model.Permission{}, &model.RolePermission{}) roleStore := postgresStore.NewRoleStore(db) permStore := postgresStore.NewPermissionStore(db) rolePermStore := postgresStore.NewRolePermissionStore(db) roleSvc := roleService.New(roleStore, permStore, rolePermStore) userCtx := middleware.SetUserContext(ctx, 1, constants.UserTypeSuperAdmin, 0) t.Run("软删除权限后重新分配可以恢复", func(t *testing.T) { // 创建测试数据 role := &model.Role{ RoleName: "恢复权限测试角色", RoleType: constants.RoleTypeSuper, Status: constants.StatusEnabled, } db.Create(role) perm := &model.Permission{ PermName: "恢复权限测试", PermCode: "restore:perm:test", PermType: constants.PermissionTypeMenu, Status: constants.StatusEnabled, } db.Create(perm) // 分配权限 _, err := roleSvc.AssignPermissions(userCtx, role.ID, []uint{perm.ID}) require.NoError(t, err) // 移除权限 err = roleSvc.RemovePermission(userCtx, role.ID, perm.ID) require.NoError(t, err) // 重新分配权限 rps, err := roleSvc.AssignPermissions(userCtx, role.ID, []uint{perm.ID}) require.NoError(t, err) assert.Len(t, rps, 1) // 验证关联已恢复 perms, err := roleSvc.GetPermissions(userCtx, role.ID) require.NoError(t, err) assert.Len(t, perms, 1) }) t.Run("批量分配和移除权限", func(t *testing.T) { // 创建测试角色 role := &model.Role{ RoleName: "批量权限测试角色", RoleType: constants.RoleTypeSuper, Status: constants.StatusEnabled, } db.Create(role) // 创建多个权限 permIDs := make([]uint, 5) for i := 0; i < 5; i++ { perm := &model.Permission{ PermName: "批量权限测试_" + string(rune('A'+i)), PermCode: "batch:perm:test:" + string(rune('a'+i)), PermType: constants.PermissionTypeMenu, Status: constants.StatusEnabled, } db.Create(perm) permIDs[i] = perm.ID } // 批量分配 _, err := roleSvc.AssignPermissions(userCtx, role.ID, permIDs) require.NoError(t, err) // 验证分配成功 perms, err := roleSvc.GetPermissions(userCtx, role.ID) require.NoError(t, err) assert.Len(t, perms, 5) // 移除部分权限 for i := 0; i < 3; i++ { err = roleSvc.RemovePermission(userCtx, role.ID, permIDs[i]) require.NoError(t, err) } // 验证剩余权限 perms, err = roleSvc.GetPermissions(userCtx, role.ID) require.NoError(t, err) assert.Len(t, perms, 2) }) } // TestRolePermissionAssociation_Cascade 测试级联行为 func TestRolePermissionAssociation_Cascade(t *testing.T) { ctx := context.Background() // 启动容器 pgContainer, err := testcontainers_postgres.RunContainer(ctx, testcontainers.WithImage("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") // 设置环境 db, _ := gorm.Open(postgres.Open(pgConnStr), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), }) _ = db.AutoMigrate(&model.Role{}, &model.Permission{}, &model.RolePermission{}) t.Run("验证无外键约束(关联表独立)", func(t *testing.T) { // 创建角色和权限 role := &model.Role{ RoleName: "级联测试角色", RoleType: constants.RoleTypeSuper, Status: constants.StatusEnabled, } db.Create(role) perm := &model.Permission{ PermName: "级联测试权限", PermCode: "cascade:test:perm", PermType: constants.PermissionTypeMenu, Status: constants.StatusEnabled, } db.Create(perm) // 创建关联 rp := &model.RolePermission{ RoleID: role.ID, PermID: perm.ID, Status: constants.StatusEnabled, } db.Create(rp) // 删除角色(软删除) db.Delete(role) // 验证关联记录仍然存在(无外键约束) var count int64 db.Model(&model.RolePermission{}).Where("role_id = ?", role.ID).Count(&count) assert.Equal(t, int64(1), count, "关联记录应该仍然存在,因为没有外键约束") // 验证可以独立查询关联记录 var rpRecord model.RolePermission err := db.Where("role_id = ? AND perm_id = ?", role.ID, perm.ID).First(&rpRecord).Error assert.NoError(t, err, "应该能查询到关联记录") }) }