Format ./web/backend/Features/Features.Auth.Tests

This commit is contained in:
Aaron Po
2026-06-20 15:09:41 -04:00
parent 79ae18b3e2
commit 5bef24696b
10 changed files with 389 additions and 202 deletions

View File

@@ -30,7 +30,7 @@ public class ConfirmUserHandlerTests
{ {
new Claim(JwtRegisteredClaimNames.Sub, userId.ToString()), new Claim(JwtRegisteredClaimNames.Sub, userId.ToString()),
new Claim(JwtRegisteredClaimNames.UniqueName, username), 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)); ClaimsPrincipal principal = new(new ClaimsIdentity(claims));
return new ValidatedToken(userId, username, principal); return new ValidatedToken(userId, username, principal);
@@ -46,10 +46,15 @@ public class ConfirmUserHandlerTests
ValidatedToken validatedToken = MakeValidatedToken(userId, username); ValidatedToken validatedToken = MakeValidatedToken(userId, username);
UserAccount userAccount = new() { UserAccountId = userId, Username = 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); _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.Should().NotBeNull();
result.UserAccountId.Should().Be(userId); result.UserAccountId.Should().Be(userId);
@@ -64,7 +69,8 @@ public class ConfirmUserHandlerTests
.Setup(x => x.ValidateConfirmationTokenAsync(invalidToken)) .Setup(x => x.ValidateConfirmationTokenAsync(invalidToken))
.ThrowsAsync(new UnauthorizedException("Invalid confirmation token")); .ThrowsAsync(new UnauthorizedException("Invalid confirmation token"));
Func<Task<ConfirmationPayload>> act = async () => await _handler.Handle(new ConfirmUserCommand(invalidToken), CancellationToken.None); Func<Task<ConfirmationPayload>> act = async () =>
await _handler.Handle(new ConfirmUserCommand(invalidToken), CancellationToken.None);
await act.Should().ThrowAsync<UnauthorizedException>(); await act.Should().ThrowAsync<UnauthorizedException>();
} }
@@ -79,10 +85,18 @@ public class ConfirmUserHandlerTests
_tokenServiceMock _tokenServiceMock
.Setup(x => x.ValidateConfirmationTokenAsync(confirmationToken)) .Setup(x => x.ValidateConfirmationTokenAsync(confirmationToken))
.ReturnsAsync(MakeValidatedToken(userId, username)); .ReturnsAsync(MakeValidatedToken(userId, username));
_authRepositoryMock.Setup(x => x.ConfirmUserAccountAsync(userId)).ReturnsAsync((UserAccount?)null); _authRepositoryMock
.Setup(x => x.ConfirmUserAccountAsync(userId))
.ReturnsAsync((UserAccount?)null);
Func<Task<ConfirmationPayload>> act = async () => await _handler.Handle(new ConfirmUserCommand(confirmationToken), CancellationToken.None); Func<Task<ConfirmationPayload>> act = async () =>
await _handler.Handle(
new ConfirmUserCommand(confirmationToken),
CancellationToken.None
);
await act.Should().ThrowAsync<UnauthorizedException>().WithMessage("*User account not found*"); await act.Should()
.ThrowAsync<UnauthorizedException>()
.WithMessage("*User account not found*");
} }
} }

View File

