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") }