createProductLogic_mock_test.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. package product
  2. import (
  3. "context"
  4. "database/sql"
  5. "errors"
  6. "testing"
  7. deptModel "perms-system-server/internal/model/dept"
  8. productModel "perms-system-server/internal/model/product"
  9. userModel "perms-system-server/internal/model/user"
  10. "perms-system-server/internal/testutil/ctxhelper"
  11. "perms-system-server/internal/testutil/mocks"
  12. "perms-system-server/internal/types"
  13. "github.com/stretchr/testify/assert"
  14. "github.com/zeromicro/go-zero/core/stores/sqlx"
  15. "go.uber.org/mock/gomock"
  16. )
  17. type fakeResult struct{ id int64 }
  18. func (r fakeResult) LastInsertId() (int64, error) { return r.id, nil }
  19. func (r fakeResult) RowsAffected() (int64, error) { return 1, nil }
  20. // TC-0065: 事务回滚-用户创建失败
  21. func TestCreateProduct_Mock_UserInsertFail(t *testing.T) {
  22. ctrl := gomock.NewController(t)
  23. defer ctrl.Finish()
  24. dbErr := errors.New("db error")
  25. mockProduct := mocks.NewMockSysProductModel(ctrl)
  26. mockProduct.EXPECT().FindOneByCode(gomock.Any(), "test_code").
  27. Return(nil, productModel.ErrNotFound)
  28. mockProduct.EXPECT().TransactCtx(gomock.Any(), gomock.Any()).
  29. DoAndReturn(func(ctx context.Context, fn func(context.Context, sqlx.Session) error) error {
  30. return fn(ctx, nil)
  31. })
  32. mockProduct.EXPECT().InsertWithTx(gomock.Any(), nil, gomock.Any()).
  33. Return(fakeResult{id: 1}, nil)
  34. mockUser := mocks.NewMockSysUserModel(ctrl)
  35. mockUser.EXPECT().FindOneByUsername(gomock.Any(), "admin_test_code").
  36. Return(nil, userModel.ErrNotFound)
  37. mockUser.EXPECT().InsertWithTx(gomock.Any(), nil, gomock.Any()).
  38. Return(nil, dbErr)
  39. // CreateProduct 必填 AdminDeptId,入库前 FindOne + 启用校验;
  40. // H-R17-1:tx 内部新增 sys_dept FOR SHARE 读以与 DeleteDept 的 X 锁串行化,
  41. // 本断链点必须补 FindOneForShareTx 期望,否则 gomock 会把失败误定位到"没人调 tx"。
  42. mockDept := mocks.NewMockSysDeptModel(ctrl)
  43. mockDept.EXPECT().FindOne(gomock.Any(), int64(88)).
  44. Return(&deptModel.SysDept{Id: 88, Path: "/88/", Status: 1}, nil)
  45. mockDept.EXPECT().FindOneForShareTx(gomock.Any(), gomock.Any(), int64(88)).
  46. Return(&deptModel.SysDept{Id: 88, Path: "/88/", Status: 1}, nil)
  47. svcCtx := mocks.NewMockServiceContext(mocks.MockModels{
  48. Product: mockProduct,
  49. User: mockUser,
  50. Dept: mockDept,
  51. })
  52. logic := NewCreateProductLogic(ctxhelper.SuperAdminCtx(), svcCtx)
  53. resp, err := logic.CreateProduct(&types.CreateProductReq{
  54. Code: "test_code",
  55. Name: "Test Product",
  56. AdminDeptId: 88,
  57. })
  58. assert.Error(t, err)
  59. assert.ErrorIs(t, err, dbErr)
  60. assert.Nil(t, resp)
  61. }
  62. // TC-0066: 事务回滚-成员创建失败
  63. func TestCreateProduct_Mock_MemberInsertFail(t *testing.T) {
  64. ctrl := gomock.NewController(t)
  65. defer ctrl.Finish()
  66. dbErr := errors.New("db error")
  67. mockProduct := mocks.NewMockSysProductModel(ctrl)
  68. mockProduct.EXPECT().FindOneByCode(gomock.Any(), "test_code").
  69. Return(nil, productModel.ErrNotFound)
  70. mockProduct.EXPECT().TransactCtx(gomock.Any(), gomock.Any()).
  71. DoAndReturn(func(ctx context.Context, fn func(context.Context, sqlx.Session) error) error {
  72. return fn(ctx, nil)
  73. })
  74. mockProduct.EXPECT().InsertWithTx(gomock.Any(), nil, gomock.Any()).
  75. Return(fakeResult{id: 1}, nil)
  76. mockUser := mocks.NewMockSysUserModel(ctrl)
  77. mockUser.EXPECT().FindOneByUsername(gomock.Any(), "admin_test_code").
  78. Return(nil, userModel.ErrNotFound)
  79. mockUser.EXPECT().InsertWithTx(gomock.Any(), nil, gomock.Any()).
  80. Return(fakeResult{id: 10}, nil)
  81. mockPM := mocks.NewMockSysProductMemberModel(ctrl)
  82. mockPM.EXPECT().InsertWithTx(gomock.Any(), nil, gomock.Any()).
  83. Return(sql.Result(nil), dbErr)
  84. // CreateProduct 必填 AdminDeptId;H-R17-1 新增 tx 内 FOR SHARE 读,mock 期望需同步补齐。
  85. mockDept := mocks.NewMockSysDeptModel(ctrl)
  86. mockDept.EXPECT().FindOne(gomock.Any(), int64(88)).
  87. Return(&deptModel.SysDept{Id: 88, Path: "/88/", Status: 1}, nil)
  88. mockDept.EXPECT().FindOneForShareTx(gomock.Any(), gomock.Any(), int64(88)).
  89. Return(&deptModel.SysDept{Id: 88, Path: "/88/", Status: 1}, nil)
  90. svcCtx := mocks.NewMockServiceContext(mocks.MockModels{
  91. Product: mockProduct,
  92. User: mockUser,
  93. ProductMember: mockPM,
  94. Dept: mockDept,
  95. })
  96. logic := NewCreateProductLogic(ctxhelper.SuperAdminCtx(), svcCtx)
  97. resp, err := logic.CreateProduct(&types.CreateProductReq{
  98. Code: "test_code",
  99. Name: "Test Product",
  100. AdminDeptId: 88,
  101. })
  102. assert.Error(t, err)
  103. assert.ErrorIs(t, err, dbErr)
  104. assert.Nil(t, resp)
  105. }