@@ -21,7 +21,10 @@ public class RefreshTokenHandlerTests
.Setup(x => x.RefreshTokenAsync("old-refresh-token")) .Setup(x => x.RefreshTokenAsync("old-refresh-token"))
.ReturnsAsync(new RefreshTokenResult(user, "new-refresh-token", "new-access-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.UserAccountId.Should().Be(userId);
result.Username.Should().Be("testuser"); result.Username.Should().Be("testuser");

View File

@@ -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] [Fact]
@@ -47,27 +57,46 @@ public class RegisterUserHandlerTests
const string hashedPassword = "hashed_password_value"; const string hashedPassword = "hashed_password_value";
Guid expectedUserId = Guid.NewGuid(); Guid expectedUserId = Guid.NewGuid();
_authRepoMock.Setup(x => x.GetUserByUsernameAsync(command.Username)).ReturnsAsync((UserAccount?)null); _authRepoMock
_authRepoMock.Setup(x => x.GetUserByEmailAsync(command.Email)).ReturnsAsync((UserAccount?)null); .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); _passwordInfraMock.Setup(x => x.Hash(command.Password)).Returns(hashedPassword);
_authRepoMock _authRepoMock
.Setup(x => x.RegisterUserAsync(command.Username, command.FirstName, command.LastName, command.Email, .Setup(x =>
command.DateOfBirth, hashedPassword)) x.RegisterUserAsync(
.ReturnsAsync(new UserAccount command.Username,
{ command.FirstName,
UserAccountId = expectedUserId, command.LastName,
Username = command.Username, command.Email,
FirstName = command.FirstName, command.DateOfBirth,
LastName = command.LastName, hashedPassword
Email = command.Email, )
DateOfBirth = command.DateOfBirth, )
CreatedAt = DateTime.UtcNow .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<UserAccount>())).Returns("access-token"); _tokenServiceMock
_tokenServiceMock.Setup(x => x.GenerateRefreshToken(It.IsAny<UserAccount>())).Returns("refresh-token"); .Setup(x => x.GenerateAccessToken(It.IsAny<UserAccount>()))
_tokenServiceMock.Setup(x => x.GenerateConfirmationToken(It.IsAny<UserAccount>())) .Returns("access-token");
_tokenServiceMock
.Setup(x => x.GenerateRefreshToken(It.IsAny<UserAccount>()))
.Returns("refresh-token");
_tokenServiceMock
.Setup(x => x.GenerateConfirmationToken(It.IsAny<UserAccount>()))
.Returns("confirmation-token"); .Returns("confirmation-token");
RegistrationPayload result = await _handler.Handle(command, CancellationToken.None); RegistrationPayload result = await _handler.Handle(command, CancellationToken.None);
@@ -89,18 +118,36 @@ public class RegisterUserHandlerTests
public async Task Handle_WithExistingUsername_ThrowsConflictException() public async Task Handle_WithExistingUsername_ThrowsConflictException()
{ {
RegisterUserCommand command = ValidCommand("existinguser"); 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
_authRepoMock.Setup(x => x.GetUserByEmailAsync(command.Email)).ReturnsAsync((UserAccount?)null); .Setup(x => x.GetUserByUsernameAsync(command.Username))
.ReturnsAsync(existingUser);
_authRepoMock
.Setup(x => x.GetUserByEmailAsync(command.Email))
.ReturnsAsync((UserAccount?)null);
Func<Task<RegistrationPayload>> act = async () => await _handler.Handle(command, CancellationToken.None); Func<Task<RegistrationPayload>> act = async () =>
await _handler.Handle(command, CancellationToken.None);
await act.Should().ThrowAsync<ConflictException>().WithMessage("Username or email already exists"); await act.Should()
.ThrowAsync<ConflictException>()
.WithMessage("Username or email already exists");
_authRepoMock.Verify( _authRepoMock.Verify(
x => x.RegisterUserAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), x =>
It.IsAny<DateTime>(), It.IsAny<string>()), x.RegisterUserAsync(
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<DateTime>(),
It.IsAny<string>()
),
Times.Never Times.Never
); );
} }
@@ -109,14 +156,23 @@ public class RegisterUserHandlerTests
public async Task Handle_WithExistingEmail_ThrowsConflictException() public async Task Handle_WithExistingEmail_ThrowsConflictException()
{ {
RegisterUserCommand command = ValidCommand(email: "existing@example.com"); 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); _authRepoMock.Setup(x => x.GetUserByEmailAsync(command.Email)).ReturnsAsync(existingUser);
Func<Task<RegistrationPayload>> act = async () => await _handler.Handle(command, CancellationToken.None); Func<Task<RegistrationPayload>> act = async () =>
await _handler.Handle(command, CancellationToken.None);
await act.Should().ThrowAsync<ConflictException>().WithMessage("Username or email already exists"); await act.Should()
.ThrowAsync<ConflictException>()
.WithMessage("Username or email already exists");
} }
[Fact] [Fact]
@@ -125,24 +181,47 @@ public class RegisterUserHandlerTests
RegisterUserCommand command = ValidCommand(); RegisterUserCommand command = ValidCommand();
const string hashedPassword = "hashed_secure_password"; const string hashedPassword = "hashed_secure_password";
_authRepoMock.Setup(x => x.GetUserByUsernameAsync(It.IsAny<string>())).ReturnsAsync((UserAccount?)null); _authRepoMock
_authRepoMock.Setup(x => x.GetUserByEmailAsync(It.IsAny<string>())).ReturnsAsync((UserAccount?)null); .Setup(x => x.GetUserByUsernameAsync(It.IsAny<string>()))
.ReturnsAsync((UserAccount?)null);
_authRepoMock
.Setup(x => x.GetUserByEmailAsync(It.IsAny<string>()))
.ReturnsAsync((UserAccount?)null);
_passwordInfraMock.Setup(x => x.Hash(command.Password)).Returns(hashedPassword); _passwordInfraMock.Setup(x => x.Hash(command.Password)).Returns(hashedPassword);
_authRepoMock _authRepoMock
.Setup(x => x.RegisterUserAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), .Setup(x =>
It.IsAny<string>(), It.IsAny<DateTime>(), hashedPassword)) x.RegisterUserAsync(
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<DateTime>(),
hashedPassword
)
)
.ReturnsAsync(new UserAccount { UserAccountId = Guid.NewGuid() }); .ReturnsAsync(new UserAccount { UserAccountId = Guid.NewGuid() });
_tokenServiceMock.Setup(x => x.GenerateAccessToken(It.IsAny<UserAccount>())).Returns("access-token"); _tokenServiceMock
_tokenServiceMock.Setup(x => x.GenerateRefreshToken(It.IsAny<UserAccount>())).Returns("refresh-token"); .Setup(x => x.GenerateAccessToken(It.IsAny<UserAccount>()))
.Returns("access-token");
_tokenServiceMock
.Setup(x => x.GenerateRefreshToken(It.IsAny<UserAccount>()))
.Returns("refresh-token");
await _handler.Handle(command, CancellationToken.None); await _handler.Handle(command, CancellationToken.None);
_passwordInfraMock.Verify(x => x.Hash(command.Password), Times.Once); _passwordInfraMock.Verify(x => x.Hash(command.Password), Times.Once);
_authRepoMock.Verify( _authRepoMock.Verify(
x => x.RegisterUserAsync(command.Username, command.FirstName, command.LastName, command.Email, x =>
command.DateOfBirth, hashedPassword), x.RegisterUserAsync(
command.Username,
command.FirstName,
command.LastName,
command.Email,
command.DateOfBirth,
hashedPassword
),
Times.Once Times.Once
); );
} }
@@ -152,19 +231,43 @@ public class RegisterUserHandlerTests
{ {
RegisterUserCommand command = ValidCommand(); RegisterUserCommand command = ValidCommand();
_authRepoMock.Setup(x => x.GetUserByUsernameAsync(It.IsAny<string>())).ReturnsAsync((UserAccount?)null); _authRepoMock
_authRepoMock.Setup(x => x.GetUserByEmailAsync(It.IsAny<string>())).ReturnsAsync((UserAccount?)null); .Setup(x => x.GetUserByUsernameAsync(It.IsAny<string>()))
.ReturnsAsync((UserAccount?)null);
_authRepoMock
.Setup(x => x.GetUserByEmailAsync(It.IsAny<string>()))
.ReturnsAsync((UserAccount?)null);
_passwordInfraMock.Setup(x => x.Hash(It.IsAny<string>())).Returns("hashed"); _passwordInfraMock.Setup(x => x.Hash(It.IsAny<string>())).Returns("hashed");
_authRepoMock _authRepoMock
.Setup(x => x.RegisterUserAsync(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), .Setup(x =>
It.IsAny<string>(), It.IsAny<DateTime>(), It.IsAny<string>())) x.RegisterUserAsync(
.ReturnsAsync(new UserAccount It.IsAny<string>(),
{ UserAccountId = Guid.NewGuid(), Username = command.Username, Email = command.Email }); It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<string>(),
It.IsAny<DateTime>(),
It.IsAny<string>()
)
)
.ReturnsAsync(
new UserAccount
{
UserAccountId = Guid.NewGuid(),
Username = command.Username,
Email = command.Email,
}
);
_tokenServiceMock.Setup(x => x.GenerateAccessToken(It.IsAny<UserAccount>())).Returns("access-token"); _tokenServiceMock
_tokenServiceMock.Setup(x => x.GenerateRefreshToken(It.IsAny<UserAccount>())).Returns("refresh-token"); .Setup(x => x.GenerateAccessToken(It.IsAny<UserAccount>()))
.Returns("access-token");
_tokenServiceMock
.Setup(x => x.GenerateRefreshToken(It.IsAny<UserAccount>()))
.Returns("refresh-token");
_mediatorMock _mediatorMock
.Setup(x => x.Send(It.IsAny<SendRegistrationEmailCommand>(), It.IsAny<CancellationToken>())) .Setup(x =>
x.Send(It.IsAny<SendRegistrationEmailCommand>(), It.IsAny<CancellationToken>())
)
.ThrowsAsync(new Exception("smtp down")); .ThrowsAsync(new Exception("smtp down"));
RegistrationPayload result = await _handler.Handle(command, CancellationToken.None); RegistrationPayload result = await _handler.Handle(command, CancellationToken.None);

View File

@@ -17,15 +17,23 @@ public class ResendConfirmationEmailHandlerTests
public ResendConfirmationEmailHandlerTests() public ResendConfirmationEmailHandlerTests()
{ {
_handler = new ResendConfirmationEmailHandler(_authRepositoryMock.Object, _tokenServiceMock.Object, _handler = new ResendConfirmationEmailHandler(
_mediatorMock.Object); _authRepositoryMock.Object,
_tokenServiceMock.Object,
_mediatorMock.Object
);
} }
[Fact] [Fact]
public async Task Handle_SendsFreshConfirmationEmail_WhenUserExistsAndUnverified() public async Task Handle_SendsFreshConfirmationEmail_WhenUserExistsAndUnverified()
{ {
Guid userId = Guid.NewGuid(); 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.GetUserByIdAsync(userId)).ReturnsAsync(user);
_authRepositoryMock.Setup(x => x.IsUserVerifiedAsync(userId)).ReturnsAsync(false); _authRepositoryMock.Setup(x => x.IsUserVerifiedAsync(userId)).ReturnsAsync(false);
@@ -34,9 +42,15 @@ public class ResendConfirmationEmailHandlerTests
await _handler.Handle(new ResendConfirmationEmailCommand(userId), CancellationToken.None); await _handler.Handle(new ResendConfirmationEmailCommand(userId), CancellationToken.None);
_mediatorMock.Verify( _mediatorMock.Verify(
x => x.Send(It.Is<SendResendConfirmationEmailCommand>(c => x =>
c.FirstName == "Aaron" && c.Email == "aaron@example.com" && c.ConfirmationToken == "fresh-token" x.Send(
), It.IsAny<CancellationToken>()), It.Is<SendResendConfirmationEmailCommand>(c =>
c.FirstName == "Aaron"
&& c.Email == "aaron@example.com"
&& c.ConfirmationToken == "fresh-token"
),
It.IsAny<CancellationToken>()
),
Times.Once Times.Once
); );
} }
@@ -50,7 +64,11 @@ public class ResendConfirmationEmailHandlerTests
await _handler.Handle(new ResendConfirmationEmailCommand(userId), CancellationToken.None); await _handler.Handle(new ResendConfirmationEmailCommand(userId), CancellationToken.None);
_mediatorMock.Verify( _mediatorMock.Verify(
x => x.Send(It.IsAny<SendResendConfirmationEmailCommand>(), It.IsAny<CancellationToken>()), x =>
x.Send(
It.IsAny<SendResendConfirmationEmailCommand>(),
It.IsAny<CancellationToken>()
),
Times.Never Times.Never
); );
} }
@@ -67,7 +85,11 @@ public class ResendConfirmationEmailHandlerTests
await _handler.Handle(new ResendConfirmationEmailCommand(userId), CancellationToken.None); await _handler.Handle(new ResendConfirmationEmailCommand(userId), CancellationToken.None);
_mediatorMock.Verify( _mediatorMock.Verify(
x => x.Send(It.IsAny<SendResendConfirmationEmailCommand>(), It.IsAny<CancellationToken>()), x =>
x.Send(
It.IsAny<SendResendConfirmationEmailCommand>(),
It.IsAny<CancellationToken>()
),
Times.Never Times.Never
); );
} }

