createDeptLogic_mock_test.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. package dept
  2. import (
  3. "context"
  4. "errors"
  5. "testing"
  6. deptModel "perms-system-server/internal/model/dept"
  7. "perms-system-server/internal/testutil/ctxhelper"
  8. "perms-system-server/internal/testutil/mocks"
  9. "perms-system-server/internal/types"
  10. "github.com/stretchr/testify/assert"
  11. "github.com/zeromicro/go-zero/core/stores/sqlx"
  12. "go.uber.org/mock/gomock"
  13. )
  14. type fakeResult struct{ id int64 }
  15. func (r fakeResult) LastInsertId() (int64, error) { return r.id, nil }
  16. func (r fakeResult) RowsAffected() (int64, error) { return 1, nil }
  17. // TC-0097: 事务回滚-Insert失败
  18. func TestCreateDept_Mock_InsertWithTxFail(t *testing.T) {
  19. ctrl := gomock.NewController(t)
  20. defer ctrl.Finish()
  21. dbErr := errors.New("db error")
  22. mockDept := mocks.NewMockSysDeptModel(ctrl)
  23. mockDept.EXPECT().TransactCtx(gomock.Any(), gomock.Any()).
  24. DoAndReturn(func(ctx context.Context, fn func(context.Context, sqlx.Session) error) error {
  25. return fn(ctx, nil)
  26. })
  27. mockDept.EXPECT().InsertWithTx(gomock.Any(), nil, gomock.Any()).
  28. Return(nil, dbErr)
  29. svcCtx := mocks.NewMockServiceContext(mocks.MockModels{
  30. Dept: mockDept,
  31. })
  32. logic := NewCreateDeptLogic(ctxhelper.SuperAdminCtx(), svcCtx)
  33. resp, err := logic.CreateDept(&types.CreateDeptReq{
  34. ParentId: 0,
  35. Name: "TestDept",
  36. })
  37. assert.Error(t, err)
  38. assert.ErrorIs(t, err, dbErr)
  39. assert.Nil(t, resp)
  40. }
  41. // TC-0094: 不传DeptType默认NORMAL
  42. func TestCreateDept_Mock_DefaultDeptType(t *testing.T) {
  43. ctrl := gomock.NewController(t)
  44. defer ctrl.Finish()
  45. mockDept := mocks.NewMockSysDeptModel(ctrl)
  46. mockDept.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. mockDept.EXPECT().InsertWithTx(gomock.Any(), nil, gomock.Any()).
  51. DoAndReturn(func(ctx context.Context, s sqlx.Session, data *deptModel.SysDept) (interface {
  52. LastInsertId() (int64, error)
  53. RowsAffected() (int64, error)
  54. }, error) {
  55. assert.Equal(t, "NORMAL", data.DeptType)
  56. return fakeResult{id: 99}, nil
  57. })
  58. // L-R17-5:Insert 后 CreateDept 以单条 `UPDATE sys_dept SET path=?,updateTime=?` 回写 path,
  59. // 相对老路径 `FindOneWithTx → UpdateWithTx` 砍掉一次 SELECT、缩短 X 锁;mock 契约必须同步。
  60. mockDept.EXPECT().UpdatePathWithTx(gomock.Any(), nil, int64(99), "/99/", gomock.Any()).Return(nil)
  61. svcCtx := mocks.NewMockServiceContext(mocks.MockModels{Dept: mockDept})
  62. logic := NewCreateDeptLogic(ctxhelper.SuperAdminCtx(), svcCtx)
  63. resp, err := logic.CreateDept(&types.CreateDeptReq{
  64. ParentId: 0,
  65. Name: "TestDept",
  66. })
  67. assert.NoError(t, err)
  68. assert.NotNil(t, resp)
  69. }
  70. // TC-0095: 传DeptType=DEV
  71. func TestCreateDept_Mock_DevDeptType(t *testing.T) {
  72. ctrl := gomock.NewController(t)
  73. defer ctrl.Finish()
  74. mockDept := mocks.NewMockSysDeptModel(ctrl)
  75. mockDept.EXPECT().TransactCtx(gomock.Any(), gomock.Any()).
  76. DoAndReturn(func(ctx context.Context, fn func(context.Context, sqlx.Session) error) error {
  77. return fn(ctx, nil)
  78. })
  79. mockDept.EXPECT().InsertWithTx(gomock.Any(), nil, gomock.Any()).
  80. DoAndReturn(func(ctx context.Context, s sqlx.Session, data *deptModel.SysDept) (interface {
  81. LastInsertId() (int64, error)
  82. RowsAffected() (int64, error)
  83. }, error) {
  84. assert.Equal(t, "DEV", data.DeptType)
  85. return fakeResult{id: 100}, nil
  86. })
  87. mockDept.EXPECT().UpdatePathWithTx(gomock.Any(), nil, int64(100), "/100/", gomock.Any()).Return(nil)
  88. svcCtx := mocks.NewMockServiceContext(mocks.MockModels{Dept: mockDept})
  89. logic := NewCreateDeptLogic(ctxhelper.SuperAdminCtx(), svcCtx)
  90. resp, err := logic.CreateDept(&types.CreateDeptReq{
  91. ParentId: 0,
  92. Name: "TestDevDept",
  93. DeptType: "DEV",
  94. })
  95. assert.NoError(t, err)
  96. assert.NotNil(t, resp)
  97. }
  98. // TC-0098: 事务回滚—path 回写失败,整个 tx 必须回滚并把 dbErr 原样冒泡出去。
  99. func TestCreateDept_Mock_UpdateWithTxFail(t *testing.T) {
  100. ctrl := gomock.NewController(t)
  101. defer ctrl.Finish()
  102. dbErr := errors.New("db error")
  103. mockDept := mocks.NewMockSysDeptModel(ctrl)
  104. mockDept.EXPECT().TransactCtx(gomock.Any(), gomock.Any()).
  105. DoAndReturn(func(ctx context.Context, fn func(context.Context, sqlx.Session) error) error {
  106. return fn(ctx, nil)
  107. })
  108. mockDept.EXPECT().InsertWithTx(gomock.Any(), nil, gomock.Any()).
  109. Return(fakeResult{id: 99}, nil)
  110. // L-R17-5:path 回写压缩成 UpdatePathWithTx 单 UPDATE;mock 在此注入失败以验证 tx 回滚。
  111. mockDept.EXPECT().UpdatePathWithTx(gomock.Any(), nil, int64(99), "/99/", gomock.Any()).
  112. Return(dbErr)
  113. svcCtx := mocks.NewMockServiceContext(mocks.MockModels{
  114. Dept: mockDept,
  115. })
  116. logic := NewCreateDeptLogic(ctxhelper.SuperAdminCtx(), svcCtx)
  117. resp, err := logic.CreateDept(&types.CreateDeptReq{
  118. ParentId: 0,
  119. Name: "TestDept",
  120. })
  121. assert.Error(t, err)
  122. assert.ErrorIs(t, err, dbErr)
  123. assert.Nil(t, resp)
  124. }