220 lines
6.6 KiB
C#
220 lines
6.6 KiB
C#
|
|
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 RegisterCommandTests
|
||
|
|
{
|
||
|
|
private readonly IUserRepository _userRepository;
|
||
|
|
private readonly IStudentRepository _studentRepository;
|
||
|
|
private readonly IPasswordService _passwordService;
|
||
|
|
private readonly IJwtService _jwtService;
|
||
|
|
private readonly IUnitOfWork _unitOfWork;
|
||
|
|
private readonly RegisterCommandHandler _handler;
|
||
|
|
|
||
|
|
public RegisterCommandTests()
|
||
|
|
{
|
||
|
|
_userRepository = Substitute.For<IUserRepository>();
|
||
|
|
_studentRepository = Substitute.For<IStudentRepository>();
|
||
|
|
_passwordService = Substitute.For<IPasswordService>();
|
||
|
|
_jwtService = Substitute.For<IJwtService>();
|
||
|
|
_unitOfWork = Substitute.For<IUnitOfWork>();
|
||
|
|
|
||
|
|
_passwordService.HashPassword(Arg.Any<string>()).Returns("hashed_value");
|
||
|
|
|
||
|
|
_handler = new RegisterCommandHandler(
|
||
|
|
_userRepository,
|
||
|
|
_studentRepository,
|
||
|
|
_passwordService,
|
||
|
|
_jwtService,
|
||
|
|
_unitOfWork
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact]
|
||
|
|
public async Task Handle_WithValidData_ShouldCreateUser()
|
||
|
|
{
|
||
|
|
// Arrange
|
||
|
|
_userRepository.ExistsAsync("newuser", Arg.Any<CancellationToken>())
|
||
|
|
.Returns(false);
|
||
|
|
_jwtService.GenerateToken(Arg.Any<User>())
|
||
|
|
.Returns("new.jwt.token");
|
||
|
|
|
||
|
|
var command = new RegisterCommand("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_WithExistingUsername_ShouldReturnError()
|
||
|
|
{
|
||
|
|
// Arrange
|
||
|
|
_userRepository.ExistsAsync("existinguser", Arg.Any<CancellationToken>())
|
||
|
|
.Returns(true);
|
||
|
|
|
||
|
|
var command = new RegisterCommand("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
|
||
|
|
_userRepository.ExistsAsync("newuser", Arg.Any<CancellationToken>())
|
||
|
|
.Returns(false);
|
||
|
|
|
||
|
|
var command = new RegisterCommand("newuser", "12345"); // 5 chars, less than 6
|
||
|
|
|
||
|
|
// 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_WithNameAndEmail_ShouldCreateStudent()
|
||
|
|
{
|
||
|
|
// Arrange
|
||
|
|
_userRepository.ExistsAsync("newuser", Arg.Any<CancellationToken>())
|
||
|
|
.Returns(false);
|
||
|
|
_jwtService.GenerateToken(Arg.Any<User>())
|
||
|
|
.Returns("new.jwt.token");
|
||
|
|
|
||
|
|
var command = new RegisterCommand(
|
||
|
|
"newuser",
|
||
|
|
"password123",
|
||
|
|
Name: "John Doe",
|
||
|
|
Email: "john@example.com"
|
||
|
|
);
|
||
|
|
|
||
|
|
// Act
|
||
|
|
var result = await _handler.Handle(command, CancellationToken.None);
|
||
|
|
|
||
|
|
// Assert
|
||
|
|
result.Success.Should().BeTrue();
|
||
|
|
_studentRepository.Received(1).Add(Arg.Is<Student>(s => s.Name == "John Doe"));
|
||
|
|
await _unitOfWork.Received().SaveChangesAsync(Arg.Any<CancellationToken>());
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact]
|
||
|
|
public async Task Handle_WithInvalidEmail_ShouldReturnError()
|
||
|
|
{
|
||
|
|
// Arrange
|
||
|
|
_userRepository.ExistsAsync("newuser", Arg.Any<CancellationToken>())
|
||
|
|
.Returns(false);
|
||
|
|
|
||
|
|
var command = new RegisterCommand(
|
||
|
|
"newuser",
|
||
|
|
"password123",
|
||
|
|
Name: "John Doe",
|
||
|
|
Email: "invalid-email"
|
||
|
|
);
|
||
|
|
|
||
|
|
// Act
|
||
|
|
var result = await _handler.Handle(command, CancellationToken.None);
|
||
|
|
|
||
|
|
// Assert
|
||
|
|
result.Success.Should().BeFalse();
|
||
|
|
result.Error.Should().NotBeNullOrEmpty();
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact]
|
||
|
|
public async Task Handle_ShouldGenerateRecoveryCode()
|
||
|
|
{
|
||
|
|
// Arrange
|
||
|
|
_userRepository.ExistsAsync("newuser", Arg.Any<CancellationToken>())
|
||
|
|
.Returns(false);
|
||
|
|
_jwtService.GenerateToken(Arg.Any<User>())
|
||
|
|
.Returns("new.jwt.token");
|
||
|
|
|
||
|
|
var command = new RegisterCommand("newuser", "password123");
|
||
|
|
|
||
|
|
// Act
|
||
|
|
var result = await _handler.Handle(command, CancellationToken.None);
|
||
|
|
|
||
|
|
// Assert
|
||
|
|
result.RecoveryCode.Should().NotBeNullOrEmpty();
|
||
|
|
result.RecoveryCode!.Length.Should().Be(12);
|
||
|
|
result.RecoveryCode.Should().MatchRegex("^[A-Z0-9]+$");
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact]
|
||
|
|
public async Task Handle_ShouldHashPasswordBeforeSaving()
|
||
|
|
{
|
||
|
|
// Arrange
|
||
|
|
_userRepository.ExistsAsync("newuser", Arg.Any<CancellationToken>())
|
||
|
|
.Returns(false);
|
||
|
|
_jwtService.GenerateToken(Arg.Any<User>())
|
||
|
|
.Returns("new.jwt.token");
|
||
|
|
|
||
|
|
var command = new RegisterCommand("newuser", "password123");
|
||
|
|
|
||
|
|
// Act
|
||
|
|
await _handler.Handle(command, CancellationToken.None);
|
||
|
|
|
||
|
|
// Assert
|
||
|
|
_passwordService.Received(1).HashPassword("password123");
|
||
|
|
await _userRepository.Received(1).AddAsync(
|
||
|
|
Arg.Is<User>(u => u.PasswordHash == "hashed_value"),
|
||
|
|
Arg.Any<CancellationToken>()
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact]
|
||
|
|
public async Task Handle_ShouldNormalizeUsernameToLowercase()
|
||
|
|
{
|
||
|
|
// Arrange
|
||
|
|
_userRepository.ExistsAsync("newuser", Arg.Any<CancellationToken>())
|
||
|
|
.Returns(false);
|
||
|
|
_jwtService.GenerateToken(Arg.Any<User>())
|
||
|
|
.Returns("new.jwt.token");
|
||
|
|
|
||
|
|
var command = new RegisterCommand("NEWUSER", "password123");
|
||
|
|
|
||
|
|
// Act
|
||
|
|
var result = await _handler.Handle(command, CancellationToken.None);
|
||
|
|
|
||
|
|
// Assert
|
||
|
|
result.Success.Should().BeTrue();
|
||
|
|
result.User!.Username.Should().Be("newuser");
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact]
|
||
|
|
public async Task Handle_NewUserShouldHaveStudentRole()
|
||
|
|
{
|
||
|
|
// Arrange
|
||
|
|
_userRepository.ExistsAsync("newuser", Arg.Any<CancellationToken>())
|
||
|
|
.Returns(false);
|
||
|
|
_jwtService.GenerateToken(Arg.Any<User>())
|
||
|
|
.Returns("new.jwt.token");
|
||
|
|
|
||
|
|
var command = new RegisterCommand("newuser", "password123");
|
||
|
|
|
||
|
|
// Act
|
||
|
|
var result = await _handler.Handle(command, CancellationToken.None);
|
||
|
|
|
||
|
|
// Assert
|
||
|
|
result.User!.Role.Should().Be(UserRoles.Student);
|
||
|
|
}
|
||
|
|
}
|