View File

@@ -8,19 +8,19 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1"/> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageReference Include="xunit" Version="2.9.2"/> <PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2"/> <PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
<PackageReference Include="Moq" Version="4.20.72"/> <PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="FluentAssertions" Version="6.9.0"/> <PackageReference Include="FluentAssertions" Version="6.9.0" />
<PackageReference Include="DbMocker" Version="1.26.0"/> <PackageReference Include="DbMocker" Version="1.26.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Using Include="Xunit"/> <Using Include="Xunit" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Features.Auth\Features.Auth.csproj"/> <ProjectReference Include="..\Features.Auth\Features.Auth.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -22,7 +22,11 @@ public class LoginHandlerTests
_authRepoMock = new Mock<IAuthRepository>(); _authRepoMock = new Mock<IAuthRepository>();
_passwordInfraMock = new Mock<IPasswordInfrastructure>(); _passwordInfraMock = new Mock<IPasswordInfrastructure>();
_tokenServiceMock = new Mock<ITokenService>(); _tokenServiceMock = new Mock<ITokenService>();
_handler = new LoginHandler(_authRepoMock.Object, _passwordInfraMock.Object, _tokenServiceMock.Object); _handler = new LoginHandler(
_authRepoMock.Object,
_passwordInfraMock.Object,
_tokenServiceMock.Object
);
} }
[Fact] [Fact]
@@ -38,7 +42,7 @@ public class LoginHandlerTests
FirstName = "René", FirstName = "René",
LastName = "Descartes", LastName = "Descartes",
Email = "r.descartes@example.com", Email = "r.descartes@example.com",
DateOfBirth = new DateTime(1596, 03, 31) DateOfBirth = new DateTime(1596, 03, 31),
}; };
UserCredential userCredential = new() UserCredential userCredential = new()
@@ -46,16 +50,27 @@ public class LoginHandlerTests
UserCredentialId = Guid.NewGuid(), UserCredentialId = Guid.NewGuid(),
UserAccountId = userAccountId, UserAccountId = userAccountId,
Hash = "some-hash", Hash = "some-hash",
Expiry = DateTime.MaxValue Expiry = DateTime.MaxValue,
}; };
_authRepoMock.Setup(x => x.GetUserByUsernameAsync(username)).ReturnsAsync(userAccount); _authRepoMock.Setup(x => x.GetUserByUsernameAsync(username)).ReturnsAsync(userAccount);
_authRepoMock.Setup(x => x.GetActiveCredentialByUserAccountIdAsync(userAccountId)).ReturnsAsync(userCredential); _authRepoMock
_passwordInfraMock.Setup(x => x.Verify(It.IsAny<string>(), It.IsAny<string>())).Returns(true); .Setup(x => x.GetActiveCredentialByUserAccountIdAsync(userAccountId))
_tokenServiceMock.Setup(x => x.GenerateAccessToken(It.IsAny<UserAccount>())).Returns("access-token"); .ReturnsAsync(userCredential);
_tokenServiceMock.Setup(x => x.GenerateRefreshToken(It.IsAny<UserAccount>())).Returns("refresh-token"); _passwordInfraMock
.Setup(x => x.Verify(It.IsAny<string>(), It.IsAny<string>()))
.Returns(true);
_tokenServiceMock
.Setup(x => x.GenerateAccessToken(It.IsAny<UserAccount>()))
.Returns("access-token");
_tokenServiceMock
.Setup(x => x.GenerateRefreshToken(It.IsAny<UserAccount>()))
.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.Should().NotBeNull();
result.UserAccountId.Should().Be(userAccountId); result.UserAccountId.Should().Be(userAccountId);
@@ -68,12 +83,18 @@ public class LoginHandlerTests
public async Task Handle_WithUnregisteredUsername_ThrowsUnauthorizedException() public async Task Handle_WithUnregisteredUsername_ThrowsUnauthorizedException()
{ {
const string username = "de_beauvoir"; const string username = "de_beauvoir";
_authRepoMock.Setup(x => x.GetUserByUsernameAsync(username)).ReturnsAsync((UserAccount?)null); _authRepoMock
.Setup(x => x.GetUserByUsernameAsync(username))
.ReturnsAsync((UserAccount?)null);
Func<Task<LoginPayload>> act = async () => await _handler.Handle(new LoginQuery(username, "password"), CancellationToken.None); Func<Task<LoginPayload>> act = async () =>
await _handler.Handle(new LoginQuery(username, "password"), CancellationToken.None);
await act.Should().ThrowAsync<UnauthorizedException>(); await act.Should().ThrowAsync<UnauthorizedException>();
_authRepoMock.Verify(x => x.GetActiveCredentialByUserAccountIdAsync(It.IsAny<Guid>()), Times.Never); _authRepoMock.Verify(
x => x.GetActiveCredentialByUserAccountIdAsync(It.IsAny<Guid>()),
Times.Never
);
} }
[Fact] [Fact]
@@ -84,13 +105,20 @@ public class LoginHandlerTests
UserAccount userAccount = new() { UserAccountId = userAccountId, Username = username }; UserAccount userAccount = new() { UserAccountId = userAccountId, Username = username };
_authRepoMock.Setup(x => x.GetUserByUsernameAsync(username)).ReturnsAsync(userAccount); _authRepoMock.Setup(x => x.GetUserByUsernameAsync(username)).ReturnsAsync(userAccount);
_authRepoMock.Setup(x => x.GetActiveCredentialByUserAccountIdAsync(userAccountId)) _authRepoMock
.Setup(x => x.GetActiveCredentialByUserAccountIdAsync(userAccountId))
.ReturnsAsync((UserCredential?)null); .ReturnsAsync((UserCredential?)null);
Func<Task<LoginPayload>> act = async () => await _handler.Handle(new LoginQuery(username, "password"), CancellationToken.None); Func<Task<LoginPayload>> act = async () =>
await _handler.Handle(new LoginQuery(username, "password"), CancellationToken.None);
await act.Should().ThrowAsync<UnauthorizedException>().WithMessage("Invalid username or password."); await act.Should()
_passwordInfraMock.Verify(x => x.Verify(It.IsAny<string>(), It.IsAny<string>()), Times.Never); .ThrowAsync<UnauthorizedException>()
.WithMessage("Invalid username or password.");
_passwordInfraMock.Verify(
x => x.Verify(It.IsAny<string>(), It.IsAny<string>()),
Times.Never
);
} }
[Fact] [Fact]
@@ -99,14 +127,29 @@ public class LoginHandlerTests
const string username = "RCarnap"; const string username = "RCarnap";
Guid userAccountId = Guid.NewGuid(); Guid userAccountId = Guid.NewGuid();
UserAccount userAccount = new() { UserAccountId = userAccountId, Username = username }; 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.GetUserByUsernameAsync(username)).ReturnsAsync(userAccount);
_authRepoMock.Setup(x => x.GetActiveCredentialByUserAccountIdAsync(userAccountId)).ReturnsAsync(userCredential); _authRepoMock
_passwordInfraMock.Setup(x => x.Verify(It.IsAny<string>(), It.IsAny<string>())).Returns(false); .Setup(x => x.GetActiveCredentialByUserAccountIdAsync(userAccountId))
.ReturnsAsync(userCredential);
_passwordInfraMock
.Setup(x => x.Verify(It.IsAny<string>(), It.IsAny<string>()))
.Returns(false);
Func<Task<LoginPayload>> act = async () => await _handler.Handle(new LoginQuery(username, "wrong-password"), CancellationToken.None); Func<Task<LoginPayload>> act = async () =>
await _handler.Handle(
new LoginQuery(username, "wrong-password"),
CancellationToken.None
);
await act.Should().ThrowAsync<UnauthorizedException>().WithMessage("Invalid username or password."); await act.Should()
.ThrowAsync<UnauthorizedException>()
.WithMessage("Invalid username or password.");
} }
} }

