Files
smart-search-back/tests/concurrent_request_test.go
vallyenfail d3d004569e
Some checks failed
Deploy Smart Search Backend Test / deploy (push) Failing after 1m31s
add service
2026-01-19 23:50:42 +03:00

220 lines
6.1 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package tests
import (
"sync"
"sync/atomic"
authpb "git.techease.ru/Smart-search/smart-search-back/pkg/pb/auth"
requestpb "git.techease.ru/Smart-search/smart-search-back/pkg/pb/request"
)
func (s *IntegrationSuite) TestConcurrentRequest_CreateTZ_LimitedBalance() {
initialBalance := 50.0
email, password, userID := s.createUniqueTestUser("limited_balance_tz", initialBalance)
loginResp, err := s.authClient.Login(s.ctx, &authpb.LoginRequest{
Email: email,
Password: password,
Ip: "127.0.0.1",
UserAgent: "test-agent",
})
s.Require().NoError(err)
validateResp, err := s.authClient.Validate(s.ctx, &authpb.ValidateRequest{
AccessToken: loginResp.AccessToken,
})
s.Require().NoError(err)
var wg sync.WaitGroup
var successCount int32
var errorCount int32
goroutines := 20
startBarrier := make(chan struct{})
for i := 0; i < goroutines; i++ {
wg.Add(1)
go func(idx int) {
defer wg.Done()
<-startBarrier
_, err := s.requestClient.CreateTZ(s.ctx, &requestpb.CreateTZRequest{
UserId: validateResp.UserId,
RequestTxt: "Параллельный CreateTZ с ограниченным балансом",
})
if err == nil {
atomic.AddInt32(&successCount, 1)
} else {
atomic.AddInt32(&errorCount, 1)
}
}(i)
}
close(startBarrier)
wg.Wait()
s.T().Logf("CreateTZ with limited balance - Success: %d, Errors: %d", successCount, errorCount)
finalBalance := s.getUserBalance(userID)
s.T().Logf("Final balance: %.4f (initial: %.4f)", finalBalance, initialBalance)
s.GreaterOrEqual(finalBalance, 0.0,
"Баланс не должен быть отрицательным")
var requestsWithTZ int
err = s.pool.QueryRow(s.ctx,
"SELECT COUNT(*) FROM requests_for_suppliers WHERE user_id = $1 AND generated_tz = true",
userID,
).Scan(&requestsWithTZ)
s.NoError(err)
s.T().Logf("Requests with generated TZ: %d", requestsWithTZ)
s.GreaterOrEqual(requestsWithTZ, 0,
"Количество запросов с TZ должно быть >= 0")
s.LessOrEqual(requestsWithTZ, int(successCount),
"Количество запросов с TZ не должно превышать успешные операции")
}
func (s *IntegrationSuite) TestConcurrentRequest_MultipleUsers_CreateTZ() {
user1Email, user1Pass, user1ID := s.createUniqueTestUser("multi_user1", 500.0)
user2Email, user2Pass, user2ID := s.createUniqueTestUser("multi_user2", 500.0)
user3Email, user3Pass, user3ID := s.createUniqueTestUser("multi_user3", 500.0)
login1, err := s.authClient.Login(s.ctx, &authpb.LoginRequest{
Email: user1Email, Password: user1Pass, Ip: "127.0.0.1", UserAgent: "test",
})
s.Require().NoError(err)
validate1, _ := s.authClient.Validate(s.ctx, &authpb.ValidateRequest{AccessToken: login1.AccessToken})
login2, err := s.authClient.Login(s.ctx, &authpb.LoginRequest{
Email: user2Email, Password: user2Pass, Ip: "127.0.0.1", UserAgent: "test",
})
s.Require().NoError(err)
validate2, _ := s.authClient.Validate(s.ctx, &authpb.ValidateRequest{AccessToken: login2.AccessToken})
login3, err := s.authClient.Login(s.ctx, &authpb.LoginRequest{
Email: user3Email, Password: user3Pass, Ip: "127.0.0.1", UserAgent: "test",
})
s.Require().NoError(err)
validate3, _ := s.authClient.Validate(s.ctx, &authpb.ValidateRequest{AccessToken: login3.AccessToken})
users := []struct {
userID int64
id int
}{
{validate1.UserId, user1ID},
{validate2.UserId, user2ID},
{validate3.UserId, user3ID},
}
var wg sync.WaitGroup
var totalSuccess int32
requestsPerUser := 5
startBarrier := make(chan struct{})
for _, user := range users {
for i := 0; i < requestsPerUser; i++ {
wg.Add(1)
go func(uid int64) {
defer wg.Done()
<-startBarrier
_, err := s.requestClient.CreateTZ(s.ctx, &requestpb.CreateTZRequest{
UserId: uid,
RequestTxt: "Multi-user concurrent CreateTZ",
})
if err == nil {
atomic.AddInt32(&totalSuccess, 1)
}
}(user.userID)
}
}
close(startBarrier)
wg.Wait()
s.T().Logf("Multi-user CreateTZ total success: %d", totalSuccess)
for _, user := range users {
balance := s.getUserBalance(user.id)
s.T().Logf("User %d final balance: %.4f", user.id, balance)
s.GreaterOrEqual(balance, 0.0,
"Баланс пользователя %d не должен быть отрицательным", user.id)
}
}
func (s *IntegrationSuite) TestConcurrentRequest_BalanceDeduction_Consistency() {
initialBalance := 1000.0
email, password, userID := s.createUniqueTestUser("balance_consistency", initialBalance)
loginResp, err := s.authClient.Login(s.ctx, &authpb.LoginRequest{
Email: email,
Password: password,
Ip: "127.0.0.1",
UserAgent: "test-agent",
})
s.Require().NoError(err)
validateResp, err := s.authClient.Validate(s.ctx, &authpb.ValidateRequest{
AccessToken: loginResp.AccessToken,
})
s.Require().NoError(err)
var wg sync.WaitGroup
var successCount int32
goroutines := 10
startBarrier := make(chan struct{})
for i := 0; i < goroutines; i++ {
wg.Add(1)
go func(idx int) {
defer wg.Done()
<-startBarrier
_, err := s.requestClient.CreateTZ(s.ctx, &requestpb.CreateTZRequest{
UserId: validateResp.UserId,
RequestTxt: "Balance consistency test",
})
if err == nil {
atomic.AddInt32(&successCount, 1)
}
}(i)
}
close(startBarrier)
wg.Wait()
s.T().Logf("Successful CreateTZ operations: %d", successCount)
finalBalance := s.getUserBalance(userID)
balanceSpent := initialBalance - finalBalance
s.T().Logf("Balance spent: %.4f", balanceSpent)
var totalTokenCost float64
err = s.pool.QueryRow(s.ctx, `
SELECT COALESCE(SUM(tu.token_cost), 0)
FROM request_token_usage tu
JOIN requests_for_suppliers r ON tu.request_id = r.id
WHERE r.user_id = $1
`, userID).Scan(&totalTokenCost)
s.NoError(err)
s.T().Logf("Total token cost from DB: %.4f", totalTokenCost)
s.GreaterOrEqual(finalBalance, 0.0,
"Баланс не должен быть отрицательным")
if totalTokenCost > 0 {
tolerance := 0.01
s.InDelta(totalTokenCost, balanceSpent, tolerance,
"Сумма token_cost должна соответствовать списанному балансу")
}
}