namespace Application.Tests.Auth; using Application.Auth; using Application.Auth.Commands; using Domain.Entities; using Domain.Ports.Repositories; using Domain.ValueObjects; using FluentAssertions; using NSubstitute; using Xunit; public class ActivateAccountCommandTests { private readonly IStudentRepository _studentRepository; private readonly IUserRepository _userRepository; private readonly IPasswordService _passwordService; private readonly IJwtService _jwtService; private readonly IUnitOfWork _unitOfWork; private readonly ActivateAccountHandler _handler; public ActivateAccountCommandTests() { _studentRepository = Substitute.For(); _userRepository = Substitute.For(); _passwordService = Substitute.For(); _jwtService = Substitute.For(); _unitOfWork = Substitute.For(); _passwordService.HashPassword(Arg.Any()).Returns("hashed_value"); _handler = new ActivateAccountHandler( _studentRepository, _userRepository, _passwordService, _jwtService, _unitOfWork ); } [Fact] public async Task Handle_WithValidActivationCode_ShouldActivateAccount() { // Arrange var student = CreateStudentWithActivationCode("John Doe", "john@test.com", "activation_hash"); _studentRepository.GetPendingActivationAsync(Arg.Any()) .Returns(new List { student }); _passwordService.VerifyPassword("VALIDCODE123", "activation_hash") .Returns(true); _userRepository.ExistsAsync("newuser", Arg.Any()) .Returns(false); _studentRepository.GetByIdAsync(student.Id, Arg.Any()) .Returns(student); _jwtService.GenerateToken(Arg.Any()) .Returns("new.jwt.token"); var command = new ActivateAccountCommand("VALIDCODE123", "newuser", "password123"); // Act var result = await _handler.Handle(command, CancellationToken.None); // Assert result.Success.Should().BeTrue(); result.Token.Should().Be("new.jwt.token"); result.RecoveryCode.Should().NotBeNullOrEmpty(); result.Error.Should().BeNull(); } [Fact] public async Task Handle_WithInvalidActivationCode_ShouldReturnError() { // Arrange var student = CreateStudentWithActivationCode("John Doe", "john@test.com", "activation_hash"); _studentRepository.GetPendingActivationAsync(Arg.Any()) .Returns(new List { student }); _passwordService.VerifyPassword("INVALIDCODE", Arg.Any()) .Returns(false); var command = new ActivateAccountCommand("INVALIDCODE", "newuser", "password123"); // Act var result = await _handler.Handle(command, CancellationToken.None); // Assert result.Success.Should().BeFalse(); result.Error.Should().Contain("invalido"); } [Fact] public async Task Handle_WithExpiredActivationCode_ShouldReturnError() { // Arrange var student = CreateStudentWithExpiredActivationCode("John Doe", "john@test.com", "activation_hash"); _studentRepository.GetPendingActivationAsync(Arg.Any()) .Returns(new List { student }); _passwordService.VerifyPassword("VALIDCODE123", "activation_hash") .Returns(true); var command = new ActivateAccountCommand("VALIDCODE123", "newuser", "password123"); // Act var result = await _handler.Handle(command, CancellationToken.None); // Assert result.Success.Should().BeFalse(); result.Error.Should().Contain("expirado"); } [Fact] public async Task Handle_WithExistingUsername_ShouldReturnError() { // Arrange var student = CreateStudentWithActivationCode("John Doe", "john@test.com", "activation_hash"); _studentRepository.GetPendingActivationAsync(Arg.Any()) .Returns(new List { student }); _passwordService.VerifyPassword("VALIDCODE123", "activation_hash") .Returns(true); _userRepository.ExistsAsync("existinguser", Arg.Any()) .Returns(true); var command = new ActivateAccountCommand("VALIDCODE123", "existinguser", "password123"); // Act var result = await _handler.Handle(command, CancellationToken.None); // Assert result.Success.Should().BeFalse(); result.Error.Should().Contain("ya existe"); } [Fact] public async Task Handle_WithShortPassword_ShouldReturnError() { // Arrange var student = CreateStudentWithActivationCode("John Doe", "john@test.com", "activation_hash"); _studentRepository.GetPendingActivationAsync(Arg.Any()) .Returns(new List { student }); _passwordService.VerifyPassword("VALIDCODE123", "activation_hash") .Returns(true); _userRepository.ExistsAsync("newuser", Arg.Any()) .Returns(false); var command = new ActivateAccountCommand("VALIDCODE123", "newuser", "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_ShouldCreateUserWithStudentRole() { // Arrange var student = CreateStudentWithActivationCode("John Doe", "john@test.com", "activation_hash"); SetEntityId(student, 5); _studentRepository.GetPendingActivationAsync(Arg.Any()) .Returns(new List { student }); _passwordService.VerifyPassword("VALIDCODE123", "activation_hash") .Returns(true); _userRepository.ExistsAsync("newuser", Arg.Any()) .Returns(false); _studentRepository.GetByIdAsync(5, Arg.Any()) .Returns(student); _jwtService.GenerateToken(Arg.Any()) .Returns("new.jwt.token"); var command = new ActivateAccountCommand("VALIDCODE123", "newuser", "password123"); // Act await _handler.Handle(command, CancellationToken.None); // Assert await _userRepository.Received(1).AddAsync( Arg.Is(u => u.Role == UserRoles.Student && u.StudentId == 5), Arg.Any() ); } [Fact] public async Task Handle_ShouldGenerateRecoveryCode() { // Arrange var student = CreateStudentWithActivationCode("John Doe", "john@test.com", "activation_hash"); _studentRepository.GetPendingActivationAsync(Arg.Any()) .Returns(new List { student }); _passwordService.VerifyPassword("VALIDCODE123", "activation_hash") .Returns(true); _userRepository.ExistsAsync("newuser", Arg.Any()) .Returns(false); _studentRepository.GetByIdAsync(student.Id, Arg.Any()) .Returns(student); _jwtService.GenerateToken(Arg.Any()) .Returns("new.jwt.token"); var command = new ActivateAccountCommand("VALIDCODE123", "newuser", "password123"); // Act var result = await _handler.Handle(command, CancellationToken.None); // Assert result.RecoveryCode.Should().NotBeNullOrEmpty(); result.RecoveryCode!.Length.Should().Be(12); } [Fact] public async Task Handle_ShouldClearActivationCodeAfterSuccess() { // Arrange var student = CreateStudentWithActivationCode("John Doe", "john@test.com", "activation_hash"); _studentRepository.GetPendingActivationAsync(Arg.Any()) .Returns(new List { student }); _passwordService.VerifyPassword("VALIDCODE123", "activation_hash") .Returns(true); _userRepository.ExistsAsync("newuser", Arg.Any()) .Returns(false); _studentRepository.GetByIdAsync(student.Id, Arg.Any()) .Returns(student); _jwtService.GenerateToken(Arg.Any()) .Returns("new.jwt.token"); var command = new ActivateAccountCommand("VALIDCODE123", "newuser", "password123"); // Act await _handler.Handle(command, CancellationToken.None); // Assert await _unitOfWork.Received(1).SaveChangesAsync(Arg.Any()); } [Fact] public async Task Handle_ShouldReturnJwtTokenForAutoLogin() { // Arrange var student = CreateStudentWithActivationCode("John Doe", "john@test.com", "activation_hash"); _studentRepository.GetPendingActivationAsync(Arg.Any()) .Returns(new List { student }); _passwordService.VerifyPassword("VALIDCODE123", "activation_hash") .Returns(true); _userRepository.ExistsAsync("newuser", Arg.Any()) .Returns(false); _studentRepository.GetByIdAsync(student.Id, Arg.Any()) .Returns(student); _jwtService.GenerateToken(Arg.Any()) .Returns("auto.login.token"); var command = new ActivateAccountCommand("VALIDCODE123", "newuser", "password123"); // Act var result = await _handler.Handle(command, CancellationToken.None); // Assert result.Token.Should().Be("auto.login.token"); _jwtService.Received(1).GenerateToken(Arg.Any()); } private static Student CreateStudentWithActivationCode(string name, string email, string activationHash) { var student = new Student(name, Email.Create(email)); typeof(Student).GetProperty("ActivationCodeHash")?.SetValue(student, activationHash); typeof(Student).GetProperty("ActivationExpiresAt")?.SetValue(student, DateTime.UtcNow.AddDays(2)); return student; } private static Student CreateStudentWithExpiredActivationCode(string name, string email, string activationHash) { var student = new Student(name, Email.Create(email)); typeof(Student).GetProperty("ActivationCodeHash")?.SetValue(student, activationHash); typeof(Student).GetProperty("ActivationExpiresAt")?.SetValue(student, DateTime.UtcNow.AddDays(-1)); return student; } private static void SetEntityId(T entity, int id) where T : class { typeof(T).GetProperty("Id")?.SetValue(entity, id); } }