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 LoginCommandTests { private readonly IUserRepository _userRepository; private readonly IPasswordService _passwordService; private readonly IJwtService _jwtService; private readonly LoginCommandHandler _handler; public LoginCommandTests() { _userRepository = Substitute.For(); _passwordService = Substitute.For(); _jwtService = Substitute.For(); _handler = new LoginCommandHandler(_userRepository, _passwordService, _jwtService); } [Fact] public async Task Handle_WithValidCredentials_ShouldReturnSuccessWithToken() { // Arrange var user = CreateUser("testuser", "hashed_password"); _userRepository.GetByUsernameAsync("testuser", Arg.Any()) .Returns(user); _passwordService.VerifyPassword("password123", "hashed_password") .Returns(true); _jwtService.GenerateToken(user) .Returns("valid.jwt.token"); var command = new LoginCommand("testuser", "password123"); // Act var result = await _handler.Handle(command, CancellationToken.None); // Assert result.Should().NotBeNull(); result.Success.Should().BeTrue(); result.Token.Should().Be("valid.jwt.token"); result.User.Should().NotBeNull(); result.User!.Username.Should().Be("testuser"); result.Error.Should().BeNull(); } [Fact] public async Task Handle_WithInvalidUsername_ShouldReturnError() { // Arrange _userRepository.GetByUsernameAsync("nonexistent", Arg.Any()) .Returns((User?)null); var command = new LoginCommand("nonexistent", "password123"); // Act var result = await _handler.Handle(command, CancellationToken.None); // Assert result.Success.Should().BeFalse(); result.Token.Should().BeNull(); result.Error.Should().Contain("incorrectos"); } [Fact] public async Task Handle_WithInvalidPassword_ShouldReturnError() { // Arrange var user = CreateUser("testuser", "hashed_password"); _userRepository.GetByUsernameAsync("testuser", Arg.Any()) .Returns(user); _passwordService.VerifyPassword("wrongpassword", "hashed_password") .Returns(false); var command = new LoginCommand("testuser", "wrongpassword"); // Act var result = await _handler.Handle(command, CancellationToken.None); // Assert result.Success.Should().BeFalse(); result.Token.Should().BeNull(); result.Error.Should().Contain("incorrectos"); } [Fact] public async Task Handle_ShouldNormalizUsernameToLowercase() { // Arrange var user = CreateUser("testuser", "hashed_password"); _userRepository.GetByUsernameAsync("testuser", Arg.Any()) .Returns(user); _passwordService.VerifyPassword("password123", "hashed_password") .Returns(true); _jwtService.GenerateToken(user) .Returns("valid.jwt.token"); var command = new LoginCommand("TESTUSER", "password123"); // Act var result = await _handler.Handle(command, CancellationToken.None); // Assert result.Success.Should().BeTrue(); await _userRepository.Received(1).GetByUsernameAsync("testuser", Arg.Any()); } [Fact] public async Task Handle_ShouldUpdateLastLoginOnSuccess() { // Arrange var user = CreateUser("testuser", "hashed_password"); _userRepository.GetByUsernameAsync("testuser", Arg.Any()) .Returns(user); _passwordService.VerifyPassword("password123", "hashed_password") .Returns(true); _jwtService.GenerateToken(user) .Returns("valid.jwt.token"); var command = new LoginCommand("testuser", "password123"); // Act await _handler.Handle(command, CancellationToken.None); // Assert await _userRepository.Received(1).UpdateAsync(user, Arg.Any()); } [Fact] public async Task Handle_WithStudentUser_ShouldIncludeStudentInfo() { // Arrange var user = CreateUserWithStudent("studentuser", "hashed_password", 1, "John Doe"); _userRepository.GetByUsernameAsync("studentuser", Arg.Any()) .Returns(user); _passwordService.VerifyPassword("password123", "hashed_password") .Returns(true); _jwtService.GenerateToken(user) .Returns("valid.jwt.token"); var command = new LoginCommand("studentuser", "password123"); // Act var result = await _handler.Handle(command, CancellationToken.None); // Assert result.Success.Should().BeTrue(); result.User!.StudentId.Should().Be(1); result.User.StudentName.Should().Be("John Doe"); result.User.Role.Should().Be(UserRoles.Student); } private static User CreateUser(string username, string passwordHash) { return User.Create(username, passwordHash, "recovery_hash", UserRoles.Student); } private static User CreateUserWithStudent(string username, string passwordHash, int studentId, string studentName) { var user = User.Create(username, passwordHash, "recovery_hash", UserRoles.Student, studentId); // Set up the Student navigation property var student = new Student(studentName, Domain.ValueObjects.Email.Create($"{username}@test.com")); typeof(User).GetProperty("Student")?.SetValue(user, student); return user; } }