package product import ( "context" "database/sql" "errors" "testing" deptModel "perms-system-server/internal/model/dept" productModel "perms-system-server/internal/model/product" userModel "perms-system-server/internal/model/user" "perms-system-server/internal/testutil/ctxhelper" "perms-system-server/internal/testutil/mocks" "perms-system-server/internal/types" "github.com/stretchr/testify/assert" "github.com/zeromicro/go-zero/core/stores/sqlx" "go.uber.org/mock/gomock" ) type fakeResult struct{ id int64 } func (r fakeResult) LastInsertId() (int64, error) { return r.id, nil } func (r fakeResult) RowsAffected() (int64, error) { return 1, nil } // TC-0065: 事务回滚-用户创建失败 func TestCreateProduct_Mock_UserInsertFail(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() dbErr := errors.New("db error") mockProduct := mocks.NewMockSysProductModel(ctrl) mockProduct.EXPECT().FindOneByCode(gomock.Any(), "test_code"). Return(nil, productModel.ErrNotFound) mockProduct.EXPECT().TransactCtx(gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, fn func(context.Context, sqlx.Session) error) error { return fn(ctx, nil) }) mockProduct.EXPECT().InsertWithTx(gomock.Any(), nil, gomock.Any()). Return(fakeResult{id: 1}, nil) mockUser := mocks.NewMockSysUserModel(ctrl) mockUser.EXPECT().FindOneByUsername(gomock.Any(), "admin_test_code"). Return(nil, userModel.ErrNotFound) mockUser.EXPECT().InsertWithTx(gomock.Any(), nil, gomock.Any()). Return(nil, dbErr) // CreateProduct 必填 AdminDeptId,入库前 FindOne + 启用校验 mockDept := mocks.NewMockSysDeptModel(ctrl) mockDept.EXPECT().FindOne(gomock.Any(), int64(88)). Return(&deptModel.SysDept{Id: 88, Path: "/88/", Status: 1}, nil) svcCtx := mocks.NewMockServiceContext(mocks.MockModels{ Product: mockProduct, User: mockUser, Dept: mockDept, }) logic := NewCreateProductLogic(ctxhelper.SuperAdminCtx(), svcCtx) resp, err := logic.CreateProduct(&types.CreateProductReq{ Code: "test_code", Name: "Test Product", AdminDeptId: 88, }) assert.Error(t, err) assert.ErrorIs(t, err, dbErr) assert.Nil(t, resp) } // TC-0066: 事务回滚-成员创建失败 func TestCreateProduct_Mock_MemberInsertFail(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() dbErr := errors.New("db error") mockProduct := mocks.NewMockSysProductModel(ctrl) mockProduct.EXPECT().FindOneByCode(gomock.Any(), "test_code"). Return(nil, productModel.ErrNotFound) mockProduct.EXPECT().TransactCtx(gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, fn func(context.Context, sqlx.Session) error) error { return fn(ctx, nil) }) mockProduct.EXPECT().InsertWithTx(gomock.Any(), nil, gomock.Any()). Return(fakeResult{id: 1}, nil) mockUser := mocks.NewMockSysUserModel(ctrl) mockUser.EXPECT().FindOneByUsername(gomock.Any(), "admin_test_code"). Return(nil, userModel.ErrNotFound) mockUser.EXPECT().InsertWithTx(gomock.Any(), nil, gomock.Any()). Return(fakeResult{id: 10}, nil) mockPM := mocks.NewMockSysProductMemberModel(ctrl) mockPM.EXPECT().InsertWithTx(gomock.Any(), nil, gomock.Any()). Return(sql.Result(nil), dbErr) // CreateProduct 必填 AdminDeptId mockDept := mocks.NewMockSysDeptModel(ctrl) mockDept.EXPECT().FindOne(gomock.Any(), int64(88)). Return(&deptModel.SysDept{Id: 88, Path: "/88/", Status: 1}, nil) svcCtx := mocks.NewMockServiceContext(mocks.MockModels{ Product: mockProduct, User: mockUser, ProductMember: mockPM, Dept: mockDept, }) logic := NewCreateProductLogic(ctxhelper.SuperAdminCtx(), svcCtx) resp, err := logic.CreateProduct(&types.CreateProductReq{ Code: "test_code", Name: "Test Product", AdminDeptId: 88, }) assert.Error(t, err) assert.ErrorIs(t, err, dbErr) assert.Nil(t, resp) }