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

3
.gitignore vendored
View File

@@ -35,4 +35,5 @@ config/config.yaml
# Build artifacts
/bin/
/dist/
.gitea
.gitea
.gitea/workflows

View File

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

View File

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

View File

@@ -68,13 +68,6 @@ type InviteRepositoryMock struct {
afterGetUserInvitesCounter uint64
beforeGetUserInvitesCounter uint64
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
@@ -106,9 +99,6 @@ func NewInviteRepositoryMock(t minimock.Tester) *InviteRepositoryMock {
m.GetUserInvitesMock = mInviteRepositoryMockGetUserInvites{mock: m}
m.GetUserInvitesMock.callArgs = []*InviteRepositoryMockGetUserInvitesParams{}
m.IncrementUsedCountMock = mInviteRepositoryMockIncrementUsedCount{mock: m}
m.IncrementUsedCountMock.callArgs = []*InviteRepositoryMockIncrementUsedCountParams{}
t.Cleanup(m.MinimockFinish)
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
func (m *InviteRepositoryMock) MinimockFinish() {
m.finishOnce.Do(func() {
@@ -2902,8 +2550,6 @@ func (m *InviteRepositoryMock) MinimockFinish() {
m.MinimockFindByCodeInspect()
m.MinimockGetUserInvitesInspect()
m.MinimockIncrementUsedCountInspect()
}
})
}
@@ -2933,6 +2579,5 @@ func (m *InviteRepositoryMock) minimockDone() bool {
m.MinimockDecrementCanBeUsedCountTxDone() &&
m.MinimockFindActiveByCodeDone() &&
m.MinimockFindByCodeDone() &&
m.MinimockGetUserInvitesDone() &&
m.MinimockIncrementUsedCountDone()
m.MinimockGetUserInvitesDone()
}

View File

@@ -7,7 +7,6 @@ type InviteCode struct {
UserID int
Code int64
CanBeUsedCount int
UsedCount int
IsActive bool
CreatedAt 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
FindByCode(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
DeactivateExpired(ctx context.Context) (int, 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) {
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",
).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{}
err = r.pool.QueryRow(ctx, sqlQuery, args...).Scan(
&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) {
@@ -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) {
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",
).From("invite_codes").Where(sq.And{
sq.Eq{"code": code},
sq.Eq{"is_active": true},
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()
@@ -97,7 +97,7 @@ func (r *inviteRepository) FindActiveByCode(ctx context.Context, code int64) (*m
invite := &model.InviteCode{}
err = r.pool.QueryRow(ctx, sqlQuery, args...).Scan(
&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) {
@@ -110,28 +110,10 @@ func (r *inviteRepository) FindActiveByCode(ctx context.Context, code int64) (*m
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 {
query := r.qb.Update("invite_codes").
Set("used_count", sq.Expr("used_count + 1")).
Set("is_active", sq.Expr("CASE WHEN used_count + 1 >= can_be_used_count THEN false ELSE is_active END")).
Set("can_be_used_count", sq.Expr("can_be_used_count - 1")).
Set("is_active", sq.Expr("CASE WHEN can_be_used_count - 1 <= 0 THEN false ELSE is_active END")).
Where(sq.Eq{"code": code})
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) {
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",
).From("invite_codes").
Where(sq.Eq{"user_id": userID}).
@@ -192,7 +174,7 @@ func (r *inviteRepository) GetUserInvites(ctx context.Context, userID int) ([]*m
invite := &model.InviteCode{}
err := rows.Scan(
&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 {
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"`
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"`
UsedCount int32 `protobuf:"varint,4,opt,name=used_count,json=usedCount,proto3" json:"used_count,omitempty"`
ExpiresAt *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=expires_at,json=expiresAt,proto3" json:"expires_at,omitempty"`
IsActive bool `protobuf:"varint,6,opt,name=is_active,json=isActive,proto3" json:"is_active,omitempty"`
CreatedAt *timestamppb.Timestamp `protobuf:"bytes,7,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
ExpiresAt *timestamppb.Timestamp `protobuf:"bytes,4,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"`
CreatedAt *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@@ -250,13 +249,6 @@ func (x *GetInfoResponse) GetCanBeUsedCount() int32 {
return 0
}
func (x *GetInfoResponse) GetUsedCount() int32 {
if x != nil {
return x.UsedCount
}
return 0
}
func (x *GetInfoResponse) GetExpiresAt() *timestamppb.Timestamp {
if x != nil {
return x.ExpiresAt
@@ -293,18 +285,16 @@ const file_invite_invite_proto_rawDesc = "" +
"\n" +
"expires_at\x18\x03 \x01(\v2\x1a.google.protobuf.TimestampR\texpiresAt\"$\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" +
"\x04code\x18\x01 \x01(\tR\x04code\x12\x17\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" +
"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" +
"expires_at\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampR\texpiresAt\x12\x1b\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" +
"created_at\x18\x06 \x01(\v2\x1a.google.protobuf.TimestampR\tcreatedAt2\x8a\x01\n" +
"\rInviteService\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"

View File

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

View File

@@ -179,8 +179,8 @@ func (s *IntegrationSuite) createTestUser(email, password string) {
func (s *IntegrationSuite) createActiveInviteCode(canBeUsedCount int) int64 {
var inviteCode int64
query := `
INSERT INTO invite_codes (user_id, code, can_be_used_count, used_count, is_active, expires_at)
VALUES (1, FLOOR(100000 + RANDOM() * 900000)::bigint, $1, 0, true, NOW() + INTERVAL '30 days')
INSERT INTO invite_codes (user_id, code, can_be_used_count, is_active, expires_at)
VALUES (1, FLOOR(100000 + RANDOM() * 900000)::bigint, $1, true, NOW() + INTERVAL '30 days')
RETURNING code
`
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 {
var inviteCode int64
query := `
INSERT INTO invite_codes (user_id, code, can_be_used_count, used_count, is_active, expires_at)
VALUES (1, FLOOR(100000 + RANDOM() * 900000)::bigint, 5, 0, true, NOW() - INTERVAL '1 day')
INSERT INTO invite_codes (user_id, code, can_be_used_count, is_active, expires_at)
VALUES (1, FLOOR(100000 + RANDOM() * 900000)::bigint, 5, true, NOW() - INTERVAL '1 day')
RETURNING code
`
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(validateResp.UserId, infoResp.UserId)
s.Equal(generateResp.MaxUses, infoResp.CanBeUsedCount)
s.Equal(int32(0), infoResp.UsedCount)
s.True(infoResp.IsActive)
}

View File

@@ -11,7 +11,7 @@ import (
"github.com/google/uuid"
)
func (s *IntegrationSuite) TestRepository_InviteIncrementUsedCount() {
func (s *IntegrationSuite) TestRepository_InviteDecrementCanBeUsedCount() {
inviteRepo := repository.NewInviteRepository(s.pool)
ctx := context.Background()
@@ -28,12 +28,19 @@ func (s *IntegrationSuite) TestRepository_InviteIncrementUsedCount() {
err = inviteRepo.Create(ctx, invite)
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)
found, err := inviteRepo.FindByCode(ctx, invite.Code)
s.NoError(err)
s.Equal(1, found.UsedCount)
s.Equal(9, found.CanBeUsedCount)
}
func (s *IntegrationSuite) TestRepository_InviteDeactivateExpired() {