using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using Domain.Entities; using Domain.Exceptions; using FluentAssertions; using Infrastructure.Repository.Auth; using Moq; using Service.Emails; namespace Service.Auth.Tests; public class ConfirmationServiceTest { private readonly Mock _authRepositoryMock; private readonly Mock _tokenServiceMock; private readonly Mock _emailServiceMock; private readonly ConfirmationService _confirmationService; public ConfirmationServiceTest() { _authRepositoryMock = new Mock(); _tokenServiceMock = new Mock(); _emailServiceMock = new Mock(); _confirmationService = new ConfirmationService( _authRepositoryMock.Object, _tokenServiceMock.Object, _emailServiceMock.Object ); } [Fact] public async Task ConfirmUserAsync_WithValidConfirmationToken_ConfirmsUser() { // Arrange var userId = Guid.NewGuid(); const string username = "testuser"; const string confirmationToken = "valid-confirmation-token"; var claims = new List { new(JwtRegisteredClaimNames.Sub, userId.ToString()), new(JwtRegisteredClaimNames.UniqueName, username), new(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), }; var claimsIdentity = new ClaimsIdentity(claims); var principal = new ClaimsPrincipal(claimsIdentity); var validatedToken = new ValidatedToken(userId, username, principal); var userAccount = new UserAccount { UserAccountId = userId, Username = username, FirstName = "Test", LastName = "User", Email = "test@example.com", DateOfBirth = new DateTime(1990, 1, 1), }; _tokenServiceMock .Setup(x => x.ValidateConfirmationTokenAsync(confirmationToken)) .ReturnsAsync(validatedToken); _authRepositoryMock .Setup(x => x.ConfirmUserAccountAsync(userId)) .ReturnsAsync(userAccount); // Act var result = await _confirmationService.ConfirmUserAsync(confirmationToken); // Assert result.Should().NotBeNull(); result.UserId.Should().Be(userId); result.ConfirmedAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromSeconds(1)); _tokenServiceMock.Verify( x => x.ValidateConfirmationTokenAsync(confirmationToken), Times.Once ); _authRepositoryMock.Verify( x => x.ConfirmUserAccountAsync(userId), Times.Once ); } [Fact] public async Task ConfirmUserAsync_WithInvalidConfirmationToken_ThrowsUnauthorizedException() { // Arrange const string invalidToken = "invalid-confirmation-token"; _tokenServiceMock .Setup(x => x.ValidateConfirmationTokenAsync(invalidToken)) .ThrowsAsync(new UnauthorizedException( "Invalid confirmation token" )); // Act & Assert await FluentActions.Invoking(async () => await _confirmationService.ConfirmUserAsync(invalidToken) ).Should().ThrowAsync(); } [Fact] public async Task ConfirmUserAsync_WithExpiredConfirmationToken_ThrowsUnauthorizedException() { // Arrange const string expiredToken = "expired-confirmation-token"; _tokenServiceMock .Setup(x => x.ValidateConfirmationTokenAsync(expiredToken)) .ThrowsAsync(new UnauthorizedException( "Confirmation token has expired" )); // Act & Assert await FluentActions.Invoking(async () => await _confirmationService.ConfirmUserAsync(expiredToken) ).Should().ThrowAsync(); } [Fact] public async Task ConfirmUserAsync_WithNonExistentUser_ThrowsUnauthorizedException() { // Arrange var userId = Guid.NewGuid(); const string username = "nonexistent"; const string confirmationToken = "valid-token-for-nonexistent-user"; var claims = new List { new(JwtRegisteredClaimNames.Sub, userId.ToString()), new(JwtRegisteredClaimNames.UniqueName, username), new(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), }; var claimsIdentity = new ClaimsIdentity(claims); var principal = new ClaimsPrincipal(claimsIdentity); var validatedToken = new ValidatedToken(userId, username, principal); _tokenServiceMock .Setup(x => x.ValidateConfirmationTokenAsync(confirmationToken)) .ReturnsAsync(validatedToken); _authRepositoryMock .Setup(x => x.ConfirmUserAccountAsync(userId)) .ReturnsAsync((UserAccount?)null); // Act & Assert await FluentActions.Invoking(async () => await _confirmationService.ConfirmUserAsync(confirmationToken) ).Should().ThrowAsync() .WithMessage("*User account not found*"); } }