View File

@@ -18,8 +18,7 @@ public class AuthRepositoryTests
Guid expectedUserId = Guid.NewGuid(); Guid expectedUserId = Guid.NewGuid();
MockDbConnection conn = new(); MockDbConnection conn = new();
conn.Mocks.When(cmd => cmd.CommandText == "USP_RegisterUser") conn.Mocks.When(cmd => cmd.CommandText == "USP_RegisterUser").ReturnsScalar(expectedUserId);
.ReturnsScalar(expectedUserId);
// Mock the subsequent read for the newly created user by id // Mock the subsequent read for the newly created user by id
conn.Mocks.When(cmd => cmd.CommandText == "usp_GetUserAccountById") conn.Mocks.When(cmd => cmd.CommandText == "usp_GetUserAccountById")
@@ -132,9 +131,7 @@ public class AuthRepositoryTests
Guid userId = Guid.NewGuid(); Guid userId = Guid.NewGuid();
MockDbConnection conn = new(); MockDbConnection conn = new();
conn.Mocks.When(cmd => conn.Mocks.When(cmd => cmd.CommandText == "usp_GetUserAccountByUsername")
cmd.CommandText == "usp_GetUserAccountByUsername"
)
.ReturnsTable( .ReturnsTable(
MockTable MockTable
.WithColumns( .WithColumns(
@@ -175,9 +172,7 @@ public class AuthRepositoryTests
{ {
MockDbConnection conn = new(); MockDbConnection conn = new();
conn.Mocks.When(cmd => conn.Mocks.When(cmd => cmd.CommandText == "usp_GetUserAccountByUsername")
cmd.CommandText == "usp_GetUserAccountByUsername"
)
.ReturnsTable(MockTable.Empty()); .ReturnsTable(MockTable.Empty());
AuthRepository repo = CreateRepo(conn); AuthRepository repo = CreateRepo(conn);
@@ -193,9 +188,7 @@ public class AuthRepositoryTests
Guid credentialId = Guid.NewGuid(); Guid credentialId = Guid.NewGuid();
MockDbConnection conn = new(); MockDbConnection conn = new();
conn.Mocks.When(cmd => conn.Mocks.When(cmd => cmd.CommandText == "USP_GetActiveUserCredentialByUserAccountId")
cmd.CommandText == "USP_GetActiveUserCredentialByUserAccountId"
)
.ReturnsTable( .ReturnsTable(
MockTable MockTable
.WithColumns( .WithColumns(
@@ -205,13 +198,7 @@ public class AuthRepositoryTests
("CreatedAt", typeof(DateTime)), ("CreatedAt", typeof(DateTime)),
("Timer", typeof(byte[])) ("Timer", typeof(byte[]))
) )
.AddRow( .AddRow(credentialId, userId, "hashed_password_value", DateTime.UtcNow, null)
credentialId,
userId,
"hashed_password_value",
DateTime.UtcNow,
null
)
); );
AuthRepository repo = CreateRepo(conn); AuthRepository repo = CreateRepo(conn);
@@ -229,9 +216,7 @@ public class AuthRepositoryTests
Guid userId = Guid.NewGuid(); Guid userId = Guid.NewGuid();
MockDbConnection conn = new(); MockDbConnection conn = new();
conn.Mocks.When(cmd => conn.Mocks.When(cmd => cmd.CommandText == "USP_GetActiveUserCredentialByUserAccountId")
cmd.CommandText == "USP_GetActiveUserCredentialByUserAccountId"
)
.ReturnsTable(MockTable.Empty()); .ReturnsTable(MockTable.Empty());
AuthRepository repo = CreateRepo(conn); AuthRepository repo = CreateRepo(conn);
@@ -247,14 +232,12 @@ public class AuthRepositoryTests
string newPasswordHash = "new_hashed_password"; string newPasswordHash = "new_hashed_password";
MockDbConnection conn = new(); MockDbConnection conn = new();
conn.Mocks.When(cmd => cmd.CommandText == "USP_RotateUserCredential") conn.Mocks.When(cmd => cmd.CommandText == "USP_RotateUserCredential").ReturnsScalar(1);
.ReturnsScalar(1);
AuthRepository repo = CreateRepo(conn); AuthRepository repo = CreateRepo(conn);
// Should not throw // Should not throw
Func<Task> act = async () => Func<Task> act = async () => await repo.RotateCredentialAsync(userId, newPasswordHash);
await repo.RotateCredentialAsync(userId, newPasswordHash);
await act.Should().NotThrowAsync(); await act.Should().NotThrowAsync();
} }
} }

View File

@@ -22,15 +22,20 @@ public class TokenServiceRefreshTests
_authRepositoryMock = new Mock<IAuthRepository>(); _authRepositoryMock = new Mock<IAuthRepository>();
// Set environment variables for tokens // Set environment variables for tokens
Environment.SetEnvironmentVariable("ACCESS_TOKEN_SECRET", "test-access-secret-that-is-very-long-1234567890"); Environment.SetEnvironmentVariable(
Environment.SetEnvironmentVariable("REFRESH_TOKEN_SECRET", "test-refresh-secret-that-is-very-long-1234567890"); "ACCESS_TOKEN_SECRET",
Environment.SetEnvironmentVariable("CONFIRMATION_TOKEN_SECRET", "test-access-secret-that-is-very-long-1234567890"
"test-confirmation-secret-that-is-very-long-1234567890");
_tokenService = new TokenService(
_tokenInfraMock.Object,
_authRepositoryMock.Object
); );
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] [Fact]
@@ -45,7 +50,7 @@ public class TokenServiceRefreshTests
{ {
new Claim(JwtRegisteredClaimNames.Sub, userId.ToString()), new Claim(JwtRegisteredClaimNames.Sub, userId.ToString()),
new Claim(JwtRegisteredClaimNames.UniqueName, username), new Claim(JwtRegisteredClaimNames.UniqueName, username),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
}; };
ClaimsIdentity claimsIdentity = new(claims); ClaimsIdentity claimsIdentity = new(claims);
@@ -58,7 +63,7 @@ public class TokenServiceRefreshTests
FirstName = "Test", FirstName = "Test",
LastName = "User", LastName = "User",
Email = "test@example.com", Email = "test@example.com",
DateOfBirth = new DateTime(1990, 1, 1) DateOfBirth = new DateTime(1990, 1, 1),
}; };
// Mock the validation of refresh token // Mock the validation of refresh token
@@ -69,11 +74,11 @@ public class TokenServiceRefreshTests
// Mock the generation of new tokens // Mock the generation of new tokens
_tokenInfraMock _tokenInfraMock
.Setup(x => x.GenerateJwt(userId, username, It.IsAny<DateTime>(), It.IsAny<string>())) .Setup(x => x.GenerateJwt(userId, username, It.IsAny<DateTime>(), It.IsAny<string>()))
.Returns((Guid _, string _, DateTime _, string _) => $"generated-token-{Guid.NewGuid()}"); .Returns(
(Guid _, string _, DateTime _, string _) => $"generated-token-{Guid.NewGuid()}"
);
_authRepositoryMock _authRepositoryMock.Setup(x => x.GetUserByIdAsync(userId)).ReturnsAsync(userAccount);
.Setup(x => x.GetUserByIdAsync(userId))
.ReturnsAsync(userAccount);
// Act // Act
RefreshTokenResult result = await _tokenService.RefreshTokenAsync(refreshToken); RefreshTokenResult result = await _tokenService.RefreshTokenAsync(refreshToken);
@@ -85,14 +90,17 @@ public class TokenServiceRefreshTests
result.AccessToken.Should().NotBeEmpty(); result.AccessToken.Should().NotBeEmpty();
result.RefreshToken.Should().NotBeEmpty(); result.RefreshToken.Should().NotBeEmpty();
_authRepositoryMock.Verify( _authRepositoryMock.Verify(x => x.GetUserByIdAsync(userId), Times.Once);
x => x.GetUserByIdAsync(userId),
Times.Once
);
// Verify tokens were generated (called twice - once for access, once for refresh) // Verify tokens were generated (called twice - once for access, once for refresh)
_tokenInfraMock.Verify( _tokenInfraMock.Verify(
x => x.GenerateJwt(It.IsAny<Guid>(), It.IsAny<string>(), It.IsAny<DateTime>(), It.IsAny<string>()), x =>
x.GenerateJwt(
It.IsAny<Guid>(),
It.IsAny<string>(),
It.IsAny<DateTime>(),
It.IsAny<string>()
),
Times.Exactly(2) Times.Exactly(2)
); );
} }
@@ -108,9 +116,10 @@ public class TokenServiceRefreshTests
.ThrowsAsync(new UnauthorizedException("Invalid refresh token")); .ThrowsAsync(new UnauthorizedException("Invalid refresh token"));
// Act & Assert // Act & Assert
await FluentActions.Invoking(async () => await FluentActions
await _tokenService.RefreshTokenAsync(invalidToken) .Invoking(async () => await _tokenService.RefreshTokenAsync(invalidToken))
).Should().ThrowAsync<UnauthorizedException>(); .Should()
.ThrowAsync<UnauthorizedException>();
} }
[Fact] [Fact]
@@ -124,9 +133,10 @@ public class TokenServiceRefreshTests
.ThrowsAsync(new UnauthorizedException("Refresh token has expired")); .ThrowsAsync(new UnauthorizedException("Refresh token has expired"));
// Act & Assert // Act & Assert
await FluentActions.Invoking(async () => await FluentActions
await _tokenService.RefreshTokenAsync(expiredToken) .Invoking(async () => await _tokenService.RefreshTokenAsync(expiredToken))
).Should().ThrowAsync<UnauthorizedException>(); .Should()
.ThrowAsync<UnauthorizedException>();
} }
[Fact] [Fact]
@@ -141,7 +151,7 @@ public class TokenServiceRefreshTests
{ {
new Claim(JwtRegisteredClaimNames.Sub, userId.ToString()), new Claim(JwtRegisteredClaimNames.Sub, userId.ToString()),
new Claim(JwtRegisteredClaimNames.UniqueName, username), new Claim(JwtRegisteredClaimNames.UniqueName, username),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
}; };
ClaimsIdentity claimsIdentity = new(claims); ClaimsIdentity claimsIdentity = new(claims);
@@ -151,14 +161,13 @@ public class TokenServiceRefreshTests
.Setup(x => x.ValidateJwtAsync(refreshToken, It.IsAny<string>())) .Setup(x => x.ValidateJwtAsync(refreshToken, It.IsAny<string>()))
.ReturnsAsync(principal); .ReturnsAsync(principal);
_authRepositoryMock _authRepositoryMock.Setup(x => x.GetUserByIdAsync(userId)).ReturnsAsync((UserAccount?)null);
.Setup(x => x.GetUserByIdAsync(userId))
.ReturnsAsync((UserAccount?)null);
// Act & Assert // Act & Assert
await FluentActions.Invoking(async () => await FluentActions
await _tokenService.RefreshTokenAsync(refreshToken) .Invoking(async () => await _tokenService.RefreshTokenAsync(refreshToken))
).Should().ThrowAsync<UnauthorizedException>() .Should()
.ThrowAsync<UnauthorizedException>()
.WithMessage("*User account not found*"); .WithMessage("*User account not found*");
} }
} }

