From 4de455f34daac2ac60b21d8db0ac60a18b4226c7 Mon Sep 17 00:00:00 2001 From: Aaron Po Date: Sat, 20 Jun 2026 15:09:41 -0400 Subject: [PATCH] Format ./web/backend/Features/Features.Auth.Tests --- .../Commands/ConfirmUserHandlerTests.cs | 30 ++- .../Commands/RefreshTokenHandlerTests.cs | 7 +- .../Commands/RegisterUserHandlerTests.cs | 199 +++++++++++++----- .../ResendConfirmationEmailHandlerTests.cs | 40 +++- .../Features.Auth.Tests.csproj | 16 +- .../Queries/LoginHandlerTests.cs | 85 ++++++-- .../Repository/AuthRepositoryTests.cs | 35 +-- .../Repository/TestConnectionFactory.cs | 2 +- .../Services/TokenServiceRefreshTests.cs | 75 ++++--- .../Services/TokenServiceValidationTests.cs | 102 +++++---- 10 files changed, 389 insertions(+), 202 deletions(-) diff --git a/web/backend/Features/Features.Auth.Tests/Commands/ConfirmUserHandlerTests.cs b/web/backend/Features/Features.Auth.Tests/Commands/ConfirmUserHandlerTests.cs index b6988a4..9ddbe72 100644 --- a/web/backend/Features/Features.Auth.Tests/Commands/ConfirmUserHandlerTests.cs +++ b/web/backend/Features/Features.Auth.Tests/Commands/ConfirmUserHandlerTests.cs @@ -30,7 +30,7 @@ public class ConfirmUserHandlerTests { new Claim(JwtRegisteredClaimNames.Sub, userId.ToString()), new Claim(JwtRegisteredClaimNames.UniqueName, username), - new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) + new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), }; ClaimsPrincipal principal = new(new ClaimsIdentity(claims)); return new ValidatedToken(userId, username, principal); @@ -46,10 +46,15 @@ public class ConfirmUserHandlerTests ValidatedToken validatedToken = MakeValidatedToken(userId, username); UserAccount userAccount = new() { UserAccountId = userId, Username = username }; - _tokenServiceMock.Setup(x => x.ValidateConfirmationTokenAsync(confirmationToken)).ReturnsAsync(validatedToken); + _tokenServiceMock + .Setup(x => x.ValidateConfirmationTokenAsync(confirmationToken)) + .ReturnsAsync(validatedToken); _authRepositoryMock.Setup(x => x.ConfirmUserAccountAsync(userId)).ReturnsAsync(userAccount); - ConfirmationPayload result = await _handler.Handle(new ConfirmUserCommand(confirmationToken), CancellationToken.None); + ConfirmationPayload result = await _handler.Handle( + new ConfirmUserCommand(confirmationToken), + CancellationToken.None + ); result.Should().NotBeNull(); result.UserAccountId.Should().Be(userId); @@ -64,7 +69,8 @@ public class ConfirmUserHandlerTests .Setup(x => x.ValidateConfirmationTokenAsync(invalidToken)) .ThrowsAsync(new UnauthorizedException("Invalid confirmation token")); - Func> act = async () => await _handler.Handle(new ConfirmUserCommand(invalidToken), CancellationToken.None); + Func> act = async () => + await _handler.Handle(new ConfirmUserCommand(invalidToken), CancellationToken.None); await act.Should().ThrowAsync(); } @@ -79,10 +85,18 @@ public class ConfirmUserHandlerTests _tokenServiceMock .Setup(x => x.ValidateConfirmationTokenAsync(confirmationToken)) .ReturnsAsync(MakeValidatedToken(userId, username)); - _authRepositoryMock.Setup(x => x.ConfirmUserAccountAsync(userId)).ReturnsAsync((UserAccount?)null); + _authRepositoryMock + .Setup(x => x.ConfirmUserAccountAsync(userId)) + .ReturnsAsync((UserAccount?)null); - Func> act = async () => await _handler.Handle(new ConfirmUserCommand(confirmationToken), CancellationToken.None); + Func> act = async () => + await _handler.Handle( + new ConfirmUserCommand(confirmationToken), + CancellationToken.None + ); - await act.Should().ThrowAsync().WithMessage("*User account not found*"); + await act.Should() + .ThrowAsync() + .WithMessage("*User account not found*"); } -} \ No newline at end of file +} diff --git a/web/backend/Features/Features.Auth.Tests/Commands/RefreshTokenHandlerTests.cs b/web/backend/Features/Features.Auth.Tests/Commands/RefreshTokenHandlerTests.cs index 610a23f..8eb5b16 100644 --- a/web/backend/Features/Features.Auth.Tests/Commands/RefreshTokenHandlerTests.cs +++ b/web/backend/Features/Features.Auth.Tests/Commands/RefreshTokenHandlerTests.cs @@ -21,11 +21,14 @@ public class RefreshTokenHandlerTests .Setup(x => x.RefreshTokenAsync("old-refresh-token")) .ReturnsAsync(new RefreshTokenResult(user, "new-refresh-token", "new-access-token")); - LoginPayload result = await handler.Handle(new RefreshTokenCommand("old-refresh-token"), CancellationToken.None); + LoginPayload result = await handler.Handle( + new RefreshTokenCommand("old-refresh-token"), + CancellationToken.None + ); result.UserAccountId.Should().Be(userId); result.Username.Should().Be("testuser"); result.RefreshToken.Should().Be("new-refresh-token"); result.AccessToken.Should().Be("new-access-token"); } -} \ No newline at end of file +} diff --git a/web/backend/Features/Features.Auth.Tests/Commands/RegisterUserHandlerTests.cs b/web/backend/Features/Features.Auth.Tests/Commands/RegisterUserHandlerTests.cs index c24bfbc..999c3c7 100644 --- a/web/backend/Features/Features.Auth.Tests/Commands/RegisterUserHandlerTests.cs +++ b/web/backend/Features/Features.Auth.Tests/Commands/RegisterUserHandlerTests.cs @@ -35,9 +35,19 @@ public class RegisterUserHandlerTests ); } - private static RegisterUserCommand ValidCommand(string username = "newuser", string email = "john.doe@example.com") + private static RegisterUserCommand ValidCommand( + string username = "newuser", + string email = "john.doe@example.com" + ) { - return new RegisterUserCommand(username, "John", "Doe", email, new DateTime(1990, 1, 1), "SecurePassword123!"); + return new RegisterUserCommand( + username, + "John", + "Doe", + email, + new DateTime(1990, 1, 1), + "SecurePassword123!" + ); } [Fact] @@ -47,27 +57,46 @@ public class RegisterUserHandlerTests const string hashedPassword = "hashed_password_value"; Guid expectedUserId = Guid.NewGuid(); - _authRepoMock.Setup(x => x.GetUserByUsernameAsync(command.Username)).ReturnsAsync((UserAccount?)null); - _authRepoMock.Setup(x => x.GetUserByEmailAsync(command.Email)).ReturnsAsync((UserAccount?)null); + _authRepoMock + .Setup(x => x.GetUserByUsernameAsync(command.Username)) + .ReturnsAsync((UserAccount?)null); + _authRepoMock + .Setup(x => x.GetUserByEmailAsync(command.Email)) + .ReturnsAsync((UserAccount?)null); _passwordInfraMock.Setup(x => x.Hash(command.Password)).Returns(hashedPassword); _authRepoMock - .Setup(x => x.RegisterUserAsync(command.Username, command.FirstName, command.LastName, command.Email, - command.DateOfBirth, hashedPassword)) - .ReturnsAsync(new UserAccount - { - UserAccountId = expectedUserId, - Username = command.Username, - FirstName = command.FirstName, - LastName = command.LastName, - Email = command.Email, - DateOfBirth = command.DateOfBirth, - CreatedAt = DateTime.UtcNow - }); + .Setup(x => + x.RegisterUserAsync( + command.Username, + command.FirstName, + command.LastName, + command.Email, + command.DateOfBirth, + hashedPassword + ) + ) + .ReturnsAsync( + new UserAccount + { + UserAccountId = expectedUserId, + Username = command.Username, + FirstName = command.FirstName, + LastName = command.LastName, + Email = command.Email, + DateOfBirth = command.DateOfBirth, + CreatedAt = DateTime.UtcNow, + } + ); - _tokenServiceMock.Setup(x => x.GenerateAccessToken(It.IsAny())).Returns("access-token"); - _tokenServiceMock.Setup(x => x.GenerateRefreshToken(It.IsAny())).Returns("refresh-token"); - _tokenServiceMock.Setup(x => x.GenerateConfirmationToken(It.IsAny())) + _tokenServiceMock + .Setup(x => x.GenerateAccessToken(It.IsAny())) + .Returns("access-token"); + _tokenServiceMock + .Setup(x => x.GenerateRefreshToken(It.IsAny())) + .Returns("refresh-token"); + _tokenServiceMock + .Setup(x => x.GenerateConfirmationToken(It.IsAny())) .Returns("confirmation-token"); RegistrationPayload result = await _handler.Handle(command, CancellationToken.None); @@ -89,18 +118,36 @@ public class RegisterUserHandlerTests public async Task Handle_WithExistingUsername_ThrowsConflictException() { RegisterUserCommand command = ValidCommand("existinguser"); - UserAccount existingUser = new() { UserAccountId = Guid.NewGuid(), Username = "existinguser" }; + UserAccount existingUser = new() + { + UserAccountId = Guid.NewGuid(), + Username = "existinguser", + }; - _authRepoMock.Setup(x => x.GetUserByUsernameAsync(command.Username)).ReturnsAsync(existingUser); - _authRepoMock.Setup(x => x.GetUserByEmailAsync(command.Email)).ReturnsAsync((UserAccount?)null); + _authRepoMock + .Setup(x => x.GetUserByUsernameAsync(command.Username)) + .ReturnsAsync(existingUser); + _authRepoMock + .Setup(x => x.GetUserByEmailAsync(command.Email)) + .ReturnsAsync((UserAccount?)null); - Func> act = async () => await _handler.Handle(command, CancellationToken.None); + Func> act = async () => + await _handler.Handle(command, CancellationToken.None); - await act.Should().ThrowAsync().WithMessage("Username or email already exists"); + await act.Should() + .ThrowAsync() + .WithMessage("Username or email already exists"); _authRepoMock.Verify( - x => x.RegisterUserAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), - It.IsAny(), It.IsAny()), + x => + x.RegisterUserAsync( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny() + ), Times.Never ); } @@ -109,14 +156,23 @@ public class RegisterUserHandlerTests public async Task Handle_WithExistingEmail_ThrowsConflictException() { RegisterUserCommand command = ValidCommand(email: "existing@example.com"); - UserAccount existingUser = new() { UserAccountId = Guid.NewGuid(), Email = "existing@example.com" }; + UserAccount existingUser = new() + { + UserAccountId = Guid.NewGuid(), + Email = "existing@example.com", + }; - _authRepoMock.Setup(x => x.GetUserByUsernameAsync(command.Username)).ReturnsAsync((UserAccount?)null); + _authRepoMock + .Setup(x => x.GetUserByUsernameAsync(command.Username)) + .ReturnsAsync((UserAccount?)null); _authRepoMock.Setup(x => x.GetUserByEmailAsync(command.Email)).ReturnsAsync(existingUser); - Func> act = async () => await _handler.Handle(command, CancellationToken.None); + Func> act = async () => + await _handler.Handle(command, CancellationToken.None); - await act.Should().ThrowAsync().WithMessage("Username or email already exists"); + await act.Should() + .ThrowAsync() + .WithMessage("Username or email already exists"); } [Fact] @@ -125,24 +181,47 @@ public class RegisterUserHandlerTests RegisterUserCommand command = ValidCommand(); const string hashedPassword = "hashed_secure_password"; - _authRepoMock.Setup(x => x.GetUserByUsernameAsync(It.IsAny())).ReturnsAsync((UserAccount?)null); - _authRepoMock.Setup(x => x.GetUserByEmailAsync(It.IsAny())).ReturnsAsync((UserAccount?)null); + _authRepoMock + .Setup(x => x.GetUserByUsernameAsync(It.IsAny())) + .ReturnsAsync((UserAccount?)null); + _authRepoMock + .Setup(x => x.GetUserByEmailAsync(It.IsAny())) + .ReturnsAsync((UserAccount?)null); _passwordInfraMock.Setup(x => x.Hash(command.Password)).Returns(hashedPassword); _authRepoMock - .Setup(x => x.RegisterUserAsync(It.IsAny(), It.IsAny(), It.IsAny(), - It.IsAny(), It.IsAny(), hashedPassword)) + .Setup(x => + x.RegisterUserAsync( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + hashedPassword + ) + ) .ReturnsAsync(new UserAccount { UserAccountId = Guid.NewGuid() }); - _tokenServiceMock.Setup(x => x.GenerateAccessToken(It.IsAny())).Returns("access-token"); - _tokenServiceMock.Setup(x => x.GenerateRefreshToken(It.IsAny())).Returns("refresh-token"); + _tokenServiceMock + .Setup(x => x.GenerateAccessToken(It.IsAny())) + .Returns("access-token"); + _tokenServiceMock + .Setup(x => x.GenerateRefreshToken(It.IsAny())) + .Returns("refresh-token"); await _handler.Handle(command, CancellationToken.None); _passwordInfraMock.Verify(x => x.Hash(command.Password), Times.Once); _authRepoMock.Verify( - x => x.RegisterUserAsync(command.Username, command.FirstName, command.LastName, command.Email, - command.DateOfBirth, hashedPassword), + x => + x.RegisterUserAsync( + command.Username, + command.FirstName, + command.LastName, + command.Email, + command.DateOfBirth, + hashedPassword + ), Times.Once ); } @@ -152,19 +231,43 @@ public class RegisterUserHandlerTests { RegisterUserCommand command = ValidCommand(); - _authRepoMock.Setup(x => x.GetUserByUsernameAsync(It.IsAny())).ReturnsAsync((UserAccount?)null); - _authRepoMock.Setup(x => x.GetUserByEmailAsync(It.IsAny())).ReturnsAsync((UserAccount?)null); + _authRepoMock + .Setup(x => x.GetUserByUsernameAsync(It.IsAny())) + .ReturnsAsync((UserAccount?)null); + _authRepoMock + .Setup(x => x.GetUserByEmailAsync(It.IsAny())) + .ReturnsAsync((UserAccount?)null); _passwordInfraMock.Setup(x => x.Hash(It.IsAny())).Returns("hashed"); _authRepoMock - .Setup(x => x.RegisterUserAsync(It.IsAny(), It.IsAny(), It.IsAny(), - It.IsAny(), It.IsAny(), It.IsAny())) - .ReturnsAsync(new UserAccount - { UserAccountId = Guid.NewGuid(), Username = command.Username, Email = command.Email }); + .Setup(x => + x.RegisterUserAsync( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny() + ) + ) + .ReturnsAsync( + new UserAccount + { + UserAccountId = Guid.NewGuid(), + Username = command.Username, + Email = command.Email, + } + ); - _tokenServiceMock.Setup(x => x.GenerateAccessToken(It.IsAny())).Returns("access-token"); - _tokenServiceMock.Setup(x => x.GenerateRefreshToken(It.IsAny())).Returns("refresh-token"); + _tokenServiceMock + .Setup(x => x.GenerateAccessToken(It.IsAny())) + .Returns("access-token"); + _tokenServiceMock + .Setup(x => x.GenerateRefreshToken(It.IsAny())) + .Returns("refresh-token"); _mediatorMock - .Setup(x => x.Send(It.IsAny(), It.IsAny())) + .Setup(x => + x.Send(It.IsAny(), It.IsAny()) + ) .ThrowsAsync(new Exception("smtp down")); RegistrationPayload result = await _handler.Handle(command, CancellationToken.None); @@ -172,4 +275,4 @@ public class RegisterUserHandlerTests result.ConfirmationEmailSent.Should().BeFalse(); result.AccessToken.Should().Be("access-token"); } -} \ No newline at end of file +} diff --git a/web/backend/Features/Features.Auth.Tests/Commands/ResendConfirmationEmailHandlerTests.cs b/web/backend/Features/Features.Auth.Tests/Commands/ResendConfirmationEmailHandlerTests.cs index 50289cd..5614723 100644 --- a/web/backend/Features/Features.Auth.Tests/Commands/ResendConfirmationEmailHandlerTests.cs +++ b/web/backend/Features/Features.Auth.Tests/Commands/ResendConfirmationEmailHandlerTests.cs @@ -17,15 +17,23 @@ public class ResendConfirmationEmailHandlerTests public ResendConfirmationEmailHandlerTests() { - _handler = new ResendConfirmationEmailHandler(_authRepositoryMock.Object, _tokenServiceMock.Object, - _mediatorMock.Object); + _handler = new ResendConfirmationEmailHandler( + _authRepositoryMock.Object, + _tokenServiceMock.Object, + _mediatorMock.Object + ); } [Fact] public async Task Handle_SendsFreshConfirmationEmail_WhenUserExistsAndUnverified() { Guid userId = Guid.NewGuid(); - UserAccount user = new() { UserAccountId = userId, FirstName = "Aaron", Email = "aaron@example.com" }; + UserAccount user = new() + { + UserAccountId = userId, + FirstName = "Aaron", + Email = "aaron@example.com", + }; _authRepositoryMock.Setup(x => x.GetUserByIdAsync(userId)).ReturnsAsync(user); _authRepositoryMock.Setup(x => x.IsUserVerifiedAsync(userId)).ReturnsAsync(false); @@ -34,9 +42,15 @@ public class ResendConfirmationEmailHandlerTests await _handler.Handle(new ResendConfirmationEmailCommand(userId), CancellationToken.None); _mediatorMock.Verify( - x => x.Send(It.Is(c => - c.FirstName == "Aaron" && c.Email == "aaron@example.com" && c.ConfirmationToken == "fresh-token" - ), It.IsAny()), + x => + x.Send( + It.Is(c => + c.FirstName == "Aaron" + && c.Email == "aaron@example.com" + && c.ConfirmationToken == "fresh-token" + ), + It.IsAny() + ), Times.Once ); } @@ -50,7 +64,11 @@ public class ResendConfirmationEmailHandlerTests await _handler.Handle(new ResendConfirmationEmailCommand(userId), CancellationToken.None); _mediatorMock.Verify( - x => x.Send(It.IsAny(), It.IsAny()), + x => + x.Send( + It.IsAny(), + It.IsAny() + ), Times.Never ); } @@ -67,8 +85,12 @@ public class ResendConfirmationEmailHandlerTests await _handler.Handle(new ResendConfirmationEmailCommand(userId), CancellationToken.None); _mediatorMock.Verify( - x => x.Send(It.IsAny(), It.IsAny()), + x => + x.Send( + It.IsAny(), + It.IsAny() + ), Times.Never ); } -} \ No newline at end of file +} diff --git a/web/backend/Features/Features.Auth.Tests/Features.Auth.Tests.csproj b/web/backend/Features/Features.Auth.Tests/Features.Auth.Tests.csproj index c788b58..7a858bb 100644 --- a/web/backend/Features/Features.Auth.Tests/Features.Auth.Tests.csproj +++ b/web/backend/Features/Features.Auth.Tests/Features.Auth.Tests.csproj @@ -8,19 +8,19 @@ - - - - - - + + + + + + - + - + diff --git a/web/backend/Features/Features.Auth.Tests/Queries/LoginHandlerTests.cs b/web/backend/Features/Features.Auth.Tests/Queries/LoginHandlerTests.cs index 64d6e01..6fce931 100644 --- a/web/backend/Features/Features.Auth.Tests/Queries/LoginHandlerTests.cs +++ b/web/backend/Features/Features.Auth.Tests/Queries/LoginHandlerTests.cs @@ -22,7 +22,11 @@ public class LoginHandlerTests _authRepoMock = new Mock(); _passwordInfraMock = new Mock(); _tokenServiceMock = new Mock(); - _handler = new LoginHandler(_authRepoMock.Object, _passwordInfraMock.Object, _tokenServiceMock.Object); + _handler = new LoginHandler( + _authRepoMock.Object, + _passwordInfraMock.Object, + _tokenServiceMock.Object + ); } [Fact] @@ -38,7 +42,7 @@ public class LoginHandlerTests FirstName = "René", LastName = "Descartes", Email = "r.descartes@example.com", - DateOfBirth = new DateTime(1596, 03, 31) + DateOfBirth = new DateTime(1596, 03, 31), }; UserCredential userCredential = new() @@ -46,16 +50,27 @@ public class LoginHandlerTests UserCredentialId = Guid.NewGuid(), UserAccountId = userAccountId, Hash = "some-hash", - Expiry = DateTime.MaxValue + Expiry = DateTime.MaxValue, }; _authRepoMock.Setup(x => x.GetUserByUsernameAsync(username)).ReturnsAsync(userAccount); - _authRepoMock.Setup(x => x.GetActiveCredentialByUserAccountIdAsync(userAccountId)).ReturnsAsync(userCredential); - _passwordInfraMock.Setup(x => x.Verify(It.IsAny(), It.IsAny())).Returns(true); - _tokenServiceMock.Setup(x => x.GenerateAccessToken(It.IsAny())).Returns("access-token"); - _tokenServiceMock.Setup(x => x.GenerateRefreshToken(It.IsAny())).Returns("refresh-token"); + _authRepoMock + .Setup(x => x.GetActiveCredentialByUserAccountIdAsync(userAccountId)) + .ReturnsAsync(userCredential); + _passwordInfraMock + .Setup(x => x.Verify(It.IsAny(), It.IsAny())) + .Returns(true); + _tokenServiceMock + .Setup(x => x.GenerateAccessToken(It.IsAny())) + .Returns("access-token"); + _tokenServiceMock + .Setup(x => x.GenerateRefreshToken(It.IsAny())) + .Returns("refresh-token"); - LoginPayload result = await _handler.Handle(new LoginQuery(username, "any-password"), CancellationToken.None); + LoginPayload result = await _handler.Handle( + new LoginQuery(username, "any-password"), + CancellationToken.None + ); result.Should().NotBeNull(); result.UserAccountId.Should().Be(userAccountId); @@ -68,12 +83,18 @@ public class LoginHandlerTests public async Task Handle_WithUnregisteredUsername_ThrowsUnauthorizedException() { const string username = "de_beauvoir"; - _authRepoMock.Setup(x => x.GetUserByUsernameAsync(username)).ReturnsAsync((UserAccount?)null); + _authRepoMock + .Setup(x => x.GetUserByUsernameAsync(username)) + .ReturnsAsync((UserAccount?)null); - Func> act = async () => await _handler.Handle(new LoginQuery(username, "password"), CancellationToken.None); + Func> act = async () => + await _handler.Handle(new LoginQuery(username, "password"), CancellationToken.None); await act.Should().ThrowAsync(); - _authRepoMock.Verify(x => x.GetActiveCredentialByUserAccountIdAsync(It.IsAny()), Times.Never); + _authRepoMock.Verify( + x => x.GetActiveCredentialByUserAccountIdAsync(It.IsAny()), + Times.Never + ); } [Fact] @@ -84,13 +105,20 @@ public class LoginHandlerTests UserAccount userAccount = new() { UserAccountId = userAccountId, Username = username }; _authRepoMock.Setup(x => x.GetUserByUsernameAsync(username)).ReturnsAsync(userAccount); - _authRepoMock.Setup(x => x.GetActiveCredentialByUserAccountIdAsync(userAccountId)) + _authRepoMock + .Setup(x => x.GetActiveCredentialByUserAccountIdAsync(userAccountId)) .ReturnsAsync((UserCredential?)null); - Func> act = async () => await _handler.Handle(new LoginQuery(username, "password"), CancellationToken.None); + Func> act = async () => + await _handler.Handle(new LoginQuery(username, "password"), CancellationToken.None); - await act.Should().ThrowAsync().WithMessage("Invalid username or password."); - _passwordInfraMock.Verify(x => x.Verify(It.IsAny(), It.IsAny()), Times.Never); + await act.Should() + .ThrowAsync() + .WithMessage("Invalid username or password."); + _passwordInfraMock.Verify( + x => x.Verify(It.IsAny(), It.IsAny()), + Times.Never + ); } [Fact] @@ -99,14 +127,29 @@ public class LoginHandlerTests const string username = "RCarnap"; Guid userAccountId = Guid.NewGuid(); UserAccount userAccount = new() { UserAccountId = userAccountId, Username = username }; - UserCredential userCredential = new() { UserCredentialId = Guid.NewGuid(), UserAccountId = userAccountId, Hash = "hashed-password" }; + UserCredential userCredential = new() + { + UserCredentialId = Guid.NewGuid(), + UserAccountId = userAccountId, + Hash = "hashed-password", + }; _authRepoMock.Setup(x => x.GetUserByUsernameAsync(username)).ReturnsAsync(userAccount); - _authRepoMock.Setup(x => x.GetActiveCredentialByUserAccountIdAsync(userAccountId)).ReturnsAsync(userCredential); - _passwordInfraMock.Setup(x => x.Verify(It.IsAny(), It.IsAny())).Returns(false); + _authRepoMock + .Setup(x => x.GetActiveCredentialByUserAccountIdAsync(userAccountId)) + .ReturnsAsync(userCredential); + _passwordInfraMock + .Setup(x => x.Verify(It.IsAny(), It.IsAny())) + .Returns(false); - Func> act = async () => await _handler.Handle(new LoginQuery(username, "wrong-password"), CancellationToken.None); + Func> act = async () => + await _handler.Handle( + new LoginQuery(username, "wrong-password"), + CancellationToken.None + ); - await act.Should().ThrowAsync().WithMessage("Invalid username or password."); + await act.Should() + .ThrowAsync() + .WithMessage("Invalid username or password."); } -} \ No newline at end of file +} diff --git a/web/backend/Features/Features.Auth.Tests/Repository/AuthRepositoryTests.cs b/web/backend/Features/Features.Auth.Tests/Repository/AuthRepositoryTests.cs index b1e133c..1bc7e70 100644 --- a/web/backend/Features/Features.Auth.Tests/Repository/AuthRepositoryTests.cs +++ b/web/backend/Features/Features.Auth.Tests/Repository/AuthRepositoryTests.cs @@ -18,8 +18,7 @@ public class AuthRepositoryTests Guid expectedUserId = Guid.NewGuid(); MockDbConnection conn = new(); - conn.Mocks.When(cmd => cmd.CommandText == "USP_RegisterUser") - .ReturnsScalar(expectedUserId); + conn.Mocks.When(cmd => cmd.CommandText == "USP_RegisterUser").ReturnsScalar(expectedUserId); // Mock the subsequent read for the newly created user by id conn.Mocks.When(cmd => cmd.CommandText == "usp_GetUserAccountById") @@ -132,9 +131,7 @@ public class AuthRepositoryTests Guid userId = Guid.NewGuid(); MockDbConnection conn = new(); - conn.Mocks.When(cmd => - cmd.CommandText == "usp_GetUserAccountByUsername" - ) + conn.Mocks.When(cmd => cmd.CommandText == "usp_GetUserAccountByUsername") .ReturnsTable( MockTable .WithColumns( @@ -175,9 +172,7 @@ public class AuthRepositoryTests { MockDbConnection conn = new(); - conn.Mocks.When(cmd => - cmd.CommandText == "usp_GetUserAccountByUsername" - ) + conn.Mocks.When(cmd => cmd.CommandText == "usp_GetUserAccountByUsername") .ReturnsTable(MockTable.Empty()); AuthRepository repo = CreateRepo(conn); @@ -193,9 +188,7 @@ public class AuthRepositoryTests Guid credentialId = Guid.NewGuid(); MockDbConnection conn = new(); - conn.Mocks.When(cmd => - cmd.CommandText == "USP_GetActiveUserCredentialByUserAccountId" - ) + conn.Mocks.When(cmd => cmd.CommandText == "USP_GetActiveUserCredentialByUserAccountId") .ReturnsTable( MockTable .WithColumns( @@ -205,13 +198,7 @@ public class AuthRepositoryTests ("CreatedAt", typeof(DateTime)), ("Timer", typeof(byte[])) ) - .AddRow( - credentialId, - userId, - "hashed_password_value", - DateTime.UtcNow, - null - ) + .AddRow(credentialId, userId, "hashed_password_value", DateTime.UtcNow, null) ); AuthRepository repo = CreateRepo(conn); @@ -229,9 +216,7 @@ public class AuthRepositoryTests Guid userId = Guid.NewGuid(); MockDbConnection conn = new(); - conn.Mocks.When(cmd => - cmd.CommandText == "USP_GetActiveUserCredentialByUserAccountId" - ) + conn.Mocks.When(cmd => cmd.CommandText == "USP_GetActiveUserCredentialByUserAccountId") .ReturnsTable(MockTable.Empty()); AuthRepository repo = CreateRepo(conn); @@ -247,14 +232,12 @@ public class AuthRepositoryTests string newPasswordHash = "new_hashed_password"; MockDbConnection conn = new(); - conn.Mocks.When(cmd => cmd.CommandText == "USP_RotateUserCredential") - .ReturnsScalar(1); + conn.Mocks.When(cmd => cmd.CommandText == "USP_RotateUserCredential").ReturnsScalar(1); AuthRepository repo = CreateRepo(conn); // Should not throw - Func act = async () => - await repo.RotateCredentialAsync(userId, newPasswordHash); + Func act = async () => await repo.RotateCredentialAsync(userId, newPasswordHash); await act.Should().NotThrowAsync(); } -} \ No newline at end of file +} diff --git a/web/backend/Features/Features.Auth.Tests/Repository/TestConnectionFactory.cs b/web/backend/Features/Features.Auth.Tests/Repository/TestConnectionFactory.cs index a20e082..95e816d 100644 --- a/web/backend/Features/Features.Auth.Tests/Repository/TestConnectionFactory.cs +++ b/web/backend/Features/Features.Auth.Tests/Repository/TestConnectionFactory.cs @@ -11,4 +11,4 @@ internal class TestConnectionFactory(DbConnection conn) : ISqlConnectionFactory { return _conn; } -} \ No newline at end of file +} diff --git a/web/backend/Features/Features.Auth.Tests/Services/TokenServiceRefreshTests.cs b/web/backend/Features/Features.Auth.Tests/Services/TokenServiceRefreshTests.cs index ac29471..76c49ab 100644 --- a/web/backend/Features/Features.Auth.Tests/Services/TokenServiceRefreshTests.cs +++ b/web/backend/Features/Features.Auth.Tests/Services/TokenServiceRefreshTests.cs @@ -22,15 +22,20 @@ public class TokenServiceRefreshTests _authRepositoryMock = new Mock(); // Set environment variables for tokens - Environment.SetEnvironmentVariable("ACCESS_TOKEN_SECRET", "test-access-secret-that-is-very-long-1234567890"); - Environment.SetEnvironmentVariable("REFRESH_TOKEN_SECRET", "test-refresh-secret-that-is-very-long-1234567890"); - Environment.SetEnvironmentVariable("CONFIRMATION_TOKEN_SECRET", - "test-confirmation-secret-that-is-very-long-1234567890"); - - _tokenService = new TokenService( - _tokenInfraMock.Object, - _authRepositoryMock.Object + Environment.SetEnvironmentVariable( + "ACCESS_TOKEN_SECRET", + "test-access-secret-that-is-very-long-1234567890" ); + Environment.SetEnvironmentVariable( + "REFRESH_TOKEN_SECRET", + "test-refresh-secret-that-is-very-long-1234567890" + ); + Environment.SetEnvironmentVariable( + "CONFIRMATION_TOKEN_SECRET", + "test-confirmation-secret-that-is-very-long-1234567890" + ); + + _tokenService = new TokenService(_tokenInfraMock.Object, _authRepositoryMock.Object); } [Fact] @@ -45,7 +50,7 @@ public class TokenServiceRefreshTests { new Claim(JwtRegisteredClaimNames.Sub, userId.ToString()), new Claim(JwtRegisteredClaimNames.UniqueName, username), - new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) + new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), }; ClaimsIdentity claimsIdentity = new(claims); @@ -58,7 +63,7 @@ public class TokenServiceRefreshTests FirstName = "Test", LastName = "User", Email = "test@example.com", - DateOfBirth = new DateTime(1990, 1, 1) + DateOfBirth = new DateTime(1990, 1, 1), }; // Mock the validation of refresh token @@ -69,11 +74,11 @@ public class TokenServiceRefreshTests // Mock the generation of new tokens _tokenInfraMock .Setup(x => x.GenerateJwt(userId, username, It.IsAny(), It.IsAny())) - .Returns((Guid _, string _, DateTime _, string _) => $"generated-token-{Guid.NewGuid()}"); + .Returns( + (Guid _, string _, DateTime _, string _) => $"generated-token-{Guid.NewGuid()}" + ); - _authRepositoryMock - .Setup(x => x.GetUserByIdAsync(userId)) - .ReturnsAsync(userAccount); + _authRepositoryMock.Setup(x => x.GetUserByIdAsync(userId)).ReturnsAsync(userAccount); // Act RefreshTokenResult result = await _tokenService.RefreshTokenAsync(refreshToken); @@ -85,14 +90,17 @@ public class TokenServiceRefreshTests result.AccessToken.Should().NotBeEmpty(); result.RefreshToken.Should().NotBeEmpty(); - _authRepositoryMock.Verify( - x => x.GetUserByIdAsync(userId), - Times.Once - ); + _authRepositoryMock.Verify(x => x.GetUserByIdAsync(userId), Times.Once); // Verify tokens were generated (called twice - once for access, once for refresh) _tokenInfraMock.Verify( - x => x.GenerateJwt(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), + x => + x.GenerateJwt( + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny() + ), Times.Exactly(2) ); } @@ -108,9 +116,10 @@ public class TokenServiceRefreshTests .ThrowsAsync(new UnauthorizedException("Invalid refresh token")); // Act & Assert - await FluentActions.Invoking(async () => - await _tokenService.RefreshTokenAsync(invalidToken) - ).Should().ThrowAsync(); + await FluentActions + .Invoking(async () => await _tokenService.RefreshTokenAsync(invalidToken)) + .Should() + .ThrowAsync(); } [Fact] @@ -124,9 +133,10 @@ public class TokenServiceRefreshTests .ThrowsAsync(new UnauthorizedException("Refresh token has expired")); // Act & Assert - await FluentActions.Invoking(async () => - await _tokenService.RefreshTokenAsync(expiredToken) - ).Should().ThrowAsync(); + await FluentActions + .Invoking(async () => await _tokenService.RefreshTokenAsync(expiredToken)) + .Should() + .ThrowAsync(); } [Fact] @@ -141,7 +151,7 @@ public class TokenServiceRefreshTests { new Claim(JwtRegisteredClaimNames.Sub, userId.ToString()), new Claim(JwtRegisteredClaimNames.UniqueName, username), - new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) + new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), }; ClaimsIdentity claimsIdentity = new(claims); @@ -151,14 +161,13 @@ public class TokenServiceRefreshTests .Setup(x => x.ValidateJwtAsync(refreshToken, It.IsAny())) .ReturnsAsync(principal); - _authRepositoryMock - .Setup(x => x.GetUserByIdAsync(userId)) - .ReturnsAsync((UserAccount?)null); + _authRepositoryMock.Setup(x => x.GetUserByIdAsync(userId)).ReturnsAsync((UserAccount?)null); // Act & Assert - await FluentActions.Invoking(async () => - await _tokenService.RefreshTokenAsync(refreshToken) - ).Should().ThrowAsync() + await FluentActions + .Invoking(async () => await _tokenService.RefreshTokenAsync(refreshToken)) + .Should() + .ThrowAsync() .WithMessage("*User account not found*"); } -} \ No newline at end of file +} diff --git a/web/backend/Features/Features.Auth.Tests/Services/TokenServiceValidationTests.cs b/web/backend/Features/Features.Auth.Tests/Services/TokenServiceValidationTests.cs index f3550cf..136a2fc 100644 --- a/web/backend/Features/Features.Auth.Tests/Services/TokenServiceValidationTests.cs +++ b/web/backend/Features/Features.Auth.Tests/Services/TokenServiceValidationTests.cs @@ -21,15 +21,20 @@ public class TokenServiceValidationTests _authRepositoryMock = new Mock(); // Set environment variables for tokens - Environment.SetEnvironmentVariable("ACCESS_TOKEN_SECRET", "test-access-secret-that-is-very-long-1234567890"); - Environment.SetEnvironmentVariable("REFRESH_TOKEN_SECRET", "test-refresh-secret-that-is-very-long-1234567890"); - Environment.SetEnvironmentVariable("CONFIRMATION_TOKEN_SECRET", - "test-confirmation-secret-that-is-very-long-1234567890"); - - _tokenService = new TokenService( - _tokenInfraMock.Object, - _authRepositoryMock.Object + Environment.SetEnvironmentVariable( + "ACCESS_TOKEN_SECRET", + "test-access-secret-that-is-very-long-1234567890" ); + Environment.SetEnvironmentVariable( + "REFRESH_TOKEN_SECRET", + "test-refresh-secret-that-is-very-long-1234567890" + ); + Environment.SetEnvironmentVariable( + "CONFIRMATION_TOKEN_SECRET", + "test-confirmation-secret-that-is-very-long-1234567890" + ); + + _tokenService = new TokenService(_tokenInfraMock.Object, _authRepositoryMock.Object); } [Fact] @@ -44,7 +49,7 @@ public class TokenServiceValidationTests { new Claim(JwtRegisteredClaimNames.Sub, userId.ToString()), new Claim(JwtRegisteredClaimNames.UniqueName, username), - new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) + new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), }; ClaimsIdentity claimsIdentity = new(claims); @@ -55,15 +60,17 @@ public class TokenServiceValidationTests .ReturnsAsync(principal); // Act - ValidatedToken result = - await _tokenService.ValidateAccessTokenAsync(token); + ValidatedToken result = await _tokenService.ValidateAccessTokenAsync(token); // Assert result.Should().NotBeNull(); result.UserId.Should().Be(userId); result.Username.Should().Be(username); result.Principal.Should().NotBeNull(); - result.Principal.FindFirst(JwtRegisteredClaimNames.Sub)?.Value.Should().Be(userId.ToString()); + result + .Principal.FindFirst(JwtRegisteredClaimNames.Sub) + ?.Value.Should() + .Be(userId.ToString()); } [Fact] @@ -78,7 +85,7 @@ public class TokenServiceValidationTests { new Claim(JwtRegisteredClaimNames.Sub, userId.ToString()), new Claim(JwtRegisteredClaimNames.UniqueName, username), - new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) + new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), }; ClaimsIdentity claimsIdentity = new(claims); @@ -89,8 +96,7 @@ public class TokenServiceValidationTests .ReturnsAsync(principal); // Act - ValidatedToken result = - await _tokenService.ValidateRefreshTokenAsync(token); + ValidatedToken result = await _tokenService.ValidateRefreshTokenAsync(token); // Assert result.Should().NotBeNull(); @@ -110,7 +116,7 @@ public class TokenServiceValidationTests { new Claim(JwtRegisteredClaimNames.Sub, userId.ToString()), new Claim(JwtRegisteredClaimNames.UniqueName, username), - new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) + new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), }; ClaimsIdentity claimsIdentity = new(claims); @@ -121,8 +127,7 @@ public class TokenServiceValidationTests .ReturnsAsync(principal); // Act - ValidatedToken result = - await _tokenService.ValidateConfirmationTokenAsync(token); + ValidatedToken result = await _tokenService.ValidateConfirmationTokenAsync(token); // Assert result.Should().NotBeNull(); @@ -141,9 +146,10 @@ public class TokenServiceValidationTests .ThrowsAsync(new UnauthorizedException("Invalid token")); // Act & Assert - await FluentActions.Invoking(async () => - await _tokenService.ValidateAccessTokenAsync(token) - ).Should().ThrowAsync(); + await FluentActions + .Invoking(async () => await _tokenService.ValidateAccessTokenAsync(token)) + .Should() + .ThrowAsync(); } [Fact] @@ -154,14 +160,13 @@ public class TokenServiceValidationTests _tokenInfraMock .Setup(x => x.ValidateJwtAsync(token, It.IsAny())) - .ThrowsAsync(new UnauthorizedException( - "Token has expired" - )); + .ThrowsAsync(new UnauthorizedException("Token has expired")); // Act & Assert - await FluentActions.Invoking(async () => - await _tokenService.ValidateAccessTokenAsync(token) - ).Should().ThrowAsync(); + await FluentActions + .Invoking(async () => await _tokenService.ValidateAccessTokenAsync(token)) + .Should() + .ThrowAsync(); } [Fact] @@ -175,7 +180,7 @@ public class TokenServiceValidationTests List claims = new() { new Claim(JwtRegisteredClaimNames.UniqueName, username), - new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) + new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), }; ClaimsIdentity claimsIdentity = new(claims); @@ -186,9 +191,10 @@ public class TokenServiceValidationTests .ReturnsAsync(principal); // Act & Assert - await FluentActions.Invoking(async () => - await _tokenService.ValidateAccessTokenAsync(token) - ).Should().ThrowAsync() + await FluentActions + .Invoking(async () => await _tokenService.ValidateAccessTokenAsync(token)) + .Should() + .ThrowAsync() .WithMessage("*missing required claims*"); } @@ -203,7 +209,7 @@ public class TokenServiceValidationTests List claims = new() { new Claim(JwtRegisteredClaimNames.Sub, userId.ToString()), - new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) + new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), }; ClaimsIdentity claimsIdentity = new(claims); @@ -214,9 +220,10 @@ public class TokenServiceValidationTests .ReturnsAsync(principal); // Act & Assert - await FluentActions.Invoking(async () => - await _tokenService.ValidateAccessTokenAsync(token) - ).Should().ThrowAsync() + await FluentActions + .Invoking(async () => await _tokenService.ValidateAccessTokenAsync(token)) + .Should() + .ThrowAsync() .WithMessage("*missing required claims*"); } @@ -232,7 +239,7 @@ public class TokenServiceValidationTests { new Claim(JwtRegisteredClaimNames.Sub, "not-a-valid-guid"), new Claim(JwtRegisteredClaimNames.UniqueName, username), - new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) + new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), }; ClaimsIdentity claimsIdentity = new(claims); @@ -243,9 +250,10 @@ public class TokenServiceValidationTests .ReturnsAsync(principal); // Act & Assert - await FluentActions.Invoking(async () => - await _tokenService.ValidateAccessTokenAsync(token) - ).Should().ThrowAsync() + await FluentActions + .Invoking(async () => await _tokenService.ValidateAccessTokenAsync(token)) + .Should() + .ThrowAsync() .WithMessage("*malformed user ID*"); } @@ -260,9 +268,10 @@ public class TokenServiceValidationTests .ThrowsAsync(new UnauthorizedException("Invalid token")); // Act & Assert - await FluentActions.Invoking(async () => - await _tokenService.ValidateRefreshTokenAsync(token) - ).Should().ThrowAsync(); + await FluentActions + .Invoking(async () => await _tokenService.ValidateRefreshTokenAsync(token)) + .Should() + .ThrowAsync(); } [Fact] @@ -276,8 +285,9 @@ public class TokenServiceValidationTests .ThrowsAsync(new UnauthorizedException("Invalid token")); // Act & Assert - await FluentActions.Invoking(async () => - await _tokenService.ValidateConfirmationTokenAsync(token) - ).Should().ThrowAsync(); + await FluentActions + .Invoking(async () => await _tokenService.ValidateConfirmationTokenAsync(token)) + .Should() + .ThrowAsync(); } -} \ No newline at end of file +}