using Application.Common.Results; using Application.DTOs; using Application.Errors; using Application.Interfaces; using Application.Models; using Application.Services; using Application.Validators; using Domain.Entities; using Domain.Interface; using Microsoft.AspNetCore.Identity; using NSubstitute; namespace Application.UnitTest.Services; public class AuthenticationServiceTests { private readonly IUnitOfWork _unitOfWork = Substitute.For(); private readonly IUserRepository _userRepository = Substitute.For(); private readonly IJwtService _jwtService = Substitute.For(); private readonly IEmailService _emailService = Substitute.For(); private readonly AuthenticationService _sut; public AuthenticationServiceTests() { _sut = new AuthenticationService( _unitOfWork, _userRepository, new LoginRequestValidator(), new RegisterRequestValidator(), _jwtService, _emailService); } [Fact] public async Task RegisterAsync_ShouldReturnSuccess_WhenRequestIsValid() { var request = new RegisterRequest("newuser", "new@example.com", "ValidP@ss1!"); _userRepository.GetUserByEmailAsync(request.Email).Returns((User?)null); _userRepository.GetUserByUsernameAsync(request.Username).Returns((User?)null); var result = await _sut.RegisterAsync(request); Assert.True(result.IsSuccess); await _userRepository.Received(1).AddAsync(Arg.Any()); await _unitOfWork.Received(1).CommitAsync(); } [Fact] public async Task RegisterAsync_ShouldReturnFailure_WhenValidationFails() { var request = new RegisterRequest("", "", ""); var result = await _sut.RegisterAsync(request); Assert.True(result.IsFailure); Assert.Equal(ErrorTypeConstant.ValidationError, result.Error.Code); } [Fact] public async Task RegisterAsync_ShouldReturnFailure_WhenEmailAlreadyExists() { var request = new RegisterRequest("newuser", "existing@example.com", "ValidP@ss1!"); _userRepository.GetUserByEmailAsync(request.Email).Returns(new User { Username = "existing", Email = "existing@example.com", Password = "hash" }); var result = await _sut.RegisterAsync(request); Assert.True(result.IsFailure); Assert.Equal(AuthError.EmailAlreadyExists, result.Error); } [Fact] public async Task RegisterAsync_ShouldReturnFailure_WhenUsernameAlreadyExists() { var request = new RegisterRequest("existinguser", "new@example.com", "ValidP@ss1!"); _userRepository.GetUserByEmailAsync(request.Email).Returns((User?)null); _userRepository.GetUserByUsernameAsync(request.Username).Returns(new User { Username = "existinguser", Email = "existing@example.com", Password = "hash" }); var result = await _sut.RegisterAsync(request); Assert.True(result.IsFailure); Assert.Equal(AuthError.UsernameAlreadyExists, result.Error); } [Fact] public async Task LoginAsync_ShouldReturnSuccess_WhenCredentialsAreValid() { var password = "ValidP@ss1!"; var user = new User { Id = 1, Username = "testuser", Email = "test@example.com", Password = new PasswordHasher().HashPassword( new User { Username = "testuser", Email = "test@example.com", Password = password }, password) }; var request = new LoginRequest(user.Email, password); _userRepository.GetUserByEmailAsync(request.Email).Returns(user); _jwtService.GenerateTokenAsync(user).Returns("access-token"); _jwtService.GenerateAndSaveRefreshTokenAsync(user).Returns("refresh-token"); var result = await _sut.LoginAsync(request); Assert.True(result.IsSuccess); _userRepository.Received(1).Update(user); } [Fact] public async Task LoginAsync_ShouldReturnFailure_WhenValidationFails() { var request = new LoginRequest("", ""); var result = await _sut.LoginAsync(request); Assert.True(result.IsFailure); Assert.Equal(ErrorTypeConstant.ValidationError, result.Error.Code); } [Fact] public async Task LoginAsync_ShouldReturnFailure_WhenUserNotFound() { var request = new LoginRequest("unknown@example.com", "password123"); _userRepository.GetUserByEmailAsync(request.Email).Returns((User?)null); var result = await _sut.LoginAsync(request); Assert.True(result.IsFailure); Assert.Equal(AuthError.UserNotFound, result.Error); } [Fact] public async Task LoginAsync_ShouldReturnFailure_WhenPasswordIsWrong() { var password = "ValidP@ss1!"; var user = new User { Id = 1, Username = "testuser", Email = "test@example.com", Password = new PasswordHasher().HashPassword( new User { Username = "testuser", Email = "test@example.com", Password = password }, password) }; var request = new LoginRequest(user.Email, "WrongPassword1!"); _userRepository.GetUserByEmailAsync(request.Email).Returns(user); var result = await _sut.LoginAsync(request); Assert.True(result.IsFailure); Assert.Equal(AuthError.InvalidPassword, result.Error); } [Fact] public async Task RefreshTokensAsync_ShouldReturnSuccess_WhenRefreshTokenIsValid() { var request = new RefreshTokenRequest { UserId = 1, RefreshToken = "valid-refresh-token" }; var user = new User { Id = 1, Username = "testuser", Email = "test@example.com", Password = "hash" }; _jwtService.ValidateRefreshTokenAsync(request.UserId, request.RefreshToken).Returns(user); _jwtService.GenerateTokenAsync(user).Returns("new-access-token"); _jwtService.GenerateAndSaveRefreshTokenAsync(user).Returns("new-refresh-token"); var result = await _sut.RefreshTokensAsync(request); Assert.True(result.IsSuccess); } [Fact] public async Task RefreshTokensAsync_ShouldReturnFailure_WhenUserNotFound() { var request = new RefreshTokenRequest { UserId = 99, RefreshToken = "invalid-token" }; _jwtService.ValidateRefreshTokenAsync(request.UserId, request.RefreshToken).Returns((User?)null); var result = await _sut.RefreshTokensAsync(request); Assert.True(result.IsFailure); Assert.Equal(AuthError.UserNotFound, result.Error); } [Fact] public async Task SendResetEmailAsync_ShouldReturnSuccess_WhenUserExists() { var email = "test@example.com"; var user = new User { Id = 1, Username = "testuser", Email = email, Password = "hash" }; _userRepository.GetUserByEmailAsync(email).Returns(user); var result = await _sut.SendResetEmailAsync(email); Assert.True(result.IsSuccess); _emailService.Received(1).SendEmailAsync(Arg.Any()); _userRepository.Received(1).Update(user); await _unitOfWork.Received(1).CommitAsync(); } [Fact] public async Task SendResetEmailAsync_ShouldReturnFailure_WhenUserNotFound() { var email = "unknown@example.com"; _userRepository.GetUserByEmailAsync(email).Returns((User?)null); var result = await _sut.SendResetEmailAsync(email); Assert.True(result.IsFailure); Assert.Equal(AuthError.UserNotFound, result.Error); } [Fact] public async Task ResetPasswordAsync_ShouldReturnSuccess_WhenTokenIsValid() { var dto = new ResetPasswordDto { Email = "test@example.com", EmailToken = "valid-token", NewPassword = "NewValidP@ss1" }; var user = new User { Id = 1, Username = "testuser", Email = dto.Email, Password = "old-hash", ResetPasswordToken = "valid-token", ResetPasswordTokenExpiryTime = DateTime.UtcNow.AddHours(1) }; _userRepository.GetUserByEmailAsync(dto.Email).Returns(user); var result = await _sut.ResetPasswordAsync(dto); Assert.True(result.IsSuccess); _userRepository.Received(1).Update(user); await _unitOfWork.Received(1).CommitAsync(); } [Fact] public async Task ResetPasswordAsync_ShouldReturnFailure_WhenUserNotFound() { var dto = new ResetPasswordDto { Email = "unknown@example.com", EmailToken = "token", NewPassword = "NewValidP@ss1" }; _userRepository.GetUserByEmailAsync(dto.Email).Returns((User?)null); var result = await _sut.ResetPasswordAsync(dto); Assert.True(result.IsFailure); Assert.Equal(AuthError.UserNotFound, result.Error); } [Fact] public async Task ResetPasswordAsync_ShouldReturnFailure_WhenTokenIsInvalid() { var dto = new ResetPasswordDto { Email = "test@example.com", EmailToken = "wrong-token", NewPassword = "NewValidP@ss1" }; var user = new User { Id = 1, Username = "testuser", Email = dto.Email, Password = "old-hash", ResetPasswordToken = "valid-token", ResetPasswordTokenExpiryTime = DateTime.UtcNow.AddHours(1) }; _userRepository.GetUserByEmailAsync(dto.Email).Returns(user); var result = await _sut.ResetPasswordAsync(dto); Assert.True(result.IsFailure); Assert.Equal(AuthError.InvalidResetLink, result.Error); } [Fact] public async Task ResetPasswordAsync_ShouldReturnFailure_WhenTokenIsExpired() { var dto = new ResetPasswordDto { Email = "test@example.com", EmailToken = "valid-token", NewPassword = "NewValidP@ss1" }; var user = new User { Id = 1, Username = "testuser", Email = dto.Email, Password = "old-hash", ResetPasswordToken = "valid-token", ResetPasswordTokenExpiryTime = DateTime.UtcNow.AddHours(-1) }; _userRepository.GetUserByEmailAsync(dto.Email).Returns(user); var result = await _sut.ResetPasswordAsync(dto); Assert.True(result.IsFailure); Assert.Equal(AuthError.InvalidResetLink, result.Error); } }