View File

@@ -21,15 +21,20 @@ public class TokenServiceValidationTests
_authRepositoryMock = new Mock<IAuthRepository>(); _authRepositoryMock = new Mock<IAuthRepository>();
// Set environment variables for tokens // Set environment variables for tokens
Environment.SetEnvironmentVariable("ACCESS_TOKEN_SECRET", "test-access-secret-that-is-very-long-1234567890"); Environment.SetEnvironmentVariable(
Environment.SetEnvironmentVariable("REFRESH_TOKEN_SECRET", "test-refresh-secret-that-is-very-long-1234567890"); "ACCESS_TOKEN_SECRET",
Environment.SetEnvironmentVariable("CONFIRMATION_TOKEN_SECRET", "test-access-secret-that-is-very-long-1234567890"
"test-confirmation-secret-that-is-very-long-1234567890");
_tokenService = new TokenService(
_tokenInfraMock.Object,
_authRepositoryMock.Object
); );
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] [Fact]
@@ -44,7 +49,7 @@ public class TokenServiceValidationTests
{ {
new Claim(JwtRegisteredClaimNames.Sub, userId.ToString()), new Claim(JwtRegisteredClaimNames.Sub, userId.ToString()),
new Claim(JwtRegisteredClaimNames.UniqueName, username), new Claim(JwtRegisteredClaimNames.UniqueName, username),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
}; };
ClaimsIdentity claimsIdentity = new(claims); ClaimsIdentity claimsIdentity = new(claims);
@@ -55,15 +60,17 @@ public class TokenServiceValidationTests
.ReturnsAsync(principal); .ReturnsAsync(principal);
// Act // Act
ValidatedToken result = ValidatedToken result = await _tokenService.ValidateAccessTokenAsync(token);
await _tokenService.ValidateAccessTokenAsync(token);
// Assert // Assert
result.Should().NotBeNull(); result.Should().NotBeNull();
result.UserId.Should().Be(userId); result.UserId.Should().Be(userId);
result.Username.Should().Be(username); result.Username.Should().Be(username);
result.Principal.Should().NotBeNull(); 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] [Fact]
@@ -78,7 +85,7 @@ public class TokenServiceValidationTests
{ {
new Claim(JwtRegisteredClaimNames.Sub, userId.ToString()), new Claim(JwtRegisteredClaimNames.Sub, userId.ToString()),
new Claim(JwtRegisteredClaimNames.UniqueName, username), new Claim(JwtRegisteredClaimNames.UniqueName, username),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
}; };
ClaimsIdentity claimsIdentity = new(claims); ClaimsIdentity claimsIdentity = new(claims);
@@ -89,8 +96,7 @@ public class TokenServiceValidationTests
.ReturnsAsync(principal); .ReturnsAsync(principal);
// Act // Act
ValidatedToken result = ValidatedToken result = await _tokenService.ValidateRefreshTokenAsync(token);
await _tokenService.ValidateRefreshTokenAsync(token);
// Assert // Assert
result.Should().NotBeNull(); result.Should().NotBeNull();
@@ -110,7 +116,7 @@ public class TokenServiceValidationTests
{ {
new Claim(JwtRegisteredClaimNames.Sub, userId.ToString()), new Claim(JwtRegisteredClaimNames.Sub, userId.ToString()),
new Claim(JwtRegisteredClaimNames.UniqueName, username), new Claim(JwtRegisteredClaimNames.UniqueName, username),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
}; };
ClaimsIdentity claimsIdentity = new(claims); ClaimsIdentity claimsIdentity = new(claims);
@@ -121,8 +127,7 @@ public class TokenServiceValidationTests
.ReturnsAsync(principal); .ReturnsAsync(principal);
// Act // Act
ValidatedToken result = ValidatedToken result = await _tokenService.ValidateConfirmationTokenAsync(token);
await _tokenService.ValidateConfirmationTokenAsync(token);
// Assert // Assert
result.Should().NotBeNull(); result.Should().NotBeNull();
@@ -141,9 +146,10 @@ public class TokenServiceValidationTests
.ThrowsAsync(new UnauthorizedException("Invalid token")); .ThrowsAsync(new UnauthorizedException("Invalid token"));
// Act & Assert // Act & Assert
await FluentActions.Invoking(async () => await FluentActions
await _tokenService.ValidateAccessTokenAsync(token) .Invoking(async () => await _tokenService.ValidateAccessTokenAsync(token))
).Should().ThrowAsync<UnauthorizedException>(); .Should()
.ThrowAsync<UnauthorizedException>();
} }
[Fact] [Fact]
@@ -154,14 +160,13 @@ public class TokenServiceValidationTests
_tokenInfraMock _tokenInfraMock
.Setup(x => x.ValidateJwtAsync(token, It.IsAny<string>())) .Setup(x => x.ValidateJwtAsync(token, It.IsAny<string>()))
.ThrowsAsync(new UnauthorizedException( .ThrowsAsync(new UnauthorizedException("Token has expired"));
"Token has expired"
));
// Act & Assert // Act & Assert
await FluentActions.Invoking(async () => await FluentActions
await _tokenService.ValidateAccessTokenAsync(token) .Invoking(async () => await _tokenService.ValidateAccessTokenAsync(token))
).Should().ThrowAsync<UnauthorizedException>(); .Should()
.ThrowAsync<UnauthorizedException>();
} }
[Fact] [Fact]
@@ -175,7 +180,7 @@ public class TokenServiceValidationTests
List<Claim> claims = new() List<Claim> claims = new()
{ {
new Claim(JwtRegisteredClaimNames.UniqueName, username), new Claim(JwtRegisteredClaimNames.UniqueName, username),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
}; };
ClaimsIdentity claimsIdentity = new(claims); ClaimsIdentity claimsIdentity = new(claims);
@@ -186,9 +191,10 @@ public class TokenServiceValidationTests
.ReturnsAsync(principal); .ReturnsAsync(principal);
// Act & Assert // Act & Assert
await FluentActions.Invoking(async () => await FluentActions
await _tokenService.ValidateAccessTokenAsync(token) .Invoking(async () => await _tokenService.ValidateAccessTokenAsync(token))
).Should().ThrowAsync<UnauthorizedException>() .Should()
.ThrowAsync<UnauthorizedException>()
.WithMessage("*missing required claims*"); .WithMessage("*missing required claims*");
} }
@@ -203,7 +209,7 @@ public class TokenServiceValidationTests
List<Claim> claims = new() List<Claim> claims = new()
{ {
new Claim(JwtRegisteredClaimNames.Sub, userId.ToString()), new Claim(JwtRegisteredClaimNames.Sub, userId.ToString()),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
}; };
ClaimsIdentity claimsIdentity = new(claims); ClaimsIdentity claimsIdentity = new(claims);
@@ -214,9 +220,10 @@ public class TokenServiceValidationTests
.ReturnsAsync(principal); .ReturnsAsync(principal);
// Act & Assert // Act & Assert
await FluentActions.Invoking(async () => await FluentActions
await _tokenService.ValidateAccessTokenAsync(token) .Invoking(async () => await _tokenService.ValidateAccessTokenAsync(token))
).Should().ThrowAsync<UnauthorizedException>() .Should()
.ThrowAsync<UnauthorizedException>()
.WithMessage("*missing required claims*"); .WithMessage("*missing required claims*");
} }
@@ -232,7 +239,7 @@ public class TokenServiceValidationTests
{ {
new Claim(JwtRegisteredClaimNames.Sub, "not-a-valid-guid"), new Claim(JwtRegisteredClaimNames.Sub, "not-a-valid-guid"),
new Claim(JwtRegisteredClaimNames.UniqueName, username), new Claim(JwtRegisteredClaimNames.UniqueName, username),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
}; };
ClaimsIdentity claimsIdentity = new(claims); ClaimsIdentity claimsIdentity = new(claims);
@@ -243,9 +250,10 @@ public class TokenServiceValidationTests
.ReturnsAsync(principal); .ReturnsAsync(principal);
// Act & Assert // Act & Assert
await FluentActions.Invoking(async () => await FluentActions
await _tokenService.ValidateAccessTokenAsync(token) .Invoking(async () => await _tokenService.ValidateAccessTokenAsync(token))
).Should().ThrowAsync<UnauthorizedException>() .Should()
.ThrowAsync<UnauthorizedException>()
.WithMessage("*malformed user ID*"); .WithMessage("*malformed user ID*");
} }
@@ -260,9 +268,10 @@ public class TokenServiceValidationTests
.ThrowsAsync(new UnauthorizedException("Invalid token")); .ThrowsAsync(new UnauthorizedException("Invalid token"));
// Act & Assert // Act & Assert
await FluentActions.Invoking(async () => await FluentActions
await _tokenService.ValidateRefreshTokenAsync(token) .Invoking(async () => await _tokenService.ValidateRefreshTokenAsync(token))
).Should().ThrowAsync<UnauthorizedException>(); .Should()
.ThrowAsync<UnauthorizedException>();
} }
[Fact] [Fact]
@@ -276,8 +285,9 @@ public class TokenServiceValidationTests
.ThrowsAsync(new UnauthorizedException("Invalid token")); .ThrowsAsync(new UnauthorizedException("Invalid token"));
// Act & Assert // Act & Assert
await FluentActions.Invoking(async () => await FluentActions
await _tokenService.ValidateConfirmationTokenAsync(token) .Invoking(async () => await _tokenService.ValidateConfirmationTokenAsync(token))
).Should().ThrowAsync<UnauthorizedException>(); .Should()
.ThrowAsync<UnauthorizedException>();
} }
} }