add service
All checks were successful
Deploy Smart Search Backend Test / deploy (push) Successful in 1m36s

This commit is contained in:
vallyenfail
2026-01-19 16:42:51 +03:00
parent c78eda8291
commit b56b833680
13 changed files with 42 additions and 418 deletions

1
.gitignore vendored
View File

@@ -36,3 +36,4 @@ config/config.yaml
/bin/ /bin/
/dist/ /dist/
.gitea .gitea
.gitea/workflows

View File

@@ -29,8 +29,7 @@ message GetInfoResponse {
string code = 1; string code = 1;
int64 user_id = 2; int64 user_id = 2;
int32 can_be_used_count = 3; int32 can_be_used_count = 3;
int32 used_count = 4; google.protobuf.Timestamp expires_at = 4;
google.protobuf.Timestamp expires_at = 5; bool is_active = 5;
bool is_active = 6; google.protobuf.Timestamp created_at = 6;
google.protobuf.Timestamp created_at = 7;
} }

View File

@@ -37,7 +37,6 @@ func (h *InviteHandler) GetInfo(ctx context.Context, req *pb.GetInfoRequest) (*p
Code: strconv.FormatInt(invite.Code, 10), Code: strconv.FormatInt(invite.Code, 10),
UserId: int64(invite.UserID), UserId: int64(invite.UserID),
CanBeUsedCount: int32(invite.CanBeUsedCount), CanBeUsedCount: int32(invite.CanBeUsedCount),
UsedCount: int32(invite.UsedCount),
ExpiresAt: timestamppb.New(invite.ExpiresAt), ExpiresAt: timestamppb.New(invite.ExpiresAt),
IsActive: invite.IsActive, IsActive: invite.IsActive,
CreatedAt: timestamppb.New(invite.CreatedAt), CreatedAt: timestamppb.New(invite.CreatedAt),

View File

@@ -68,13 +68,6 @@ type InviteRepositoryMock struct {
afterGetUserInvitesCounter uint64 afterGetUserInvitesCounter uint64
beforeGetUserInvitesCounter uint64 beforeGetUserInvitesCounter uint64
GetUserInvitesMock mInviteRepositoryMockGetUserInvites GetUserInvitesMock mInviteRepositoryMockGetUserInvites
funcIncrementUsedCount func(ctx context.Context, code int64) (err error)
funcIncrementUsedCountOrigin string
inspectFuncIncrementUsedCount func(ctx context.Context, code int64)
afterIncrementUsedCountCounter uint64
beforeIncrementUsedCountCounter uint64
IncrementUsedCountMock mInviteRepositoryMockIncrementUsedCount
} }
// NewInviteRepositoryMock returns a mock for mm_repository.InviteRepository // NewInviteRepositoryMock returns a mock for mm_repository.InviteRepository
@@ -106,9 +99,6 @@ func NewInviteRepositoryMock(t minimock.Tester) *InviteRepositoryMock {
m.GetUserInvitesMock = mInviteRepositoryMockGetUserInvites{mock: m} m.GetUserInvitesMock = mInviteRepositoryMockGetUserInvites{mock: m}
m.GetUserInvitesMock.callArgs = []*InviteRepositoryMockGetUserInvitesParams{} m.GetUserInvitesMock.callArgs = []*InviteRepositoryMockGetUserInvitesParams{}
m.IncrementUsedCountMock = mInviteRepositoryMockIncrementUsedCount{mock: m}
m.IncrementUsedCountMock.callArgs = []*InviteRepositoryMockIncrementUsedCountParams{}
t.Cleanup(m.MinimockFinish) t.Cleanup(m.MinimockFinish)
return m return m
@@ -2543,348 +2533,6 @@ func (m *InviteRepositoryMock) MinimockGetUserInvitesInspect() {
} }
} }
type mInviteRepositoryMockIncrementUsedCount struct {
optional bool
mock *InviteRepositoryMock
defaultExpectation *InviteRepositoryMockIncrementUsedCountExpectation
expectations []*InviteRepositoryMockIncrementUsedCountExpectation
callArgs []*InviteRepositoryMockIncrementUsedCountParams
mutex sync.RWMutex
expectedInvocations uint64
expectedInvocationsOrigin string
}
// InviteRepositoryMockIncrementUsedCountExpectation specifies expectation struct of the InviteRepository.IncrementUsedCount
type InviteRepositoryMockIncrementUsedCountExpectation struct {
mock *InviteRepositoryMock
params *InviteRepositoryMockIncrementUsedCountParams
paramPtrs *InviteRepositoryMockIncrementUsedCountParamPtrs
expectationOrigins InviteRepositoryMockIncrementUsedCountExpectationOrigins
results *InviteRepositoryMockIncrementUsedCountResults
returnOrigin string
Counter uint64
}
// InviteRepositoryMockIncrementUsedCountParams contains parameters of the InviteRepository.IncrementUsedCount
type InviteRepositoryMockIncrementUsedCountParams struct {
ctx context.Context
code int64
}
// InviteRepositoryMockIncrementUsedCountParamPtrs contains pointers to parameters of the InviteRepository.IncrementUsedCount
type InviteRepositoryMockIncrementUsedCountParamPtrs struct {
ctx *context.Context
code *int64
}
// InviteRepositoryMockIncrementUsedCountResults contains results of the InviteRepository.IncrementUsedCount
type InviteRepositoryMockIncrementUsedCountResults struct {
err error
}
// InviteRepositoryMockIncrementUsedCountOrigins contains origins of expectations of the InviteRepository.IncrementUsedCount
type InviteRepositoryMockIncrementUsedCountExpectationOrigins struct {
origin string
originCtx string
originCode string
}
// Marks this method to be optional. The default behavior of any method with Return() is '1 or more', meaning
// the test will fail minimock's automatic final call check if the mocked method was not called at least once.
// Optional() makes method check to work in '0 or more' mode.
// It is NOT RECOMMENDED to use this option unless you really need it, as default behaviour helps to
// catch the problems when the expected method call is totally skipped during test run.
func (mmIncrementUsedCount *mInviteRepositoryMockIncrementUsedCount) Optional() *mInviteRepositoryMockIncrementUsedCount {
mmIncrementUsedCount.optional = true
return mmIncrementUsedCount
}
// Expect sets up expected params for InviteRepository.IncrementUsedCount
func (mmIncrementUsedCount *mInviteRepositoryMockIncrementUsedCount) Expect(ctx context.Context, code int64) *mInviteRepositoryMockIncrementUsedCount {
if mmIncrementUsedCount.mock.funcIncrementUsedCount != nil {
mmIncrementUsedCount.mock.t.Fatalf("InviteRepositoryMock.IncrementUsedCount mock is already set by Set")
}
if mmIncrementUsedCount.defaultExpectation == nil {
mmIncrementUsedCount.defaultExpectation = &InviteRepositoryMockIncrementUsedCountExpectation{}
}
if mmIncrementUsedCount.defaultExpectation.paramPtrs != nil {
mmIncrementUsedCount.mock.t.Fatalf("InviteRepositoryMock.IncrementUsedCount mock is already set by ExpectParams functions")
}
mmIncrementUsedCount.defaultExpectation.params = &InviteRepositoryMockIncrementUsedCountParams{ctx, code}
mmIncrementUsedCount.defaultExpectation.expectationOrigins.origin = minimock.CallerInfo(1)
for _, e := range mmIncrementUsedCount.expectations {
if minimock.Equal(e.params, mmIncrementUsedCount.defaultExpectation.params) {
mmIncrementUsedCount.mock.t.Fatalf("Expectation set by When has same params: %#v", *mmIncrementUsedCount.defaultExpectation.params)
}
}
return mmIncrementUsedCount
}
// ExpectCtxParam1 sets up expected param ctx for InviteRepository.IncrementUsedCount
func (mmIncrementUsedCount *mInviteRepositoryMockIncrementUsedCount) ExpectCtxParam1(ctx context.Context) *mInviteRepositoryMockIncrementUsedCount {
if mmIncrementUsedCount.mock.funcIncrementUsedCount != nil {
mmIncrementUsedCount.mock.t.Fatalf("InviteRepositoryMock.IncrementUsedCount mock is already set by Set")
}
if mmIncrementUsedCount.defaultExpectation == nil {
mmIncrementUsedCount.defaultExpectation = &InviteRepositoryMockIncrementUsedCountExpectation{}
}
if mmIncrementUsedCount.defaultExpectation.params != nil {
mmIncrementUsedCount.mock.t.Fatalf("InviteRepositoryMock.IncrementUsedCount mock is already set by Expect")
}
if mmIncrementUsedCount.defaultExpectation.paramPtrs == nil {
mmIncrementUsedCount.defaultExpectation.paramPtrs = &InviteRepositoryMockIncrementUsedCountParamPtrs{}
}
mmIncrementUsedCount.defaultExpectation.paramPtrs.ctx = &ctx
mmIncrementUsedCount.defaultExpectation.expectationOrigins.originCtx = minimock.CallerInfo(1)
return mmIncrementUsedCount
}
// ExpectCodeParam2 sets up expected param code for InviteRepository.IncrementUsedCount
func (mmIncrementUsedCount *mInviteRepositoryMockIncrementUsedCount) ExpectCodeParam2(code int64) *mInviteRepositoryMockIncrementUsedCount {
if mmIncrementUsedCount.mock.funcIncrementUsedCount != nil {
mmIncrementUsedCount.mock.t.Fatalf("InviteRepositoryMock.IncrementUsedCount mock is already set by Set")
}
if mmIncrementUsedCount.defaultExpectation == nil {
mmIncrementUsedCount.defaultExpectation = &InviteRepositoryMockIncrementUsedCountExpectation{}
}
if mmIncrementUsedCount.defaultExpectation.params != nil {
mmIncrementUsedCount.mock.t.Fatalf("InviteRepositoryMock.IncrementUsedCount mock is already set by Expect")
}
if mmIncrementUsedCount.defaultExpectation.paramPtrs == nil {
mmIncrementUsedCount.defaultExpectation.paramPtrs = &InviteRepositoryMockIncrementUsedCountParamPtrs{}
}
mmIncrementUsedCount.defaultExpectation.paramPtrs.code = &code
mmIncrementUsedCount.defaultExpectation.expectationOrigins.originCode = minimock.CallerInfo(1)
return mmIncrementUsedCount
}
// Inspect accepts an inspector function that has same arguments as the InviteRepository.IncrementUsedCount
func (mmIncrementUsedCount *mInviteRepositoryMockIncrementUsedCount) Inspect(f func(ctx context.Context, code int64)) *mInviteRepositoryMockIncrementUsedCount {
if mmIncrementUsedCount.mock.inspectFuncIncrementUsedCount != nil {
mmIncrementUsedCount.mock.t.Fatalf("Inspect function is already set for InviteRepositoryMock.IncrementUsedCount")
}
mmIncrementUsedCount.mock.inspectFuncIncrementUsedCount = f
return mmIncrementUsedCount
}
// Return sets up results that will be returned by InviteRepository.IncrementUsedCount
func (mmIncrementUsedCount *mInviteRepositoryMockIncrementUsedCount) Return(err error) *InviteRepositoryMock {
if mmIncrementUsedCount.mock.funcIncrementUsedCount != nil {
mmIncrementUsedCount.mock.t.Fatalf("InviteRepositoryMock.IncrementUsedCount mock is already set by Set")
}
if mmIncrementUsedCount.defaultExpectation == nil {
mmIncrementUsedCount.defaultExpectation = &InviteRepositoryMockIncrementUsedCountExpectation{mock: mmIncrementUsedCount.mock}
}
mmIncrementUsedCount.defaultExpectation.results = &InviteRepositoryMockIncrementUsedCountResults{err}
mmIncrementUsedCount.defaultExpectation.returnOrigin = minimock.CallerInfo(1)
return mmIncrementUsedCount.mock
}
// Set uses given function f to mock the InviteRepository.IncrementUsedCount method
func (mmIncrementUsedCount *mInviteRepositoryMockIncrementUsedCount) Set(f func(ctx context.Context, code int64) (err error)) *InviteRepositoryMock {
if mmIncrementUsedCount.defaultExpectation != nil {
mmIncrementUsedCount.mock.t.Fatalf("Default expectation is already set for the InviteRepository.IncrementUsedCount method")
}
if len(mmIncrementUsedCount.expectations) > 0 {
mmIncrementUsedCount.mock.t.Fatalf("Some expectations are already set for the InviteRepository.IncrementUsedCount method")
}
mmIncrementUsedCount.mock.funcIncrementUsedCount = f
mmIncrementUsedCount.mock.funcIncrementUsedCountOrigin = minimock.CallerInfo(1)
return mmIncrementUsedCount.mock
}
// When sets expectation for the InviteRepository.IncrementUsedCount which will trigger the result defined by the following
// Then helper
func (mmIncrementUsedCount *mInviteRepositoryMockIncrementUsedCount) When(ctx context.Context, code int64) *InviteRepositoryMockIncrementUsedCountExpectation {
if mmIncrementUsedCount.mock.funcIncrementUsedCount != nil {
mmIncrementUsedCount.mock.t.Fatalf("InviteRepositoryMock.IncrementUsedCount mock is already set by Set")
}
expectation := &InviteRepositoryMockIncrementUsedCountExpectation{
mock: mmIncrementUsedCount.mock,
params: &InviteRepositoryMockIncrementUsedCountParams{ctx, code},
expectationOrigins: InviteRepositoryMockIncrementUsedCountExpectationOrigins{origin: minimock.CallerInfo(1)},
}
mmIncrementUsedCount.expectations = append(mmIncrementUsedCount.expectations, expectation)
return expectation
}
// Then sets up InviteRepository.IncrementUsedCount return parameters for the expectation previously defined by the When method
func (e *InviteRepositoryMockIncrementUsedCountExpectation) Then(err error) *InviteRepositoryMock {
e.results = &InviteRepositoryMockIncrementUsedCountResults{err}
return e.mock
}
// Times sets number of times InviteRepository.IncrementUsedCount should be invoked
func (mmIncrementUsedCount *mInviteRepositoryMockIncrementUsedCount) Times(n uint64) *mInviteRepositoryMockIncrementUsedCount {
if n == 0 {
mmIncrementUsedCount.mock.t.Fatalf("Times of InviteRepositoryMock.IncrementUsedCount mock can not be zero")
}
mm_atomic.StoreUint64(&mmIncrementUsedCount.expectedInvocations, n)
mmIncrementUsedCount.expectedInvocationsOrigin = minimock.CallerInfo(1)
return mmIncrementUsedCount
}
func (mmIncrementUsedCount *mInviteRepositoryMockIncrementUsedCount) invocationsDone() bool {
if len(mmIncrementUsedCount.expectations) == 0 && mmIncrementUsedCount.defaultExpectation == nil && mmIncrementUsedCount.mock.funcIncrementUsedCount == nil {
return true
}
totalInvocations := mm_atomic.LoadUint64(&mmIncrementUsedCount.mock.afterIncrementUsedCountCounter)
expectedInvocations := mm_atomic.LoadUint64(&mmIncrementUsedCount.expectedInvocations)
return totalInvocations > 0 && (expectedInvocations == 0 || expectedInvocations == totalInvocations)
}
// IncrementUsedCount implements mm_repository.InviteRepository
func (mmIncrementUsedCount *InviteRepositoryMock) IncrementUsedCount(ctx context.Context, code int64) (err error) {
mm_atomic.AddUint64(&mmIncrementUsedCount.beforeIncrementUsedCountCounter, 1)
defer mm_atomic.AddUint64(&mmIncrementUsedCount.afterIncrementUsedCountCounter, 1)
mmIncrementUsedCount.t.Helper()
if mmIncrementUsedCount.inspectFuncIncrementUsedCount != nil {
mmIncrementUsedCount.inspectFuncIncrementUsedCount(ctx, code)
}
mm_params := InviteRepositoryMockIncrementUsedCountParams{ctx, code}
// Record call args
mmIncrementUsedCount.IncrementUsedCountMock.mutex.Lock()
mmIncrementUsedCount.IncrementUsedCountMock.callArgs = append(mmIncrementUsedCount.IncrementUsedCountMock.callArgs, &mm_params)
mmIncrementUsedCount.IncrementUsedCountMock.mutex.Unlock()
for _, e := range mmIncrementUsedCount.IncrementUsedCountMock.expectations {
if minimock.Equal(*e.params, mm_params) {
mm_atomic.AddUint64(&e.Counter, 1)
return e.results.err
}
}
if mmIncrementUsedCount.IncrementUsedCountMock.defaultExpectation != nil {
mm_atomic.AddUint64(&mmIncrementUsedCount.IncrementUsedCountMock.defaultExpectation.Counter, 1)
mm_want := mmIncrementUsedCount.IncrementUsedCountMock.defaultExpectation.params
mm_want_ptrs := mmIncrementUsedCount.IncrementUsedCountMock.defaultExpectation.paramPtrs
mm_got := InviteRepositoryMockIncrementUsedCountParams{ctx, code}
if mm_want_ptrs != nil {
if mm_want_ptrs.ctx != nil && !minimock.Equal(*mm_want_ptrs.ctx, mm_got.ctx) {
mmIncrementUsedCount.t.Errorf("InviteRepositoryMock.IncrementUsedCount got unexpected parameter ctx, expected at\n%s:\nwant: %#v\n got: %#v%s\n",
mmIncrementUsedCount.IncrementUsedCountMock.defaultExpectation.expectationOrigins.originCtx, *mm_want_ptrs.ctx, mm_got.ctx, minimock.Diff(*mm_want_ptrs.ctx, mm_got.ctx))
}
if mm_want_ptrs.code != nil && !minimock.Equal(*mm_want_ptrs.code, mm_got.code) {
mmIncrementUsedCount.t.Errorf("InviteRepositoryMock.IncrementUsedCount got unexpected parameter code, expected at\n%s:\nwant: %#v\n got: %#v%s\n",
mmIncrementUsedCount.IncrementUsedCountMock.defaultExpectation.expectationOrigins.originCode, *mm_want_ptrs.code, mm_got.code, minimock.Diff(*mm_want_ptrs.code, mm_got.code))
}
} else if mm_want != nil && !minimock.Equal(*mm_want, mm_got) {
mmIncrementUsedCount.t.Errorf("InviteRepositoryMock.IncrementUsedCount got unexpected parameters, expected at\n%s:\nwant: %#v\n got: %#v%s\n",
mmIncrementUsedCount.IncrementUsedCountMock.defaultExpectation.expectationOrigins.origin, *mm_want, mm_got, minimock.Diff(*mm_want, mm_got))
}
mm_results := mmIncrementUsedCount.IncrementUsedCountMock.defaultExpectation.results
if mm_results == nil {
mmIncrementUsedCount.t.Fatal("No results are set for the InviteRepositoryMock.IncrementUsedCount")
}
return (*mm_results).err
}
if mmIncrementUsedCount.funcIncrementUsedCount != nil {
return mmIncrementUsedCount.funcIncrementUsedCount(ctx, code)
}
mmIncrementUsedCount.t.Fatalf("Unexpected call to InviteRepositoryMock.IncrementUsedCount. %v %v", ctx, code)
return
}
// IncrementUsedCountAfterCounter returns a count of finished InviteRepositoryMock.IncrementUsedCount invocations
func (mmIncrementUsedCount *InviteRepositoryMock) IncrementUsedCountAfterCounter() uint64 {
return mm_atomic.LoadUint64(&mmIncrementUsedCount.afterIncrementUsedCountCounter)
}
// IncrementUsedCountBeforeCounter returns a count of InviteRepositoryMock.IncrementUsedCount invocations
func (mmIncrementUsedCount *InviteRepositoryMock) IncrementUsedCountBeforeCounter() uint64 {
return mm_atomic.LoadUint64(&mmIncrementUsedCount.beforeIncrementUsedCountCounter)
}
// Calls returns a list of arguments used in each call to InviteRepositoryMock.IncrementUsedCount.
// The list is in the same order as the calls were made (i.e. recent calls have a higher index)
func (mmIncrementUsedCount *mInviteRepositoryMockIncrementUsedCount) Calls() []*InviteRepositoryMockIncrementUsedCountParams {
mmIncrementUsedCount.mutex.RLock()
argCopy := make([]*InviteRepositoryMockIncrementUsedCountParams, len(mmIncrementUsedCount.callArgs))
copy(argCopy, mmIncrementUsedCount.callArgs)
mmIncrementUsedCount.mutex.RUnlock()
return argCopy
}
// MinimockIncrementUsedCountDone returns true if the count of the IncrementUsedCount invocations corresponds
// the number of defined expectations
func (m *InviteRepositoryMock) MinimockIncrementUsedCountDone() bool {
if m.IncrementUsedCountMock.optional {
// Optional methods provide '0 or more' call count restriction.
return true
}
for _, e := range m.IncrementUsedCountMock.expectations {
if mm_atomic.LoadUint64(&e.Counter) < 1 {
return false
}
}
return m.IncrementUsedCountMock.invocationsDone()
}
// MinimockIncrementUsedCountInspect logs each unmet expectation
func (m *InviteRepositoryMock) MinimockIncrementUsedCountInspect() {
for _, e := range m.IncrementUsedCountMock.expectations {
if mm_atomic.LoadUint64(&e.Counter) < 1 {
m.t.Errorf("Expected call to InviteRepositoryMock.IncrementUsedCount at\n%s with params: %#v", e.expectationOrigins.origin, *e.params)
}
}
afterIncrementUsedCountCounter := mm_atomic.LoadUint64(&m.afterIncrementUsedCountCounter)
// if default expectation was set then invocations count should be greater than zero
if m.IncrementUsedCountMock.defaultExpectation != nil && afterIncrementUsedCountCounter < 1 {
if m.IncrementUsedCountMock.defaultExpectation.params == nil {
m.t.Errorf("Expected call to InviteRepositoryMock.IncrementUsedCount at\n%s", m.IncrementUsedCountMock.defaultExpectation.returnOrigin)
} else {
m.t.Errorf("Expected call to InviteRepositoryMock.IncrementUsedCount at\n%s with params: %#v", m.IncrementUsedCountMock.defaultExpectation.expectationOrigins.origin, *m.IncrementUsedCountMock.defaultExpectation.params)
}
}
// if func was set then invocations count should be greater than zero
if m.funcIncrementUsedCount != nil && afterIncrementUsedCountCounter < 1 {
m.t.Errorf("Expected call to InviteRepositoryMock.IncrementUsedCount at\n%s", m.funcIncrementUsedCountOrigin)
}
if !m.IncrementUsedCountMock.invocationsDone() && afterIncrementUsedCountCounter > 0 {
m.t.Errorf("Expected %d calls to InviteRepositoryMock.IncrementUsedCount at\n%s but found %d calls",
mm_atomic.LoadUint64(&m.IncrementUsedCountMock.expectedInvocations), m.IncrementUsedCountMock.expectedInvocationsOrigin, afterIncrementUsedCountCounter)
}
}
// MinimockFinish checks that all mocked methods have been called the expected number of times // MinimockFinish checks that all mocked methods have been called the expected number of times
func (m *InviteRepositoryMock) MinimockFinish() { func (m *InviteRepositoryMock) MinimockFinish() {
m.finishOnce.Do(func() { m.finishOnce.Do(func() {
@@ -2902,8 +2550,6 @@ func (m *InviteRepositoryMock) MinimockFinish() {
m.MinimockFindByCodeInspect() m.MinimockFindByCodeInspect()
m.MinimockGetUserInvitesInspect() m.MinimockGetUserInvitesInspect()
m.MinimockIncrementUsedCountInspect()
} }
}) })
} }
@@ -2933,6 +2579,5 @@ func (m *InviteRepositoryMock) minimockDone() bool {
m.MinimockDecrementCanBeUsedCountTxDone() && m.MinimockDecrementCanBeUsedCountTxDone() &&
m.MinimockFindActiveByCodeDone() && m.MinimockFindActiveByCodeDone() &&
m.MinimockFindByCodeDone() && m.MinimockFindByCodeDone() &&
m.MinimockGetUserInvitesDone() && m.MinimockGetUserInvitesDone()
m.MinimockIncrementUsedCountDone()
} }

