package user import ( "errors" "testing" "time" "perms-system-server/internal/consts" "perms-system-server/internal/loaders" memberModel "perms-system-server/internal/model/productmember" roleModel "perms-system-server/internal/model/role" "perms-system-server/internal/response" "perms-system-server/internal/svc" "perms-system-server/internal/testutil" "perms-system-server/internal/testutil/ctxhelper" "perms-system-server/internal/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func insertTestMember(t *testing.T, svcCtx *svc.ServiceContext, productCode string, userId int64) int64 { t.Helper() now := time.Now().Unix() res, err := svcCtx.SysProductMemberModel.Insert(ctxhelper.SuperAdminCtx(), &memberModel.SysProductMember{ ProductCode: productCode, UserId: userId, MemberType: "MEMBER", Status: 1, CreateTime: now, UpdateTime: now, }) require.NoError(t, err) id, _ := res.LastInsertId() return id } func insertTestRole(t *testing.T, svcCtx *svc.ServiceContext, productCode string, status int64) int64 { t.Helper() now := time.Now().Unix() res, err := svcCtx.SysRoleModel.Insert(ctxhelper.SuperAdminCtx(), &roleModel.SysRole{ ProductCode: productCode, Name: "role_" + testutil.UniqueId(), Status: status, PermsLevel: 1, CreateTime: now, UpdateTime: now, }) require.NoError(t, err) id, _ := res.LastInsertId() return id } // TC-0184: 正常绑定 func TestBindRoles_Success(t *testing.T) { ctx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() username := testutil.UniqueId() userId := insertTestUser(t, ctx, username, testutil.HashPassword("pass")) mId := insertTestMember(t, svcCtx, "test_product", userId) r1 := insertTestRole(t, svcCtx, "test_product", 1) r2 := insertTestRole(t, svcCtx, "test_product", 1) t.Cleanup(func() { testutil.CleanTableByField(ctx, conn, "`sys_user_role`", "userId", userId) testutil.CleanTable(ctx, conn, "`sys_product_member`", mId) testutil.CleanTable(ctx, conn, "`sys_user`", userId) testutil.CleanTable(ctx, conn, "`sys_role`", r1, r2) }) logic := NewBindRolesLogic(ctx, svcCtx) err := logic.BindRoles(&types.BindRolesReq{ UserId: userId, RoleIds: []int64{r1, r2}, }) require.NoError(t, err) roleIds, err := svcCtx.SysUserRoleModel.FindRoleIdsByUserId(ctx, userId) require.NoError(t, err) assert.ElementsMatch(t, []int64{r1, r2}, roleIds) } // TC-0185: 用户不存在 func TestBindRoles_UserNotFound(t *testing.T) { ctx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) logic := NewBindRolesLogic(ctx, svcCtx) err := logic.BindRoles(&types.BindRolesReq{ UserId: 999999999, RoleIds: []int64{1}, }) require.Error(t, err) var codeErr *response.CodeError require.True(t, errors.As(err, &codeErr)) assert.Equal(t, 404, codeErr.Code()) assert.Equal(t, "用户不存在", codeErr.Error()) } // TC-0186: 清空角色 func TestBindRoles_EmptyRoleIds_ClearsAll(t *testing.T) { ctx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() username := testutil.UniqueId() userId := insertTestUser(t, ctx, username, testutil.HashPassword("pass")) mId := insertTestMember(t, svcCtx, "test_product", userId) r1 := insertTestRole(t, svcCtx, "test_product", 1) t.Cleanup(func() { testutil.CleanTableByField(ctx, conn, "`sys_user_role`", "userId", userId) testutil.CleanTable(ctx, conn, "`sys_product_member`", mId) testutil.CleanTable(ctx, conn, "`sys_user`", userId) testutil.CleanTable(ctx, conn, "`sys_role`", r1) }) logic := NewBindRolesLogic(ctx, svcCtx) err := logic.BindRoles(&types.BindRolesReq{ UserId: userId, RoleIds: []int64{r1}, }) require.NoError(t, err) err = logic.BindRoles(&types.BindRolesReq{ UserId: userId, RoleIds: []int64{}, }) require.NoError(t, err) roleIds, err := svcCtx.SysUserRoleModel.FindRoleIdsByUserId(ctx, userId) require.NoError(t, err) assert.Empty(t, roleIds) } // TC-0184: 正常重新绑定 func TestBindRoles_Rebind(t *testing.T) { ctx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() username := testutil.UniqueId() userId := insertTestUser(t, ctx, username, testutil.HashPassword("pass")) mId := insertTestMember(t, svcCtx, "test_product", userId) r1 := insertTestRole(t, svcCtx, "test_product", 1) r2 := insertTestRole(t, svcCtx, "test_product", 1) r3 := insertTestRole(t, svcCtx, "test_product", 1) t.Cleanup(func() { testutil.CleanTableByField(ctx, conn, "`sys_user_role`", "userId", userId) testutil.CleanTable(ctx, conn, "`sys_product_member`", mId) testutil.CleanTable(ctx, conn, "`sys_user`", userId) testutil.CleanTable(ctx, conn, "`sys_role`", r1, r2, r3) }) logic := NewBindRolesLogic(ctx, svcCtx) err := logic.BindRoles(&types.BindRolesReq{ UserId: userId, RoleIds: []int64{r1, r2}, }) require.NoError(t, err) err = logic.BindRoles(&types.BindRolesReq{ UserId: userId, RoleIds: []int64{r2, r3}, }) require.NoError(t, err) roleIds, err := svcCtx.SysUserRoleModel.FindRoleIdsByUserId(ctx, userId) require.NoError(t, err) assert.ElementsMatch(t, []int64{r2, r3}, roleIds) } // TC-0188: 角色不属于当前产品 func TestBindRoles_RoleBelongsToOtherProduct(t *testing.T) { ctx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() username := testutil.UniqueId() userId := insertTestUser(t, ctx, username, testutil.HashPassword("pass")) mId := insertTestMember(t, svcCtx, "test_product", userId) otherRole := insertTestRole(t, svcCtx, "other_product", 1) t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_product_member`", mId) testutil.CleanTable(ctx, conn, "`sys_user`", userId) testutil.CleanTable(ctx, conn, "`sys_role`", otherRole) }) logic := NewBindRolesLogic(ctx, svcCtx) err := logic.BindRoles(&types.BindRolesReq{ UserId: userId, RoleIds: []int64{otherRole}, }) require.Error(t, err) var codeErr *response.CodeError require.True(t, errors.As(err, &codeErr)) assert.Equal(t, 400, codeErr.Code()) assert.Contains(t, codeErr.Error(), "其他产品的角色") } // TC-0189: 角色已禁用 func TestBindRoles_RoleDisabled(t *testing.T) { ctx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() username := testutil.UniqueId() userId := insertTestUser(t, ctx, username, testutil.HashPassword("pass")) mId := insertTestMember(t, svcCtx, "test_product", userId) disabledRole := insertTestRole(t, svcCtx, "test_product", 2) t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_product_member`", mId) testutil.CleanTable(ctx, conn, "`sys_user`", userId) testutil.CleanTable(ctx, conn, "`sys_role`", disabledRole) }) logic := NewBindRolesLogic(ctx, svcCtx) err := logic.BindRoles(&types.BindRolesReq{ UserId: userId, RoleIds: []int64{disabledRole}, }) require.Error(t, err) var codeErr *response.CodeError require.True(t, errors.As(err, &codeErr)) assert.Equal(t, 400, codeErr.Code()) assert.Contains(t, codeErr.Error(), "已禁用的角色") } // TC-0190: 角色不存在 func TestBindRoles_RoleNotExists(t *testing.T) { ctx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() username := testutil.UniqueId() userId := insertTestUser(t, ctx, username, testutil.HashPassword("pass")) mId := insertTestMember(t, svcCtx, "test_product", userId) t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_product_member`", mId) testutil.CleanTable(ctx, conn, "`sys_user`", userId) }) logic := NewBindRolesLogic(ctx, svcCtx) err := logic.BindRoles(&types.BindRolesReq{ UserId: userId, RoleIds: []int64{999999999}, }) require.Error(t, err) var codeErr *response.CodeError require.True(t, errors.As(err, &codeErr)) assert.Equal(t, 400, codeErr.Code()) assert.Contains(t, codeErr.Error(), "无效的角色ID") } func insertTestRoleWithLevel(t *testing.T, svcCtx *svc.ServiceContext, productCode string, status int64, permsLevel int64) int64 { t.Helper() now := time.Now().Unix() res, err := svcCtx.SysRoleModel.Insert(ctxhelper.SuperAdminCtx(), &roleModel.SysRole{ ProductCode: productCode, Name: "role_" + testutil.UniqueId(), Status: status, PermsLevel: permsLevel, CreateTime: now, UpdateTime: now, }) require.NoError(t, err) id, _ := res.LastInsertId() return id } // TC-0208: 非超管不能分配权限级别高于自身的角色(审计#2修复验证) func TestBindRoles_PermsLevelEscalation_Rejected(t *testing.T) { svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() superCtx := ctxhelper.SuperAdminCtx() productCode := "test_product" username := testutil.UniqueId() userId := insertTestUser(t, superCtx, username, testutil.HashPassword("pass")) mId := insertTestMember(t, svcCtx, productCode, userId) highLevelRole := insertTestRoleWithLevel(t, svcCtx, productCode, 1, 1) t.Cleanup(func() { testutil.CleanTableByField(superCtx, conn, "`sys_user_role`", "userId", userId) testutil.CleanTable(superCtx, conn, "`sys_product_member`", mId) testutil.CleanTable(superCtx, conn, "`sys_user`", userId) testutil.CleanTable(superCtx, conn, "`sys_role`", highLevelRole) }) ctx := ctxhelper.CustomCtx(&loaders.UserDetails{ UserId: 999998, Username: "admin_caller", IsSuperAdmin: false, MemberType: consts.MemberTypeAdmin, Status: consts.StatusEnabled, ProductCode: productCode, DeptId: 1, DeptPath: "/1/", MinPermsLevel: 50, }) logic := NewBindRolesLogic(ctx, svcCtx) err := logic.BindRoles(&types.BindRolesReq{ UserId: userId, RoleIds: []int64{highLevelRole}, }) require.Error(t, err) var ce *response.CodeError require.True(t, errors.As(err, &ce)) assert.Equal(t, 403, ce.Code()) assert.Contains(t, ce.Error(), "不能分配权限级别高于自身的角色") } // TC-0209: 超管可以分配任意权限级别的角色 func TestBindRoles_SuperAdminCanAssignAnyLevel(t *testing.T) { ctx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() productCode := "test_product" username := testutil.UniqueId() userId := insertTestUser(t, ctx, username, testutil.HashPassword("pass")) mId := insertTestMember(t, svcCtx, productCode, userId) highLevelRole := insertTestRoleWithLevel(t, svcCtx, productCode, 1, 1) t.Cleanup(func() { testutil.CleanTableByField(ctx, conn, "`sys_user_role`", "userId", userId) testutil.CleanTable(ctx, conn, "`sys_product_member`", mId) testutil.CleanTable(ctx, conn, "`sys_user`", userId) testutil.CleanTable(ctx, conn, "`sys_role`", highLevelRole) }) logic := NewBindRolesLogic(ctx, svcCtx) err := logic.BindRoles(&types.BindRolesReq{ UserId: userId, RoleIds: []int64{highLevelRole}, }) require.NoError(t, err) roleIds, err := svcCtx.SysUserRoleModel.FindRoleIdsByUserId(ctx, userId) require.NoError(t, err) assert.Contains(t, roleIds, highLevelRole) } // TC-0191: 目标用户不是当前产品成员时拒绝绑定角色(L-4修复验证) func TestBindRoles_NonMemberRejected(t *testing.T) { ctx := ctxhelper.SuperAdminCtx() svcCtx := svc.NewServiceContext(testutil.GetTestConfig()) conn := testutil.GetTestSqlConn() username := testutil.UniqueId() userId := insertTestUser(t, ctx, username, testutil.HashPassword("pass")) t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_user`", userId) }) logic := NewBindRolesLogic(ctx, svcCtx) err := logic.BindRoles(&types.BindRolesReq{ UserId: userId, RoleIds: []int64{}, }) require.Error(t, err) var codeErr2 *response.CodeError require.True(t, errors.As(err, &codeErr2)) assert.Equal(t, 400, codeErr2.Code()) assert.Contains(t, codeErr2.Error(), "不是当前产品的成员") }