mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-07-16 17:47:22 +00:00
Format ./web/backend/Features/Features.Auth.Tests
This commit is contained in:
@@ -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*");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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");
|
||||||
|
|||||||
@@ -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,14 +57,27 @@ 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,
|
||||||
|
command.LastName,
|
||||||
|
command.Email,
|
||||||
|
command.DateOfBirth,
|
||||||
|
hashedPassword
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.ReturnsAsync(
|
||||||
|
new UserAccount
|
||||||
{
|
{
|
||||||
UserAccountId = expectedUserId,
|
UserAccountId = expectedUserId,
|
||||||
Username = command.Username,
|
Username = command.Username,
|
||||||
@@ -62,12 +85,18 @@ public class RegisterUserHandlerTests
|
|||||||
LastName = command.LastName,
|
LastName = command.LastName,
|
||||||
Email = command.Email,
|
Email = command.Email,
|
||||||
DateOfBirth = command.DateOfBirth,
|
DateOfBirth = command.DateOfBirth,
|
||||||
CreatedAt = DateTime.UtcNow
|
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);
|
||||||
|
|||||||
@@ -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
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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*");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user