| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360 |
- package role
- import (
- "context"
- "errors"
- "testing"
- "time"
- "perms-system-server/internal/consts"
- "perms-system-server/internal/loaders"
- "perms-system-server/internal/middleware"
- permModel "perms-system-server/internal/model/perm"
- roleModel "perms-system-server/internal/model/role"
- "perms-system-server/internal/model/roleperm"
- "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/testutil/mocks"
- "perms-system-server/internal/types"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
- "github.com/zeromicro/go-zero/core/stores/sqlx"
- "go.uber.org/mock/gomock"
- )
- // TC-0129: 正常绑定
- func TestBindRolePerms_Normal(t *testing.T) {
- ctx := ctxhelper.SuperAdminCtx()
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- conn := testutil.GetTestSqlConn()
- now := time.Now().Unix()
- pc := testutil.UniqueId()
- roleRes, err := svcCtx.SysRoleModel.Insert(ctx, &roleModel.SysRole{
- ProductCode: pc, Name: testutil.UniqueId(), Status: 1, PermsLevel: 1,
- CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- roleId, _ := roleRes.LastInsertId()
- p1Res, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
- ProductCode: pc, Name: testutil.UniqueId(), Code: testutil.UniqueId(),
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- p1Id, _ := p1Res.LastInsertId()
- p2Res, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
- ProductCode: pc, Name: testutil.UniqueId(), Code: testutil.UniqueId(),
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- p2Id, _ := p2Res.LastInsertId()
- t.Cleanup(func() {
- testutil.CleanTableByField(ctx, conn, "`sys_role_perm`", "roleId", roleId)
- testutil.CleanTable(ctx, conn, "`sys_perm`", p1Id, p2Id)
- testutil.CleanTable(ctx, conn, "`sys_role`", roleId)
- })
- logic := NewBindRolePermsLogic(ctx, svcCtx)
- err = logic.BindRolePerms(&types.BindPermsReq{
- RoleId: roleId,
- PermIds: []int64{p1Id, p2Id},
- })
- require.NoError(t, err)
- permIds, err := svcCtx.SysRolePermModel.FindPermIdsByRoleId(ctx, roleId)
- require.NoError(t, err)
- assert.ElementsMatch(t, []int64{p1Id, p2Id}, permIds)
- }
- // TC-0130: 角色不存在
- func TestBindRolePerms_RoleNotFound(t *testing.T) {
- ctx := ctxhelper.SuperAdminCtx()
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- logic := NewBindRolePermsLogic(ctx, svcCtx)
- err := logic.BindRolePerms(&types.BindPermsReq{
- RoleId: 999999999,
- PermIds: []int64{1},
- })
- require.Error(t, err)
- var ce *response.CodeError
- require.True(t, errors.As(err, &ce))
- assert.Equal(t, 404, ce.Code())
- assert.Equal(t, "角色不存在", ce.Error())
- }
- // TC-0131: 清空权限
- func TestBindRolePerms_EmptyPermIds(t *testing.T) {
- ctx := ctxhelper.SuperAdminCtx()
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- conn := testutil.GetTestSqlConn()
- now := time.Now().Unix()
- pc := testutil.UniqueId()
- roleRes, err := svcCtx.SysRoleModel.Insert(ctx, &roleModel.SysRole{
- ProductCode: pc, Name: testutil.UniqueId(), Status: 1, PermsLevel: 1,
- CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- roleId, _ := roleRes.LastInsertId()
- pRes, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
- ProductCode: pc, Name: testutil.UniqueId(), Code: testutil.UniqueId(),
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- pId, _ := pRes.LastInsertId()
- rpRes, err := svcCtx.SysRolePermModel.Insert(ctx, &roleperm.SysRolePerm{
- RoleId: roleId, PermId: pId, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- rpId, _ := rpRes.LastInsertId()
- t.Cleanup(func() {
- testutil.CleanTable(ctx, conn, "`sys_role_perm`", rpId)
- testutil.CleanTable(ctx, conn, "`sys_perm`", pId)
- testutil.CleanTable(ctx, conn, "`sys_role`", roleId)
- })
- logic := NewBindRolePermsLogic(ctx, svcCtx)
- err = logic.BindRolePerms(&types.BindPermsReq{
- RoleId: roleId,
- PermIds: []int64{},
- })
- require.NoError(t, err)
- permIds, err := svcCtx.SysRolePermModel.FindPermIdsByRoleId(ctx, roleId)
- require.NoError(t, err)
- assert.Empty(t, permIds)
- }
- // TC-0129: 正常绑定
- func TestBindRolePerms_Rebind(t *testing.T) {
- ctx := ctxhelper.SuperAdminCtx()
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- conn := testutil.GetTestSqlConn()
- now := time.Now().Unix()
- pc := testutil.UniqueId()
- roleRes, err := svcCtx.SysRoleModel.Insert(ctx, &roleModel.SysRole{
- ProductCode: pc, Name: testutil.UniqueId(), Status: 1, PermsLevel: 1,
- CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- roleId, _ := roleRes.LastInsertId()
- var permIds []int64
- for i := 0; i < 3; i++ {
- pRes, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
- ProductCode: pc, Name: testutil.UniqueId(), Code: testutil.UniqueId(),
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- pId, _ := pRes.LastInsertId()
- permIds = append(permIds, pId)
- }
- t.Cleanup(func() {
- testutil.CleanTableByField(ctx, conn, "`sys_role_perm`", "roleId", roleId)
- testutil.CleanTable(ctx, conn, "`sys_perm`", permIds...)
- testutil.CleanTable(ctx, conn, "`sys_role`", roleId)
- })
- logic := NewBindRolePermsLogic(ctx, svcCtx)
- err = logic.BindRolePerms(&types.BindPermsReq{
- RoleId: roleId,
- PermIds: []int64{permIds[0], permIds[1]},
- })
- require.NoError(t, err)
- got, err := svcCtx.SysRolePermModel.FindPermIdsByRoleId(ctx, roleId)
- require.NoError(t, err)
- assert.ElementsMatch(t, []int64{permIds[0], permIds[1]}, got)
- err = logic.BindRolePerms(&types.BindPermsReq{
- RoleId: roleId,
- PermIds: []int64{permIds[1], permIds[2]},
- })
- require.NoError(t, err)
- got, err = svcCtx.SysRolePermModel.FindPermIdsByRoleId(ctx, roleId)
- require.NoError(t, err)
- assert.ElementsMatch(t, []int64{permIds[1], permIds[2]}, got)
- }
- // TC-0132: 重复permId — 后静默去重
- func TestBindRolePerms_DuplicatePermId(t *testing.T) {
- ctx := ctxhelper.SuperAdminCtx()
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- conn := testutil.GetTestSqlConn()
- now := time.Now().Unix()
- pc := testutil.UniqueId()
- roleRes, err := svcCtx.SysRoleModel.Insert(ctx, &roleModel.SysRole{
- ProductCode: pc, Name: testutil.UniqueId(), Status: 1, PermsLevel: 1,
- CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- roleId, _ := roleRes.LastInsertId()
- pRes, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
- ProductCode: pc, Name: testutil.UniqueId(), Code: testutil.UniqueId(),
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- pId, _ := pRes.LastInsertId()
- t.Cleanup(func() {
- testutil.CleanTableByField(ctx, conn, "`sys_role_perm`", "roleId", roleId)
- testutil.CleanTable(ctx, conn, "`sys_perm`", pId)
- testutil.CleanTable(ctx, conn, "`sys_role`", roleId)
- })
- logic := NewBindRolePermsLogic(ctx, svcCtx)
- err = logic.BindRolePerms(&types.BindPermsReq{
- RoleId: roleId,
- PermIds: []int64{pId, pId},
- })
- require.NoError(t, err, "重复permId应被静默去重(H-5修复)")
- permIds, err := svcCtx.SysRolePermModel.FindPermIdsByRoleId(ctx, roleId)
- require.NoError(t, err)
- assert.Equal(t, []int64{pId}, permIds, "去重后应只绑定1个权限")
- }
- // TC-0541: bindRolePerms非管理员拒绝
- func TestBindRolePerms_MemberRejected(t *testing.T) {
- pc := "test_product"
- ctx := ctxhelper.MemberCtx(pc)
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- conn := testutil.GetTestSqlConn()
- now := time.Now().Unix()
- roleRes, err := svcCtx.SysRoleModel.Insert(ctx, &roleModel.SysRole{
- ProductCode: pc, Name: testutil.UniqueId(), Status: 1, PermsLevel: 1,
- CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- roleId, _ := roleRes.LastInsertId()
- t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_role`", roleId) })
- logic := NewBindRolePermsLogic(ctx, svcCtx)
- err = logic.BindRolePerms(&types.BindPermsReq{RoleId: roleId, PermIds: []int64{1}})
- require.Error(t, err)
- var ce *response.CodeError
- require.True(t, errors.As(err, &ce))
- assert.Equal(t, 403, ce.Code())
- }
- // TC-1121: L-R14-1 非超管 BindRolePerms 针对别产品的 roleId 必须 404 "角色不存在",
- // 与 UpdateRole / DeleteRole / RoleDetail 对齐。
- func TestBindRolePerms_L_R14_1_CrossProductReturns404(t *testing.T) {
- ctx := ctxhelper.SuperAdminCtx()
- svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
- conn := testutil.GetTestSqlConn()
- now := time.Now().Unix()
- otherProduct := "l_r14_1_brp_" + testutil.UniqueId()
- res, err := svcCtx.SysRoleModel.Insert(ctx, &roleModel.SysRole{
- ProductCode: otherProduct, Name: testutil.UniqueId(), Status: 1, PermsLevel: 1,
- CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- roleId, _ := res.LastInsertId()
- // 预先在别产品下插一个权限以构造 permIds 入参 —— 不过因为 Resolve 环节就返回 404,
- // 这个 perm 不会被真实写入 sys_role_perm;这里的价值只是为了让 req 合理。
- pRes, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
- ProductCode: otherProduct, Name: testutil.UniqueId(), Code: testutil.UniqueId(),
- Status: 1, CreateTime: now, UpdateTime: now,
- })
- require.NoError(t, err)
- permId, _ := pRes.LastInsertId()
- t.Cleanup(func() {
- testutil.CleanTableByField(ctx, conn, "`sys_role_perm`", "roleId", roleId)
- testutil.CleanTable(ctx, conn, "`sys_perm`", permId)
- testutil.CleanTable(ctx, conn, "`sys_role`", roleId)
- })
- adminCtx := ctxhelper.AdminCtx("test_product")
- err = NewBindRolePermsLogic(adminCtx, svcCtx).BindRolePerms(&types.BindPermsReq{
- RoleId: roleId,
- PermIds: []int64{permId},
- })
- require.Error(t, err)
- var ce *response.CodeError
- require.True(t, errors.As(err, &ce))
- assert.Equal(t, 404, ce.Code(), "跨产品 roleId 必须 404")
- assert.Equal(t, "角色不存在", ce.Error())
- // sys_role_perm 不得出现任何入行
- gotPermIds, err := svcCtx.SysRolePermModel.FindPermIdsByRoleId(ctx, roleId)
- require.NoError(t, err)
- assert.Empty(t, gotPermIds, "跨产品被拒绝的 BindRolePerms 不得对 sys_role_perm 有副作用")
- }
- // 覆盖目标:角色更新 / 角色权限绑定的 post-commit 缓存清理
- // 必须是 "尽力而为":事务已 COMMIT 成功后,任何缓存清理路径的失败只应记 Errorf,
- // 不得把 degraded 成功映射成 5xx 让客户端误触发重试。
- // adminCtx 是 post-commit 缓存降级回归用例共享的 super-admin context 构造器。
- // 同 package 下 TestUpdateRole_PostCommitUserIdsError_StaysSuccess 亦通过 package 作用域复用。
- func adminCtx(productCode string) context.Context {
- return middleware.WithUserDetails(context.Background(), &loaders.UserDetails{
- UserId: 1,
- Username: "admin",
- IsSuperAdmin: true,
- MemberType: consts.MemberTypeAdmin,
- Status: consts.StatusEnabled,
- ProductCode: productCode,
- })
- }
- // TC-0858: BindRolePerms —— tx 成功、FindUserIdsByRoleId 抛 err,logic 返回 nil。
- func TestBindRolePerms_PostCommitUserIdsError_StaysSuccess(t *testing.T) {
- ctrl := gomock.NewController(t)
- t.Cleanup(ctrl.Finish)
- roleMock := mocks.NewMockSysRoleModel(ctrl)
- rpMock := mocks.NewMockSysRolePermModel(ctrl)
- urMock := mocks.NewMockSysUserRoleModel(ctrl)
- roleMock.EXPECT().FindOne(gomock.Any(), int64(7)).
- Return(&roleModel.SysRole{Id: 7, ProductCode: "pc_m4", PermsLevel: 50, Status: 1}, nil)
- // existing 读 + diff + delete/insert 全部收敛进事务;事务首步 LockByIdTx 锁 sys_role 行。
- rpMock.EXPECT().TransactCtx(gomock.Any(), gomock.Any()).
- DoAndReturn(func(ctx context.Context, fn func(context.Context, sqlx.Session) error) error {
- return fn(ctx, nil)
- })
- roleMock.EXPECT().LockByIdTx(gomock.Any(), nil, int64(7)).
- Return(&roleModel.SysRole{Id: 7, ProductCode: "pc_m4", PermsLevel: 50, Status: 1}, nil)
- rpMock.EXPECT().FindPermIdsByRoleIdTx(gomock.Any(), nil, int64(7)).Return([]int64{1}, nil)
- rpMock.EXPECT().DeleteByRoleIdAndPermIdsTx(gomock.Any(), nil, int64(7), []int64{1}).
- Return(nil)
- // 关键断言:post-commit FindUserIdsByRoleId 返回 err,logic 必须吞掉 err。
- urMock.EXPECT().FindUserIdsByRoleId(gomock.Any(), int64(7)).
- Return(nil, errors.New("redis/db transient error"))
- svcCtx := mocks.NewMockServiceContext(mocks.MockModels{
- Role: roleMock, RolePerm: rpMock, UserRole: urMock,
- })
- err := NewBindRolePermsLogic(adminCtx("pc_m4"), svcCtx).BindRolePerms(&types.BindPermsReq{
- RoleId: 7, PermIds: []int64{},
- })
- require.NoError(t, err,
- "post-commit 缓存步骤的 transient err 不应把 degraded 成功映射成 500")
- }
|