104 lines
4.4 KiB
C#
104 lines
4.4 KiB
C#
|
|
using NSubstitute;
|
||
|
|
using SIGCM2.Application.Abstractions.Persistence;
|
||
|
|
using SIGCM2.Application.Abstractions.Security;
|
||
|
|
using SIGCM2.Application.Auth.Login;
|
||
|
|
using SIGCM2.Domain.Entities;
|
||
|
|
using SIGCM2.Domain.Exceptions;
|
||
|
|
|
||
|
|
namespace SIGCM2.Application.Tests.Auth.Login;
|
||
|
|
|
||
|
|
public class LoginCommandHandlerTests
|
||
|
|
{
|
||
|
|
private readonly IUsuarioRepository _repository = Substitute.For<IUsuarioRepository>();
|
||
|
|
private readonly IPasswordHasher _hasher = Substitute.For<IPasswordHasher>();
|
||
|
|
private readonly IJwtService _jwtService = Substitute.For<IJwtService>();
|
||
|
|
private readonly LoginCommandHandler _handler;
|
||
|
|
|
||
|
|
public LoginCommandHandlerTests()
|
||
|
|
{
|
||
|
|
_handler = new LoginCommandHandler(_repository, _hasher, _jwtService);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Scenario: valid credentials → returns token response with usuario populated
|
||
|
|
[Fact]
|
||
|
|
public async Task Handle_ValidCredentials_ReturnsTokenResponse()
|
||
|
|
{
|
||
|
|
var usuario = new Usuario(1, "admin", "$2a$12$hash", "Admin", "Sys", null, "admin", "[\"*\"]", true);
|
||
|
|
_repository.GetByUsernameAsync("admin").Returns(usuario);
|
||
|
|
_hasher.Verify("@Diego550@", "$2a$12$hash").Returns(true);
|
||
|
|
_jwtService.GenerateAccessToken(usuario).Returns("jwt.token.here");
|
||
|
|
|
||
|
|
var command = new LoginCommand("admin", "@Diego550@");
|
||
|
|
var result = await _handler.Handle(command);
|
||
|
|
|
||
|
|
Assert.Equal("jwt.token.here", result.AccessToken);
|
||
|
|
Assert.False(string.IsNullOrWhiteSpace(result.RefreshToken));
|
||
|
|
Assert.Equal(3600, result.ExpiresIn);
|
||
|
|
|
||
|
|
// Contract: Usuario must be populated
|
||
|
|
Assert.NotNull(result.Usuario);
|
||
|
|
Assert.Equal(1, result.Usuario.Id);
|
||
|
|
Assert.Equal("Admin Sys", result.Usuario.Nombre);
|
||
|
|
Assert.Equal("admin", result.Usuario.Rol);
|
||
|
|
Assert.NotNull(result.Usuario.Permisos);
|
||
|
|
Assert.Contains("*", result.Usuario.Permisos);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Triangulation: Usuario object maps id/nombre/rol/permisos from authenticated user
|
||
|
|
[Fact]
|
||
|
|
public async Task Handle_ValidCredentials_UsuarioMatchesAuthenticatedUser()
|
||
|
|
{
|
||
|
|
var usuario = new Usuario(42, "cajero1", "$2a$12$hash3", "María", "González", null, "Cajero",
|
||
|
|
"[\"ventas:contado:create\",\"ventas:contado:read\"]", true);
|
||
|
|
_repository.GetByUsernameAsync("cajero1").Returns(usuario);
|
||
|
|
_hasher.Verify("pass123", "$2a$12$hash3").Returns(true);
|
||
|
|
_jwtService.GenerateAccessToken(usuario).Returns("jwt.cajero.token");
|
||
|
|
|
||
|
|
var command = new LoginCommand("cajero1", "pass123");
|
||
|
|
var result = await _handler.Handle(command);
|
||
|
|
|
||
|
|
Assert.Equal(42, result.Usuario.Id);
|
||
|
|
Assert.Equal("María González", result.Usuario.Nombre);
|
||
|
|
Assert.Equal("Cajero", result.Usuario.Rol);
|
||
|
|
Assert.Equal(2, result.Usuario.Permisos.Length);
|
||
|
|
Assert.Contains("ventas:contado:create", result.Usuario.Permisos);
|
||
|
|
Assert.Contains("ventas:contado:read", result.Usuario.Permisos);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Scenario: user does not exist → throws InvalidCredentialsException
|
||
|
|
[Fact]
|
||
|
|
public async Task Handle_UserNotFound_ThrowsInvalidCredentialsException()
|
||
|
|
{
|
||
|
|
_repository.GetByUsernameAsync("noexiste").Returns((Usuario?)null);
|
||
|
|
|
||
|
|
var command = new LoginCommand("noexiste", "anything");
|
||
|
|
|
||
|
|
await Assert.ThrowsAsync<InvalidCredentialsException>(() => _handler.Handle(command));
|
||
|
|
}
|
||
|
|
|
||
|
|
// Scenario: user is inactive → throws InvalidCredentialsException
|
||
|
|
[Fact]
|
||
|
|
public async Task Handle_InactiveUser_ThrowsInvalidCredentialsException()
|
||
|
|
{
|
||
|
|
var inactive = new Usuario(2, "operador", "$2a$12$hash2", "Juan", "Pérez", null, "vendedor", "[]", false);
|
||
|
|
_repository.GetByUsernameAsync("operador").Returns(inactive);
|
||
|
|
|
||
|
|
var command = new LoginCommand("operador", "correctpassword");
|
||
|
|
|
||
|
|
await Assert.ThrowsAsync<InvalidCredentialsException>(() => _handler.Handle(command));
|
||
|
|
}
|
||
|
|
|
||
|
|
// Scenario: wrong password → throws InvalidCredentialsException
|
||
|
|
[Fact]
|
||
|
|
public async Task Handle_WrongPassword_ThrowsInvalidCredentialsException()
|
||
|
|
{
|
||
|
|
var usuario = new Usuario(1, "admin", "$2a$12$hash", "Admin", "Sys", null, "admin", "[\"*\"]", true);
|
||
|
|
_repository.GetByUsernameAsync("admin").Returns(usuario);
|
||
|
|
_hasher.Verify("WrongPass1", "$2a$12$hash").Returns(false);
|
||
|
|
|
||
|
|
var command = new LoginCommand("admin", "WrongPass1");
|
||
|
|
|
||
|
|
await Assert.ThrowsAsync<InvalidCredentialsException>(() => _handler.Handle(command));
|
||
|
|
}
|
||
|
|
}
|