Skip to content

Conversation

@YaroslavRosnovskyi
Copy link
Collaborator

@YaroslavRosnovskyi YaroslavRosnovskyi commented Feb 11, 2025

Summary of issue

Current version of recaptcha uses checkbox that enforces user to click it

Summary of change

Updated captcha to version 3 therefore it authomatically cheks if user is not a robot

Summary by CodeRabbit

  • Authentication & User Management
    • Google login now accepts a structured token request for improved authentication.
    • Logout logic has been streamlined and user retrieval now operates via email.
    • New methods added for checking user existence by username and retrieving other users by username.
    • Password reset functionality has been enhanced with additional validation scenarios.
    • User deletion and update functionalities have been refined with comprehensive test coverage.
    • New functionality introduced for checking user existence by username.
  • Analytics
    • The endpoint for creating statistic records has been removed.
    • Asynchronous processing for updates has been enhanced.
    • New functionality added to check the existence of statistic records by QR ID.
    • Comprehensive tests have been introduced for various scenarios related to statistic records.
  • Instagram
    • Endpoints for retrieving Instagram posts have been removed.
  • Configuration & Services
    • Email settings and reCAPTCHA keys have been updated.
    • New service registration enhances external dependency management.
    • Mock services for testing have been introduced to improve reliability in unit tests.

@coderabbitai
Copy link

coderabbitai bot commented Feb 11, 2025

Walkthrough

This pull request implements significant changes across authentication, analytics, user management, and testing modules. New components for Google sign-in have been added, including a DTO, interface, and service that validate tokens asynchronously. Legacy analytics commands and Instagram endpoints were removed or refactored to adopt async patterns and reduce dependencies. User handlers have been updated to use email rather than username, with corresponding adjustments in controllers and tests. Additionally, project files, configuration settings, helpers, and test suites have been reorganized and refactored for improved consistency and reliability.

Changes

File(s) Change Summary
Google Authentication
(GoogleLoginRequest.cs, IGoogleService.cs, GoogleService.cs, LoginGoogleHandler.cs, AuthController)
Added a new DTO, interface, and service to encapsulate and validate Google ID tokens. Handlers now use the structured GoogleLoginRequest and delegate token validation to IGoogleService.
Analytics & Statistic Records
(CreateStatisticRecordCommand/Handler, DeleteStatisticRecordHandler.cs, ExistStatisticRecordByQrIdHandler.cs, GetAllStatisticRecordsQuery.cs, UpdateCountStatisticRecordHandler.cs, StatisticRecordController, etc.)
Removed obsolete commands/handlers, refactored deletion and update processes to use async repository methods, and updated query signatures. Test clients and extractors for statistic records were also added.
Authentication Logout
(LogoutCommand.cs, LogoutHandler.cs, AuthenticationClient.cs)
Updated the logout command to a parameterless record and added IHttpContextAccessor in the handler to obtain the current user; client methods were updated accordingly.
User Management
(GetByEmailHandler/Query, GetOtherUserByUserNameHandler, UsersController, ForgotPasswordHandler, UpdateUserHandler, UpdateForgotPasswordHandler, etc.)
Refactored user retrieval to use email instead of username, simplified handler constructor parameters, and updated error handling and mapping. Tests and namespaces were adjusted to reflect these changes.
Instagram
(InstagramService.cs, InstagramController.cs, GetAllPostsHandler/Query.cs)
Removed Instagram post query endpoints and controllers; refactored InstagramService to use IHttpClientFactory for improved HTTP client management.
Project & Configuration
(Streetcode.BLL.csproj, appsettings.IntegrationTests.json, appsettings.Local.json, AuthConstants.cs)
Modified project files by removing Instagram DTO references and adding a new GoogleLogin folder; updated ReCaptcha keys, added EmailConfiguration, and introduced normalized email/username fields for testing.
Utilities & Helpers
(HttpContextHelper.cs, UserHelper.cs, StatisticRecordValidator.cs, BaseUserValidator.cs)
Updated helper methods to return non‑nullable strings; refactored user helper routines; removed obsolete validators and simplified error messages in user validation.
Test Suite Enhancements
(Controller tests, client classes, custom web factory, extractor classes, unit tests)
Expanded integration and unit test coverage by introducing new client classes, test extractors, and mock setups. Various test projects were realigned with updated dependencies and naming conventions.
Miscellaneous & Refactoring
(BaseClient.cs, SqlDbHelper.cs, formatting updates)
Streamlined base client method signatures (including reduced “this” usage), introduced concurrent lock management in SQL helpers, and performed general code cleanup across files for consistency.

Sequence Diagram(s)

sequenceDiagram
    participant U as User
    participant AC as AuthController
    participant GS as GoogleService (via IGoogleService)
    participant UM as UserManager
    U->>AC: Sends GoogleLogin(GoogleLoginRequest)
    AC->>GS: ValidateGoogleToken(idToken)
    GS-->>AC: Returns token payload
    AC->>UM: Retrieves user data
    UM-->>AC: Returns user info
    AC-->>U: Responds with auth token & user details
Loading

Poem

In the realm of code, fresh features arise,
New gateways to Google, where the token flies.
Analytics streamlined, old paths put to rest,
Users now shine by email, at their very best.
Tests and helpers sing in a symphony so bright,
A dance of clean code, turning challenges to light.
🚀✨ Happy refactoring!


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 29

🔭 Outside diff range comments (11)
Streetcode/Streetcode.BLL/Streetcode.BLL.csproj (1)

385-387: ⚠️ Potential issue

Fix the empty Generator tag for FailedToCreateSharedResource.en-US.resx.

The Generator tag is empty, while other similar resource files use ResXFileCodeGenerator. This could cause build issues.

Apply this diff to fix the Generator tag:

    <EmbeddedResource Update="Resources\SharedResource.FailedToCreateSharedResource.en-US.resx">
-      <Generator></Generator>
+      <Generator>ResXFileCodeGenerator</Generator>
    </EmbeddedResource>
Streetcode/Streetcode.BLL/MediatR/Users/UpdateForgotPassword/UpdateForgotPasswordHandler.cs (3)

38-39: 🛠️ Refactor suggestion

Add validation for token format before decoding.

The URL decoding of username and token should be protected against malformed input that could cause exceptions.

-            var decodedUserName = Uri.UnescapeDataString(request.UpdateForgotPasswordDto.Username);
-            var decodedToken = Uri.UnescapeDataString(request.UpdateForgotPasswordDto.Token);
+            var decodedUserName = Uri.TryCreate(
+                request.UpdateForgotPasswordDto.Username,
+                UriKind.RelativeOrAbsolute,
+                out _)
+                    ? Uri.UnescapeDataString(request.UpdateForgotPasswordDto.Username)
+                    : request.UpdateForgotPasswordDto.Username;
+
+            var decodedToken = Uri.TryCreate(
+                request.UpdateForgotPasswordDto.Token,
+                UriKind.RelativeOrAbsolute,
+                out _)
+                    ? Uri.UnescapeDataString(request.UpdateForgotPasswordDto.Token)
+                    : request.UpdateForgotPasswordDto.Token;

50-53: 🛠️ Refactor suggestion

Consider adding token expiration validation.

The password reset flow should validate that the token hasn't expired before attempting to reset the password. This is a security best practice to limit the window of vulnerability.

+            // Verify token hasn't expired
+            var isValidToken = await _userManager.VerifyUserTokenAsync(
+                user,
+                _userManager.Options.Tokens.PasswordResetTokenProvider,
+                "ResetPassword",
+                decodedToken);
+
+            if (!isValidToken)
+            {
+                string errorMessage = _localizer["PasswordResetTokenExpired"];
+                _logger.LogError(request, errorMessage);
+                return Result.Fail(errorMessage);
+            }
+
             var result = await _userManager.ResetPasswordAsync(

27-69: 💡 Verification agent

🧩 Analysis chain

Consider implementing rate limiting for password reset attempts.

To prevent brute force attacks, consider implementing rate limiting on password reset attempts. This could be done through a middleware or by tracking attempts in a distributed cache.

Let me check if there's any existing rate limiting implementation:


🏁 Script executed:

#!/bin/bash
# Search for rate limiting implementations
rg -i "ratelimit" -A 5
rg -i "throttle" -A 5

Length of output: 7315


Rate Limiting Not Applied to Password Reset Endpoint

While our codebase does implement rate limiting for email-related endpoints (as seen in the EmailController and middleware configuration), there is no rate limiting logic applied to the password reset functionality in UpdateForgotPasswordHandler.cs. This omission could leave the forgot password workflow vulnerable to brute force attempts.

  • Observation: The search results confirm that rate limiting is enabled for email endpoints, but there are no attributes or middleware configurations limiting password reset attempts.
  • Suggestion: Consider applying similar rate limiting (e.g., using the [EnableRateLimiting] attribute or middleware configuration in Program.cs) to this endpoint to protect against potential abuse.
Streetcode/Streetcode.BLL/MediatR/Users/ForgotPassword/ForgotPasswordHandler.cs (1)

44-84: 💡 Verification agent

🧩 Analysis chain

Consider additional security measures for the password reset flow.

While the current implementation is solid, consider these security enhancements:

  1. Add token expiration time limit
  2. Implement rate limiting for password reset requests
  3. Add more specific success/failure messages without exposing sensitive information

Example implementation for token expiration:

 var token = await _userManager.GeneratePasswordResetTokenAsync(user);
+// Configure token lifetime in startup
+_userManager.Options.Tokens.PasswordResetTokenProvider = TokenOptions.DefaultProvider;
+_userManager.Options.Tokens.PasswordResetTokenLifespan = TimeSpan.FromHours(24);

Let's check if rate limiting is already implemented elsewhere:


🏁 Script executed:

#!/bin/bash
# Search for rate limiting implementations
rg -i "rate.*limit|throttle" --type cs

Length of output: 1859


Security Enhancements for Forgot Password Flow

  • Token Expiration: There’s no explicit configuration for the password reset token’s lifespan in the ForgotPassword flow. Consider setting a token expiration (e.g., 24 hours) via the UserManager’s options in your startup configuration to reduce the window for token misuse.
  • Rate Limiting: The repository shows that rate limiting is implemented—specifically in the WebAPI’s email endpoint (with an "EmailRateLimit" policy). Ensure that the Forgot Password endpoint (which consumes this MediatR command) is similarly protected to prevent abuse.
  • Message Specificity: The current success/failure messages use localized strings and avoid exposing sensitive details, but you might revisit them to ensure they balance user clarity with security best practices.

Overall, while rate limiting is in place, explicitly configuring token expiration would be a valuable security enhancement.

Streetcode/Streetcode.WebApi/appsettings.IntegrationTests.json (1)

2-4: 🛠️ Refactor suggestion

Secure database connection string.

The connection string contains sensitive credentials. Consider using environment variables or secure secrets management for the database credentials as well.

  "ConnectionStrings": {
-    "DefaultConnection": "Server=localhost,1455;Database=Streetcode_IntegrationTests_Db;User Id=sa;Password=sdlLKMCD234!@#lkcmds;MultipleActiveResultSets=True;TrustServerCertificate=true"
+    "DefaultConnection": "Server=${DB_SERVER};Database=${DB_NAME};User Id=${DB_USER};Password=${DB_PASSWORD};MultipleActiveResultSets=True;TrustServerCertificate=true"
  }
Streetcode/Streetcode.XIntegrationTest/ControllerTests/Utils/Extracter/Authentication/UserExtracter.cs (1)

28-30: ⚠️ Potential issue

Fix incorrect role assignment.

The role assignment is using a hardcoded User role instead of the provided roleName parameter.

-IdentityRole role = RoleExtracter.Extract(nameof(UserRole.User));
+IdentityRole role = RoleExtracter.Extract(roleName);
 RoleExtracter.AddUserRole(testUser.Id, role.Id);
Streetcode/Streetcode.BLL/MediatR/Users/GetByEmail/GetByEmailHandler.cs (1)

48-48: ⚠️ Potential issue

Update error message to reference email instead of username.

The error message still references username despite the transition to email-based queries.

-            string errorMessage = _localizer["UserWithSuchUsernameNotExists"];
+            string errorMessage = _localizer["UserWithSuchEmailNotExists"];
Streetcode/Streetcode.WebApi/Controllers/Authentication/AuthController.cs (2)

40-53: ⚠️ Potential issue

Verify authentication state in Logout method

The removal of user ID validation from claims reduces security checks. Consider maintaining authentication validation to prevent unauthorized logout attempts.

 [Authorize]
 [HttpPost]
 [ProducesResponseType(StatusCodes.Status200OK)]
 public async Task<IActionResult> Logout()
 {
+    var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
+    if (string.IsNullOrEmpty(userId))
+    {
+        return Unauthorized("User not authenticated");
+    }
     var result = await Mediator.Send(new LogoutCommand());
     if (result.IsFailed)
     {
         return BadRequest(result.Errors.First().Message);
     }
     return Ok("Logout successful. Refresh token invalidated.");
 }

55-67: 🛠️ Refactor suggestion

Enhance Google login security with reCAPTCHA v3

Since we're updating to reCAPTCHA v3, consider adding score validation to prevent automated attacks.

 [HttpPost]
 [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(LoginResponseDTO))]
 public async Task<IActionResult> GoogleLogin(GoogleLoginRequest googleLoginRequest)
 {
+    if (googleLoginRequest?.IdToken == null)
+    {
+        return BadRequest("Invalid request");
+    }
+    // Verify reCAPTCHA v3 score
+    if (!await _recaptchaService.ValidateToken(googleLoginRequest.RecaptchaToken, 0.5))
+    {
+        return BadRequest("Invalid reCAPTCHA token");
+    }
     var result = await Mediator.Send(new LoginGoogleQuery(googleLoginRequest.IdToken));
     if (result.IsSuccess)
     {
         return Ok(result.Value);
     }
     return Unauthorized(new { message = result.Errors.FirstOrDefault()?.Message });
 }
Streetcode/Streetcode.BLL/MediatR/Authentication/LoginGoogle/LoginGoogleHandler.cs (1)

113-117: 🛠️ Refactor suggestion

Enhance error handling with specific exception types.

The catch block catches all exceptions, which could mask specific issues. Consider handling different types of exceptions separately.

-        catch (Exception ex)
+        catch (InvalidJwtException ex)
+        {
+            _logger.LogError(request, $"Invalid JWT token: {ex.Message}");
+            return Result.Fail($"Invalid Google Token format: {ex.Message}");
+        }
+        catch (GoogleJsonWebSignature.InvalidTokenException ex)
+        {
+            _logger.LogError(request, $"Token validation failed: {ex.Message}");
+            return Result.Fail("Invalid or expired Google token");
+        }
+        catch (Exception ex)
         {
             _logger.LogError(request, ex.Message);
-            return Result.Fail($"Invalid Google Token: {ex.Message}");
+            return Result.Fail("An unexpected error occurred during authentication");
         }
🧹 Nitpick comments (75)
Streetcode/Streetcode.BLL/MediatR/Analytics/StatisticRecord/Delete/DeleteStatisticRecordHandler.cs (1)

29-55: Consider enhancing error handling and logging.

While the implementation is generally solid, consider these improvements for better maintainability and debugging:

  1. Add transaction scope for atomic operations
  2. Include more context in error logs (e.g., QR ID value)
  3. Consider using a custom Result type for domain-specific errors

Here's a suggested enhancement:

 public async Task<Result<Unit>> Handle(DeleteStatisticRecordCommand request, CancellationToken cancellationToken)
 {
+    using var transaction = await _repositoryWrapper.BeginTransactionAsync();
     try
     {
         var statRecord = await _repositoryWrapper.StatisticRecordRepository
             .GetFirstOrDefaultAsync(predicate: (sr) => sr.QrId == request.qrId);

         if (statRecord is null)
         {
             string errorMsg = _stringLocalizerCannotFind["CannotFindRecordWithQrId"].Value;
-            _logger.LogError(request, errorMsg);
+            _logger.LogError(request, $"{errorMsg} QrId: {request.qrId}");
             return Result.Fail(new Error(errorMsg));
         }

         _repositoryWrapper.StatisticRecordRepository.Delete(statRecord);

         var resultIsSuccess = await _repositoryWrapper.SaveChangesAsync() > 0;

         if (resultIsSuccess)
         {
+            await transaction.CommitAsync();
             return Result.Ok(Unit.Value);
         }
         else
         {
             string errorMsg = _stringLocalizerFailedToDelete["FailedToDeleteTheRecord"].Value;
-            _logger.LogError(request, errorMsg);
+            _logger.LogError(request, $"{errorMsg} QrId: {request.qrId}");
+            await transaction.RollbackAsync();
             return Result.Fail(new Error(errorMsg));
         }
+    }
+    catch (Exception ex)
+    {
+        await transaction.RollbackAsync();
+        _logger.LogError(request, $"Unexpected error deleting record for QrId: {request.qrId}. Error: {ex.Message}");
+        throw;
+    }
 }
