| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475 |
- package user
- import (
- "context"
- "errors"
- "testing"
- memberModel "perms-system-server/internal/model/productmember"
- roleModel "perms-system-server/internal/model/role"
- 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"
- )
- // TC-0187: 事务回滚
- func TestBindRoles_Mock_BatchInsertFail(t *testing.T) {
- ctrl := gomock.NewController(t)
- defer ctrl.Finish()
- dbErr := errors.New("db error")
- mockUser := mocks.NewMockSysUserModel(ctrl)
- mockUser.EXPECT().FindOne(gomock.Any(), int64(1)).
- Return(&userModel.SysUser{Id: 1}, nil)
- mockPM := mocks.NewMockSysProductMemberModel(ctrl)
- mockPM.EXPECT().FindOneByProductCodeUserId(gomock.Any(), "test_product", int64(1)).
- Return(&memberModel.SysProductMember{Id: 1, ProductCode: "test_product", UserId: 1, Status: 1}, nil)
- // 事务首步 FindOneForUpdateTx 锁 sys_product_member 行
- mockPM.EXPECT().FindOneForUpdateTx(gomock.Any(), nil, int64(1)).
- Return(&memberModel.SysProductMember{Id: 1, ProductCode: "test_product", UserId: 1, Status: 1}, nil)
- mockRole := mocks.NewMockSysRoleModel(ctrl)
- mockRole.EXPECT().FindByIds(gomock.Any(), []int64{10, 20}).
- Return([]*roleModel.SysRole{
- {Id: 10, ProductCode: "test_product", Status: 1},
- {Id: 20, ProductCode: "test_product", Status: 1},
- }, nil)
- // 事务内对 toAdd/入参 roleIds 加 S 锁以闭合 BindRoles × DeleteRole 写偏斜。
- // mock 直接放行(真实表现为 `SELECT id FROM sys_role WHERE id IN (10,20) AND status=1 LOCK IN SHARE MODE`
- // 命中两行无错)。
- mockRole.EXPECT().LockRolesForShareTx(gomock.Any(), nil, []int64{10, 20}).Return(nil)
- mockUR := mocks.NewMockSysUserRoleModel(ctrl)
- mockUR.EXPECT().FindRoleIdsByUserIdForProductTx(gomock.Any(), nil, int64(1), "test_product").Return([]int64{}, nil)
- mockUR.EXPECT().TableName().Return("`sys_user_role`").AnyTimes()
- mockUR.EXPECT().TransactCtx(gomock.Any(), gomock.Any()).
- DoAndReturn(func(ctx context.Context, fn func(context.Context, sqlx.Session) error) error {
- return fn(ctx, nil)
- })
- // audit 修复:循环 DELETE 替换为批量 DeleteByUserIdAndRoleIdsTx;toRemove 为空时也被调用
- mockUR.EXPECT().DeleteByUserIdAndRoleIdsTx(gomock.Any(), nil, int64(1), []int64(nil)).Return(nil)
- mockUR.EXPECT().BatchInsertWithTx(gomock.Any(), nil, gomock.Any()).Return(dbErr)
- svcCtx := mocks.NewMockServiceContext(mocks.MockModels{
- User: mockUser,
- Role: mockRole,
- UserRole: mockUR,
- ProductMember: mockPM,
- })
- logic := NewBindRolesLogic(ctxhelper.SuperAdminCtx(), svcCtx)
- err := logic.BindRoles(&types.BindRolesReq{
- UserId: 1,
- RoleIds: []int64{10, 20},
- })
- assert.Error(t, err)
- assert.ErrorIs(t, err, dbErr)
- }
|