244 lines
6.3 KiB
Go
244 lines
6.3 KiB
Go
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"
|
||
"google.golang.org/grpc/codes"
|
||
"google.golang.org/grpc/status"
|
||
)
|
||
|
||
func (s *IntegrationSuite) TestConcurrentOwnership_User2TriesApproveTZ_WhileUser1Creates() {
|
||
email1, password1, _ := s.createUniqueTestUser("owner1", 1000.0)
|
||
email2, password2, _ := s.createUniqueTestUser("attacker1", 1000.0)
|
||
|
||
login1, err := s.authClient.Login(s.ctx, &authpb.LoginRequest{
|
||
Email: email1,
|
||
Password: password1,
|
||
Ip: "127.0.0.1",
|
||
UserAgent: "test-agent",
|
||
})
|
||
s.Require().NoError(err)
|
||
|
||
validate1, err := s.authClient.Validate(s.ctx, &authpb.ValidateRequest{
|
||
AccessToken: login1.AccessToken,
|
||
})
|
||
s.Require().NoError(err)
|
||
user1ID := validate1.UserId
|
||
|
||
login2, err := s.authClient.Login(s.ctx, &authpb.LoginRequest{
|
||
Email: email2,
|
||
Password: password2,
|
||
Ip: "127.0.0.1",
|
||
UserAgent: "test-agent",
|
||
})
|
||
s.Require().NoError(err)
|
||
|
||
validate2, err := s.authClient.Validate(s.ctx, &authpb.ValidateRequest{
|
||
AccessToken: login2.AccessToken,
|
||
})
|
||
s.Require().NoError(err)
|
||
user2ID := validate2.UserId
|
||
|
||
createResp, err := s.requestClient.CreateTZ(s.ctx, &requestpb.CreateTZRequest{
|
||
UserId: user1ID,
|
||
RequestTxt: "Request от User1 для теста ownership",
|
||
})
|
||
s.Require().NoError(err)
|
||
requestID := createResp.RequestId
|
||
|
||
var wg sync.WaitGroup
|
||
var user1Success, user2Denied int32
|
||
|
||
startBarrier := make(chan struct{})
|
||
|
||
wg.Add(1)
|
||
go func() {
|
||
defer wg.Done()
|
||
<-startBarrier
|
||
|
||
_, err := s.requestClient.ApproveTZ(s.ctx, &requestpb.ApproveTZRequest{
|
||
RequestId: requestID,
|
||
FinalTz: "User1 approves",
|
||
UserId: user1ID,
|
||
})
|
||
if err == nil {
|
||
atomic.AddInt32(&user1Success, 1)
|
||
}
|
||
}()
|
||
|
||
for i := 0; i < 5; i++ {
|
||
wg.Add(1)
|
||
go func() {
|
||
defer wg.Done()
|
||
<-startBarrier
|
||
|
||
_, err := s.requestClient.ApproveTZ(s.ctx, &requestpb.ApproveTZRequest{
|
||
RequestId: requestID,
|
||
FinalTz: "User2 tries to approve",
|
||
UserId: user2ID,
|
||
})
|
||
if err != nil {
|
||
st, ok := status.FromError(err)
|
||
if ok && st.Code() == codes.PermissionDenied {
|
||
atomic.AddInt32(&user2Denied, 1)
|
||
}
|
||
}
|
||
}()
|
||
}
|
||
|
||
close(startBarrier)
|
||
wg.Wait()
|
||
|
||
s.T().Logf("User1 success: %d, User2 denied: %d", user1Success, user2Denied)
|
||
|
||
s.Equal(int32(5), user2Denied,
|
||
"Все попытки User2 должны быть отклонены с PermissionDenied")
|
||
}
|
||
|
||
func (s *IntegrationSuite) TestConcurrentOwnership_ConcurrentApproveTZ_SameRequest() {
|
||
email, password, _ := s.createUniqueTestUser("concurrent_approve", 1000.0)
|
||
|
||
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)
|
||
userID := validateResp.UserId
|
||
|
||
createResp, err := s.requestClient.CreateTZ(s.ctx, &requestpb.CreateTZRequest{
|
||
UserId: userID,
|
||
RequestTxt: "Request для concurrent ApproveTZ",
|
||
})
|
||
s.Require().NoError(err)
|
||
requestID := createResp.RequestId
|
||
|
||
var wg sync.WaitGroup
|
||
var successCount int32
|
||
goroutines := 5
|
||
|
||
startBarrier := make(chan struct{})
|
||
|
||
for i := 0; i < goroutines; i++ {
|
||
wg.Add(1)
|
||
go func(idx int) {
|
||
defer wg.Done()
|
||
<-startBarrier
|
||
|
||
_, err := s.requestClient.ApproveTZ(s.ctx, &requestpb.ApproveTZRequest{
|
||
RequestId: requestID,
|
||
FinalTz: "Concurrent approve attempt",
|
||
UserId: userID,
|
||
})
|
||
if err == nil {
|
||
atomic.AddInt32(&successCount, 1)
|
||
}
|
||
}(i)
|
||
}
|
||
|
||
close(startBarrier)
|
||
wg.Wait()
|
||
|
||
s.T().Logf("Concurrent ApproveTZ success count: %d", successCount)
|
||
|
||
suppliersCount := s.getRequestSuppliersCount(requestID)
|
||
s.T().Logf("Total suppliers for request: %d", suppliersCount)
|
||
}
|
||
|
||
func (s *IntegrationSuite) TestConcurrentOwnership_SessionIsolation_AfterLogout() {
|
||
email1, password1, _ := s.createUniqueTestUser("session_iso1", 1000.0)
|
||
email2, password2, _ := s.createUniqueTestUser("session_iso2", 1000.0)
|
||
|
||
login1, err := s.authClient.Login(s.ctx, &authpb.LoginRequest{
|
||
Email: email1,
|
||
Password: password1,
|
||
Ip: "127.0.0.1",
|
||
UserAgent: "test-agent",
|
||
})
|
||
s.Require().NoError(err)
|
||
user1Token := login1.AccessToken
|
||
|
||
validate1, err := s.authClient.Validate(s.ctx, &authpb.ValidateRequest{
|
||
AccessToken: user1Token,
|
||
})
|
||
s.Require().NoError(err)
|
||
s.True(validate1.Valid)
|
||
|
||
login2, err := s.authClient.Login(s.ctx, &authpb.LoginRequest{
|
||
Email: email2,
|
||
Password: password2,
|
||
Ip: "127.0.0.1",
|
||
UserAgent: "test-agent",
|
||
})
|
||
s.Require().NoError(err)
|
||
user2Token := login2.AccessToken
|
||
|
||
_, err = s.authClient.Logout(s.ctx, &authpb.LogoutRequest{
|
||
AccessToken: user1Token,
|
||
})
|
||
s.Require().NoError(err)
|
||
|
||
validate1After, err := s.authClient.Validate(s.ctx, &authpb.ValidateRequest{
|
||
AccessToken: user1Token,
|
||
})
|
||
s.NoError(err)
|
||
s.False(validate1After.Valid,
|
||
"Токен User1 должен быть невалиден после logout")
|
||
|
||
validate2After, err := s.authClient.Validate(s.ctx, &authpb.ValidateRequest{
|
||
AccessToken: user2Token,
|
||
})
|
||
s.NoError(err)
|
||
s.True(validate2After.Valid,
|
||
"Токен User2 должен оставаться валидным после logout User1")
|
||
|
||
var wg sync.WaitGroup
|
||
var user1Invalid, user2Valid int32
|
||
goroutines := 10
|
||
|
||
startBarrier := make(chan struct{})
|
||
|
||
for i := 0; i < goroutines; i++ {
|
||
wg.Add(1)
|
||
go func(idx int) {
|
||
defer wg.Done()
|
||
<-startBarrier
|
||
|
||
if idx%2 == 0 {
|
||
resp, err := s.authClient.Validate(s.ctx, &authpb.ValidateRequest{
|
||
AccessToken: user1Token,
|
||
})
|
||
if err == nil && !resp.Valid {
|
||
atomic.AddInt32(&user1Invalid, 1)
|
||
}
|
||
} else {
|
||
resp, err := s.authClient.Validate(s.ctx, &authpb.ValidateRequest{
|
||
AccessToken: user2Token,
|
||
})
|
||
if err == nil && resp.Valid {
|
||
atomic.AddInt32(&user2Valid, 1)
|
||
}
|
||
}
|
||
}(i)
|
||
}
|
||
|
||
close(startBarrier)
|
||
wg.Wait()
|
||
|
||
s.T().Logf("Session isolation - User1 invalid: %d, User2 valid: %d", user1Invalid, user2Valid)
|
||
|
||
s.Equal(int32(goroutines/2), user1Invalid,
|
||
"Все проверки токена User1 должны показать invalid")
|
||
s.Equal(int32(goroutines/2), user2Valid,
|
||
"Все проверки токена User2 должны показать valid")
|
||
}
|