syncPermsDedup_audit_test.go 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. package pub
  2. import (
  3. "context"
  4. "testing"
  5. permModel "perms-system-server/internal/model/perm"
  6. productModel "perms-system-server/internal/model/product"
  7. "perms-system-server/internal/testutil/mocks"
  8. "github.com/stretchr/testify/assert"
  9. "github.com/stretchr/testify/require"
  10. "github.com/zeromicro/go-zero/core/stores/sqlx"
  11. "go.uber.org/mock/gomock"
  12. "golang.org/x/crypto/bcrypt"
  13. )
  14. // TC-0826: 请求体内 perm.code 重复时,service 必须先在入参上去重,避免同一个 tx 内
  15. // BatchInsert 自己和自己撞 UNIQUE(productCode, code) 引发 1062。
  16. // 旧文件 syncPermsConflict_audit_test.go 一并覆盖了 1062→409 的映射契约,但 H-3 引入
  17. // LockByCodeTx 串行化同步后,1062 在实践中已不可达,该 409 映射契约也被一起取消;只有
  18. // 这一条"请求内去重"仍然是当前产品契约,必须继续回归。
  19. func TestExecuteSyncPerms_DeduplicatesRequest(t *testing.T) {
  20. ctrl := gomock.NewController(t)
  21. t.Cleanup(ctrl.Finish)
  22. hashedSecret, err := bcrypt.GenerateFromPassword([]byte("s"), bcrypt.MinCost)
  23. require.NoError(t, err)
  24. mockProduct := mocks.NewMockSysProductModel(ctrl)
  25. mockProduct.EXPECT().FindOneByAppKey(gomock.Any(), "ak").
  26. Return(&productModel.SysProduct{
  27. Id: 1, Code: "pc_dedup", AppKey: "ak", AppSecret: string(hashedSecret), Status: 1,
  28. }, nil)
  29. // 审计 M-R10-1:LockByCodeTx 拿到的行必须 Status=1
  30. mockProduct.EXPECT().LockByCodeTx(gomock.Any(), gomock.Any(), "pc_dedup").
  31. Return(&productModel.SysProduct{Id: 1, Code: "pc_dedup", Status: 1}, nil)
  32. mockPerm := mocks.NewMockSysPermModel(ctrl)
  33. mockPerm.EXPECT().FindMapByProductCodeWithTx(gomock.Any(), gomock.Any(), "pc_dedup").
  34. Return(map[string]*permModel.SysPerm{}, nil)
  35. var captured []*permModel.SysPerm
  36. mockPerm.EXPECT().TransactCtx(gomock.Any(), gomock.Any()).
  37. DoAndReturn(func(ctx context.Context, fn func(context.Context, sqlx.Session) error) error {
  38. return fn(ctx, nil)
  39. })
  40. mockPerm.EXPECT().BatchInsertWithTx(gomock.Any(), nil, gomock.Any()).
  41. DoAndReturn(func(ctx context.Context, s sqlx.Session, items []*permModel.SysPerm) error {
  42. captured = items
  43. return nil
  44. })
  45. // 去重后 codes 只剩一个,DisableNotInCodesWithTx 用去重后的集合做 NOT IN。
  46. mockPerm.EXPECT().DisableNotInCodesWithTx(gomock.Any(), nil, "pc_dedup", []string{"dup_code"}, gomock.Any()).
  47. Return(int64(0), nil)
  48. svcCtx := mocks.NewMockServiceContext(mocks.MockModels{
  49. Product: mockProduct, Perm: mockPerm,
  50. })
  51. result, err := ExecuteSyncPerms(context.Background(), svcCtx, "ak", "s", []SyncPermItem{
  52. {Code: "dup_code", Name: "A"},
  53. {Code: "dup_code", Name: "A-again"},
  54. {Code: "dup_code", Name: "A-yet-again"},
  55. })
  56. require.NoError(t, err)
  57. require.NotNil(t, result)
  58. require.Len(t, captured, 1, "入参内 code 重复必须去重为 1 条,避免自撞 1062")
  59. assert.Equal(t, "dup_code", captured[0].Code)
  60. assert.Equal(t, "A", captured[0].Name,
  61. "去重策略应稳定到首次出现,使行为可预测")
  62. }