- Migraciones V003 (tabla Rol + 8 seeds canonicos) y V004 (drop CK + FK Usuario.Rol) - Dominio: Rol entity + 3 excepciones (RolNotFound/AlreadyExists/InUse) - Infraestructura: RolRepository (Dapper) con List/Get/ExistsActive/Add/Update/HasActiveUsuarios - Application: CRUD queries y commands (List, Get, Create, Update, Deactivate) + validators (codigo regex ^[a-z][a-z0-9_]*$) - Validator UDT-003: whitelist alineada a codigos canonicos (full IRolRepository lookup diferido a Phase 5.1) - Tests: 169 application + 15 api (todos verdes). Respawn configurado para re-seedear Rol canonical post-reset. - Estricto TDD: RED/GREEN/TRIANGULATE en todos los handlers nuevos.
199 lines
7.3 KiB
C#
199 lines
7.3 KiB
C#
using FluentValidation.TestHelper;
|
|
using SIGCM2.Application.Auth;
|
|
using SIGCM2.Application.Usuarios.Create;
|
|
|
|
namespace SIGCM2.Application.Tests.Usuarios.Create;
|
|
|
|
public class CreateUsuarioCommandValidatorTests
|
|
{
|
|
private static CreateUsuarioCommandValidator BuildValidator(AuthOptions? opts = null) =>
|
|
new(opts ?? new AuthOptions());
|
|
|
|
private static CreateUsuarioCommand ValidCommand() => new(
|
|
Username: "operador1",
|
|
Password: "Secreto123",
|
|
Nombre: "Juan",
|
|
Apellido: "Pérez",
|
|
Email: null,
|
|
Rol: "cajero");
|
|
|
|
// ── Happy paths ──────────────────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public void Validate_ValidCommand_NoErrors()
|
|
{
|
|
var result = BuildValidator().TestValidate(ValidCommand());
|
|
result.ShouldNotHaveAnyValidationErrors();
|
|
}
|
|
|
|
[Fact]
|
|
public void Validate_NullEmail_IsValid()
|
|
{
|
|
var cmd = ValidCommand() with { Email = null };
|
|
BuildValidator().TestValidate(cmd).ShouldNotHaveAnyValidationErrors();
|
|
}
|
|
|
|
[Fact]
|
|
public void Validate_ValidEmailPresent_NoErrors()
|
|
{
|
|
var cmd = ValidCommand() with { Email = "juan@example.com" };
|
|
BuildValidator().TestValidate(cmd).ShouldNotHaveAnyValidationErrors();
|
|
}
|
|
|
|
// ── Username ─────────────────────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public void Validate_EmptyUsername_HasError()
|
|
{
|
|
var cmd = ValidCommand() with { Username = "" };
|
|
BuildValidator().TestValidate(cmd).ShouldHaveValidationErrorFor(c => c.Username);
|
|
}
|
|
|
|
[Fact]
|
|
public void Validate_UsernameTooShort_HasError()
|
|
{
|
|
var cmd = ValidCommand() with { Username = "ab" };
|
|
BuildValidator().TestValidate(cmd).ShouldHaveValidationErrorFor(c => c.Username);
|
|
}
|
|
|
|
[Fact]
|
|
public void Validate_UsernameTooLong_HasError()
|
|
{
|
|
var cmd = ValidCommand() with { Username = new string('a', 51) };
|
|
BuildValidator().TestValidate(cmd).ShouldHaveValidationErrorFor(c => c.Username);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("abc")] // 3 chars — boundary valid
|
|
[InlineData("user.name")] // dot allowed
|
|
[InlineData("user-name")] // dash allowed
|
|
[InlineData("user_name")] // underscore allowed
|
|
[InlineData("user123")] // alphanumeric
|
|
public void Validate_UsernameValidFormats_NoError(string username)
|
|
{
|
|
var cmd = ValidCommand() with { Username = username };
|
|
BuildValidator().TestValidate(cmd).ShouldNotHaveValidationErrorFor(c => c.Username);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("user name")] // space not allowed
|
|
[InlineData("user@name")] // @ not allowed
|
|
[InlineData("user#1")] // # not allowed
|
|
public void Validate_UsernameInvalidChars_HasError(string username)
|
|
{
|
|
var cmd = ValidCommand() with { Username = username };
|
|
BuildValidator().TestValidate(cmd).ShouldHaveValidationErrorFor(c => c.Username);
|
|
}
|
|
|
|
// ── Password ─────────────────────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public void Validate_EmptyPassword_HasError()
|
|
{
|
|
var cmd = ValidCommand() with { Password = "" };
|
|
BuildValidator().TestValidate(cmd).ShouldHaveValidationErrorFor(c => c.Password);
|
|
}
|
|
|
|
[Fact]
|
|
public void Validate_PasswordTooShort_HasError()
|
|
{
|
|
var cmd = ValidCommand() with { Password = "Ab1cd5" }; // 6 chars < 8
|
|
BuildValidator().TestValidate(cmd).ShouldHaveValidationErrorFor(c => c.Password);
|
|
}
|
|
|
|
[Fact]
|
|
public void Validate_PasswordNoLetter_HasError()
|
|
{
|
|
var cmd = ValidCommand() with { Password = "12345678" }; // digits only
|
|
BuildValidator().TestValidate(cmd).ShouldHaveValidationErrorFor(c => c.Password);
|
|
}
|
|
|
|
[Fact]
|
|
public void Validate_PasswordNoDigit_HasError()
|
|
{
|
|
var cmd = ValidCommand() with { Password = "abcdefgh" }; // letters only
|
|
BuildValidator().TestValidate(cmd).ShouldHaveValidationErrorFor(c => c.Password);
|
|
}
|
|
|
|
[Fact]
|
|
public void Validate_PasswordExactMinLength_NoError()
|
|
{
|
|
var cmd = ValidCommand() with { Password = "Secre123" }; // exactly 8, letter + digit
|
|
BuildValidator().TestValidate(cmd).ShouldNotHaveValidationErrorFor(c => c.Password);
|
|
}
|
|
|
|
// ── Nombre / Apellido ────────────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public void Validate_EmptyNombre_HasError()
|
|
{
|
|
var cmd = ValidCommand() with { Nombre = "" };
|
|
BuildValidator().TestValidate(cmd).ShouldHaveValidationErrorFor(c => c.Nombre);
|
|
}
|
|
|
|
[Fact]
|
|
public void Validate_EmptyApellido_HasError()
|
|
{
|
|
var cmd = ValidCommand() with { Apellido = "" };
|
|
BuildValidator().TestValidate(cmd).ShouldHaveValidationErrorFor(c => c.Apellido);
|
|
}
|
|
|
|
[Fact]
|
|
public void Validate_NombreTooLong_HasError()
|
|
{
|
|
var cmd = ValidCommand() with { Nombre = new string('a', 101) };
|
|
BuildValidator().TestValidate(cmd).ShouldHaveValidationErrorFor(c => c.Nombre);
|
|
}
|
|
|
|
[Fact]
|
|
public void Validate_ApellidoTooLong_HasError()
|
|
{
|
|
var cmd = ValidCommand() with { Apellido = new string('a', 101) };
|
|
BuildValidator().TestValidate(cmd).ShouldHaveValidationErrorFor(c => c.Apellido);
|
|
}
|
|
|
|
// ── Rol ──────────────────────────────────────────────────────────────────
|
|
|
|
[Theory]
|
|
[InlineData("admin")]
|
|
[InlineData("cajero")]
|
|
[InlineData("operador_ctacte")]
|
|
[InlineData("picadora")]
|
|
[InlineData("jefe_publicidad")]
|
|
[InlineData("productor")]
|
|
[InlineData("diagramacion")]
|
|
[InlineData("reportes")]
|
|
public void Validate_ValidRoles_NoError(string rol)
|
|
{
|
|
var cmd = ValidCommand() with { Rol = rol };
|
|
BuildValidator().TestValidate(cmd).ShouldNotHaveValidationErrorFor(c => c.Rol);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("superuser")]
|
|
[InlineData("ADMIN")] // case-sensitive
|
|
[InlineData("root")]
|
|
[InlineData("")]
|
|
public void Validate_InvalidRol_HasError(string rol)
|
|
{
|
|
var cmd = ValidCommand() with { Rol = rol };
|
|
BuildValidator().TestValidate(cmd).ShouldHaveValidationErrorFor(c => c.Rol);
|
|
}
|
|
|
|
// ── Email ────────────────────────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public void Validate_InvalidEmail_HasError()
|
|
{
|
|
var cmd = ValidCommand() with { Email = "not-an-email" };
|
|
BuildValidator().TestValidate(cmd).ShouldHaveValidationErrorFor(c => c.Email);
|
|
}
|
|
|
|
[Fact]
|
|
public void Validate_EmailTooLong_HasError()
|
|
{
|
|
var cmd = ValidCommand() with { Email = new string('a', 145) + "@b.com" }; // >150
|
|
BuildValidator().TestValidate(cmd).ShouldHaveValidationErrorFor(c => c.Email);
|
|
}
|
|
}
|