View File

@@ -7,7 +7,6 @@ type InviteCode struct {
UserID int UserID int
Code int64 Code int64
CanBeUsedCount int CanBeUsedCount int
UsedCount int
IsActive bool IsActive bool
CreatedAt time.Time CreatedAt time.Time
ExpiresAt time.Time ExpiresAt time.Time

View File

@@ -37,7 +37,6 @@ type InviteRepository interface {
CreateTx(ctx context.Context, tx pgx.Tx, invite *model.InviteCode) error CreateTx(ctx context.Context, tx pgx.Tx, invite *model.InviteCode) error
FindByCode(ctx context.Context, code int64) (*model.InviteCode, error) FindByCode(ctx context.Context, code int64) (*model.InviteCode, error)
FindActiveByCode(ctx context.Context, code int64) (*model.InviteCode, error) FindActiveByCode(ctx context.Context, code int64) (*model.InviteCode, error)
IncrementUsedCount(ctx context.Context, code int64) error
DecrementCanBeUsedCountTx(ctx context.Context, tx pgx.Tx, code int64) error DecrementCanBeUsedCountTx(ctx context.Context, tx pgx.Tx, code int64) error
DeactivateExpired(ctx context.Context) (int, error) DeactivateExpired(ctx context.Context) (int, error)
GetUserInvites(ctx context.Context, userID int) ([]*model.InviteCode, error) GetUserInvites(ctx context.Context, userID int) ([]*model.InviteCode, error)

View File

@@ -53,7 +53,7 @@ func (r *inviteRepository) createWithExecutor(ctx context.Context, exec DBTX, in
func (r *inviteRepository) FindByCode(ctx context.Context, code int64) (*model.InviteCode, error) { func (r *inviteRepository) FindByCode(ctx context.Context, code int64) (*model.InviteCode, error) {
query := r.qb.Select( query := r.qb.Select(
"id", "user_id", "code", "can_be_used_count", "used_count", "id", "user_id", "code", "can_be_used_count",
"is_active", "created_at", "expires_at", "is_active", "created_at", "expires_at",
).From("invite_codes").Where(sq.Eq{"code": code}) ).From("invite_codes").Where(sq.Eq{"code": code})
@@ -65,7 +65,7 @@ func (r *inviteRepository) FindByCode(ctx context.Context, code int64) (*model.I
invite := &model.InviteCode{} invite := &model.InviteCode{}
err = r.pool.QueryRow(ctx, sqlQuery, args...).Scan( err = r.pool.QueryRow(ctx, sqlQuery, args...).Scan(
&invite.ID, &invite.UserID, &invite.Code, &invite.CanBeUsedCount, &invite.ID, &invite.UserID, &invite.Code, &invite.CanBeUsedCount,
&invite.UsedCount, &invite.IsActive, &invite.CreatedAt, &invite.ExpiresAt, &invite.IsActive, &invite.CreatedAt, &invite.ExpiresAt,
) )
if errors.Is(err, pgx.ErrNoRows) { if errors.Is(err, pgx.ErrNoRows) {
@@ -80,13 +80,13 @@ func (r *inviteRepository) FindByCode(ctx context.Context, code int64) (*model.I
func (r *inviteRepository) FindActiveByCode(ctx context.Context, code int64) (*model.InviteCode, error) { func (r *inviteRepository) FindActiveByCode(ctx context.Context, code int64) (*model.InviteCode, error) {
query := r.qb.Select( query := r.qb.Select(
"id", "user_id", "code", "can_be_used_count", "used_count", "id", "user_id", "code", "can_be_used_count",
"is_active", "created_at", "expires_at", "is_active", "created_at", "expires_at",
).From("invite_codes").Where(sq.And{ ).From("invite_codes").Where(sq.And{
sq.Eq{"code": code}, sq.Eq{"code": code},
sq.Eq{"is_active": true}, sq.Eq{"is_active": true},
sq.Expr("expires_at > now()"), sq.Expr("expires_at > now()"),
sq.Expr("can_be_used_count > used_count"), sq.Expr("can_be_used_count > 0"),
}) })
sqlQuery, args, err := query.ToSql() sqlQuery, args, err := query.ToSql()
@@ -97,7 +97,7 @@ func (r *inviteRepository) FindActiveByCode(ctx context.Context, code int64) (*m
invite := &model.InviteCode{} invite := &model.InviteCode{}
err = r.pool.QueryRow(ctx, sqlQuery, args...).Scan( err = r.pool.QueryRow(ctx, sqlQuery, args...).Scan(
&invite.ID, &invite.UserID, &invite.Code, &invite.CanBeUsedCount, &invite.ID, &invite.UserID, &invite.Code, &invite.CanBeUsedCount,
&invite.UsedCount, &invite.IsActive, &invite.CreatedAt, &invite.ExpiresAt, &invite.IsActive, &invite.CreatedAt, &invite.ExpiresAt,
) )
if errors.Is(err, pgx.ErrNoRows) { if errors.Is(err, pgx.ErrNoRows) {
@@ -110,28 +110,10 @@ func (r *inviteRepository) FindActiveByCode(ctx context.Context, code int64) (*m
return invite, nil return invite, nil
} }
func (r *inviteRepository) IncrementUsedCount(ctx context.Context, code int64) error {
query := r.qb.Update("invite_codes").
Set("used_count", sq.Expr("used_count + 1")).
Where(sq.Eq{"code": code})
sqlQuery, args, err := query.ToSql()
if err != nil {
return errs.NewInternalError(errs.DatabaseError, "failed to build query", err)
}
_, err = r.pool.Exec(ctx, sqlQuery, args...)
if err != nil {
return errs.NewInternalError(errs.DatabaseError, "failed to increment used count", err)
}
return nil
}
func (r *inviteRepository) DecrementCanBeUsedCountTx(ctx context.Context, tx pgx.Tx, code int64) error { func (r *inviteRepository) DecrementCanBeUsedCountTx(ctx context.Context, tx pgx.Tx, code int64) error {
query := r.qb.Update("invite_codes"). query := r.qb.Update("invite_codes").
Set("used_count", sq.Expr("used_count + 1")). Set("can_be_used_count", sq.Expr("can_be_used_count - 1")).
Set("is_active", sq.Expr("CASE WHEN used_count + 1 >= can_be_used_count THEN false ELSE is_active END")). Set("is_active", sq.Expr("CASE WHEN can_be_used_count - 1 <= 0 THEN false ELSE is_active END")).
Where(sq.Eq{"code": code}) Where(sq.Eq{"code": code})
sqlQuery, args, err := query.ToSql() sqlQuery, args, err := query.ToSql()
@@ -170,7 +152,7 @@ func (r *inviteRepository) DeactivateExpired(ctx context.Context) (int, error) {
func (r *inviteRepository) GetUserInvites(ctx context.Context, userID int) ([]*model.InviteCode, error) { func (r *inviteRepository) GetUserInvites(ctx context.Context, userID int) ([]*model.InviteCode, error) {
query := r.qb.Select( query := r.qb.Select(
"id", "user_id", "code", "can_be_used_count", "used_count", "id", "user_id", "code", "can_be_used_count",
"is_active", "created_at", "expires_at", "is_active", "created_at", "expires_at",
).From("invite_codes"). ).From("invite_codes").
Where(sq.Eq{"user_id": userID}). Where(sq.Eq{"user_id": userID}).
@@ -192,7 +174,7 @@ func (r *inviteRepository) GetUserInvites(ctx context.Context, userID int) ([]*m
invite := &model.InviteCode{} invite := &model.InviteCode{}
err := rows.Scan( err := rows.Scan(
&invite.ID, &invite.UserID, &invite.Code, &invite.CanBeUsedCount, &invite.ID, &invite.UserID, &invite.Code, &invite.CanBeUsedCount,
&invite.UsedCount, &invite.IsActive, &invite.CreatedAt, &invite.ExpiresAt, &invite.IsActive, &invite.CreatedAt, &invite.ExpiresAt,
) )
if err != nil { if err != nil {
return nil, errs.NewInternalError(errs.DatabaseError, "failed to scan invite", err) return nil, errs.NewInternalError(errs.DatabaseError, "failed to scan invite", err)

View File

@@ -0,0 +1,5 @@
-- +goose Up
ALTER TABLE invite_codes DROP COLUMN IF EXISTS used_count;
-- +goose Down
ALTER TABLE invite_codes ADD COLUMN used_count INT DEFAULT 0;

View File

@@ -191,10 +191,9 @@ type GetInfoResponse struct {
Code string `protobuf:"bytes,1,opt,name=code,proto3" json:"code,omitempty"` Code string `protobuf:"bytes,1,opt,name=code,proto3" json:"code,omitempty"`
UserId int64 `protobuf:"varint,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"` UserId int64 `protobuf:"varint,2,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
CanBeUsedCount int32 `protobuf:"varint,3,opt,name=can_be_used_count,json=canBeUsedCount,proto3" json:"can_be_used_count,omitempty"` CanBeUsedCount int32 `protobuf:"varint,3,opt,name=can_be_used_count,json=canBeUsedCount,proto3" json:"can_be_used_count,omitempty"`
UsedCount int32 `protobuf:"varint,4,opt,name=used_count,json=usedCount,proto3" json:"used_count,omitempty"` ExpiresAt *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=expires_at,json=expiresAt,proto3" json:"expires_at,omitempty"`
ExpiresAt *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=expires_at,json=expiresAt,proto3" json:"expires_at,omitempty"` IsActive bool `protobuf:"varint,5,opt,name=is_active,json=isActive,proto3" json:"is_active,omitempty"`
IsActive bool `protobuf:"varint,6,opt,name=is_active,json=isActive,proto3" json:"is_active,omitempty"` CreatedAt *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
CreatedAt *timestamppb.Timestamp `protobuf:"bytes,7,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
unknownFields protoimpl.UnknownFields unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache sizeCache protoimpl.SizeCache
} }
@@ -250,13 +249,6 @@ func (x *GetInfoResponse) GetCanBeUsedCount() int32 {
return 0 return 0
} }
func (x *GetInfoResponse) GetUsedCount() int32 {
if x != nil {
return x.UsedCount
}
return 0
}
func (x *GetInfoResponse) GetExpiresAt() *timestamppb.Timestamp { func (x *GetInfoResponse) GetExpiresAt() *timestamppb.Timestamp {
if x != nil { if x != nil {
return x.ExpiresAt return x.ExpiresAt
@@ -293,18 +285,16 @@ const file_invite_invite_proto_rawDesc = "" +
"\n" + "\n" +
"expires_at\x18\x03 \x01(\v2\x1a.google.protobuf.TimestampR\texpiresAt\"$\n" + "expires_at\x18\x03 \x01(\v2\x1a.google.protobuf.TimestampR\texpiresAt\"$\n" +
"\x0eGetInfoRequest\x12\x12\n" + "\x0eGetInfoRequest\x12\x12\n" +
"\x04code\x18\x01 \x01(\tR\x04code\"\x9b\x02\n" + "\x04code\x18\x01 \x01(\tR\x04code\"\xfc\x01\n" +
"\x0fGetInfoResponse\x12\x12\n" + "\x0fGetInfoResponse\x12\x12\n" +
"\x04code\x18\x01 \x01(\tR\x04code\x12\x17\n" + "\x04code\x18\x01 \x01(\tR\x04code\x12\x17\n" +
"\auser_id\x18\x02 \x01(\x03R\x06userId\x12)\n" + "\auser_id\x18\x02 \x01(\x03R\x06userId\x12)\n" +
"\x11can_be_used_count\x18\x03 \x01(\x05R\x0ecanBeUsedCount\x12\x1d\n" + "\x11can_be_used_count\x18\x03 \x01(\x05R\x0ecanBeUsedCount\x129\n" +
"\n" + "\n" +
"used_count\x18\x04 \x01(\x05R\tusedCount\x129\n" + "expires_at\x18\x04 \x01(\v2\x1a.google.protobuf.TimestampR\texpiresAt\x12\x1b\n" +
"\tis_active\x18\x05 \x01(\bR\bisActive\x129\n" +
"\n" + "\n" +
"expires_at\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampR\texpiresAt\x12\x1b\n" + "created_at\x18\x06 \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt2\x8a\x01\n" +
"\tis_active\x18\x06 \x01(\bR\bisActive\x129\n" +
"\n" +
"created_at\x18\a \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt2\x8a\x01\n" +
"\rInviteService\x12=\n" + "\rInviteService\x12=\n" +
"\bGenerate\x12\x17.invite.GenerateRequest\x1a\x18.invite.GenerateResponse\x12:\n" + "\bGenerate\x12\x17.invite.GenerateRequest\x1a\x18.invite.GenerateResponse\x12:\n" +
"\aGetInfo\x12\x16.invite.GetInfoRequest\x1a\x17.invite.GetInfoResponseB>Z<git.techease.ru/Smart-search/smart-search-back/pkg/pb/inviteb\x06proto3" "\aGetInfo\x12\x16.invite.GetInfoRequest\x1a\x17.invite.GetInfoResponseB>Z<git.techease.ru/Smart-search/smart-search-back/pkg/pb/inviteb\x06proto3"

View File

@@ -172,7 +172,6 @@ func (s *IntegrationSuite) TestFullFlow_InviteCodeLifecycle() {
s.Equal(inviteCode, inviteInfoResp.Code) s.Equal(inviteCode, inviteInfoResp.Code)
s.Equal(userID, inviteInfoResp.UserId) s.Equal(userID, inviteInfoResp.UserId)
s.Equal(generateInviteResp.MaxUses, inviteInfoResp.CanBeUsedCount) s.Equal(generateInviteResp.MaxUses, inviteInfoResp.CanBeUsedCount)
s.Equal(int32(0), inviteInfoResp.UsedCount)
s.True(inviteInfoResp.IsActive) s.True(inviteInfoResp.IsActive)
logoutReq := &authpb.LogoutRequest{ logoutReq := &authpb.LogoutRequest{

View File

@@ -179,8 +179,8 @@ func (s *IntegrationSuite) createTestUser(email, password string) {
func (s *IntegrationSuite) createActiveInviteCode(canBeUsedCount int) int64 { func (s *IntegrationSuite) createActiveInviteCode(canBeUsedCount int) int64 {
var inviteCode int64 var inviteCode int64
query := ` query := `
INSERT INTO invite_codes (user_id, code, can_be_used_count, used_count, is_active, expires_at) INSERT INTO invite_codes (user_id, code, can_be_used_count, is_active, expires_at)
VALUES (1, FLOOR(100000 + RANDOM() * 900000)::bigint, $1, 0, true, NOW() + INTERVAL '30 days') VALUES (1, FLOOR(100000 + RANDOM() * 900000)::bigint, $1, true, NOW() + INTERVAL '30 days')
RETURNING code RETURNING code
` `
err := s.pool.QueryRow(s.ctx, query, canBeUsedCount).Scan(&inviteCode) err := s.pool.QueryRow(s.ctx, query, canBeUsedCount).Scan(&inviteCode)
@@ -191,8 +191,8 @@ func (s *IntegrationSuite) createActiveInviteCode(canBeUsedCount int) int64 {
func (s *IntegrationSuite) createExpiredInviteCode() int64 { func (s *IntegrationSuite) createExpiredInviteCode() int64 {
var inviteCode int64 var inviteCode int64
query := ` query := `
INSERT INTO invite_codes (user_id, code, can_be_used_count, used_count, is_active, expires_at) INSERT INTO invite_codes (user_id, code, can_be_used_count, is_active, expires_at)
VALUES (1, FLOOR(100000 + RANDOM() * 900000)::bigint, 5, 0, true, NOW() - INTERVAL '1 day') VALUES (1, FLOOR(100000 + RANDOM() * 900000)::bigint, 5, true, NOW() - INTERVAL '1 day')
RETURNING code RETURNING code
` `
err := s.pool.QueryRow(s.ctx, query).Scan(&inviteCode) err := s.pool.QueryRow(s.ctx, query).Scan(&inviteCode)

View File

@@ -96,7 +96,6 @@ func (s *IntegrationSuite) TestInviteHandler_GenerateAndGetInfoFlow() {
s.Equal(generateResp.Code, infoResp.Code) s.Equal(generateResp.Code, infoResp.Code)
s.Equal(validateResp.UserId, infoResp.UserId) s.Equal(validateResp.UserId, infoResp.UserId)
s.Equal(generateResp.MaxUses, infoResp.CanBeUsedCount) s.Equal(generateResp.MaxUses, infoResp.CanBeUsedCount)
s.Equal(int32(0), infoResp.UsedCount)
s.True(infoResp.IsActive) s.True(infoResp.IsActive)
} }

View File

@@ -11,7 +11,7 @@ import (
"github.com/google/uuid" "github.com/google/uuid"
) )
func (s *IntegrationSuite) TestRepository_InviteIncrementUsedCount() { func (s *IntegrationSuite) TestRepository_InviteDecrementCanBeUsedCount() {
inviteRepo := repository.NewInviteRepository(s.pool) inviteRepo := repository.NewInviteRepository(s.pool)
ctx := context.Background() ctx := context.Background()
@@ -28,12 +28,19 @@ func (s *IntegrationSuite) TestRepository_InviteIncrementUsedCount() {
err = inviteRepo.Create(ctx, invite) err = inviteRepo.Create(ctx, invite)
s.Require().NoError(err) s.Require().NoError(err)
err = inviteRepo.IncrementUsedCount(ctx, invite.Code) tx, err := s.pool.Begin(ctx)
s.Require().NoError(err)
defer func() { _ = tx.Rollback(ctx) }()
err = inviteRepo.DecrementCanBeUsedCountTx(ctx, tx, invite.Code)
s.NoError(err)
err = tx.Commit(ctx)
s.NoError(err) s.NoError(err)
found, err := inviteRepo.FindByCode(ctx, invite.Code) found, err := inviteRepo.FindByCode(ctx, invite.Code)
s.NoError(err) s.NoError(err)
s.Equal(1, found.UsedCount) s.Equal(9, found.CanBeUsedCount)
} }
func (s *IntegrationSuite) TestRepository_InviteDeactivateExpired() { func (s *IntegrationSuite) TestRepository_InviteDeactivateExpired() {