updateRoleLogic_test.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. package role
  2. import (
  3. "errors"
  4. "testing"
  5. "time"
  6. "perms-system-server/internal/consts"
  7. roleModel "perms-system-server/internal/model/role"
  8. "perms-system-server/internal/response"
  9. "perms-system-server/internal/svc"
  10. "perms-system-server/internal/testutil"
  11. "perms-system-server/internal/testutil/ctxhelper"
  12. "perms-system-server/internal/testutil/mocks"
  13. "perms-system-server/internal/types"
  14. "github.com/stretchr/testify/assert"
  15. "github.com/stretchr/testify/require"
  16. "go.uber.org/mock/gomock"
  17. )
  18. // TC-0120: 正常更新
  19. func TestUpdateRole_Normal(t *testing.T) {
  20. ctx := ctxhelper.SuperAdminCtx()
  21. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  22. conn := testutil.GetTestSqlConn()
  23. now := time.Now().Unix()
  24. pc := testutil.UniqueId()
  25. res, err := svcCtx.SysRoleModel.Insert(ctx, &roleModel.SysRole{
  26. ProductCode: pc, Name: testutil.UniqueId(), Status: 1, PermsLevel: 1,
  27. CreateTime: now, UpdateTime: now,
  28. })
  29. require.NoError(t, err)
  30. roleId, _ := res.LastInsertId()
  31. t.Cleanup(func() {
  32. testutil.CleanTable(ctx, conn, "`sys_role`", roleId)
  33. })
  34. newName := testutil.UniqueId()
  35. logic := NewUpdateRoleLogic(ctx, svcCtx)
  36. err = logic.UpdateRole(&types.UpdateRoleReq{
  37. Id: roleId,
  38. Name: newName,
  39. Remark: "updated remark",
  40. PermsLevel: 2,
  41. Status: 2,
  42. })
  43. require.NoError(t, err)
  44. updated, err := svcCtx.SysRoleModel.FindOne(ctx, roleId)
  45. require.NoError(t, err)
  46. assert.Equal(t, newName, updated.Name)
  47. assert.Equal(t, "updated remark", updated.Remark)
  48. assert.Equal(t, int64(2), updated.PermsLevel)
  49. assert.Equal(t, int64(2), updated.Status)
  50. }
  51. // TC-0121: 不存在
  52. func TestUpdateRole_NotFound(t *testing.T) {
  53. ctx := ctxhelper.SuperAdminCtx()
  54. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  55. logic := NewUpdateRoleLogic(ctx, svcCtx)
  56. err := logic.UpdateRole(&types.UpdateRoleReq{
  57. Id: 999999999,
  58. Name: "whatever",
  59. PermsLevel: 1,
  60. })
  61. require.Error(t, err)
  62. var ce *response.CodeError
  63. require.True(t, errors.As(err, &ce))
  64. assert.Equal(t, 404, ce.Code())
  65. assert.Equal(t, "角色不存在", ce.Error())
  66. }
  67. // TC-0539: updateRole非管理员拒绝
  68. func TestUpdateRole_MemberRejected(t *testing.T) {
  69. pc := "test_product"
  70. ctx := ctxhelper.MemberCtx(pc)
  71. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  72. conn := testutil.GetTestSqlConn()
  73. now := time.Now().Unix()
  74. roleRes, err := svcCtx.SysRoleModel.Insert(ctx, &roleModel.SysRole{
  75. ProductCode: pc, Name: testutil.UniqueId(), Status: 1, PermsLevel: 1,
  76. CreateTime: now, UpdateTime: now,
  77. })
  78. require.NoError(t, err)
  79. roleId, _ := roleRes.LastInsertId()
  80. t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_role`", roleId) })
  81. logic := NewUpdateRoleLogic(ctx, svcCtx)
  82. err = logic.UpdateRole(&types.UpdateRoleReq{Id: roleId, Name: "test", PermsLevel: 1})
  83. require.Error(t, err)
  84. var ce *response.CodeError
  85. require.True(t, errors.As(err, &ce))
  86. assert.Equal(t, 403, ce.Code())
  87. }
  88. // 覆盖目标:事务已 COMMIT 成功后,
  89. // 任何缓存清理路径的失败只应记 Errorf,不得把 degraded 成功映射成 5xx 让客户端误触发重试。
  90. // adminCtx helper 定义于 bindRolePermsLogic_test.go (同 package role)。
  91. // TC-1119: L-R14-1 非超管 UpdateRole 访问别产品的 roleId 必须返回 404 "角色不存在",
  92. // 与 RoleDetail 的 M-N3 口径一致,消除 404 vs 403 的跨产品 roleId 枚举 oracle。
  93. func TestUpdateRole_L_R14_1_CrossProductReturns404(t *testing.T) {
  94. ctx := ctxhelper.SuperAdminCtx()
  95. svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
  96. conn := testutil.GetTestSqlConn()
  97. now := time.Now().Unix()
  98. otherProduct := "l_r14_1_upd_" + testutil.UniqueId()
  99. res, err := svcCtx.SysRoleModel.Insert(ctx, &roleModel.SysRole{
  100. ProductCode: otherProduct, Name: testutil.UniqueId(), Status: 1, PermsLevel: 1,
  101. CreateTime: now, UpdateTime: now,
  102. })
  103. require.NoError(t, err)
  104. roleId, _ := res.LastInsertId()
  105. t.Cleanup(func() { testutil.CleanTable(ctx, conn, "`sys_role`", roleId) })
  106. adminCtx := ctxhelper.AdminCtx("test_product")
  107. err = NewUpdateRoleLogic(adminCtx, svcCtx).UpdateRole(&types.UpdateRoleReq{
  108. Id: roleId, Name: "should_not_update", PermsLevel: 1,
  109. })
  110. require.Error(t, err)
  111. var ce *response.CodeError
  112. require.True(t, errors.As(err, &ce))
  113. assert.Equal(t, 404, ce.Code(),
  114. "L-R14-1:跨产品 roleId 必须 404,不得以 403 暴露存在性")
  115. assert.Equal(t, "角色不存在", ce.Error(),
  116. "L-R14-1:文案必须与 'id 不存在' 完全一致,彻底消除枚举 oracle")
  117. // DB 不得被污染
  118. after, err := svcCtx.SysRoleModel.FindOne(ctx, roleId)
  119. require.NoError(t, err)
  120. assert.NotEqual(t, "should_not_update", after.Name,
  121. "跨产品被拒绝的请求不得对 DB 产生任何副作用")
  122. }
  123. // TC-0859: UpdateRole —— UpdateWithOptLock 成功,FindUserIdsByRoleId 失败,handler 返回 nil。
  124. func TestUpdateRole_PostCommitUserIdsError_StaysSuccess(t *testing.T) {
  125. ctrl := gomock.NewController(t)
  126. t.Cleanup(ctrl.Finish)
  127. roleMock := mocks.NewMockSysRoleModel(ctrl)
  128. urMock := mocks.NewMockSysUserRoleModel(ctrl)
  129. roleMock.EXPECT().FindOne(gomock.Any(), int64(9)).
  130. Return(&roleModel.SysRole{
  131. Id: 9, ProductCode: "pc_m4u", Name: "before",
  132. PermsLevel: 50, Status: consts.StatusEnabled, UpdateTime: 100,
  133. }, nil)
  134. // UpdateWithOptLock 成功;签名:UpdateWithOptLock(ctx, role, prevUpdateTime)。
  135. roleMock.EXPECT().UpdateWithOptLock(gomock.Any(), gomock.Any(), int64(100)).Return(nil)
  136. // 关键断言:post-commit transient err 不应导致 handler 失败。
  137. urMock.EXPECT().FindUserIdsByRoleId(gomock.Any(), int64(9)).
  138. Return(nil, errors.New("boom"))
  139. svcCtx := mocks.NewMockServiceContext(mocks.MockModels{
  140. Role: roleMock, UserRole: urMock,
  141. })
  142. err := NewUpdateRoleLogic(adminCtx("pc_m4u"), svcCtx).UpdateRole(&types.UpdateRoleReq{
  143. Id: 9, Name: "after", Remark: "r", PermsLevel: 60, Status: 0,
  144. })
  145. assert.NoError(t, err,
  146. "UpdateRole 已提交成功,post-commit 缓存失败只记日志,handler 必须返回 nil")
  147. }