namespace Application.Tests.Auth; using Application.Auth; using Application.Auth.Commands; using Domain.Entities; using Domain.Ports.Repositories; using FluentAssertions; using NSubstitute; using Xunit; public class ResetPasswordCommandTests { private readonly IUserRepository _userRepository; private readonly IPasswordService _passwordService; private readonly IUnitOfWork _unitOfWork; private readonly ResetPasswordCommandHandler _handler; public ResetPasswordCommandTests() { _userRepository = Substitute.For(); _passwordService = Substitute.For(); _unitOfWork = Substitute.For(); _passwordService.HashPassword(Arg.Any()).Returns("new_hashed_password"); _handler = new ResetPasswordCommandHandler( _userRepository, _passwordService, _unitOfWork ); } [Fact] public async Task Handle_WithValidRecoveryCode_ShouldResetPassword() { // Arrange var user = CreateUser("testuser", "old_password_hash", "recovery_code_hash"); _userRepository.GetByUsernameAsync("testuser", Arg.Any()) .Returns(user); _passwordService.VerifyPassword("VALIDCODE123", "recovery_code_hash") .Returns(true); var command = new ResetPasswordCommand("testuser", "VALIDCODE123", "newpassword123"); // Act var result = await _handler.Handle(command, CancellationToken.None); // Assert result.Success.Should().BeTrue(); result.Error.Should().BeNull(); } [Fact] public async Task Handle_WithInvalidRecoveryCode_ShouldReturnError() { // Arrange var user = CreateUser("testuser", "old_password_hash", "recovery_code_hash"); _userRepository.GetByUsernameAsync("testuser", Arg.Any()) .Returns(user); _passwordService.VerifyPassword("INVALIDCODE", "recovery_code_hash") .Returns(false); var command = new ResetPasswordCommand("testuser", "INVALIDCODE", "newpassword123"); // Act var result = await _handler.Handle(command, CancellationToken.None); // Assert result.Success.Should().BeFalse(); result.Error.Should().Contain("invalido"); } [Fact] public async Task Handle_WithNonExistentUser_ShouldReturnError() { // Arrange _userRepository.GetByUsernameAsync("nonexistent", Arg.Any()) .Returns((User?)null); var command = new ResetPasswordCommand("nonexistent", "ANYCODE123", "newpassword123"); // Act var result = await _handler.Handle(command, CancellationToken.None); // Assert result.Success.Should().BeFalse(); result.Error.Should().Contain("no encontrado"); } [Fact] public async Task Handle_WithShortNewPassword_ShouldReturnError() { // Arrange var user = CreateUser("testuser", "old_password_hash", "recovery_code_hash"); _userRepository.GetByUsernameAsync("testuser", Arg.Any()) .Returns(user); var command = new ResetPasswordCommand("testuser", "VALIDCODE123", "12345"); // 5 chars // Act var result = await _handler.Handle(command, CancellationToken.None); // Assert result.Success.Should().BeFalse(); result.Error.Should().Contain("al menos 6 caracteres"); } [Fact] public async Task Handle_ShouldHashNewPassword() { // Arrange var user = CreateUser("testuser", "old_password_hash", "recovery_code_hash"); _userRepository.GetByUsernameAsync("testuser", Arg.Any()) .Returns(user); _passwordService.VerifyPassword("VALIDCODE123", "recovery_code_hash") .Returns(true); var command = new ResetPasswordCommand("testuser", "VALIDCODE123", "newpassword123"); // Act await _handler.Handle(command, CancellationToken.None); // Assert _passwordService.Received(1).HashPassword("newpassword123"); } [Fact] public async Task Handle_ShouldSaveChangesOnSuccess() { // Arrange var user = CreateUser("testuser", "old_password_hash", "recovery_code_hash"); _userRepository.GetByUsernameAsync("testuser", Arg.Any()) .Returns(user); _passwordService.VerifyPassword("VALIDCODE123", "recovery_code_hash") .Returns(true); var command = new ResetPasswordCommand("testuser", "VALIDCODE123", "newpassword123"); // Act await _handler.Handle(command, CancellationToken.None); // Assert await _unitOfWork.Received(1).SaveChangesAsync(Arg.Any()); } [Fact] public async Task Handle_ShouldNotSaveChangesOnError() { // Arrange _userRepository.GetByUsernameAsync("nonexistent", Arg.Any()) .Returns((User?)null); var command = new ResetPasswordCommand("nonexistent", "ANYCODE123", "newpassword123"); // Act await _handler.Handle(command, CancellationToken.None); // Assert await _unitOfWork.DidNotReceive().SaveChangesAsync(Arg.Any()); } [Fact] public async Task Handle_ShouldUpdateUserPasswordHash() { // Arrange var user = CreateUser("testuser", "old_password_hash", "recovery_code_hash"); _userRepository.GetByUsernameAsync("testuser", Arg.Any()) .Returns(user); _passwordService.VerifyPassword("VALIDCODE123", "recovery_code_hash") .Returns(true); var command = new ResetPasswordCommand("testuser", "VALIDCODE123", "newpassword123"); // Act await _handler.Handle(command, CancellationToken.None); // Assert user.PasswordHash.Should().Be("new_hashed_password"); } private static User CreateUser(string username, string passwordHash, string recoveryCodeHash) { return User.Create(username, passwordHash, recoveryCodeHash, UserRoles.Student); } }