postCommitCacheDegraded_audit_test.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. package role
  2. import (
  3. "context"
  4. "errors"
  5. "testing"
  6. "perms-system-server/internal/consts"
  7. "perms-system-server/internal/loaders"
  8. "perms-system-server/internal/middleware"
  9. roleModel "perms-system-server/internal/model/role"
  10. "perms-system-server/internal/testutil/mocks"
  11. "perms-system-server/internal/types"
  12. "github.com/stretchr/testify/assert"
  13. "github.com/stretchr/testify/require"
  14. "github.com/zeromicro/go-zero/core/stores/sqlx"
  15. "go.uber.org/mock/gomock"
  16. )
  17. // ---------------------------------------------------------------------------
  18. // 覆盖目标:审计第 6 轮 M-4 修复回归 —— 角色更新 / 角色权限绑定的 post-commit 缓存清理
  19. // 必须是 "尽力而为":事务已 COMMIT 成功后,任何缓存清理路径的失败只应记 Errorf,
  20. // 不得把 degraded 成功映射成 5xx 让客户端误触发重试。
  21. //
  22. // 场景:事务外 `FindUserIdsByRoleId` 返回 err。
  23. // 期望:handler 仍返回 nil,200 OK;客户端无须重试;旧缓存最终靠 TTL 过期兜底。
  24. // ---------------------------------------------------------------------------
  25. func adminCtx(productCode string) context.Context {
  26. return middleware.WithUserDetails(context.Background(), &loaders.UserDetails{
  27. UserId: 1,
  28. Username: "admin",
  29. IsSuperAdmin: true,
  30. MemberType: consts.MemberTypeAdmin,
  31. Status: consts.StatusEnabled,
  32. ProductCode: productCode,
  33. })
  34. }
  35. // TC-0858: BindRolePerms —— tx 成功、FindUserIdsByRoleId 抛 err,logic 返回 nil。
  36. func TestBindRolePerms_PostCommitUserIdsError_StaysSuccess(t *testing.T) {
  37. ctrl := gomock.NewController(t)
  38. t.Cleanup(ctrl.Finish)
  39. roleMock := mocks.NewMockSysRoleModel(ctrl)
  40. rpMock := mocks.NewMockSysRolePermModel(ctrl)
  41. urMock := mocks.NewMockSysUserRoleModel(ctrl)
  42. roleMock.EXPECT().FindOne(gomock.Any(), int64(7)).
  43. Return(&roleModel.SysRole{Id: 7, ProductCode: "pc_m4", PermsLevel: 50, Status: 1}, nil)
  44. // permIds=[] 走 "全部删除" 路径;existingIds=[1] 需触发 tx。
  45. rpMock.EXPECT().FindPermIdsByRoleId(gomock.Any(), int64(7)).Return([]int64{1}, nil)
  46. rpMock.EXPECT().TransactCtx(gomock.Any(), gomock.Any()).
  47. DoAndReturn(func(ctx context.Context, fn func(context.Context, sqlx.Session) error) error {
  48. return fn(ctx, nil)
  49. })
  50. rpMock.EXPECT().DeleteByRoleIdAndPermIdsTx(gomock.Any(), nil, int64(7), []int64{1}).
  51. Return(nil)
  52. // 关键断言:post-commit FindUserIdsByRoleId 返回 err,logic 必须吞掉 err。
  53. urMock.EXPECT().FindUserIdsByRoleId(gomock.Any(), int64(7)).
  54. Return(nil, errors.New("redis/db transient error"))
  55. svcCtx := mocks.NewMockServiceContext(mocks.MockModels{
  56. Role: roleMock, RolePerm: rpMock, UserRole: urMock,
  57. })
  58. err := NewBindRolePermsLogic(adminCtx("pc_m4"), svcCtx).BindRolePerms(&types.BindPermsReq{
  59. RoleId: 7, PermIds: []int64{},
  60. })
  61. require.NoError(t, err,
  62. "M-4:post-commit 缓存步骤的 transient err 不应把 degraded 成功映射成 500")
  63. }
  64. // TC-0859: UpdateRole —— UpdateWithOptLock 成功,FindUserIdsByRoleId 失败,handler 返回 nil。
  65. func TestUpdateRole_PostCommitUserIdsError_StaysSuccess(t *testing.T) {
  66. ctrl := gomock.NewController(t)
  67. t.Cleanup(ctrl.Finish)
  68. roleMock := mocks.NewMockSysRoleModel(ctrl)
  69. urMock := mocks.NewMockSysUserRoleModel(ctrl)
  70. roleMock.EXPECT().FindOne(gomock.Any(), int64(9)).
  71. Return(&roleModel.SysRole{
  72. Id: 9, ProductCode: "pc_m4u", Name: "before",
  73. PermsLevel: 50, Status: consts.StatusEnabled, UpdateTime: 100,
  74. }, nil)
  75. // UpdateWithOptLock 成功;签名:UpdateWithOptLock(ctx, role, prevUpdateTime)。
  76. roleMock.EXPECT().UpdateWithOptLock(gomock.Any(), gomock.Any(), int64(100)).Return(nil)
  77. // 关键断言:post-commit transient err 不应导致 handler 失败。
  78. urMock.EXPECT().FindUserIdsByRoleId(gomock.Any(), int64(9)).
  79. Return(nil, errors.New("boom"))
  80. svcCtx := mocks.NewMockServiceContext(mocks.MockModels{
  81. Role: roleMock, UserRole: urMock,
  82. })
  83. err := NewUpdateRoleLogic(adminCtx("pc_m4u"), svcCtx).UpdateRole(&types.UpdateRoleReq{
  84. Id: 9, Name: "after", Remark: "r", PermsLevel: 60, Status: 0,
  85. })
  86. assert.NoError(t, err,
  87. "M-4:UpdateRole 已提交成功,post-commit 缓存失败只记日志,handler 必须返回 nil")
  88. }