package pub import ( "context" "testing" permModel "perms-system-server/internal/model/perm" productModel "perms-system-server/internal/model/product" "perms-system-server/internal/testutil/mocks" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/zeromicro/go-zero/core/stores/sqlx" "go.uber.org/mock/gomock" "golang.org/x/crypto/bcrypt" ) // TC-0826: 请求体内 perm.code 重复时,service 必须先在入参上去重,避免同一个 tx 内 // BatchInsert 自己和自己撞 UNIQUE(productCode, code) 引发 1062。 // 旧文件 syncPermsConflict_audit_test.go 一并覆盖了 1062→409 的映射契约,但 H-3 引入 // LockByCodeTx 串行化同步后,1062 在实践中已不可达,该 409 映射契约也被一起取消;只有 // 这一条"请求内去重"仍然是当前产品契约,必须继续回归。 func TestExecuteSyncPerms_DeduplicatesRequest(t *testing.T) { ctrl := gomock.NewController(t) t.Cleanup(ctrl.Finish) hashedSecret, err := bcrypt.GenerateFromPassword([]byte("s"), bcrypt.MinCost) require.NoError(t, err) mockProduct := mocks.NewMockSysProductModel(ctrl) mockProduct.EXPECT().FindOneByAppKey(gomock.Any(), "ak"). Return(&productModel.SysProduct{ Id: 1, Code: "pc_dedup", AppKey: "ak", AppSecret: string(hashedSecret), Status: 1, }, nil) // 审计 M-R10-1:LockByCodeTx 拿到的行必须 Status=1 mockProduct.EXPECT().LockByCodeTx(gomock.Any(), gomock.Any(), "pc_dedup"). Return(&productModel.SysProduct{Id: 1, Code: "pc_dedup", Status: 1}, nil) mockPerm := mocks.NewMockSysPermModel(ctrl) mockPerm.EXPECT().FindMapByProductCodeWithTx(gomock.Any(), gomock.Any(), "pc_dedup"). Return(map[string]*permModel.SysPerm{}, nil) var captured []*permModel.SysPerm mockPerm.EXPECT().TransactCtx(gomock.Any(), gomock.Any()). DoAndReturn(func(ctx context.Context, fn func(context.Context, sqlx.Session) error) error { return fn(ctx, nil) }) mockPerm.EXPECT().BatchInsertWithTx(gomock.Any(), nil, gomock.Any()). DoAndReturn(func(ctx context.Context, s sqlx.Session, items []*permModel.SysPerm) error { captured = items return nil }) // 去重后 codes 只剩一个,DisableNotInCodesWithTx 用去重后的集合做 NOT IN。 mockPerm.EXPECT().DisableNotInCodesWithTx(gomock.Any(), nil, "pc_dedup", []string{"dup_code"}, gomock.Any()). Return(int64(0), nil) svcCtx := mocks.NewMockServiceContext(mocks.MockModels{ Product: mockProduct, Perm: mockPerm, }) result, err := ExecuteSyncPerms(context.Background(), svcCtx, "ak", "s", []SyncPermItem{ {Code: "dup_code", Name: "A"}, {Code: "dup_code", Name: "A-again"}, {Code: "dup_code", Name: "A-yet-again"}, }) require.NoError(t, err) require.NotNil(t, result) require.Len(t, captured, 1, "入参内 code 重复必须去重为 1 条,避免自撞 1062") assert.Equal(t, "dup_code", captured[0].Code) assert.Equal(t, "A", captured[0].Name, "去重策略应稳定到首次出现,使行为可预测") }