bindRolePermsLogic_test.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. package role
  2. import (
  3. "context"
  4. "errors"
  5. "testing"
  6. "time"
  7. "perms-system-server/internal/consts"
  8. "perms-system-server/internal/loaders"
  9. "perms-system-server/internal/middleware"
  10. permModel "perms-system-server/internal/model/perm"
  11. roleModel "perms-system-server/internal/model/role"
  12. "perms-system-server/internal/model/roleperm"
  13. "perms-system-server/internal/response"
  14. "perms-system-server/internal/svc"
  15. "perms-system-server/internal/testutil"
  16. "perms-system-server/internal/testutil/ctxhelper"
  17. "perms-system-server/internal/testutil/mocks"
  18. "perms-system-server/internal/types"
  19. "github.com/stretchr/testify/assert"
  20. "github.com/stretchr/testify/require"
  21. "github.com/zeromicro/go-zero/core/stores/sqlx"
  22. "go.uber.org/mock/gomock"
  23. )
  24. // TC-0129: 正常绑定
  25. func TestBindRolePerms_Normal(t *testing.T) {
  26. ctx := ctxhelper.SuperAdminCtx()
  27. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  28. conn := testutil.GetTestSqlConn()
  29. now := time.Now().Unix()
  30. pc := testutil.UniqueId()
  31. roleRes, err := svcCtx.SysRoleModel.Insert(ctx, &roleModel.SysRole{
  32. ProductCode: pc, Name: testutil.UniqueId(), Status: 1, PermsLevel: 1,
  33. CreateTime: now, UpdateTime: now,
  34. })
  35. require.NoError(t, err)
  36. roleId, _ := roleRes.LastInsertId()
  37. p1Res, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
  38. ProductCode: pc, Name: testutil.UniqueId(), Code: testutil.UniqueId(),
  39. Status: 1, CreateTime: now, UpdateTime: now,
  40. })
  41. require.NoError(t, err)
  42. p1Id, _ := p1Res.LastInsertId()
  43. p2Res, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
  44. ProductCode: pc, Name: testutil.UniqueId(), Code: testutil.UniqueId(),
  45. Status: 1, CreateTime: now, UpdateTime: now,
  46. })
  47. require.NoError(t, err)
  48. p2Id, _ := p2Res.LastInsertId()
  49. t.Cleanup(func() {
  50. testutil.CleanTableByField(ctx, conn, "`sys_role_perm`", "roleId", roleId)
  51. testutil.CleanTable(ctx, conn, "`sys_perm`", p1Id, p2Id)
  52. testutil.CleanTable(ctx, conn, "`sys_role`", roleId)
  53. })
  54. logic := NewBindRolePermsLogic(ctx, svcCtx)
  55. err = logic.BindRolePerms(&types.BindPermsReq{
  56. RoleId: roleId,
  57. PermIds: []int64{p1Id, p2Id},
  58. })
  59. require.NoError(t, err)
  60. permIds, err := svcCtx.SysRolePermModel.FindPermIdsByRoleId(ctx, roleId)
  61. require.NoError(t, err)
  62. assert.ElementsMatch(t, []int64{p1Id, p2Id}, permIds)
  63. }
  64. // TC-0130: 角色不存在
  65. func TestBindRolePerms_RoleNotFound(t *testing.T) {
  66. ctx := ctxhelper.SuperAdminCtx()
  67. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  68. logic := NewBindRolePermsLogic(ctx, svcCtx)
  69. err := logic.BindRolePerms(&types.BindPermsReq{
  70. RoleId: 999999999,
  71. PermIds: []int64{1},
  72. })
  73. require.Error(t, err)
  74. var ce *response.CodeError
  75. require.True(t, errors.As(err, &ce))
  76. assert.Equal(t, 404, ce.Code())
  77. assert.Equal(t, "角色不存在", ce.Error())
  78. }
  79. // TC-0131: 清空权限
  80. func TestBindRolePerms_EmptyPermIds(t *testing.T) {
  81. ctx := ctxhelper.SuperAdminCtx()
  82. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  83. conn := testutil.GetTestSqlConn()
  84. now := time.Now().Unix()
  85. pc := testutil.UniqueId()
  86. roleRes, err := svcCtx.SysRoleModel.Insert(ctx, &roleModel.SysRole{
  87. ProductCode: pc, Name: testutil.UniqueId(), Status: 1, PermsLevel: 1,
  88. CreateTime: now, UpdateTime: now,
  89. })
  90. require.NoError(t, err)
  91. roleId, _ := roleRes.LastInsertId()
  92. pRes, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
  93. ProductCode: pc, Name: testutil.UniqueId(), Code: testutil.UniqueId(),
  94. Status: 1, CreateTime: now, UpdateTime: now,
  95. })
  96. require.NoError(t, err)
  97. pId, _ := pRes.LastInsertId()
  98. rpRes, err := svcCtx.SysRolePermModel.Insert(ctx, &roleperm.SysRolePerm{
  99. RoleId: roleId, PermId: pId, CreateTime: now, UpdateTime: now,
  100. })
  101. require.NoError(t, err)
  102. rpId, _ := rpRes.LastInsertId()
  103. t.Cleanup(func() {
  104. testutil.CleanTable(ctx, conn, "`sys_role_perm`", rpId)
  105. testutil.CleanTable(ctx, conn, "`sys_perm`", pId)
  106. testutil.CleanTable(ctx, conn, "`sys_role`", roleId)
  107. })
  108. logic := NewBindRolePermsLogic(ctx, svcCtx)
  109. err = logic.BindRolePerms(&types.BindPermsReq{
  110. RoleId: roleId,
  111. PermIds: []int64{},
  112. })
  113. require.NoError(t, err)
  114. permIds, err := svcCtx.SysRolePermModel.FindPermIdsByRoleId(ctx, roleId)
  115. require.NoError(t, err)
  116. assert.Empty(t, permIds)
  117. }
  118. // TC-0129: 正常绑定
  119. func TestBindRolePerms_Rebind(t *testing.T) {
  120. ctx := ctxhelper.SuperAdminCtx()
  121. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  122. conn := testutil.GetTestSqlConn()
  123. now := time.Now().Unix()
  124. pc := testutil.UniqueId()
  125. roleRes, err := svcCtx.SysRoleModel.Insert(ctx, &roleModel.SysRole{
  126. ProductCode: pc, Name: testutil.UniqueId(), Status: 1, PermsLevel: 1,
  127. CreateTime: now, UpdateTime: now,
  128. })
  129. require.NoError(t, err)
  130. roleId, _ := roleRes.LastInsertId()
  131. var permIds []int64
  132. for i := 0; i < 3; i++ {
  133. pRes, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
  134. ProductCode: pc, Name: testutil.UniqueId(), Code: testutil.UniqueId(),
  135. Status: 1, CreateTime: now, UpdateTime: now,
  136. })
  137. require.NoError(t, err)
  138. pId, _ := pRes.LastInsertId()
  139. permIds = append(permIds, pId)
  140. }
  141. t.Cleanup(func() {
  142. testutil.CleanTableByField(ctx, conn, "`sys_role_perm`", "roleId", roleId)
  143. testutil.CleanTable(ctx, conn, "`sys_perm`", permIds...)
  144. testutil.CleanTable(ctx, conn, "`sys_role`", roleId)
  145. })
  146. logic := NewBindRolePermsLogic(ctx, svcCtx)
  147. err = logic.BindRolePerms(&types.BindPermsReq{
  148. RoleId: roleId,
  149. PermIds: []int64{permIds[0], permIds[1]},
  150. })
  151. require.NoError(t, err)
  152. got, err := svcCtx.SysRolePermModel.FindPermIdsByRoleId(ctx, roleId)
  153. require.NoError(t, err)
  154. assert.ElementsMatch(t, []int64{permIds[0], permIds[1]}, got)
  155. err = logic.BindRolePerms(&types.BindPermsReq{
  156. RoleId: roleId,
  157. PermIds: []int64{permIds[1], permIds[2]},
  158. })
  159. require.NoError(t, err)
  160. got, err = svcCtx.SysRolePermModel.FindPermIdsByRoleId(ctx, roleId)
  161. require.NoError(t, err)
  162. assert.ElementsMatch(t, []int64{permIds[1], permIds[2]}, got)
  163. }
  164. // TC-0132: 重复permId — 后静默去重
  165. func TestBindRolePerms_DuplicatePermId(t *testing.T) {
  166. ctx := ctxhelper.SuperAdminCtx()
  167. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  168. conn := testutil.GetTestSqlConn()
  169. now := time.Now().Unix()
  170. pc := testutil.UniqueId()
  171. roleRes, err := svcCtx.SysRoleModel.Insert(ctx, &roleModel.SysRole{
  172. ProductCode: pc, Name: testutil.UniqueId(), Status: 1, PermsLevel: 1,
  173. CreateTime: now, UpdateTime: now,
  174. })
  175. require.NoError(t, err)
  176. roleId, _ := roleRes.LastInsertId()
  177. pRes, err := svcCtx.SysPermModel.Insert(ctx, &permModel.SysPerm{
  178. ProductCode: pc, Name: testutil.UniqueId(), Code: testutil.UniqueId(),
  179. Status: 1, CreateTime: now, UpdateTime: now,
  180. })
  181. require.NoError(t, err)
  182. pId, _ := pRes.LastInsertId()
  183. t.Cleanup(func() {
  184. testutil.CleanTableByField(ctx, conn, "`sys_role_perm`", "roleId", roleId)
  185. testutil.CleanTable(ctx, conn, "`sys_perm`", pId)
  186. testutil.CleanTable(ctx, conn, "`sys_role`", roleId)
  187. })
  188. logic := NewBindRolePermsLogic(ctx, svcCtx)
  189. err = logic.BindRolePerms(&types.BindPermsReq{
  190. RoleId: roleId,
  191. PermIds: []int64{pId, pId},
  192. })
  193. require.NoError(t, err, "重复permId应被静默去重(H-5修复)")
  194. permIds, err := svcCtx.SysRolePermModel.FindPermIdsByRoleId(ctx, roleId)
  195. require.NoError(t, err)
  196. assert.Equal(t, []int64{pId}, permIds, "去重后应只绑定1个权限")
  197. }
  198. // TC-0541: bindRolePerms非管理员拒绝
  199. func TestBindRolePerms_MemberRejected(t *testing.T) {
  200. pc := "test_product"
  201. ctx := ctxhelper.MemberCtx(pc)
  202. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  203. conn := testutil.GetTestSqlConn()
  204. now := time.Now().Unix()
  205. roleRes, err := svcCtx.SysRoleModel.Insert(ctx, &roleModel.SysRole{
  206. ProductCode: pc, Name: testutil.UniqueId(), Status: 1, PermsLevel: 1,
  207. CreateTime: now, UpdateTime: now,
  208. })
  209. require.NoError(t, err)
  210. roleId, _ := roleRes.LastInsertId()
  211. t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_role`", roleId) })
  212. logic := NewBindRolePermsLogic(ctx, svcCtx)
  213. err = logic.BindRolePerms(&types.BindPermsReq{RoleId: roleId, PermIds: []int64{1}})
  214. require.Error(t, err)
  215. var ce *response.CodeError
  216. require.True(t, errors.As(err, &ce))
  217. assert.Equal(t, 403, ce.Code())
  218. }
  219. // 覆盖目标:角色更新 / 角色权限绑定的 post-commit 缓存清理
  220. // 必须是 "尽力而为":事务已 COMMIT 成功后,任何缓存清理路径的失败只应记 Errorf,
  221. // 不得把 degraded 成功映射成 5xx 让客户端误触发重试。
  222. // adminCtx 是 post-commit 缓存降级回归用例共享的 super-admin context 构造器。
  223. // 同 package 下 TestUpdateRole_PostCommitUserIdsError_StaysSuccess 亦通过 package 作用域复用。
  224. func adminCtx(productCode string) context.Context {
  225. return middleware.WithUserDetails(context.Background(), &loaders.UserDetails{
  226. UserId: 1,
  227. Username: "admin",
  228. IsSuperAdmin: true,
  229. MemberType: consts.MemberTypeAdmin,
  230. Status: consts.StatusEnabled,
  231. ProductCode: productCode,
  232. })
  233. }
  234. // TC-0858: BindRolePerms —— tx 成功、FindUserIdsByRoleId 抛 err,logic 返回 nil。
  235. func TestBindRolePerms_PostCommitUserIdsError_StaysSuccess(t *testing.T) {
  236. ctrl := gomock.NewController(t)
  237. t.Cleanup(ctrl.Finish)
  238. roleMock := mocks.NewMockSysRoleModel(ctrl)
  239. rpMock := mocks.NewMockSysRolePermModel(ctrl)
  240. urMock := mocks.NewMockSysUserRoleModel(ctrl)
  241. roleMock.EXPECT().FindOne(gomock.Any(), int64(7)).
  242. Return(&roleModel.SysRole{Id: 7, ProductCode: "pc_m4", PermsLevel: 50, Status: 1}, nil)
  243. // existing 读 + diff + delete/insert 全部收敛进事务;事务首步 LockByIdTx 锁 sys_role 行。
  244. rpMock.EXPECT().TransactCtx(gomock.Any(), gomock.Any()).
  245. DoAndReturn(func(ctx context.Context, fn func(context.Context, sqlx.Session) error) error {
  246. return fn(ctx, nil)
  247. })
  248. roleMock.EXPECT().LockByIdTx(gomock.Any(), nil, int64(7)).
  249. Return(&roleModel.SysRole{Id: 7, ProductCode: "pc_m4", PermsLevel: 50, Status: 1}, nil)
  250. rpMock.EXPECT().FindPermIdsByRoleIdTx(gomock.Any(), nil, int64(7)).Return([]int64{1}, nil)
  251. rpMock.EXPECT().DeleteByRoleIdAndPermIdsTx(gomock.Any(), nil, int64(7), []int64{1}).
  252. Return(nil)
  253. // 关键断言:post-commit FindUserIdsByRoleId 返回 err,logic 必须吞掉 err。
  254. urMock.EXPECT().FindUserIdsByRoleId(gomock.Any(), int64(7)).
  255. Return(nil, errors.New("redis/db transient error"))
  256. svcCtx := mocks.NewMockServiceContext(mocks.MockModels{
  257. Role: roleMock, RolePerm: rpMock, UserRole: urMock,
  258. })
  259. err := NewBindRolePermsLogic(adminCtx("pc_m4"), svcCtx).BindRolePerms(&types.BindPermsReq{
  260. RoleId: 7, PermIds: []int64{},
  261. })
  262. require.NoError(t, err,
  263. "post-commit 缓存步骤的 transient err 不应把 degraded 成功映射成 500")
  264. }