feat(api): UDT-004 dominio + repositorio + application roles (tdd)
- 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.
This commit is contained in:
@@ -28,7 +28,9 @@ public sealed class SqlTestFixture : IAsyncLifetime
|
||||
|
||||
_respawner = await Respawner.CreateAsync(_connection, new RespawnerOptions
|
||||
{
|
||||
DbAdapter = DbAdapter.SqlServer
|
||||
DbAdapter = DbAdapter.SqlServer,
|
||||
// Rol is a lookup table seeded by migration V003 — never wipe or Usuario FK breaks.
|
||||
TablesToIgnore = [new Respawn.Graph.Table("dbo", "Rol")]
|
||||
});
|
||||
|
||||
await ResetAndSeedAsync();
|
||||
@@ -37,9 +39,33 @@ public sealed class SqlTestFixture : IAsyncLifetime
|
||||
public async Task ResetAndSeedAsync()
|
||||
{
|
||||
await _respawner.ResetAsync(_connection);
|
||||
await SeedRolCanonicalAsync();
|
||||
await SeedAdminAsync();
|
||||
}
|
||||
|
||||
private async Task SeedRolCanonicalAsync()
|
||||
{
|
||||
const string sql = """
|
||||
SET QUOTED_IDENTIFIER ON;
|
||||
MERGE dbo.Rol AS t
|
||||
USING (VALUES
|
||||
('admin', N'Administrador', N'Supervisor total'),
|
||||
('cajero', N'Cajero', N'Mostrador contado'),
|
||||
('operador_ctacte', N'Operador Cta Cte', N'Cuenta corriente'),
|
||||
('picadora', N'Picadora/Correctora', N'Edición de textos'),
|
||||
('jefe_publicidad', N'Jefe de Publicidad', N'Supervisión de pauta'),
|
||||
('productor', N'Productor', N'Carga restringida'),
|
||||
('diagramacion', N'Diagramación/Taller', N'Solo lectura pauta'),
|
||||
('reportes', N'Reportes', N'Solo lectura reportes')
|
||||
) AS s (Codigo, Nombre, Descripcion)
|
||||
ON t.Codigo = s.Codigo
|
||||
WHEN NOT MATCHED BY TARGET THEN
|
||||
INSERT (Codigo, Nombre, Descripcion, Activo)
|
||||
VALUES (s.Codigo, s.Nombre, s.Descripcion, 1);
|
||||
""";
|
||||
await _connection.ExecuteAsync(sql);
|
||||
}
|
||||
|
||||
public async Task DisposeAsync()
|
||||
{
|
||||
if (_connection is not null)
|
||||
|
||||
Reference in New Issue
Block a user