feat(api): UDT-003 registro de usuarios — backend completo (Phases 1-6)

- Domain: Usuario.ForCreation factory, UsernameAlreadyExistsException, IUsuarioRepository extendido
- Application: CreateUsuarioCommand/Validator/Handler, UsuarioCreatedDto, AuthOptions password policy
- Infrastructure: UsuarioRepository.ExistsByUsernameAsync + AddAsync (INSERT OUTPUT INSERTED.Id), RoleClaimType="rol" en TokenValidationParameters
- Api: UsuariosController POST api/v1/users [Authorize(Roles="admin")], ExceptionFilter mapea UsernameAlreadyExistsException + SqlException 2627 → 409
- Tests (unit): 43 tests — 33 validator + 10 handler (107 total, green)
- Tests (integration): 7 tests CreateUsuarioEndpoint — 401/403/400/201/409/race/e2e (green)
- Fix: TestWebAppFactory.ConfigureTestServices reemplaza SqlConnectionFactory singleton con CS de test correcto
This commit is contained in:
2026-04-15 10:47:48 -03:00
parent 023d30fce4
commit 3d598faffc
19 changed files with 1079 additions and 1 deletions

View File

@@ -33,4 +33,28 @@ public sealed class Usuario
PermisosJson = permisosJson;
Activo = activo;
}
/// <summary>
/// Factory for creating a new user (no Id — DB assigns via IDENTITY).
/// Defaults: Activo=true, PermisosJson="[]".
/// </summary>
public static Usuario ForCreation(
string username,
string passwordHash,
string nombre,
string apellido,
string? email,
string rol)
{
return new Usuario(
id: 0,
username: username,
passwordHash: passwordHash,
nombre: nombre,
apellido: apellido,
email: email,
rol: rol,
permisosJson: "[]",
activo: true);
}
}

View File

@@ -0,0 +1,12 @@
namespace SIGCM2.Domain.Exceptions;
public sealed class UsernameAlreadyExistsException : Exception
{
public string Username { get; }
public UsernameAlreadyExistsException(string username)
: base($"El nombre de usuario '{username}' ya está en uso.")
{
Username = username;
}
}