Streetcode/Streetcode.XIntegrationTest/ControllerTests/Utils/Extracter/Analytics/StatisticRecordExtracter.cs (2)

8-22: Add documentation and improve parameter handling.

Consider these enhancements for better maintainability and reliability:

  1. Add XML documentation to explain the method's purpose and parameters
  2. Add input validation
  3. Make coordinate values configurable or more realistic

Here's a suggested implementation:

+    /// <summary>
+    /// Extracts a test StatisticRecord with the specified ID and StreetcodeId
+    /// </summary>
+    /// <param name="id">The ID to assign to the StatisticRecord</param>
+    /// <param name="streetcodeId">The StreetcodeId to assign to both StatisticRecord and StreetcodeCoordinate</param>
+    /// <returns>A configured StatisticRecord for testing</returns>
+    /// <exception cref="ArgumentException">Thrown when id or streetcodeId is less than 1</exception>
     public static StatisticRecord Extract(int id, int streetcodeId)
     {
+        if (id < 1)
+            throw new ArgumentException("ID must be greater than 0", nameof(id));
+        if (streetcodeId < 1)
+            throw new ArgumentException("StreetcodeId must be greater than 0", nameof(streetcodeId));
+
         StatisticRecord testStatisticRecord = TestDataProvider.GetTestData<StatisticRecord>();
         StreetcodeCoordinate testStreetcodeCoordinate = new StreetcodeCoordinate
         {
             StreetcodeId = streetcodeId,
-            Latitude = 50,
-            Longtitude = 50,
+            Latitude = 50.450001,    // Example: Kyiv latitude
+            Longtitude = 30.523333,  // Example: Kyiv longitude
         };

24-27: Add documentation for the Remove method.

The implementation is clean and focused. Consider adding XML documentation for consistency.

Here's a suggested addition:

+    /// <summary>
+    /// Removes a StatisticRecord from the test data store
+    /// </summary>
+    /// <param name="entity">The StatisticRecord entity to remove</param>
     public static void Remove(StatisticRecord entity)
Streetcode/Streetcode.XIntegrationTest/ControllerTests/Users/Expertises/ExpertiseControllerTests.cs (1)

14-21: Consider enhancing test data management.

The test setup looks good with unique ID generation, but consider adding:

  1. Test data cleanup in a Dispose method
  2. A test data factory or constants class for reusable test cases

Example implementation:

 private readonly Expertise _testExpertise;
+private const string DEFAULT_EXPERTISE_NAME = "Test Expertise";
+
+public void Dispose()
+{
+    // Cleanup test data after each test
+    // await Client.Delete(_testExpertise.Id);
+}
Streetcode/Streetcode.XIntegrationTest/ControllerTests/Analytics/StatisticRecordControllerTests.cs (4)

21-32: Consider extracting test data setup to a separate method.

The constructor contains complex setup logic that could be moved to a dedicated method for better readability and maintainability.

 public StatisticRecordControllerTests(CustomWebApplicationFactory<Program> factory, TokenStorage tokenStorage)
     : base(factory, "/api/StatisticRecord", tokenStorage)
 {
-    var uniqueStreetcodeId = UniqueNumberGenerator.GenerateInt();
-    var uniqueStatisticRecordId = UniqueNumberGenerator.GenerateInt();
-    StreetcodeContentExtracter
-        .Extract(
-            uniqueStreetcodeId,
-            uniqueStreetcodeId,
-            Guid.NewGuid().ToString());
-    _statisticRecord = StatisticRecordExtracter.Extract(uniqueStatisticRecordId, uniqueStreetcodeId);
+    _statisticRecord = SetupTestData();
+}
+
+private StatisticRecord SetupTestData()
+{
+    var uniqueStreetcodeId = UniqueNumberGenerator.GenerateInt();
+    var uniqueStatisticRecordId = UniqueNumberGenerator.GenerateInt();
+    
+    StreetcodeContentExtracter.Extract(
+        uniqueStreetcodeId,
+        uniqueStreetcodeId,
+        Guid.NewGuid().ToString());
+        
+    return StatisticRecordExtracter.Extract(uniqueStatisticRecordId, uniqueStreetcodeId);
 }

65-65: Consider adding error handling for JSON deserialization.

The CaseIsensitiveJsonDeserializer.Deserialize call could throw an exception if the response content is malformed.

-var returnedValue = CaseIsensitiveJsonDeserializer.Deserialize<List<StatisticRecordDTO>>(response.Content);
+List<StatisticRecordDTO> returnedValue;
+try
+{
+    returnedValue = CaseIsensitiveJsonDeserializer.Deserialize<List<StatisticRecordDTO>>(response.Content);
+}
+catch (Exception ex)
+{
+    Assert.Fail($"Failed to deserialize response: {ex.Message}");
+    return;
+}

110-110: Consider using more robust boolean parsing.

The current boolean parsing using TryParse doesn't handle the case where parsing fails.

-bool.TryParse(response.Content, out bool result);
+Assert.True(bool.TryParse(response.Content, out bool result), "Response content could not be parsed as boolean");

1-193: Consider adding test cases for edge cases and authorization scenarios.

The test suite covers the basic CRUD operations well, but could benefit from additional test cases:

  1. Concurrent access scenarios
  2. Different user roles and permissions
  3. Rate limiting
  4. Invalid token scenarios
  5. Malformed request payloads

Would you like me to generate additional test cases for these scenarios?

Streetcode/Streetcode.BLL/MediatR/Analytics/StatisticRecord/ExistByQrId/ExistStatisticRecordByQrIdHandler.cs (1)

1-1: Remove unused AutoMapper import.

The AutoMapper import is no longer needed since the IMapper dependency was removed.

-using AutoMapper;
Streetcode/Streetcode.XUnitTest/MediatRTests/Analytics/ExistStatisticRecordByQrIdTests.cs (1)

52-76: Consider adding edge cases.

While the current tests cover basic positive and negative scenarios, consider adding tests for:

  • Invalid QR IDs (e.g., 0 or extremely large values)
  • Repository throwing exceptions
  • Cancelled operations using CancellationToken

Example test case for exception handling:

[Fact]
public async Task Handle_RepositoryThrows_ShouldReturnFailure()
{
    // Arrange
    var qrId = 1;
    _repositoryWrapperMock.Setup(repo => repo.StatisticRecordRepository.GetFirstOrDefaultAsync(
            It.IsAny<Expression<Func<StatisticRecord, bool>>>(),
            It.IsAny<Func<IQueryable<StatisticRecord>, IIncludableQueryable<StatisticRecord, object>>>()))
        .ThrowsAsync(new Exception("Database error"));

    var request = new ExistStatisticRecordByQrIdCommand(qrId);

    // Act
    var result = await _handler.Handle(request, CancellationToken.None);

    // Assert
    Assert.False(result.IsSuccess);
}
Streetcode/Streetcode.XUnitTest/Validators/Users/UpdateUserValidatorTests.cs (2)

17-32: Consider enhancing mock setup for better test isolation.

The mock setup is good, but could be improved in a few ways:

  1. Consider using constants for test values to make maintenance easier
  2. The base validator's setup could be more specific about what validation context it expects

Here's a suggested improvement:

+ private const string ValidUserName = "testusername";
+ private const string ValidEmail = "testemail";

  public UpdateUserValidatorTests()
  {
      mockNamesLocalizer = new MockFieldNamesLocalizer();
      mockValidationLocalizer = new MockFailedToValidateLocalizer();
      mockUserSharedResourceLocalizer = new MockUserSharedResourceLocalizer();
      _mockRepositoryWrapper = new Mock<IRepositoryWrapper>();
      mockBaseValidator = new Mock<BaseUserValidator>(mockValidationLocalizer, mockNamesLocalizer, _mockRepositoryWrapper.Object);
-     mockBaseValidator.Setup(x => x.Validate(It.IsAny<ValidationContext<UpdateUserDTO>>()))
+     mockBaseValidator.Setup(x => x.Validate(It.Is<ValidationContext<UpdateUserDTO>>(
+         ctx => ctx.InstanceToValidate.UserName == ValidUserName &&
+                ctx.InstanceToValidate.Email == ValidEmail)))
          .Returns(new ValidationResult());
  }

50-80: Enhance test data quality and validation.

The test data could be improved in several ways:

  1. Email should follow a valid format (e.g., "test@example.com")
  2. PhoneNumber is marked as null but has a null! operator
  3. Consider using a test data builder pattern for more flexible test data creation

Here's a suggested improvement:

  private UpdateUserDTO GetValidUser()
  {
      return new UpdateUserDTO
      {
          Name = "TestName",
          Surname = "TestSurname",
          UserName = "testusername",
          AboutYourself = null,
          AvatarId = null,
          Expertises = new List<ExpertiseDTO>()
          {
              new () { Id = 1, Title = "testTitle1" },
              new () { Id = 2, Title = "testTitle2" },
              new () { Id = 3, Title = "testTitle3" },
          },
-         PhoneNumber = null!,
+         PhoneNumber = null,
-         Email = "testemail",
+         Email = "test@example.com",
      };
  }
Streetcode/Streetcode.BLL/MediatR/Analytics/StatisticRecord/GetAllByStreetcodeId/GetAllStatisticRecordsByStreetcodeIdHandler.cs (1)

38-43: Consider using null-coalescing operator for cleaner error handling.

The current implementation can be simplified while maintaining the same functionality.

-if (!statisticRecords.Any())
-{
-    string errorMsg = _stringLocalizerCannotFind["CannotFindRecordWithStreetcodeId", request.streetcodeId];
-    _logger.LogError(request, errorMsg);
-    return Result.Fail(new Error(errorMsg));
-}
+return !statisticRecords.Any() 
+    ? Result.Fail(new Error(_stringLocalizerCannotFind["CannotFindRecordWithStreetcodeId", request.streetcodeId]))
+        .Tap(error => _logger.LogError(request, error.Message))
+    : Result.Ok(_mapper.Map<IEnumerable<StatisticRecordDTO>>(statisticRecords));
Streetcode/Streetcode.XUnitTest/MediatRTests/Analytics/GetAllStatisticRecordsByStreetcodeIdTests.cs (2)

40-72: Consider adding edge case tests.

While the current tests cover basic scenarios well, consider adding tests for:

  1. Edge cases with very large counts
  2. Cases with duplicate QrIds
  3. Boundary conditions for streetcodeId

99-114: Consider using test data builder pattern.

The test data creation could be made more maintainable using a builder pattern.

+private class StatisticRecordBuilder
+{
+    private readonly StatisticRecord _record = new();
+    
+    public StatisticRecordBuilder WithId(int id)
+    {
+        _record.Id = id;
+        return this;
+    }
+    
+    public StatisticRecordBuilder WithQrId(int qrId)
+    {
+        _record.QrId = qrId;
+        return this;
+    }
+    
+    public StatisticRecord Build() => _record;
+}
Streetcode/Streetcode.XUnitTest/MediatRTests/Analytics/GetAllStatisticRecordsTests.cs (1)

41-71: Consider using Theory for parameterized testing.

Convert the test to use [Theory] with [InlineData] to test multiple scenarios with different record counts and ordering.

-[Fact]
-public async Task Handle_RecordsExist_ShouldReturnOrderedRecords()
+[Theory]
+[InlineData(new[] { 10, 20, 15 })]
+[InlineData(new[] { 5, 5, 5 })]
+[InlineData(new[] { 1 })]
+public async Task Handle_RecordsExist_ShouldReturnOrderedRecords(int[] counts)
Streetcode/Streetcode.XUnitTest/MediatRTests/Email/SendEmailHandlerTests.cs (4)

41-59: Enhance test coverage with additional assertions.

While the test verifies basic success, consider adding assertions to validate:

  • The reCAPTCHA token was actually validated
  • The email content was correctly passed to the email service
 // assert
 Assert.True(result.IsSuccess);
+_mockHttpClientFactory.Verify(f => f.CreateClient(It.IsAny<string>()), Times.Once);
+_mockEmailService.Verify(s => s.SendEmailAsync(It.Is<MessageData>(m => 
+    m.Content == emailDto.Content && 
+    m.From == emailDto.From)), Times.Once);

61-80: Verify email service is not called on reCAPTCHA validation failure.

Add verification to ensure the email service is not called when reCAPTCHA validation fails.

 // assert
 Assert.False(result.IsSuccess);
 Assert.Equal(expectedErrorMessage, result.Errors[0].Message);
+_mockEmailService.Verify(s => s.SendEmailAsync(It.IsAny<MessageData>()), Times.Never);

82-102: Verify email service is not called on invalid token.

Add verification to ensure the email service is not called when the reCAPTCHA token is invalid.

 // assert
 Assert.False(result.IsSuccess);
 Assert.Equal(expectedErrorMessage, result.Errors[0].Message);
+_mockEmailService.Verify(s => s.SendEmailAsync(It.IsAny<MessageData>()), Times.Never);

104-125: Verify operation order in email sending failure test.

Add verification to ensure reCAPTCHA validation occurs before attempting to send the email.

 // assert
 Assert.False(result.IsSuccess);
 Assert.Equal(expectedErrorMessage, result.Errors[0].Message);
+var sequence = new MockSequence();
+_mockHttpClientFactory.InSequence(sequence).Verify(f => f.CreateClient(It.IsAny<string>()));
+_mockEmailService.InSequence(sequence).Verify(s => s.SendEmailAsync(It.IsAny<MessageData>()));
Streetcode/Streetcode.XUnitTest/Mocks/MockUserSharedResourceLocalizer.cs (1)

9-26: Consider organizing error messages into logical groups.

The current implementation uses a single group (key: 0) for all error messages. Consider organizing them into logical categories for better maintainability:

     return new Dictionary<int, List<string>>
     {
         {
-            0, new List<string>
+            1, new List<string> // Authentication errors
             {
-                "UserWithSuchUsernameNotExists",
-                "UserWithSuchUsernameExists",
-                "UserWithSuchEmailNotFound",
-                "UserWithSuchEmailExists",
-                "UserNotFound",
-                "UserNameOrTokenIsEmpty",
-                "UserManagerError",
                 "IncorrectPassword",
+                "RecaptchaValidationFailed",
+                "RecaptchaScoreTooLow",
+                "GoogleTokenValidationFailed",
+                "GoogleAuthenticationFailed",
             }
         },
+        {
+            2, new List<string> // User existence errors
+            {
+                "UserWithSuchUsernameNotExists",
+                "UserWithSuchUsernameExists",
+                "UserWithSuchEmailNotExists",
+                "UserWithSuchEmailExists",
+                "UserNotFound",
+            }
+        },
+        {
+            3, new List<string> // Validation errors
+            {
+                "UserNameOrTokenIsEmpty",
+                "UserManagerError",
+                "EmailNotMatch",
+            }
+        },
     };
Streetcode/Streetcode.WebApi/Extensions/ServiceCollectionExtensions.cs (1)

70-71: Consider consolidating reCAPTCHA v3 with Google service registration.

Since we're updating to reCAPTCHA v3 which uses behavior analysis, consider if the GoogleService could be utilized by the CaptchaService for token validation. This would promote code reuse and maintain a single source of truth for Google-related validations.

Would you like me to propose a design that integrates these services efficiently?

Streetcode/Streetcode.XIntegrationTest/ControllerTests/Users/UserControllerTests.cs (6)

47-53: Consider using a builder pattern for test data setup.

The test user extraction could be made more maintainable using a builder pattern, which would make the test setup more readable and flexible.

Consider refactoring to use a builder pattern:

public class TestUserBuilder
{
    private string _userId = Guid.NewGuid().ToString();
    private string _userName = Guid.NewGuid().ToString();
    private string _password = GenerateTestPassword();
    private string[] _roles = new[] { nameof(UserRole.User), nameof(UserRole.Admin) };

    public TestUserBuilder WithUserId(string userId)
    {
        _userId = userId;
        return this;
    }

    public (User, string) Build() => UserExtracter.Extract(_userId, _userName, _password, _roles);
}

55-65: Add edge cases for email validation.

The email retrieval test only covers the happy path. Consider adding tests for edge cases like empty email, malformed email, and non-existent email.

Example test to add:

[Theory]
[InlineData("")]
[InlineData("invalid.email")]
[InlineData("nonexistent@example.com")]
public async Task GetByEmail_InvalidEmail_ReturnsBadRequest(string email)
{
    // Act
    var response = await Client.GetByEmail(email, TokenStorage.UserAccessToken);

    // Assert
    Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
}

141-178: Enhance update tests with more validation scenarios.

The update tests could be more comprehensive. Consider adding tests for:

  • Invalid expertise data
  • Empty or whitespace username
  • Maximum length validation

Example test to add:

[Theory]
[ExtractUpdateTestUser]
[InlineData("")]
[InlineData(" ")]
[InlineData("a")] // Too short
[InlineData("thisusernameiswaytoolongandshouldfail")]
public async Task UpdateUser_InvalidUserName_ReturnsBadRequest(string userName)
{
    // Arrange
    var userToUpdate = ExtractUpdateTestUserAttribute.UserForTest;
    userToUpdate.UserName = userName;

    // Act
    var response = await Client.Update(userToUpdate, TokenStorage.UserAccessToken);

    // Assert
    Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
}

180-208: Add tests for unauthorized deletion and cascade effects.

The deletion tests should verify:

  1. Unauthorized users cannot delete accounts
  2. Related data is properly cleaned up after deletion

Example test to add:

[Fact]
public async Task DeleteUser_UnauthorizedToken_ReturnsUnauthorized()
{
    // Act
    var response = await Client.Delete(_testUser.Email!, "invalid_token");

    // Assert
    Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
}

[Fact]
public async Task DeleteUser_VerifyCascadeDelete_Success()
{
    // Arrange
    await TokenStorage.GenerateNewTokens(_testUser);

    // Act
    await Client.Delete(_testUser.Email!, TokenStorage.UserAccessToken);

    // Assert
    // Verify related data is deleted
    var expertises = await _expertiseRepository.GetByUserId(_testUser.Id);
    Assert.Empty(expertises);
}

244-282: Add password complexity and token expiration tests.

The password reset tests should verify:

  1. Password complexity requirements
  2. Token expiration handling

Example tests to add:

[Theory]
[InlineData("short")] // Too short
[InlineData("NoSpecialChar1")] // No special characters
[InlineData("no-upper-1!")] // No uppercase
[InlineData("NO-LOWER-1!")] // No lowercase
public async Task UpdateForgotPassword_InvalidPasswordComplexity_ReturnsBadRequest(string password)
{
    // Arrange
    var forgotPassword = new UpdateForgotPasswordDTO
    {
        Username = Uri.EscapeDataString(_testUser.UserName),
        Password = password,
        ConfirmPassword = password
    };
    forgotPassword.Token = await _userManager.GeneratePasswordResetTokenAsync(_testUser);

    // Act
    var response = await Client.UpdateForgotPassword(forgotPassword);

    // Assert
    Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
}

284-288: Enhance password generation security.

The current password generation method could be improved to ensure generated passwords meet all complexity requirements consistently.

Consider using a more robust password generator:

-private static string GenerateTestPassword()
-{
-    string guid = Guid.NewGuid().ToString();
-    return $"TestPass123_{guid.Substring(0, 10)}";
-}
+private static string GenerateTestPassword(int length = 16)
+{
+    const string upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+    const string lower = "abcdefghijklmnopqrstuvwxyz";
+    const string number = "0123456789";
+    const string special = "!@#$%^&*()_+-=[]{}|;:,.<>?";
+
+    var random = new Random();
+    var result = new StringBuilder();
+    
+    // Ensure at least one of each required char type
+    result.Append(upper[random.Next(upper.Length)]);
+    result.Append(lower[random.Next(lower.Length)]);
+    result.Append(number[random.Next(number.Length)]);
+    result.Append(special[random.Next(special.Length)]);
+
+    // Fill remaining length with random chars
+    var chars = upper + lower + number + special;
+    while (result.Length < length)
+    {
+        result.Append(chars[random.Next(chars.Length)]);
+    }
+
+    return new string(result.ToString().ToCharArray().OrderBy(x => random.Next()).ToArray());
+}
Streetcode/Streetcode.XUnitTest/MediatRTests/Analytics/DeleteStatisticRecordTests.cs (2)

15-35: Add XML documentation and consider using constants for test data.

The test class setup is well-structured. Consider these improvements for better maintainability:

  1. Add XML documentation to describe the test class purpose
  2. Consider extracting test data like QR IDs into constants
+/// <summary>
+/// Unit tests for <see cref="DeleteStatisticRecordHandler"/> covering successful deletion,
+/// record not found, and failed deletion scenarios.
+/// </summary>
 public class DeleteStatisticRecordTests
 {
+    private const int ValidQrId = 1;
+    private const int InvalidQrId = -1;
+
     private readonly Mock<IRepositoryWrapper> _repositoryWrapperMock;

37-64: Enhance test coverage by verifying logger calls.

The test effectively verifies the happy path. Consider these enhancements:

  1. Verify that success is logged via _loggerMock
  2. Use more descriptive test data by adding meaningful properties to the StatisticRecord
 [Fact]
 public async Task Handle_RecordIsDeletedSuccessfully_ShouldReturnSuccess()
 {
     // Arrange
     var qrId = 1;
     var statisticRecords = new List<StatisticRecord>
     {
-        new () { QrId = qrId },
+        new () { 
+            QrId = qrId,
+            CreatedAt = DateTime.UtcNow,
+            StreetcodeId = 42
+        },
     };

     // ... existing test code ...

     // Assert
     Assert.True(result.IsSuccess);
     Assert.Equal(Unit.Value, result.Value);
     _repositoryWrapperMock.Verify(repo => repo.StatisticRecordRepository.Delete(It.IsAny<StatisticRecord>()), Times.Once);
     _repositoryWrapperMock.Verify(repo => repo.SaveChangesAsync(), Times.Once);
+    _loggerMock.Verify(
+        x => x.Log(
+            LogLevel.Information,
+            It.Is<string>(s => s.Contains($"Successfully deleted statistic record with QrId: {qrId}"))),
+        Times.Once);
 }
Streetcode/Streetcode.XUnitTest/MediatRTests/Users/UpdateUserTest.cs (4)

45-45: Consider extracting test constants.

Extract magic strings like email addresses into constants for better maintainability and reusability.

+    private const string TEST_EMAIL = "test@example.com";
-        var fakeEmail = "test@example.com";
+        var fakeEmail = TEST_EMAIL;

66-98: Enhance test coverage for successful update.

The test verifies basic properties but could be more comprehensive. Consider:

  • Verifying all updated fields (surname, avatarId, aboutYourself, phoneNumber).
  • Adding descriptive messages to assertions for better test failure diagnostics.
         Assert.True(result.IsSuccess);
-        Assert.Equal("newusername", result.Value.UserName);
-        Assert.Equal("New Name", result.Value.Name);
+        Assert.Equal("newusername", result.Value.UserName, "Username should be updated");
+        Assert.Equal("New Name", result.Value.Name, "Name should be updated");
+        Assert.Equal("New Surname", result.Value.Surname, "Surname should be updated");
+        Assert.Equal(2, result.Value.AvatarId, "Avatar ID should be updated");
+        Assert.Equal("About Myself", result.Value.AboutYourself, "About section should be updated");
+        Assert.Equal("1234567890", result.Value.PhoneNumber, "Phone number should be updated");
         Assert.Contains(result.Value.Expertises, e => e.Title == "New Expertise");

100-130: Consider extracting common test setup.

The test contains duplicate setup code from the success test. Consider extracting common setup to a private method or using test fixtures.

+    private void SetupCommonMocks(User existingUser)
+    {
+        _userManagerMock.Setup(um => um.Users)
+            .Returns(new List<User> { existingUser }.AsQueryable().BuildMockDbSet().Object);
+
+        _repositoryWrapperMock.Setup(rw => rw.ExpertiseRepository.GetAllAsync(
+                It.IsAny<Expression<Func<Expertise, bool>>>(),
+                It.IsAny<Func<IQueryable<Expertise>, IIncludableQueryable<Expertise, object>>>()))
+            .ReturnsAsync(new List<Expertise> { new () { Id = 2, Title = "New Expertise" } });
+
+        _mapperMock.Setup(m => m.Map<UserDTO>(It.IsAny<User>()))
+            .Returns(GetUserDto);
+    }

Then use it in tests:

-        _userManagerMock.Setup(um => um.Users)
-            .Returns(new List<User> { existingUser }.AsQueryable().BuildMockDbSet().Object);
-
-        _repositoryWrapperMock.Setup(rw => rw.ExpertiseRepository.GetAllAsync(
-                It.IsAny<Expression<Func<Expertise, bool>>>(),
-                It.IsAny<Func<IQueryable<Expertise>, IIncludableQueryable<Expertise, object>>>()))
-            .ReturnsAsync(new List<Expertise> { new () { Id = 2, Title = "New Expertise" } });
-
-        _mapperMock.Setup(m => m.Map<UserDTO>(It.IsAny<User>()))
-            .Returns(GetUserDto);
+        SetupCommonMocks(existingUser);

132-156: Enhance user not found test assertions.

The test could be more specific about the error condition. Consider:

  • Verifying the specific error message.
  • Simplifying the test setup since the user won't be found anyway.
         // Assert
-        Assert.False(result.IsSuccess);
+        Assert.True(result.IsFailed);
+        Assert.Contains("User not found", result.Errors[0].Message);
Streetcode/Streetcode.XIntegrationTest/ControllerTests/Utils/AuthorizationFixture/TokenStorage.cs (2)

45-49: Add 'Async' suffix to async method name.

Following C# naming conventions, async methods should have the 'Async' suffix.

-public async Task GenerateNewTokens(User user)
+public async Task GenerateNewTokensAsync(User user)
 {
     UserAccessToken = (await _tokenService.GenerateAccessTokenAsync(user)).RawData;
     UserRefreshToken = _tokenService.SetNewRefreshTokenForUser(user);
 }

81-90: Add error handling for missing configuration.

The GetDbContext method should handle cases where the connection string is missing or invalid.

 private StreetcodeDbContext GetDbContext()
 {
     var sqlConnectionString = new ConfigurationBuilder()
         .AddJsonFile("appsettings.IntegrationTests.json")
         .Build()
         .GetConnectionString("DefaultConnection");
+    
+    if (string.IsNullOrEmpty(sqlConnectionString))
+    {
+        throw new InvalidOperationException("Connection string 'DefaultConnection' not found in test settings.");
+    }
+    
     var optionBuilder = new DbContextOptionsBuilder<StreetcodeDbContext>();
     optionBuilder.UseSqlServer(sqlConnectionString);
     return new StreetcodeDbContext(optionBuilder.Options);
 }
Streetcode/Streetcode.XIntegrationTest/ControllerTests/Utils/Client/Analytics/AnalyticsClient.cs (2)

7-12: Add XML documentation and input validation.

Consider enhancing the class with:

  1. XML documentation to describe the class purpose and constructor parameters
  2. Input validation for the HttpClient parameter
+/// <summary>
+/// Client for interacting with analytics endpoints in integration tests.
+/// </summary>
 public class AnalyticsClient : BaseClient
 {
+    /// <summary>
+    /// Initializes a new instance of the AnalyticsClient class.
+    /// </summary>
+    /// <param name="client">The HTTP client to use for requests.</param>
+    /// <param name="secondPartUrl">Optional URL segment to append to the base URL.</param>
+    /// <exception cref="ArgumentNullException">Thrown when client is null.</exception>
     public AnalyticsClient(HttpClient client, string secondPartUrl = "")
         : base(client, secondPartUrl)
     {
+        ArgumentNullException.ThrowIfNull(client);
     }

1-48: Consider implementing retry logic for transient failures.

Since this is an integration test client making HTTP requests, it would be beneficial to implement retry logic for handling transient failures.

Consider using a resilience pattern like Polly:

// Example implementation using Polly
private readonly IAsyncPolicy<RestResponse> _retryPolicy = Policy<RestResponse>
    .Handle<HttpRequestException>()
    .Or<TimeoutException>()
    .WaitAndRetryAsync(3, retryAttempt => 
        TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));

// Then wrap your SendCommand/SendQuery calls:
public async Task<RestResponse> GetAll(string authToken = "")
{
    return await _retryPolicy.ExecuteAsync(async () => 
        await SendQuery($"/GetAll", authToken));
}
Streetcode/Streetcode.XIntegrationTest/Streetcode.XIntegrationTest.csproj (1)

61-70: Consider implementing tests for the new reCAPTCHA v3 integration.

The new test folder structure is well-organized, but I notice there's no dedicated folder for reCAPTCHA-related tests. Given that this PR implements reCAPTCHA v3, consider adding test coverage for this feature.

Would you like me to help create a test structure for the reCAPTCHA v3 implementation?

Streetcode/Streetcode.XUnitTest/Validators/Email/EmailTests.cs (1)

10-125: Consider adding tests for reCAPTCHA v3 specific scenarios.

Since we're transitioning to reCAPTCHA v3, consider adding tests for:

  • Score-based validation
  • Automatic token generation
  • Token expiration handling

Would you like me to help generate test cases for these scenarios?

Streetcode/Streetcode.BLL/DTO/Authentication/GoogleLogin/GoogleLoginRequest.cs (1)

3-6: LGTM! Consider adding XML documentation.

The class follows DTO pattern best practices with a good default value for IdToken. Consider adding XML documentation to describe the purpose and usage of this DTO.

 public class GoogleLoginRequest
 {
+    /// <summary>
+    /// Gets or sets the Google ID token used for authentication.
+    /// </summary>
     public string IdToken { get; set; } = string.Empty;
 }
Streetcode/Streetcode.BLL/Interfaces/Authentication/IGoogleService.cs (1)

7-7: Improve parameter name clarity.

The parameter name 'id' could be more descriptive to indicate it's a token.

-Task<GoogleJsonWebSignature.Payload> ValidateGoogleToken(string id);
+Task<GoogleJsonWebSignature.Payload> ValidateGoogleToken(string idToken);
Streetcode/Streetcode.XIntegrationTest/ControllerTests/Utils/Client/Users/Expertises/ExpertiseClient.cs (2)

6-11: Add XML documentation for the class and constructor.

Consider adding XML documentation to improve code maintainability.

+/// <summary>
+/// Client for interacting with expertise-related endpoints in integration tests.
+/// </summary>
 public class ExpertiseClient : BaseClient
 {
+    /// <summary>
+    /// Initializes a new instance of the <see cref="ExpertiseClient"/> class.
+    /// </summary>
+    /// <param name="client">The HTTP client to use for requests.</param>
+    /// <param name="secondPartUrl">Optional URL segment to append to the base URL.</param>
     public ExpertiseClient(HttpClient client, string secondPartUrl = "")
         : base(client, secondPartUrl)
     {
     }

13-16: Add input validation and XML documentation for GetAll method.

Consider adding validation for the authToken parameter and XML documentation.

+    /// <summary>
+    /// Retrieves all expertise entries.
+    /// </summary>
+    /// <param name="authToken">Optional authentication token.</param>
+    /// <returns>A response containing the expertise entries.</returns>
     public async Task<RestResponse> GetAll(string authToken = "")
     {
+        if (authToken != null && authToken.Length > 0 && !authToken.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
+        {
+            authToken = $"Bearer {authToken}";
+        }
         return await SendQuery("/GetAll", authToken);
     }
Streetcode/Streetcode.XIntegrationTest/ControllerTests/BaseController/BaseAuthorizationControllerTests.cs (1)

14-14: Consider keeping TokenStorage immutable.

Making TokenStorage mutable could lead to test state issues if modified during test execution. Consider keeping it read-only and initializing through constructor injection.

-protected TokenStorage TokenStorage { get; set; }
+protected TokenStorage TokenStorage { get; }
Streetcode/Streetcode.XIntegrationTest/ControllerTests/Utils/Extracter/Expertises/ExpertiseExtracter.cs (1)

7-14: Simplify Extract method and improve variable naming.

The Extract method can be simplified, and variable names can be more descriptive.

 public static Expertise Extract(int expertiseId)
 {
-    var testExpertise = TestDataProvider.GetTestData<Expertise>();
-
-    testExpertise.Id = expertiseId;
-    var extracter = BaseExtracter.Extract<Expertise>(testExpertise, e => e.Id == expertiseId);
-    return extracter;
+    var expertise = TestDataProvider.GetTestData<Expertise>();
+    expertise.Id = expertiseId;
+    return BaseExtracter.Extract<Expertise>(expertise, e => e.Id == expertiseId);
 }
Streetcode/Streetcode.BLL/MediatR/Authentication/Logout/LogoutHandler.cs (2)

26-28: Consider using ID-based lookup for better performance

Looking up users by username is less efficient than using the primary key (ID). Consider retrieving the user ID from claims:

-var userUserName = HttpContextHelper.GetCurrentUserName(_httpContextAccessor);
-var user = await _repositoryWrapper.UserRepository.GetFirstOrDefaultAsync(u => u.UserName == userUserName);
+var userId = HttpContextHelper.GetCurrentUserId(_httpContextAccessor);
+var user = await _repositoryWrapper.UserRepository.GetByIdAsync(userId);

37-39: Add transaction handling for database operations

The update operation should be wrapped in a transaction to ensure data consistency:

+await using var transaction = await _repositoryWrapper.BeginTransactionAsync();
 _repositoryWrapper.UserRepository.Update(user);
-var resultIsSuccess = await _repositoryWrapper.SaveChangesAsync() > 0;
+try
+{
+    var resultIsSuccess = await _repositoryWrapper.SaveChangesAsync() > 0;
+    if (resultIsSuccess)
+    {
+        await transaction.CommitAsync();
+        return Result.Ok();
+    }
+    await transaction.RollbackAsync();
+    return Result.Fail("Failed to logout");
+}
+catch (Exception ex)
+{
+    await transaction.RollbackAsync();
+    return Result.Fail(new Error($"Failed to logout: {ex.Message}"));
+}
Streetcode/Streetcode.XIntegrationTest/ControllerTests/Utils/Client/Authentication/AuthenticationClient.cs (1)

32-35: Consider adding response validation for the Logout method.

The method implementation is clean, but consider adding response validation to handle potential edge cases.

 public async Task<RestResponse> Logout(string authToken = "")
 {
-    return await SendCommand("/Logout", Method.Post, authToken: authToken);
+    var response = await SendCommand("/Logout", Method.Post, authToken: authToken);
+    if (!response.IsSuccessful)
+    {
+        // Handle unsuccessful logout
+    }
+    return response;
 }
Streetcode/Streetcode.XUnitTest/Factories/MessageDataFactory/MessageDataConcreteFactoryTests.cs (2)

29-43: Enhance test coverage for CreateFeedbackMessageData

While the test verifies message creation, it should also validate the message properties to ensure correct data mapping.

 [Fact]
 public void CreateFeedbackMessageData_ShouldReturnValidMessage()
 {
     // Arrange
     string from = "user@example.com";
     string source = "test_source";
     string content = "test_content";

     // Act
     var message = _factory.CreateFeedbackMessageData(from, source, content);

     // Assert
     Assert.NotNull(message);
     Assert.IsType<FeedbackMessageData>(message);
+    Assert.Equal(from, message.From);
+    Assert.Equal(source, message.Source);
+    Assert.Equal(content, message.Content);
+    Assert.Equal(_emailConfig.To, message.To);
 }

45-60: Enhance test coverage for CreateForgotPasswordMessageData

Similar to the feedback message test, this test should validate the message properties.

 [Fact]
 public void CreateForgotPasswordMessageData_ShouldReturnValidMessage()
 {
     // Arrange
     string[] to = { "user@example.com" };
     string token = "reset-token";
     string username = "TestUser";
     string currentDomain = "https://example.com";

     // Act
     var message = _factory.CreateForgotPasswordMessageData(to, token, username, currentDomain);

     // Assert
     Assert.NotNull(message);
     Assert.IsType<ForgotPasswordMessageData>(message);
+    Assert.Equal(to, message.To);
+    Assert.Equal(_emailConfig.From, message.From);
+    Assert.Contains(token, message.Content);
+    Assert.Contains(username, message.Content);
+    Assert.Contains(currentDomain, message.Content);
 }
Streetcode/Streetcode.BLL/Services/Instagram/InstagramService.cs (1)

15-15: Make post limit configurable

The post limit is hardcoded and used in multiple places. Consider making it configurable through InstagramEnvirovmentVariables.

-private static int postLimit = 10;
+private readonly int _postLimit;

 public InstagramService(IOptions<InstagramEnvirovmentVariables> instagramEnvirovment, IHttpClientFactory httpClientFactory)
 {
     _httpClientFactory = httpClientFactory;
     _envirovment = instagramEnvirovment.Value;
     _userId = _envirovment.InstagramID;
     _accessToken = _envirovment.InstagramToken;
+    _postLimit = _envirovment.PostLimit;
 }

Also applies to: 27-27

Streetcode/Streetcode.XIntegrationTest/ControllerTests/Utils/BeforeAndAfterTestAtribute/Users/ExtractUpdateTestUserAttribute.cs (2)

44-44: Use a consistent test phone number format.

Consider using a more generic phone number format for testing or extracting it to a constants file.

-            PhoneNumber = "+380735004490",
+            PhoneNumber = "+1234567890", // Generic test phone number

60-64: Enhance password generation security.

The current password generation could be more robust by ensuring it meets common password requirements.

     private string GenerateTestPassword()
     {
-        string guid = Guid.NewGuid().ToString();
-        return $"TestPass123_{guid.Substring(0, 10)}";
+        const string special = "!@#$%^&*";
+        var guid = Guid.NewGuid().ToString();
+        return $"TestPass{special[new Random().Next(special.Length)]}{guid.Substring(0, 10)}";
     }
Streetcode/Streetcode.XIntegrationTest/ControllerTests/Utils/Client/Base/BaseClient.cs (1)

34-42: Consider enhancing request validation

The null check for requestDto is good, but consider adding validation for requestString to prevent potential null reference exceptions.

 protected async Task<RestResponse> SendCommand<T>(string requestString, Method method, T? requestDto = default, string authToken = "")
     where T : class?
 {
+    if (string.IsNullOrEmpty(requestString))
+    {
+        throw new ArgumentException("Request string cannot be null or empty", nameof(requestString));
+    }
     var request = new RestRequest($"{SecondPartUrl}{requestString}", method);
     if (requestDto is not null)
     {
         request.AddJsonBody(requestDto);
     }
Streetcode/Streetcode.WebApi/Controllers/Users/UsersController.cs (1)

18-39: Document API changes for username to email migration

The transition from username to email-based identification is good, but consider adding XML documentation to clarify the purpose of each endpoint.

+    /// <summary>
+    /// Retrieves the current user's information using their email from the authentication token
+    /// </summary>
     [HttpGet]
     [Authorize]
     [ProducesResponseType(StatusCodes.Status200OK)]
     public async Task<IActionResult> GetByEmail()
     {
         return HandleResult(await Mediator.Send(new GetByEmailQuery()));
     }

+    /// <summary>
+    /// Checks if a user exists with the specified username
+    /// </summary>
     [HttpGet("{userName}")]
     [Authorize]
     [ProducesResponseType(StatusCodes.Status200OK)]
     public async Task<IActionResult> ExistWithUserName([FromRoute] string userName)
     {
         return HandleResult(await Mediator.Send(new ExistWithUserNameQuery(userName)));
     }
Streetcode/Streetcode.XUnitTest/MediatRTests/Users/GetOtherUserByUserNameHandlerTests.cs (3)

33-34: Consider simplifying UserManager mock setup using a helper method.

The UserManager mock setup could be extracted into a reusable helper method to improve maintainability and reduce duplication across test classes.

+// In MockHelpers.cs
+public static Mock<UserManager<User>> CreateMockUserManager()
+{
+    return new Mock<UserManager<User>>(
+        new Mock<IUserStore<User>>().Object,
+        null, null, null, null, null, null, null, null);
+}

-var userStoreMock = new Mock<IUserStore<User>>();
-_userManagerMock = new Mock<UserManager<User>>(userStoreMock.Object, null, null, null, null, null, null, null, null);
+_userManagerMock = MockHelpers.CreateMockUserManager();

44-69: Enhance test coverage with additional scenarios.

The test covers the happy path well, but consider adding test cases for:

  • Users with multiple roles
  • Users with special characters in username
  • Case sensitivity handling

71-90: Enhance failure scenario assertions.

The test should also verify:

  • The localized error message content
  • The error details in the result object
 // Assert
 Assert.False(result.IsSuccess);
+Assert.Equal("User does not exist", result.Error);
+Assert.NotNull(result.ErrorDetails);
Streetcode/Streetcode.XUnitTest/Mocks/MockHelpers.cs (2)

70-70: Fix method name typo.

The method name contains a typo: "FirstOfDefault" should be "FirstOrDefault" to match the repository method name.

-public static void SetupMockUserRepositoryGetFirstOfDefaultAsync
+public static void SetupMockUserRepositoryGetFirstOrDefaultAsync

70-76: Enhance mock setup flexibility.

Consider accepting a User object parameter instead of just email to support more test scenarios.

-public static void SetupMockUserRepositoryGetFirstOfDefaultAsync(Mock<IRepositoryWrapper> mockRepositoryWrapper, string email)
+public static void SetupMockUserRepositoryGetFirstOrDefaultAsync(Mock<IRepositoryWrapper> mockRepositoryWrapper, User user)
 {
     mockRepositoryWrapper.Setup(x => x.UserRepository.GetFirstOrDefaultAsync(
             It.IsAny<Expression<Func<User, bool>>>(),
             It.IsAny<Func<IQueryable<User>, IIncludableQueryable<User, object>>>()))
-        .ReturnsAsync(new User { Email = email });
+        .ReturnsAsync(user);
 }
Streetcode/Streetcode.XIntegrationTest/ControllerTests/Utils/CustomWebApplicationFactory.cs (2)

31-51: Improve mock setup flexibility and maintainability.

Consider extracting the hardcoded invalid token string to a constant and making the error message configurable.

+private const string InvalidGoogleToken = "invalid_google_id_token";

-public void SetupMockGoogleLogin(User user, string? token = null)
+public void SetupMockGoogleLogin(User user, string? token = null, string? errorMessage = null)
 {
     if (token is null)
     {
         GoogleServiceMock
-            .Setup(gs => gs.ValidateGoogleToken("invalid_google_id_token"))
-            .ThrowsAsync(new InvalidJwtException("Invalid Google Token"));
+            .Setup(gs => gs.ValidateGoogleToken(InvalidGoogleToken))
+            .ThrowsAsync(new InvalidJwtException(errorMessage ?? "Invalid Google Token"));
     }

89-107: Make HTTP client mock setup more configurable.

Consider parameterizing the response status code and content to support different test scenarios.

-private void SetupMockHttpClient()
+private void SetupMockHttpClient(
+    HttpStatusCode statusCode = HttpStatusCode.OK,
+    string content = "{\"success\": true}")
 {
     var mockResponse = new HttpResponseMessage
     {
-        StatusCode = HttpStatusCode.OK,
-        Content = new StringContent("{\"success\": true}"),
+        StatusCode = statusCode,
+        Content = new StringContent(content),
     };
Streetcode/Streetcode.XUnitTest/MediatRTests/Authentication/Logout/LogoutHandlerTests.cs (1)

85-105: Consider adding timeout test case.

While the current error cases are well covered, consider adding a test for timeout scenarios using the CancellationToken.

+[Fact]
+public async Task Handle_Timeout_ShouldReturnFail()
+{
+    // Arrange
+    var request = new LogoutCommand();
+    var cts = new CancellationTokenSource();
+    cts.Cancel();
+
+    // Act
+    var result = await _handler.Handle(request, cts.Token);
+
+    // Assert
+    Assert.True(result.IsFailed);
+    Assert.Contains("Operation cancelled", result.Errors[0].Message);
+}
Streetcode/Streetcode.XUnitTest/MediatRTests/Users/DeleteUserHandlerTests.cs (1)

93-110: Consider adding role-based test cases.

The email mismatch test is good, but consider adding tests for role-based authorization scenarios.

+[Fact]
+public async Task Handle_UserNotAuthorized_ReturnsFailure()
+{
+    // Arrange
+    var cancellationToken = CancellationToken.None;
+    var request = new DeleteUserCommand("user@example.com");
+    var user = new User { Email = "user@example.com" };
+
+    _userManagerMock.Setup(um => um.FindByEmailAsync("user@example.com"))
+        .ReturnsAsync(user);
+    _userManagerMock.Setup(um => um.IsInRoleAsync(user, "Admin"))
+        .ReturnsAsync(false);
+
+    // Act
+    var result = await _handler.Handle(request, cancellationToken);
+
+    // Assert
+    Assert.False(result.IsSuccess);
+    Assert.Contains("Unauthorized", result.Errors[0].Message);
+}
Streetcode/Streetcode.XUnitTest/MediatRTests/Analytics/UpdateCountStatisticRecordTests.cs (1)

117-125: Consider parameterized test data.

The test data helper method could be enhanced to support parameterized test cases.

-private StatisticRecord GetStatisticRecord(int qrId)
+private StatisticRecord GetStatisticRecord(int qrId, int initialCount = 5)
 {
     return new StatisticRecord
     {
         Id = 1,
         QrId = qrId,
-        Count = 5,
+        Count = initialCount,
     };
 }
Streetcode/Streetcode.XUnitTest/MediatRTests/Users/UpdateForgotPasswordHandlerTests.cs (2)

44-45: Consider using a more secure password for testing.

The test password "NewSecurePassword123!" is relatively simple. Consider using a stronger password that meets all your application's password requirements.

-            Password = "NewSecurePassword123!",
+            Password = "P@ssw0rd_2024_Str0ng!#$%^&*()",

93-94: Enhance test coverage for weak password scenarios.

The test could be more comprehensive by checking multiple weak password scenarios (too short, no special characters, etc.).

-            Password = "weak",
+            Password = "a", // Test extremely short password
+            // Add more test cases in separate test methods:
+            // - Password without numbers
+            // - Password without special characters
+            // - Password without uppercase letters
Streetcode/Streetcode.XIntegrationTest/ControllerTests/Streetcode/StreetcodeCreateControllerTests.cs (1)

30-32: Improve documentation for skipped tests.

The skip message could be more descriptive about why images are required and how to fix the test.

-        [Fact(Skip = "There are no images in the streetcode, so the test will fail without them.")]
+        [Fact(Skip = "Test requires streetcode images to be present in the test database. " +
+              "To fix: Add test images using TestImageSeeder or mock the image requirement.")]
Streetcode/Streetcode.XUnitTest/MediatRTests/Analytics/GetStatisticRecordByQrIdTests.cs (2)

42-71: Consider adding edge cases to test suite.

The test coverage could be enhanced by adding test cases for:

  • Negative QR IDs
  • Maximum possible QR ID values
  • Null or malformed repository responses

127-136: Consider extracting test data to constants.

The test data like QR IDs and user data could be extracted to constants or a test data factory for better maintainability and reuse across test methods.

+private const int ValidQrId = 1;
+private const int InvalidQrId = -1;
+private const int MaxQrId = int.MaxValue;
Streetcode/Streetcode.XUnitTest/MediatRTests/Users/ForgotPasswordTests.cs (1)

74-96: Consider adding email content validation test.

Add a test to verify the content of the email being sent, including:

  • Subject line
  • Email body format
  • Reset link structure

Example test:

[Fact]
public async Task Handle_ShouldSendEmailWithCorrectContent()
{
    // Arrange
    var user = new User { UserName = "TestUser", Email = "test@example.com" };
    var request = new ForgotPasswordCommand(new ForgotPasswordDTO { Email = "test@example.com" });
    MessageData capturedMessage = null;

    SetupUserManagerMock(user);
    _emailServiceMock.Setup(es => es.SendEmailAsync(It.IsAny<MessageData>()))
        .Callback<MessageData>(msg => capturedMessage = msg)
        .ReturnsAsync(true);

    // Act
    var result = await _handler.Handle(request, CancellationToken.None);

    // Assert
    Assert.NotNull(capturedMessage);
    Assert.Contains("reset", capturedMessage.Subject.ToLower());
    Assert.Contains("reset", capturedMessage.Body.ToLower());
}
Streetcode/Streetcode.XUnitTest/Validators/Users/BaseUserValidatorTests.cs (1)

76-93: Consider using Theory for validation format tests.

The username format validation tests could be combined into a single parameterized test for better maintainability.

-[Fact]
-public void Validate_UserNameIsInvalid_ShouldReturnError()
+[Theory]
+[InlineData("Нікнейм1", "Invalid characters")]
+[InlineData("NAME", "All uppercase")]
+[InlineData("!name", "Special characters")]
+[InlineData("a", "Too short")]
+[InlineData("verylongusername123456", "Too long")]
+public void Validate_UserNameIsInvalid_ShouldReturnError(string userName, string testCase)
Streetcode/Streetcode.XIntegrationTest/ControllerTests/Authentication/AuthControllerTests.cs (2)

204-268: Add tests for reCAPTCHA v3 integration.

Since the PR objectives mention updating to reCAPTCHA v3, add test cases to verify:

  • Automatic verification without checkbox
  • Score-based bot detection
  • Token validation

Would you like me to generate test cases for reCAPTCHA v3 integration?


227-244: Enhance Google login error test coverage.

Consider adding test cases for:

  • Expired tokens
  • Malformed tokens
  • Network failures during token validation

Example test:

[Fact]
public async Task LoginGoogle_ExpiredToken_ReturnsUnauthorized()
{
    // Arrange
    var expiredToken = "expired_google_token";
    var request = new GoogleLoginRequest { IdToken = expiredToken };
    _factory.GoogleServiceMock
        .Setup(s => s.ValidateGoogleToken(expiredToken))
        .ThrowsAsync(new SecurityTokenExpiredException());

    // Act
    var response = await Client.GoogleLogin(request);

    // Assert
    Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
    Assert.Contains("token expired", response.Content.ToLower());
}
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 05e0edc and 8fa5c1f.

⛔ Files ignored due to path filters (3)
  • Streetcode/Streetcode.XIntegrationTest/TestData/Expertise.json is excluded by !**/Streetcode/Streetcode.XIntegrationTest/TestData/**
  • Streetcode/Streetcode.XIntegrationTest/TestData/StatisticRecord.json is excluded by !**/Streetcode/Streetcode.XIntegrationTest/TestData/**
  • build/Utils/AuthConstants.cs is excluded by !**/build/**
📒 Files selected for processing (94)
  • Streetcode/Streetcode.BLL/DTO/Authentication/GoogleLogin/GoogleLoginRequest.cs (1 hunks)
  • Streetcode/Streetcode.BLL/Interfaces/Authentication/IGoogleService.cs (1 hunks)
  • Streetcode/Streetcode.BLL/MediatR/Analytics/StatisticRecord/Create/CreateStatisticRecordCommand.cs (0 hunks)
  • Streetcode/Streetcode.BLL/MediatR/Analytics/StatisticRecord/Create/CreateStatisticRecordHandler.cs (0 hunks)
  • Streetcode/Streetcode.BLL/MediatR/Analytics/StatisticRecord/Delete/DeleteStatisticRecordHandler.cs (2 hunks)
  • Streetcode/Streetcode.BLL/MediatR/Analytics/StatisticRecord/ExistByQrId/ExistStatisticRecordByQrIdHandler.cs (1 hunks)
  • Streetcode/Streetcode.BLL/MediatR/Analytics/StatisticRecord/GetAll/GetAllStatisticRecordsQuery.cs (1 hunks)
  • Streetcode/Streetcode.BLL/MediatR/Analytics/StatisticRecord/GetAllByStreetcodeId/GetAllStatisticRecordsByStreetcodeIdHandler.cs (2 hunks)
  • Streetcode/Streetcode.BLL/MediatR/Analytics/StatisticRecord/UpdateCount/UpdateCountStatisticRecordHandler.cs (1 hunks)
  • Streetcode/Streetcode.BLL/MediatR/Authentication/LoginGoogle/LoginGoogleHandler.cs (1 hunks)
  • Streetcode/Streetcode.BLL/MediatR/Authentication/Logout/LogoutCommand.cs (1 hunks)
  • Streetcode/Streetcode.BLL/MediatR/Authentication/Logout/LogoutHandler.cs (2 hunks)
  • Streetcode/Streetcode.BLL/MediatR/Email/SendEmailHandler.cs (0 hunks)
  • Streetcode/Streetcode.BLL/MediatR/Instagram/GetAll/GetAllPostsHandler.cs (0 hunks)
  • Streetcode/Streetcode.BLL/MediatR/Instagram/GetAll/GetAllPostsQuery.cs (0 hunks)
  • Streetcode/Streetcode.BLL/MediatR/Users/ExistWithUserName/ExistWithUserNameHandler.cs (1 hunks)
  • Streetcode/Streetcode.BLL/MediatR/Users/ExistWithUserName/ExistWithUserNameQuery.cs (1 hunks)
  • Streetcode/Streetcode.BLL/MediatR/Users/ForgotPassword/ForgotPasswordHandler.cs (3 hunks)
  • Streetcode/Streetcode.BLL/MediatR/Users/GetByEmail/GetByEmailHandler.cs (4 hunks)
  • Streetcode/Streetcode.BLL/MediatR/Users/GetByEmail/GetByEmailQuery.cs (1 hunks)
  • Streetcode/Streetcode.BLL/MediatR/Users/GetByName/GetByUserNameQuery.cs (0 hunks)
  • Streetcode/Streetcode.BLL/MediatR/Users/GetOtherUserByUserName/GetOtherUserByUserNameHandler.cs (2 hunks)
  • Streetcode/Streetcode.BLL/MediatR/Users/GetOtherUserByUserName/GetOtherUserByUserNameQuery.cs (1 hunks)
  • Streetcode/Streetcode.BLL/MediatR/Users/Update/UpdateUserHandler.cs (3 hunks)
  • Streetcode/Streetcode.BLL/MediatR/Users/UpdateForgotPassword/UpdateForgotPasswordHandler.cs (2 hunks)
  • Streetcode/Streetcode.BLL/Services/Authentication/GoogleService.cs (1 hunks)
  • Streetcode/Streetcode.BLL/Services/Instagram/InstagramService.cs (2 hunks)
  • Streetcode/Streetcode.BLL/Streetcode.BLL.csproj (2 hunks)
  • Streetcode/Streetcode.BLL/Util/Helpers/HttpContextHelper.cs (1 hunks)
  • Streetcode/Streetcode.BLL/Util/Helpers/UserHelper.cs (2 hunks)
  • Streetcode/Streetcode.BLL/Validators/Analytics/StatisticRecordValidator.cs (0 hunks)
  • Streetcode/Streetcode.BLL/Validators/Users/BaseUserValidator.cs (2 hunks)
  • Streetcode/Streetcode.WebApi/Controllers/Analytics/StatisticRecordController.cs (0 hunks)
  • Streetcode/Streetcode.WebApi/Controllers/Authentication/AuthController.cs (3 hunks)
  • Streetcode/Streetcode.WebApi/Controllers/Instagram/InstagramController.cs (0 hunks)
  • Streetcode/Streetcode.WebApi/Controllers/Users/UsersController.cs (2 hunks)
  • Streetcode/Streetcode.WebApi/Extensions/ServiceCollectionExtensions.cs (1 hunks)
  • Streetcode/Streetcode.WebApi/appsettings.IntegrationTests.json (1 hunks)
  • Streetcode/Streetcode.WebApi/appsettings.Local.json (1 hunks)
  • Streetcode/Streetcode.XIntegrationTest/Constants/ControllerTests/AuthConstants.cs (2 hunks)
  • Streetcode/Streetcode.XIntegrationTest/ControllerTests/AdditionalContent/TagControllerTests.cs (1 hunks)
  • Streetcode/Streetcode.XIntegrationTest/ControllerTests/AdditionalContent/TeamPositionsControllerTests.cs (1 hunks)
  • Streetcode/Streetcode.XIntegrationTest/ControllerTests/Analytics/StatisticRecordControllerTests.cs (1 hunks)
  • Streetcode/Streetcode.XIntegrationTest/ControllerTests/Authentication/AuthControllerTests.cs (4 hunks)
  • Streetcode/Streetcode.XIntegrationTest/ControllerTests/BaseController/BaseAuthorizationControllerTests.cs (2 hunks)
  • Streetcode/Streetcode.XIntegrationTest/ControllerTests/Email/EmailControllerTests.cs (1 hunks)
  • Streetcode/Streetcode.XIntegrationTest/ControllerTests/Jobs/Create/JobCreateControllerTests.cs (1 hunks)
  • Streetcode/Streetcode.XIntegrationTest/ControllerTests/News/NewsGetAllControllerTests.cs (0 hunks)
  • Streetcode/Streetcode.XIntegrationTest/ControllerTests/Partners/PartnersControllerTests.cs (1 hunks)
  • Streetcode/Streetcode.XIntegrationTest/ControllerTests/Streetcode/StreetcodeCreateControllerTests.cs (1 hunks)
  • Streetcode/Streetcode.XIntegrationTest/ControllerTests/Streetcode/StreetcodeUpdateControllerTests.cs (1 hunks)
  • Streetcode/Streetcode.XIntegrationTest/ControllerTests/Timeline/HistoricalContextControllerTests.cs (1 hunks)
  • Streetcode/Streetcode.XIntegrationTest/ControllerTests/Users/Expertises/ExpertiseControllerTests.cs (1 hunks)
  • Streetcode/Streetcode.XIntegrationTest/ControllerTests/Users/UserControllerTests.cs (1 hunks)
  • Streetcode/Streetcode.XIntegrationTest/ControllerTests/Utils/AuthorizationFixture/AuthorizationCollection.cs (1 hunks)
  • Streetcode/Streetcode.XIntegrationTest/ControllerTests/Utils/AuthorizationFixture/TokenStorage.cs (3 hunks)
  • Streetcode/Streetcode.XIntegrationTest/ControllerTests/Utils/BeforeAndAfterTestAtribute/Streetcode/ExtractCreateTestStreetcodeAttribute.cs (0 hunks)
  • Streetcode/Streetcode.XIntegrationTest/ControllerTests/Utils/BeforeAndAfterTestAtribute/Users/ExtractUpdateTestUserAttribute.cs (1 hunks)
  • Streetcode/Streetcode.XIntegrationTest/ControllerTests/Utils/Client/Analytics/AnalyticsClient.cs (1 hunks)
  • Streetcode/Streetcode.XIntegrationTest/ControllerTests/Utils/Client/Authentication/AuthenticationClient.cs (2 hunks)
  • Streetcode/Streetcode.XIntegrationTest/ControllerTests/Utils/Client/Base/BaseClient.cs (3 hunks)
  • Streetcode/Streetcode.XIntegrationTest/ControllerTests/Utils/Client/Email/EmailClient.cs (1 hunks)
  • Streetcode/Streetcode.XIntegrationTest/ControllerTests/Utils/Client/Users/Expertises/ExpertiseClient.cs (1 hunks)
  • Streetcode/Streetcode.XIntegrationTest/ControllerTests/Utils/Client/Users/UserClient.cs (1 hunks)
  • Streetcode/Streetcode.XIntegrationTest/ControllerTests/Utils/CustomWebApplicationFactory.cs (1 hunks)
  • Streetcode/Streetcode.XIntegrationTest/ControllerTests/Utils/Extracter/Analytics/StatisticRecordExtracter.cs (1 hunks)
  • Streetcode/Streetcode.XIntegrationTest/ControllerTests/Utils/Extracter/Authentication/UserExtracter.cs (1 hunks)
  • Streetcode/Streetcode.XIntegrationTest/ControllerTests/Utils/Extracter/Expertises/ExpertiseExtracter.cs (1 hunks)
  • Streetcode/Streetcode.XIntegrationTest/ControllerTests/Utils/SqlDbHelper.cs (2 hunks)
  • Streetcode/Streetcode.XIntegrationTest/Streetcode.XIntegrationTest.csproj (2 hunks)
  • Streetcode/Streetcode.XUnitTest/Factories/MessageDataFactory/MessageDataConcreteFactoryTests.cs (1 hunks)
  • Streetcode/Streetcode.XUnitTest/MediatRTests/Analytics/DeleteStatisticRecordTests.cs (1 hunks)
  • Streetcode/Streetcode.XUnitTest/MediatRTests/Analytics/ExistStatisticRecordByQrIdTests.cs (1 hunks)
  • Streetcode/Streetcode.XUnitTest/MediatRTests/Analytics/GetAllStatisticRecordsByStreetcodeIdTests.cs (1 hunks)
  • Streetcode/Streetcode.XUnitTest/MediatRTests/Analytics/GetAllStatisticRecordsTests.cs (1 hunks)
  • Streetcode/Streetcode.XUnitTest/MediatRTests/Analytics/GetStatisticRecordByQrIdTests.cs (1 hunks)
  • Streetcode/Streetcode.XUnitTest/MediatRTests/Analytics/UpdateCountStatisticRecordTests.cs (1 hunks)
  • Streetcode/Streetcode.XUnitTest/MediatRTests/Authentication/Logout/LogoutHandlerTests.cs (1 hunks)
  • Streetcode/Streetcode.XUnitTest/MediatRTests/Email/SendEmailHandlerTests.cs (6 hunks)
  • Streetcode/Streetcode.XUnitTest/MediatRTests/Users/DeleteUserHandlerTests.cs (1 hunks)
  • Streetcode/Streetcode.XUnitTest/MediatRTests/Users/ExistWithUserNameHandlerTests.cs (1 hunks)
  • Streetcode/Streetcode.XUnitTest/MediatRTests/Users/ForgotPasswordTests.cs (1 hunks)
  • Streetcode/Streetcode.XUnitTest/MediatRTests/Users/GetByUserNameTest.cs (1 hunks)
  • Streetcode/Streetcode.XUnitTest/MediatRTests/Users/GetOtherUserByUserNameHandlerTests.cs (1 hunks)
  • Streetcode/Streetcode.XUnitTest/MediatRTests/Users/UpdateForgotPasswordHandlerTests.cs (1 hunks)
  • Streetcode/Streetcode.XUnitTest/MediatRTests/Users/UpdateUserTest.cs (1 hunks)
  • Streetcode/Streetcode.XUnitTest/Mocks/MockFailedToValidateLocalizer.cs (3 hunks)
  • Streetcode/Streetcode.XUnitTest/Mocks/MockFieldNamesLocalizer.cs (1 hunks)
  • Streetcode/Streetcode.XUnitTest/Mocks/MockHelpers.cs (3 hunks)
  • Streetcode/Streetcode.XUnitTest/Mocks/MockUserSharedResourceLocalizer.cs (1 hunks)
  • Streetcode/Streetcode.XUnitTest/Streetcode.XUnitTest.csproj (2 hunks)
  • Streetcode/Streetcode.XUnitTest/Validators/Email/EmailTests.cs (1 hunks)
  • Streetcode/Streetcode.XUnitTest/Validators/Users/BaseUserValidatorTests.cs (1 hunks)
  • Streetcode/Streetcode.XUnitTest/Validators/Users/UpdateUserValidatorTests.cs (1 hunks)
💤 Files with no reviewable changes (11)
  • Streetcode/Streetcode.BLL/MediatR/Email/SendEmailHandler.cs
  • Streetcode/Streetcode.BLL/MediatR/Instagram/GetAll/GetAllPostsQuery.cs
  • Streetcode/Streetcode.XIntegrationTest/ControllerTests/Utils/BeforeAndAfterTestAtribute/Streetcode/ExtractCreateTestStreetcodeAttribute.cs
  • Streetcode/Streetcode.BLL/MediatR/Instagram/GetAll/GetAllPostsHandler.cs
  • Streetcode/Streetcode.WebApi/Controllers/Analytics/StatisticRecordController.cs
  • Streetcode/Streetcode.WebApi/Controllers/Instagram/InstagramController.cs
  • Streetcode/Streetcode.XIntegrationTest/ControllerTests/News/NewsGetAllControllerTests.cs
  • Streetcode/Streetcode.BLL/Validators/Analytics/StatisticRecordValidator.cs
  • Streetcode/Streetcode.BLL/MediatR/Analytics/StatisticRecord/Create/CreateStatisticRecordCommand.cs
  • Streetcode/Streetcode.BLL/MediatR/Users/GetByName/GetByUserNameQuery.cs
  • Streetcode/Streetcode.BLL/MediatR/Analytics/StatisticRecord/Create/CreateStatisticRecordHandler.cs
✅ Files skipped from review due to trivial changes (11)
  • Streetcode/Streetcode.XIntegrationTest/ControllerTests/Utils/AuthorizationFixture/AuthorizationCollection.cs
  • Streetcode/Streetcode.BLL/MediatR/Users/ExistWithUserName/ExistWithUserNameHandler.cs
  • Streetcode/Streetcode.XIntegrationTest/ControllerTests/Streetcode/StreetcodeUpdateControllerTests.cs
  • Streetcode/Streetcode.XIntegrationTest/ControllerTests/Partners/PartnersControllerTests.cs
  • Streetcode/Streetcode.WebApi/appsettings.Local.json
  • Streetcode/Streetcode.BLL/MediatR/Users/GetOtherUserByUserName/GetOtherUserByUserNameQuery.cs
  • Streetcode/Streetcode.XUnitTest/Mocks/MockFieldNamesLocalizer.cs
  • Streetcode/Streetcode.XIntegrationTest/ControllerTests/AdditionalContent/TeamPositionsControllerTests.cs
  • Streetcode/Streetcode.BLL/MediatR/Users/ExistWithUserName/ExistWithUserNameQuery.cs
  • Streetcode/Streetcode.XIntegrationTest/ControllerTests/AdditionalContent/TagControllerTests.cs
  • Streetcode/Streetcode.XIntegrationTest/ControllerTests/Timeline/HistoricalContextControllerTests.cs
🔇 Additional comments (40)
Streetcode/Streetcode.XUnitTest/Streetcode.XUnitTest.csproj (2)

34-34: Good addition of MockQueryable.Moq package.

The addition of MockQueryable.Moq 7.0.3 aligns well with the project's .NET 7.0 target framework and will enhance the testing capabilities for IQueryable scenarios, particularly useful for the new authentication and user management features.


54-57: Well-structured test organization.

The new folder structure effectively supports the PR's focus on authentication improvements:

  • MessageDataFactory for test data generation
  • Authentication/Logout for logout functionality tests
  • Users for user management tests
  • Validators/Users for user validation tests

This organization promotes better test maintainability and clear separation of concerns.

Streetcode/Streetcode.XIntegrationTest/ControllerTests/Jobs/Create/JobCreateControllerTests.cs (2)

7-7: LGTM! Good enhancement to authorization testing.

The addition of AuthorizationFixture import aligns well with the broader improvements to authorization testing across the codebase. This change helps maintain consistency in how authorization is handled in tests.


28-98: Excellent test coverage and structure!

The test suite demonstrates thorough coverage of job creation scenarios:

  • Success case with proper authorization
  • Unauthorized access handling
  • Role-based access control (admin vs non-admin)
  • Input validation
  • Response verification

The consistent use of the Arrange-Act-Assert pattern and meaningful test names makes the test intentions clear and maintainable.

Streetcode/Streetcode.BLL/Streetcode.BLL.csproj (2)

11-15: LGTM! Clean removal of Instagram-related items.

The removal of Instagram-related items is clean and comprehensive, covering all associated compile, embedded resource, and none items.


51-51: LGTM! New folder structure for Google authentication.

The addition of the GoogleLogin folder aligns well with the implementation of Google sign-in components.

Streetcode/Streetcode.BLL/MediatR/Analytics/StatisticRecord/Delete/DeleteStatisticRecordHandler.cs (2)

43-43: LGTM! Good async/await practice.

The switch to SaveChangesAsync() follows best practices for database operations, improving scalability by preventing thread blocking during I/O operations.


1-58: Verify the scope of changes against PR objectives.

The changes in this file (async refactoring and mapper removal) don't appear to be directly related to the PR's stated objective of updating reCAPTCHA from v2 to v3. Could you clarify if this is part of a broader refactoring effort?

Streetcode/Streetcode.XIntegrationTest/ControllerTests/Utils/Extracter/Analytics/StatisticRecordExtracter.cs (1)

4-7: Well-structured test utility class!

The class follows good practices with a clear namespace organization and appropriate use of the static modifier for utility methods.

Streetcode/Streetcode.XIntegrationTest/ControllerTests/Analytics/StatisticRecordControllerTests.cs (1)

184-192: LGTM! Well-structured cleanup.

The Dispose method properly handles cleanup of test resources and follows the dispose pattern correctly.

Streetcode/Streetcode.BLL/MediatR/Analytics/StatisticRecord/ExistByQrId/ExistStatisticRecordByQrIdHandler.cs (2)

13-16: LGTM! Constructor simplified appropriately.

Good job removing the unused IMapper dependency. The constructor now only includes the necessary IRepositoryWrapper dependency, following the YAGNI principle.


18-24: LGTM! Clean and efficient implementation.

The Handle method is well-implemented with:

  • Proper async/await usage
  • Clear null check using pattern matching
  • Appropriate use of Result type for error handling
Streetcode/Streetcode.XUnitTest/MediatRTests/Analytics/ExistStatisticRecordByQrIdTests.cs (2)

13-21: LGTM! Well-structured test setup.

Good job setting up the test class with:

  • Clean mock initialization
  • Proper dependency injection in constructor
  • Reusable mock repository for all tests

23-50: LGTM! Comprehensive positive test case.

The test effectively verifies the success scenario with:

  • Clear AAA pattern
  • Appropriate mock setup
  • Thorough assertions
Streetcode/Streetcode.BLL/MediatR/Analytics/StatisticRecord/GetAll/GetAllStatisticRecordsQuery.cs (1)

7-8: LGTM! Clean implementation of MediatR query.

The record correctly implements IRequest with a Result wrapper, following best practices for error handling.

Streetcode/Streetcode.XUnitTest/MediatRTests/Email/SendEmailHandlerTests.cs (2)

20-39: Update tests to align with reCAPTCHA v3 implementation.

The test class is currently designed for reCAPTCHA v2, but the PR objectives mention updating to v3. The tests should be updated to reflect v3's automatic verification approach, which may require different mock setups and validation logic.

Could you confirm if these tests need to be updated for reCAPTCHA v3? I can help suggest the necessary changes based on the v3 implementation details.


127-183: Helper methods are well-structured and focused!

The helper methods follow good practices:

  • Single responsibility principle
  • Clear naming
  • Focused setup methods
Streetcode/Streetcode.BLL/MediatR/Authentication/Logout/LogoutCommand.cs (1)

6-6: LGTM! Clean and focused command implementation.

The simplified parameterless command aligns well with CQRS principles, allowing the handler to manage user identification through IHttpContextAccessor.

Let's verify that all consumers have been updated to use the new parameterless constructor:

✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Search for any remaining LogoutCommand instantiations that might still try to pass parameters
rg -l "new LogoutCommand\(" | xargs rg "new LogoutCommand\([^)]+\)"

Length of output: 69


Consumer usage of LogoutCommand verified – no parameterized instantiations found.

The search script did not return any instances where LogoutCommand is instantiated with parameters, which indicates that all consumers have been updated to the new parameterless constructor. This confirms that the change aligns with the intended authentication refactoring. Please continue to monitor new changes to ensure consistency.

Streetcode/Streetcode.WebApi/Extensions/ServiceCollectionExtensions.cs (1)

71-71: Add Google.Apis.Auth NuGet package for GoogleJsonWebSignature.

The GoogleService registration looks good, but ensure the required Google authentication package is added to the project.

Run this script to check if the package is properly referenced:

✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Check if Google.Apis.Auth package is referenced in the project
rg -l "Google.Apis.Auth" --type csproj

Length of output: 69


I'll generate a new verification script to search all *.csproj files for a reference to "Google.Apis.Auth" since the previous command using the csproj file type was not recognized. Please run the following script:

Once executed, check if any csproj file lists the "Google.Apis.Auth" package. If no references are found, then the package needs to be added to the project dependencies to support GoogleJsonWebSignature as used in the GoogleService integration.

Please confirm the results after running this script so that we can ensure that the project's dependencies are complete.


🏁 Script executed:

#!/bin/bash
# Search for the Google.Apis.Auth package reference in all .csproj files
find . -type f -name "*.csproj" -exec grep -H "Google.Apis.Auth" {} \;

Length of output: 192


Google Service Dependency is Satisfied

The new scoped registration for IGoogleService is well-placed, and our verification shows that the Google.Apis.Auth package is already referenced in the solution (specifically in Streetcode.BLL/Streetcode.BLL.csproj). Please ensure that the project using GoogleService (i.e., Streetcode.WebApi) either directly includes the package or accesses it transitively via a reference to Streetcode.BLL.

Streetcode/Streetcode.XUnitTest/MediatRTests/Analytics/DeleteStatisticRecordTests.cs (1)

91-121: Well-implemented failure scenario test!

The test effectively covers the failed deletion scenario with appropriate verifications of error messages and repository calls.

Streetcode/Streetcode.BLL/MediatR/Users/UpdateForgotPassword/UpdateForgotPasswordHandler.cs (1)

17-25: LGTM! Constructor refactoring improves maintainability.

The simplified constructor with focused dependencies follows SOLID principles and improves maintainability.

Streetcode/Streetcode.BLL/MediatR/Users/ForgotPassword/ForgotPasswordHandler.cs (2)

26-42: Well-structured constructor with clear dependency injection!

The constructor follows best practices with clear dependency injection and proper initialization of all fields.


59-66: Good security practice with URL encoding!

Properly encoding both the token and username prevents potential URL injection vulnerabilities and ensures special characters are handled correctly.

Streetcode/Streetcode.XIntegrationTest/ControllerTests/Utils/AuthorizationFixture/TokenStorage.cs (1)

51-68: Well-implemented dispose pattern!

The dispose pattern implementation follows best practices with proper null checks, disposed flag, and finalization suppression.

Streetcode/Streetcode.XIntegrationTest/Streetcode.XIntegrationTest.csproj (1)

33-33: Verify RestSharp version upgrade compatibility.

The upgrade from preview version 109.0.0-preview.1 to stable version 112.1.0 is significant. Please ensure compatibility with existing code.

❌ Verification inconclusive

🌐 Web query:

What are the breaking changes in RestSharp 112.1.0 compared to version 109?

💡 Result:

RestSharp 112.1.0 introduces several breaking changes compared to version 109, primarily focused on thread safety, API simplification, and modernized dependencies. Here are the key differences:

Core architectural changes

  • HTTP client modernization: Complete replacement of legacy HttpWebRequest with HttpClient (started in v107)[1][7]
  • Immutable client configuration: RestClientOptions became read-only after instantiation to ensure thread safety[4][7]
  • Interface reduction: The IRestClient interface was significantly simplified across versions (deprecated in v107 then modified again in v110/112)[2][5]

Breaking API changes

Feature Version 109 Version 112.1.0
Timeout configuration MaxTimeout property Renamed to Timeout with TimeSpan type[3]
Cookie handling Per-request cookies Added client-level cookie container[2]
Serialization Mixed JSON implementations Full transition to System.Text.Json[1][7]
Authentication NtlmAuthenticator class Replaced with Credentials in RestClientOptions[1]
Request configuration Mutable client properties Locked behind RestClientOptions constructor[4][7]

Specific breaking changes

  1. Interface signature changes

    • IRestClient became narrower with reduced surface area[2][5]
    • Custom implementations require adaptation for new method signatures[5]
  2. Parameter handling

    • AddParameter("application/json", ...) pattern deprecated in favor of AddBody[1]
    • Header management automated (manual Content-Type/Accept headers discouraged)[1]
  3. Security enhancements

    • CRLF sequences in headers now prohibited (v112)[5]
    • Cookie parsing became stricter about RFC compliance[4]
  4. Thread safety improvements

    • DefaultParameters collection uses locking for thread-safe mutations[2]
    • All client configuration must be set during initialization via RestClientOptions[4][7]
  5. Authentication changes

    • NtlmAuthenticator removed in favor of handler-level credential configuration[1]
    • OAuth1 signature handling improved for special characters[5]

Migration requirements

  • Client instantiation must use RestClientOptions instead of property setters:
    // Old (v109)
    var client = new RestClient();
    client.Timeout = 1000;
    
    // New (v112+)
    var options = new RestClientOptions { Timeout = TimeSpan.FromSeconds(1) };
    var client = new RestClient(options);
  • File uploads require using Stream parameters instead of byte arrays[1]
  • Custom serializers must implement updated interfaces from System.Text.Json[1][7]

These changes address long-standing issues with connection pooling, async handling, and HTTP/2 support while modernizing RestSharp's architecture[1][3][5]. Developers should audit their use of client configuration, parameter handling, and authentication when upgrading.[1][2][4][7]

Citations:


Action Required: Confirm Integration Updates for RestSharp Breaking Changes

The update from RestSharp 109 (preview) to 112.1.0 introduces several breaking changes—such as new client instantiation via immutable RestClientOptions, revised timeout and parameter handling, and interface signature updates—that may require corresponding adjustments in our integration test code. Please review integration test methods where RestSharp is used and ensure that any instantiations, configuration, or parameter usage align with the new API conventions.

Streetcode/Streetcode.XUnitTest/Validators/Email/EmailTests.cs (3)

18-20: LGTM! Clean initialization of dependencies.

The removal of the this keyword simplifies the code while maintaining clear initialization of mocks and validator.


24-24: LGTM! Improved test method naming.

The new test method names follow a clear pattern (Validate_Condition_ExpectedResult) that makes the test purpose immediately obvious.

Also applies to: 37-37, 53-53, 69-69, 85-85, 101-101


85-98: Consider updating token validation for reCAPTCHA v3.

Since the PR objectives mention transitioning from reCAPTCHA v2 to v3, this test might need updates to validate the new token format and handling.

Let's verify if there are any other reCAPTCHA-related tests that might need updates:

❌ Verification inconclusive

🏁 Script executed:

#!/bin/bash
# Search for reCAPTCHA-related tests
rg -l "captcha|recaptcha|token" --type cs -g "*test*.cs"

Length of output: 58


Below is a new comprehensive search script to cover potential case sensitivity issues. Please run it to verify if any additional reCAPTCHA‑ or token‑related tests exist:

Next steps for you:

  • If additional tests are found: Review them to ensure they accommodate the new reCAPTCHA v3 behavior.
  • If no other tests appear: Confirm that the only test affected is in Streetcode/Streetcode.XUnitTest/Validators/Email/EmailTests.cs, and then decide if the reCAPTCHA v3 update requires modifying the existing test or adding new ones.

Please verify the output of the above search so we can determine the next course of action.


🏁 Script executed:

[run_scripts]
#!/bin/bash
# Perform a comprehensive, case-insensitive search across all C# files for the terms "captcha", "recaptcha", or "token"
rg -l "(?i)(captcha|recaptcha|token)" --type cs

Length of output: 34671


Below is an additional shell script to search the repository for any references to "recaptcha v3" (or similar) to see if updated token validation has been implemented elsewhere:

Next steps for you:

  • If references are found: Investigate how the token is expected to behave under reCAPTCHA v3, and update the test and/or validators accordingly.
  • If no references are found: Confirm that this test remains the only point for reCAPTCHA-related validation and decide if additional test cases (e.g. new token format checks) are needed for the updated reCAPTCHA v3 behavior.

Please verify the output of the above search so we can decide on the proper adjustment strategy.


🏁 Script executed:

[run_scripts]
#!/bin/bash
# Search for references to "recaptcha v3"
rg -i "recaptcha v3"

Length of output: 144


Action Required: Confirm ReCAPTCHA Version Implementation

Based on our searches:

  • We did not find any explicit references or modifications indicating reCAPTCHA v3–specific handling in the repository.
  • The current test in Streetcode/Streetcode.XUnitTest/Validators/Email/EmailTests.cs still verifies that an empty token produces the expected error "CannotBeEmpty" for the captcha token.

Recommendations:

  • If the update to reCAPTCHA v3 is already implemented elsewhere:
    The test should be updated to validate the new expected token format or behavior (e.g., checking token verification rules specific to reCAPTCHA v3).

  • If reCAPTCHA v3 changes have not yet been introduced:
    The current test remains valid until the reCAPTCHA service is fully transitioned to v3. Consider adding new tests that specifically cover the valid token format or behavior expected from reCAPTCHA v3 once that implementation is available.

Please confirm with the team or compare with the updated requirements to decide whether to revise this test or supplement it with additional cases.

Streetcode/Streetcode.XIntegrationTest/ControllerTests/Utils/Client/Email/EmailClient.cs (1)

9-12: LGTM! Constructor properly initializes the base class.

The constructor correctly passes the HttpClient and optional URL to the base class.

Streetcode/Streetcode.BLL/Util/Helpers/UserHelper.cs (1)

10-10: Verify null handling for Email property.

The null-forgiving operator (!) assumes Email won't be null, but this should be explicitly validated.

-var cleanEmail = RemoveNonAlphaNumericFromEmail(user.Email!);
+if (string.IsNullOrEmpty(user.Email))
+{
+    throw new ArgumentException("Email cannot be null or empty", nameof(user));
+}
+var cleanEmail = RemoveNonAlphaNumericFromEmail(user.Email);
Streetcode/Streetcode.XUnitTest/Mocks/MockFailedToValidateLocalizer.cs (1)

21-23: LGTM! Good addition of validation messages

The new validation messages for username format, name format, surname format, expertise limit, and length constraints are well-organized within their respective groups.

Also applies to: 40-40, 51-51

Streetcode/Streetcode.BLL/Services/Instagram/InstagramService.cs (1)

11-12: LGTM! Good use of IHttpClientFactory

The switch from direct HttpClient instantiation to IHttpClientFactory is a best practice that helps manage connection pooling and lifetime.

Also applies to: 17-20

Streetcode/Streetcode.BLL/MediatR/Analytics/StatisticRecord/UpdateCount/UpdateCountStatisticRecordHandler.cs (1)

47-47: LGTM! Good async implementation.

The transition to async/await pattern is well-implemented, maintaining consistency with other handlers.

Streetcode/Streetcode.XIntegrationTest/Constants/ControllerTests/AuthConstants.cs (1)

36-39: LGTM! Well-structured normalized fields.

The addition of normalized email and username fields follows Identity best practices for case-insensitive comparisons.

Also applies to: 49-52

Streetcode/Streetcode.BLL/MediatR/Users/Update/UpdateUserHandler.cs (3)

27-33: LGTM! Improved constructor readability.

The constructor parameters have been nicely formatted with one parameter per line, enhancing code readability.


50-50: LGTM! Added cancellation support.

The addition of cancellationToken to SingleOrDefaultAsync improves the handling of async operation cancellations.


81-81: Verify null safety of FirstOrDefault call.

The null-forgiving operator (!) suggests you're certain GetRolesAsync will always return a non-null collection with at least one role. Please verify this assumption.

❌ Verification inconclusive

🏁 Script executed:

#!/bin/bash
# Search for role assignments to verify if users always have at least one role
ast-grep --pattern 'await _userManager.AddToRoleAsync($user, $_)'

Length of output: 178


I'll run an updated search that excludes JSON files (which might be tripping up our initial script) to look for any instance of role assignment via AddToRoleAsync in the repository. This should give us a better picture of whether users are always assigned a role.


🏁 Script executed:

#!/bin/bash
rg -n "await _userManager.AddToRoleAsync" -g '!*.json'

Length of output: 388


Action: Confirm that all user creation paths securely assign a role or add a safeguard for missing roles.

Our search shows that role assignment (via AddToRoleAsync) is applied in both the registration and login handlers—suggesting that new users should receive a role. However, this evidence alone doesn’t guarantee that every user, including legacy or exceptional cases, always has at least one role. It might be beneficial to either add explicit null checking/fallback logic or enforce a constraint that prevents any user from existing without a role.

Streetcode/Streetcode.XUnitTest/MediatRTests/Authentication/Logout/LogoutHandlerTests.cs (1)

45-62: LGTM! Well-structured test for successful logout.

The test properly verifies the success path with good assertions and mocking setup.

Streetcode/Streetcode.XUnitTest/MediatRTests/Users/DeleteUserHandlerTests.cs (1)

24-51: LGTM! Well-structured test setup.

The test constructor properly initializes all required mocks and dependencies.

Streetcode/Streetcode.XUnitTest/MediatRTests/Analytics/UpdateCountStatisticRecordTests.cs (1)

34-60: LGTM! Comprehensive success scenario test.

The test properly verifies count increment and repository interactions.

Comment on lines 23 to 33
[Fact]
public async Task GetAll_Expertises_ReturnsSuccessStatusCode()
{
// Act
var response = await Client.GetAll();
var returnedValue = CaseIsensitiveJsonDeserializer.Deserialize<List<ExpertiseDTO>>(response.Content);

// Assert
Assert.True(response.IsSuccessStatusCode);
Assert.NotNull(returnedValue);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance test coverage with additional test cases.

While the basic success case is covered, consider adding:

  1. Edge cases (empty list, large result set)
  2. Response content validation
  3. Error scenarios

Example additions:

[Fact]
public async Task GetAll_WhenNoExpertises_ReturnsEmptyList()
{
    // Arrange: Ensure no expertises exist
    
    // Act
    var response = await Client.GetAll();
    var returnedValue = CaseIsensitiveJsonDeserializer.Deserialize<List<ExpertiseDTO>>(response.Content);
    
    // Assert
    Assert.True(response.IsSuccessStatusCode);
    Assert.Empty(returnedValue);
}

[Fact]
public async Task GetAll_ValidatesResponseContent()
{
    // Arrange: Add test expertise
    await Client.Create(_testExpertise);
    
    // Act
    var response = await Client.GetAll();
    var returnedValue = CaseIsensitiveJsonDeserializer.Deserialize<List<ExpertiseDTO>>(response.Content);
    
    // Assert
    Assert.True(response.IsSuccessStatusCode);
    Assert.Contains(returnedValue, e => e.Id == _testExpertise.Id);
    // Add more specific assertions for expertise properties
}

Comment on lines 34 to 48
[Fact]
public async Task Validate_WhenCalled_ShouldCallBaseValidator()
{
// Arrange
var createValidator = new UpdateUserValidator(mockBaseValidator.Object, _mockRepositoryWrapper.Object, mockUserSharedResourceLocalizer);
var user = GetValidUser();
var createCommand = new UpdateUserCommand(user);
MockHelpers.SetupMockUserRepositoryGetFirstOfDefaultAsync(_mockRepositoryWrapper, user.UserName);

// Act
await createValidator.TestValidateAsync(createCommand);

// Assert
mockBaseValidator.Verify(x => x.ValidateAsync(It.IsAny<ValidationContext<UpdateUserDTO>>(), CancellationToken.None), Times.Once);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Expand test coverage with additional test cases.

While the current test verifies the base validator call, consider adding these test cases:

  1. Validation failure scenarios
  2. Duplicate username validation
  3. Invalid email format validation
  4. Empty required fields validation

Here's an example of an additional test case:

[Fact]
public async Task Validate_WithDuplicateUsername_ShouldReturnValidationError()
{
    // Arrange
    var createValidator = new UpdateUserValidator(mockBaseValidator.Object, _mockRepositoryWrapper.Object, mockUserSharedResourceLocalizer);
    var user = GetValidUser();
    var createCommand = new UpdateUserCommand(user);
    
    // Setup mock to simulate existing user
    MockHelpers.SetupMockUserRepositoryGetFirstOfDefaultAsync(_mockRepositoryWrapper, user.UserName, returnExistingUser: true);

    // Act
    var result = await createValidator.TestValidateAsync(createCommand);

    // Assert
    result.ShouldHaveValidationErrorFor(x => x.User.UserName);
}

Comment on lines 11 to 24
{
0, new List<string>
{
"UserWithSuchUsernameNotExists",
"UserWithSuchUsernameExists",
"UserWithSuchEmailNotFound",
"UserWithSuchEmailExists",
"UserNotFound",
"UserNameOrTokenIsEmpty",
"UserManagerError",
"IncorrectPassword",
"EmailNotMatch",
}
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider adding reCAPTCHA and Google authentication error messages.

Given the PR's focus on implementing reCAPTCHA v3 and Google authentication, consider adding relevant error messages to handle these scenarios:

     {
         0, new List<string>
         {
             "UserWithSuchUsernameNotExists",
             "UserWithSuchUsernameExists",
             "UserWithSuchEmailNotFound",
             "UserWithSuchEmailExists",
             "UserNotFound",
             "UserNameOrTokenIsEmpty",
             "UserManagerError",
             "IncorrectPassword",
             "EmailNotMatch",
+            "RecaptchaValidationFailed",
+            "RecaptchaScoreTooLow",
+            "GoogleTokenValidationFailed",
+            "GoogleAuthenticationFailed",
         }
     },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{
0, new List<string>
{
"UserWithSuchUsernameNotExists",
"UserWithSuchUsernameExists",
"UserWithSuchEmailNotFound",
"UserWithSuchEmailExists",
"UserNotFound",
"UserNameOrTokenIsEmpty",
"UserManagerError",
"IncorrectPassword",
"EmailNotMatch",
}
},
{
0, new List<string>
{
"UserWithSuchUsernameNotExists",
"UserWithSuchUsernameExists",
"UserWithSuchEmailNotFound",
"UserWithSuchEmailExists",
"UserNotFound",
"UserNameOrTokenIsEmpty",
"UserManagerError",
"IncorrectPassword",
"EmailNotMatch",
"RecaptchaValidationFailed",
"RecaptchaScoreTooLow",
"GoogleTokenValidationFailed",
"GoogleAuthenticationFailed",
}
},

Comment on lines 66 to 89
[Fact]
public async Task Handle_RecordIsNotFound_ShouldReturnError()
{
// Arrange
var qrId = -1;

_repositoryWrapperMock.Setup(repo => repo.StatisticRecordRepository.GetFirstOrDefaultAsync(
It.IsAny<Expression<Func<StatisticRecord, bool>>>(),
It.IsAny<Func<IQueryable<StatisticRecord>, IIncludableQueryable<StatisticRecord, object>>>()))
.ReturnsAsync((StatisticRecord)null!);

_localizerCannotFindMock.Setup(loc => loc["CannotFindRecordWithQrId"]).Returns(new LocalizedString("CannotFindRecordWithQrId", "Record not found"));

var request = new DeleteStatisticRecordCommand(qrId);

// Act
var result = await _handler.Handle(request, CancellationToken.None);

// Assert
Assert.False(result.IsSuccess);
Assert.Contains("Record not found", result.Errors[0].Message);
_repositoryWrapperMock.Verify(repo => repo.StatisticRecordRepository.Delete(It.IsAny<StatisticRecord>()), Times.Never);
_repositoryWrapperMock.Verify(repo => repo.SaveChanges(), Times.Never);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix inconsistent async method verification.

There's an inconsistency in the repository mock verification. The test verifies SaveChanges() but the handler uses SaveChangesAsync().

     // Assert
     Assert.False(result.IsSuccess);
     Assert.Contains("Record not found", result.Errors[0].Message);
     _repositoryWrapperMock.Verify(repo => repo.StatisticRecordRepository.Delete(It.IsAny<StatisticRecord>()), Times.Never);
-    _repositoryWrapperMock.Verify(repo => repo.SaveChanges(), Times.Never);
+    _repositoryWrapperMock.Verify(repo => repo.SaveChangesAsync(), Times.Never);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
[Fact]
public async Task Handle_RecordIsNotFound_ShouldReturnError()
{
// Arrange
var qrId = -1;
_repositoryWrapperMock.Setup(repo => repo.StatisticRecordRepository.GetFirstOrDefaultAsync(
It.IsAny<Expression<Func<StatisticRecord, bool>>>(),
It.IsAny<Func<IQueryable<StatisticRecord>, IIncludableQueryable<StatisticRecord, object>>>()))
.ReturnsAsync((StatisticRecord)null!);
_localizerCannotFindMock.Setup(loc => loc["CannotFindRecordWithQrId"]).Returns(new LocalizedString("CannotFindRecordWithQrId", "Record not found"));
var request = new DeleteStatisticRecordCommand(qrId);
// Act
var result = await _handler.Handle(request, CancellationToken.None);
// Assert
Assert.False(result.IsSuccess);
Assert.Contains("Record not found", result.Errors[0].Message);
_repositoryWrapperMock.Verify(repo => repo.StatisticRecordRepository.Delete(It.IsAny<StatisticRecord>()), Times.Never);
_repositoryWrapperMock.Verify(repo => repo.SaveChanges(), Times.Never);
}
[Fact]
public async Task Handle_RecordIsNotFound_ShouldReturnError()
{
// Arrange
var qrId = -1;
_repositoryWrapperMock.Setup(repo => repo.StatisticRecordRepository.GetFirstOrDefaultAsync(
It.IsAny<Expression<Func<StatisticRecord, bool>>>(),
It.IsAny<Func<IQueryable<StatisticRecord>, IIncludableQueryable<StatisticRecord, object>>>()))
.ReturnsAsync((StatisticRecord)null!);
_localizerCannotFindMock.Setup(loc => loc["CannotFindRecordWithQrId"]).Returns(new LocalizedString("CannotFindRecordWithQrId", "Record not found"));
var request = new DeleteStatisticRecordCommand(qrId);
// Act
var result = await _handler.Handle(request, CancellationToken.None);
// Assert
Assert.False(result.IsSuccess);
Assert.Contains("Record not found", result.Errors[0].Message);
_repositoryWrapperMock.Verify(repo => repo.StatisticRecordRepository.Delete(It.IsAny<StatisticRecord>()), Times.Never);
_repositoryWrapperMock.Verify(repo => repo.SaveChangesAsync(), Times.Never);
}

Comment on lines 164 to 169
_userManagerMock.Setup(um => um.Users)
.Returns(new List<User>().AsQueryable().BuildMockDbSet().Object);

_userManagerMock.Setup(um => um.Users)
.Returns(new List<User>().AsQueryable().BuildMockDbSet().Object);

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove duplicate setup code.

The UserManager.Users setup is duplicated. Remove the redundant setup to improve test maintainability.

         _userManagerMock.Setup(um => um.Users)
             .Returns(new List<User>().AsQueryable().BuildMockDbSet().Object);
-
-        _userManagerMock.Setup(um => um.Users)
-            .Returns(new List<User>().AsQueryable().BuildMockDbSet().Object);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
_userManagerMock.Setup(um => um.Users)
.Returns(new List<User>().AsQueryable().BuildMockDbSet().Object);
_userManagerMock.Setup(um => um.Users)
.Returns(new List<User>().AsQueryable().BuildMockDbSet().Object);
_userManagerMock.Setup(um => um.Users)
.Returns(new List<User>().AsQueryable().BuildMockDbSet().Object);

Comment on lines 61 to 69
private async Task<RestResponse> SendRequest(RestRequest request, string authToken)
{
request.OnBeforeDeserialization = resp => { resp.ContentType = "application/json"; };
request.AddHeader("Authorization", $"Bearer {authToken}");
request.AddHeader("Content-Type", "application/json");

var response = await this.client.ExecuteAsync(request);
var response = await client.ExecuteAsync(request);
return response;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Implement logging for better error tracking

The comment "Add logging" should be addressed to improve error tracking and debugging capabilities.

Consider implementing structured logging:

 private async Task<RestResponse> SendRequest(RestRequest request, string authToken)
 {
     request.OnBeforeDeserialization = resp => { resp.ContentType = "application/json"; };
     request.AddHeader("Authorization", $"Bearer {authToken}");
     request.AddHeader("Content-Type", "application/json");

+    try {
         var response = await client.ExecuteAsync(request);
+        if (!response.IsSuccessful)
+        {
+            _logger.LogWarning("Request failed: {StatusCode} {ErrorMessage}", 
+                response.StatusCode, response.ErrorMessage);
+        }
         return response;
+    }
+    catch (Exception ex)
+    {
+        _logger.LogError(ex, "Request execution failed");
+        throw;
+    }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private async Task<RestResponse> SendRequest(RestRequest request, string authToken)
{
request.OnBeforeDeserialization = resp => { resp.ContentType = "application/json"; };
request.AddHeader("Authorization", $"Bearer {authToken}");
request.AddHeader("Content-Type", "application/json");
var response = await this.client.ExecuteAsync(request);
var response = await client.ExecuteAsync(request);
return response;
}
private async Task<RestResponse> SendRequest(RestRequest request, string authToken)
{
request.OnBeforeDeserialization = resp => { resp.ContentType = "application/json"; };
request.AddHeader("Authorization", $"Bearer {authToken}");
request.AddHeader("Content-Type", "application/json");
try {
var response = await client.ExecuteAsync(request);
if (!response.IsSuccessful)
{
_logger.LogWarning("Request failed: {StatusCode} {ErrorMessage}",
response.StatusCode, response.ErrorMessage);
}
return response;
}
catch (Exception ex)
{
_logger.LogError(ex, "Request execution failed");
throw;
}
}

Comment on lines 35 to 49
RuleFor(dto => dto.UserName)
.Matches(@"^[a-z0-9'-_]+$").WithMessage(localizer["NameFormat", fieldLocalizer["UserName"], MinLengthSurname])
.Matches(@"^[a-z0-9'\-_]+$").WithMessage(localizer["UserNameFormat"])
.NotEmpty().WithMessage(localizer["CannotBeEmpty", fieldLocalizer["UserName"]])
.MinimumLength(MinLengthName).WithMessage(localizer["MinLength", fieldLocalizer["UserName"], MinLengthUserName])
.MaximumLength(MaxLengthName).WithMessage(localizer["MaxLength", fieldLocalizer["UserName"], MaxLengthUserName]);

RuleFor(dto => dto.Name)
.Matches(@"^[a-zA-Zа-яА-ЯґҐєЄіІїЇ'-]+$").WithMessage(localizer["NameFormat", fieldLocalizer["Name"], MinLengthSurname])
.Matches(@"^[a-zA-Zа-яА-ЯґҐєЄіІїЇ'-]+$").WithMessage(localizer["NameFormat"])
.NotEmpty().WithMessage(localizer["CannotBeEmpty", fieldLocalizer["Name"]])
.MinimumLength(MinLengthName).WithMessage(localizer["MinLength", fieldLocalizer["Name"], MinLengthName])
.MaximumLength(MaxLengthName).WithMessage(localizer["MaxLength", fieldLocalizer["Name"], MaxLengthName]);

RuleFor(dto => dto.Surname)
.Matches(@"^[a-zA-Zа-яА-ЯґҐєЄіІїЇ'-]+$").WithMessage(localizer["SurnameFormat", fieldLocalizer["Surname"], MinLengthSurname])
.Matches(@"^[a-zA-Zа-яА-ЯґҐєЄіІїЇ'-]+$").WithMessage(localizer["SurnameFormat"])
.NotEmpty().WithMessage(localizer["CannotBeEmpty", fieldLocalizer["Surname"]])
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

🧩 Analysis chain

Enhance validation patterns for better security and usability

The current regex patterns could be improved to better handle international characters and prevent potential security issues.

-    .Matches(@"^[a-z0-9'\-_]+$").WithMessage(localizer["UserNameFormat"])
+    .Matches(@"^[\w\-']{2,128}$").WithMessage(localizer["UserNameFormat"])

-    .Matches(@"^[a-zA-Zа-яА-ЯґҐєЄіІїЇ'-]+$").WithMessage(localizer["NameFormat"])
+    .Matches(@"^[\p{L}'-]+$").WithMessage(localizer["NameFormat"])

-    .Matches(@"^[a-zA-Zа-яА-ЯґҐєЄіІїЇ'-]+$").WithMessage(localizer["SurnameFormat"])
+    .Matches(@"^[\p{L}'-]+$").WithMessage(localizer["SurnameFormat"])

The updated patterns:

  • Use \w for username to include all word characters
  • Use \p{L} to support all Unicode letters for names
  • Add length constraints directly in the pattern

🌐 Web query:

What are the best practices for name validation regex patterns in 2024?

💡 Result:

When creating name validation regex patterns in 2024, best practices focus on balancing accuracy with cultural inclusivity while optimizing performance and security. Here are key recommendations:

Core Principles

1. Cultural Sensitivity

  • Allow spaces, hyphens/apostrophes (e.g., O'Connor), and multi-part names (e.g., María del Carmen)
  • Avoid assuming Western naming conventions
  • Use \p{L} instead of [A-Za-z] to support accented characters:
    ^[\p{L} \-']+(?: [\p{L} \-']+)+$

2. Security & Performance

  • Prevent ReDoS attacks by avoiding:
    • Nested quantifiers ((a+)+)
    • Complex lookaheads/lookbehinds
    • Unbounded repetitions
  • Test edge cases like extremely long names (>50 chars)

Common Patterns

Use Case Regex Pattern Example Notes
Basic Western names ^[A-Z][a-z]+(?: [A-Z][a-z]*)+$ John Doe Requires title case
International names ^\p{L}+(?:[ \-']\p{L}+)*$ François O'Conner Unicode support
Minimum two-part names ^\p{L}+(?: \p{L}+){1,}$ Li Wei Blocks single-word entries

Implementation Tips

Form Validation

  1. Combine client-side (HTML5) and server-side checks:
    <input pattern="^\p{L}+(?: \p{L}+)+$" title="Full name required">
  2. Use case-insensitive matching where appropriate (/i flag)

Error Handling

  • Provide clear examples:
    "Valid format: Firstname Lastname (special characters allowed: - ' )"
  • Avoid exposing regex specifics in error messages

Testing & Optimization

  • Benchmark patterns with tools like Regex101
  • Validate against diverse datasets including:
    • Multi-generational names (e.g., José María García Lorca)
    • Hyphenated surnames (Smith-Johnson)
    • Shortened forms (Xiaoling → Xiao Ling)

Alternatives to Consider

For complex validation needs:

// Use dedicated libraries like:
import {isValidName} from 'human-name-validator';

These practices ensure your name validation remains robust yet flexible enough to handle global naming conventions while maintaining system performance and security[1][3][5][9].

Citations:


Regex Patterns: Consider Allowing Spaces for International Names

While the updated patterns leverage Unicode support with \p{L}, best practices in 2024 recommend permitting spaces in names to accommodate multi-part entries (e.g., "María del Carmen" or "Jean Claude"). You might consider updating the Name and Surname rules from:

- .Matches(@"^[\p{L}'-]+$").WithMessage(localizer["NameFormat"])
+ .Matches(@"^\p{L}+(?:[ \-']\p{L}+)*$").WithMessage(localizer["NameFormat"])

Key Points:

  • Internationalization: The revised pattern ^\p{L}+(?:[ \-']\p{L}+)*$ allows spaces, hyphens, and apostrophes while ensuring that names with multiple parts are accepted.
  • Consistency: Ensure the regex changes complement the separate minimum/maximum length validations instead of duplicating length constraints in the regex.
  • Username Validation: The suggested update for usernames (^[\w\-']{2,128}$) appears reasonable if the intent is to include word characters; however, verify this aligns with your broader naming requirements.

Adopting these refinements will enhance cultural inclusivity and align the validation logic with contemporary security and usability recommendations.

[found_issues]

Comment on lines 61 to 63
[Fact]
public async Task ShouldReturnPaginatedNews_CorrectPage()
{
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Correct test method name.

The method name ShouldReturnPaginatedNews_CorrectPage is incorrect for a user retrieval test.

-public async Task ShouldReturnPaginatedNews_CorrectPage()
+public async Task Handle_UserExists_ReturnsUserDTO()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
[Fact]
public async Task ShouldReturnPaginatedNews_CorrectPage()
{
[Fact]
public async Task Handle_UserExists_ReturnsUserDTO()
{


namespace Streetcode.XUnitTest.MediatRTests.Users;

public class GetByUserNameTest
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Update class name to match functionality.

The class name GetByUserNameTest doesn't match the functionality being tested (GetByEmail). Consider renaming for clarity.

-public class GetByUserNameTest
+public class GetByEmailHandlerTests
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public class GetByUserNameTest
public class GetByEmailHandlerTests

Comment on lines 83 to 84
string tableSchema = this.dbContext.Model.FindEntityType(typeof(T))?.GetSchema() !;
string tableName = this.dbContext.Model.FindEntityType(typeof(T))?.GetTableName() !;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Replace null-forgiving operators with proper null checks.

Using the null-forgiving operator (!) is risky. Consider adding proper null checks and error handling.

-                    string tableSchema = this.dbContext.Model.FindEntityType(typeof(T))?.GetSchema() !;
-                    string tableName = this.dbContext.Model.FindEntityType(typeof(T))?.GetTableName() !;
+                    var entityType = this.dbContext.Model.FindEntityType(typeof(T));
+                    if (entityType == null)
+                    {
+                        throw new InvalidOperationException($"Entity type {typeof(T).Name} not found in the model");
+                    }
+                    string tableSchema = entityType.GetSchema() ?? throw new InvalidOperationException($"Schema not found for entity {typeof(T).Name}");
+                    string tableName = entityType.GetTableName() ?? throw new InvalidOperationException($"Table name not found for entity {typeof(T).Name}");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
string tableSchema = this.dbContext.Model.FindEntityType(typeof(T))?.GetSchema() !;
string tableName = this.dbContext.Model.FindEntityType(typeof(T))?.GetTableName() !;
var entityType = this.dbContext.Model.FindEntityType(typeof(T));
if (entityType == null)
{
throw new InvalidOperationException($"Entity type {typeof(T).Name} not found in the model");
}
string tableSchema = entityType.GetSchema() ?? throw new InvalidOperationException($"Schema not found for entity {typeof(T).Name}");
string tableName = entityType.GetTableName() ?? throw new InvalidOperationException($"Table name not found for entity {typeof(T).Name}");

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🔭 Outside diff range comments (1)
Streetcode/Streetcode.BLL/MediatR/Email/SendEmailHandler.cs (1)

46-49: 💡 Verification agent

🧩 Analysis chain

Verify email content sanitization.

Ensure that the CreateFeedbackMessageData method properly sanitizes user input to prevent potential XSS or email injection attacks.


🏁 Script executed:

#!/bin/bash
# Search for input sanitization in the message factory implementation
ast-grep --pattern 'class $_ implements IMessageDataAbstractFactory {
  $$$
  CreateFeedbackMessageData($_) {
    $$$
  }
}'

Length of output: 126


I'll need to gather additional information to confirm whether user inputs are properly sanitized inside the CreateFeedbackMessageData method. The initial ast-grep search didn’t return any matches for sanitization code, which means we haven’t yet located any explicit sanitization logic (such as HTML encoding or similar measures) in the factory method implementation.

Let's run a more general search to locate the definition of CreateFeedbackMessageData and inspect its implementation context:

Once you review the output, please verify manually whether the method implementation includes necessary sanitation (e.g., using encoding libraries or sanitization functions) to prevent XSS or injection attacks. If no sanitization is present here, consider adding explicit sanitization to handle user-supplied input before using it.

Please let me know the output of this command to further determine if a code change is required.


🏁 Script executed:

#!/bin/bash
# Locate the implementation of CreateFeedbackMessageData and display context to see if sanitization is done.
rg -n -A 15 "CreateFeedbackMessageData\s*\(" .

Length of output: 6434


Input Sanitization Missing in CreateFeedbackMessageData Implementation

The implementation in MessageDataConcreteFactory.cs passes inputs (i.e., from, source, and content) directly to the new FeedbackMessageData without any explicit sanitization. This exposes the code to potential XSS or email injection issues if the incoming data contains malicious content.

  • Location: Streetcode/Streetcode.BLL/Factories/MessageDataFactory/Concretes/MessageDataConcreteFactory.cs, lines 19-28
  • Recommendation: Introduce input sanitization (e.g., using encoding or validation methods) either at the point of input handling in SendEmailHandler.cs, within the factory method, or via a separate validation/sanitization layer before passing data to the email service.
🧹 Nitpick comments (1)
Streetcode/Streetcode.BLL/MediatR/Email/SendEmailHandler.cs (1)

38-44: Consider enhancing error handling with more specific error types.

While the error handling is functional, consider creating dedicated error types for different reCAPTCHA validation failures to enable more granular error handling by consumers.

-                return Result.Fail(new Error(captchaErrorMessage));
+                return Result.Fail(new ReCaptchaValidationError(captchaErrorMessage));

Additionally, consider adding debug logging before the validation attempt:

+            _logger.LogDebug("Validating reCAPTCHA token for email from {sender}", request.Email.From);
             var reCaptchaValidationResult = await _captchaService.ValidateReCaptchaAsync(request.Email.Token, cancellationToken);
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8fa5c1f and 37bc65c.

📒 Files selected for processing (2)
  • Streetcode/Streetcode.BLL/MediatR/Email/SendEmailHandler.cs (2 hunks)
  • Streetcode/Streetcode.XUnitTest/MediatRTests/Email/SendEmailHandlerTests.cs (6 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • Streetcode/Streetcode.XUnitTest/MediatRTests/Email/SendEmailHandlerTests.cs
🔇 Additional comments (1)
Streetcode/Streetcode.BLL/MediatR/Email/SendEmailHandler.cs (1)

8-8: LGTM! Good separation of concerns.

The introduction of ICaptchaService and removal of direct HTTP client dependencies improves modularity and aligns with the upgrade to reCAPTCHA v3.

Also applies to: 20-20, 26-27, 33-33

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
Streetcode/Streetcode.BLL/MediatR/Email/SendEmailHandler.cs (1)

39-44: Consider handling multiple validation errors.

Currently, only the first error message is used. Consider aggregating all validation errors to provide more comprehensive feedback.

Here's a suggested improvement:

-            if (reCaptchaValidationResult.IsFailed)
-            {
-                string captchaErrorMessage = reCaptchaValidationResult.Errors[0].Message;
-                _logger.LogError(request, captchaErrorMessage);
-                return Result.Fail(new Error(captchaErrorMessage));
-            }
+            if (reCaptchaValidationResult.IsFailed)
+            {
+                var captchaErrorMessages = string.Join("; ", reCaptchaValidationResult.Errors.Select(e => e.Message));
+                _logger.LogError(request, captchaErrorMessages);
+                return Result.Fail(new Error(captchaErrorMessages));
+            }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 37bc65c and 33b387d.

📒 Files selected for processing (1)
  • Streetcode/Streetcode.BLL/MediatR/Email/SendEmailHandler.cs (2 hunks)
🔇 Additional comments (2)
Streetcode/Streetcode.BLL/MediatR/Email/SendEmailHandler.cs (2)

8-8: Well-structured dependency management!

Good job extracting reCAPTCHA validation into a dedicated service. This improves separation of concerns and makes the code more maintainable.

Also applies to: 20-20, 22-34


38-38: Clean reCAPTCHA validation implementation!

The implementation aligns perfectly with the PR objective to update reCAPTCHA. Good job propagating the cancellation token for proper async operation handling.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
Streetcode/Streetcode.XIntegrationTest/ControllerTests/Utils/CustomWebApplicationFactory.cs (3)

37-57: Consider enhancing Google login test coverage.

While the current implementation handles basic success and failure cases, consider adding test cases for:

  • Expired tokens
  • Malformed tokens
  • Missing required claims

Here's an example of how to add these cases:

 public void SetupMockGoogleLogin(User user, string? token = null)
 {
     if (token is null)
     {
         GoogleServiceMock
             .Setup(gs => gs.ValidateGoogleToken("invalid_google_id_token"))
             .ThrowsAsync(new InvalidJwtException("Invalid Google Token"));
+
+        // Add cases for different failure scenarios
+        GoogleServiceMock
+            .Setup(gs => gs.ValidateGoogleToken("expired_token"))
+            .ThrowsAsync(new InvalidJwtException("Token has expired"));
+
+        GoogleServiceMock
+            .Setup(gs => gs.ValidateGoogleToken("malformed_token"))
+            .ThrowsAsync(new InvalidJwtException("Malformed token"));
     }
     else
     {

69-99: Reduce duplication in service registration.

The service removal logic is repetitive. Consider extracting it into a helper method.

Here's a suggested refactor:

 builder.ConfigureServices(services =>
 {
-    var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(IEmailService));
-    if (descriptor != null)
-    {
-        services.Remove(descriptor);
-    }
-
-    var googleDescriptor = services.SingleOrDefault(d => d.ServiceType == typeof(IGoogleService));
-    if (googleDescriptor != null)
-    {
-        services.Remove(googleDescriptor);
-    }
-
-    var httpClientFactoryDescriptor = services.SingleOrDefault(d => d.ServiceType == typeof(IHttpClientFactory));
-    if (httpClientFactoryDescriptor != null)
-    {
-        services.Remove(httpClientFactoryDescriptor);
-    }
-
-    var captchaDescriptor = services.SingleOrDefault(d => d.ServiceType == typeof(ICaptchaService));
-    if (captchaDescriptor != null)
-    {
-        services.Remove(captchaDescriptor);
-    }
+    RemoveService<IEmailService>(services);
+    RemoveService<IGoogleService>(services);
+    RemoveService<IHttpClientFactory>(services);
+    RemoveService<ICaptchaService>(services);

     services.AddSingleton(EmailServiceMock.Object);
     services.AddSingleton(GoogleServiceMock.Object);
     services.AddSingleton(HttpClientFactoryMock.Object);
     services.AddSingleton(CaptchaServiceMock.Object);
 });

+private void RemoveService<T>(IServiceCollection services)
+{
+    var descriptor = services.SingleOrDefault(d => d.ServiceType == typeof(T));
+    if (descriptor != null)
+    {
+        services.Remove(descriptor);
+    }
+}

122-127: Enhance CaptchaService mock with failure scenarios.

The current setup only handles the success case. Consider adding failure scenarios to make the tests more robust.

Here's a suggested enhancement:

 private void SetupMockCaptchaService()
 {
     this.CaptchaServiceMock
         .Setup(service => service.ValidateReCaptchaAsync(It.IsAny<string>(), It.IsAny<CancellationToken?>()))
         .ReturnsAsync(Result.Ok());
+
+    // Add setup for invalid token scenario
+    this.CaptchaServiceMock
+        .Setup(service => service.ValidateReCaptchaAsync("invalid_token", It.IsAny<CancellationToken?>()))
+        .ReturnsAsync(Result.Fail("Invalid reCAPTCHA token"));
+
+    // Add setup for timeout scenario
+    this.CaptchaServiceMock
+        .Setup(service => service.ValidateReCaptchaAsync("timeout_token", It.IsAny<CancellationToken?>()))
+        .ReturnsAsync(Result.Fail("reCAPTCHA verification timeout"));
 }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 33b387d and 765f834.

📒 Files selected for processing (1)
  • Streetcode/Streetcode.XIntegrationTest/ControllerTests/Utils/CustomWebApplicationFactory.cs (1 hunks)
🔇 Additional comments (1)
Streetcode/Streetcode.XIntegrationTest/ControllerTests/Utils/CustomWebApplicationFactory.cs (1)

20-29: Well-structured mock declarations!

Good job on maintaining encapsulation with private setters and proper initialization of all mock services.

@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
E Security Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

},
"ReCaptcha": {
"SecretKey": "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe",
"SecretKey": "6LePO88qAAAAAIP7znxf4kS3OL6W8n1WGuvytyTX",

Check failure

Code scanning / SonarCloud

reCaptcha secret keys should not be disclosed High

Make sure this reCaptcha key gets revoked, changed, and removed from the code. See more on SonarQube Cloud
},
"ReCaptcha": {
"SecretKey": "6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe",
"SecretKey": "6LePO88qAAAAAIP7znxf4kS3OL6W8n1WGuvytyTX",

Check failure

Code scanning / SonarCloud

reCaptcha secret keys should not be disclosed High

Make sure this reCaptcha key gets revoked, changed, and removed from the code. See more on SonarQube Cloud
@YaroslavRosnovskyi YaroslavRosnovskyi changed the base branch from release/1.2.0 to release/1.2.3 March 11, 2025 11:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants