using Dapper; using Microsoft.Data.SqlClient; using SIGCM2.Infrastructure.Persistence; namespace SIGCM2.Application.Tests.Integration; /// /// Integration tests for PermisoRepository against SIGCM2_Test. /// RED: written before the repository implementation exists. /// [Collection("Database")] public class PermisoRepositoryTests : IAsyncLifetime { private const string ConnectionString = TestConnectionStrings.AppTestDb; private SqlConnection _connection = null!; private PermisoRepository _repository = null!; public async Task InitializeAsync() { _connection = new SqlConnection(ConnectionString); await _connection.OpenAsync(); // Ensure the 18 canonical permisos are present — idempotent MERGE. // Needed because other test classes (RefreshTokenRepositoryTests, UsuarioRepositoryTests) // may call Respawn.ResetAsync before us, which would clear Permiso even if listed in // TablesToIgnore of the central SqlTestFixture (each class configures its own Respawner). await SeedPermisosCanonicalAsync(); var factory = new SqlConnectionFactory(ConnectionString); _repository = new PermisoRepository(factory); } private async Task SeedPermisosCanonicalAsync() { const string sql = """ SET QUOTED_IDENTIFIER ON; MERGE dbo.Permiso AS t USING (VALUES ('ventas:contado:crear', N'Cargar orden contado', NULL, 'ventas'), ('ventas:contado:modificar', N'Modificar orden contado', NULL, 'ventas'), ('ventas:contado:cobrar', N'Cobrar orden contado', NULL, 'ventas'), ('ventas:contado:facturar', N'Facturar orden contado', NULL, 'ventas'), ('ventas:ctacte:crear', N'Cargar orden cuenta corriente', NULL, 'ventas'), ('ventas:ctacte:facturar', N'Facturar lote cuenta corriente', NULL, 'ventas'), ('textos:editar', N'Editar textos', NULL, 'textos'), ('textos:reclamos:ver', N'Ver reclamos de textos', NULL, 'textos'), ('pauta:azanu:ver', N'Ver AZANU en pauta', NULL, 'pauta'), ('pauta:limpiar', N'Limpieza de pauta', NULL, 'pauta'), ('pauta:recursos:fueradehora', N'Recursos fuera de hora', NULL, 'pauta'), ('productores:deuda:ver', N'Ver deuda propia de productores', NULL, 'productores'), ('productores:pendientes:crear', N'Cargar pendientes de productores', NULL, 'productores'), ('productores:deuda:bypass', N'Bypass de deuda de productores', NULL, 'productores'), ('administracion:usuarios:gestionar', N'Gestionar usuarios del sistema', N'Crear, editar y desactivar usuarios', 'administracion'), ('administracion:tarifarios:gestionar', N'Gestionar tarifarios', N'Crear y modificar tarifarios de publicidad', 'administracion'), ('administracion:medios:gestionar', N'Gestionar medios publicitarios', N'Alta y configuracion de medios', 'administracion'), ('administracion:auditoria:ver', N'Ver logs de auditoria', N'Acceso al dashboard de auditoria', 'administracion') ) AS s (Codigo, Nombre, Descripcion, Modulo) ON t.Codigo = s.Codigo WHEN NOT MATCHED BY TARGET THEN INSERT (Codigo, Nombre, Descripcion, Modulo) VALUES (s.Codigo, s.Nombre, s.Descripcion, s.Modulo); """; await _connection.ExecuteAsync(sql); } public async Task DisposeAsync() { await _connection.CloseAsync(); await _connection.DisposeAsync(); } // ── ListAsync ──────────────────────────────────────────────────────────── [Fact] public async Task ListAsync_Returns26CanonicalSeeds() { var list = await _repository.ListAsync(); // V005 seeds 18 canonical permisos + V007 (UDT-006) adds 3 admin permisos // + V011 (ADM-001) adds 'administracion:secciones:gestionar' // + V013 (ADM-008) adds 'administracion:puntos_de_venta:gestionar' // + V014 (ADM-009) adds 'administracion:fiscal:gestionar' // + V016 (CAT-001) adds 'catalogo:rubros:gestionar' // + V017 (PRD-001) adds 'catalogo:tipos:gestionar' // + V018 (PRD-002) adds 'catalogo:productos:gestionar' = 27 total Assert.Equal(27, list.Count); } [Fact] public async Task ListAsync_ContainsExpectedCodigos() { var list = await _repository.ListAsync(); var codigos = list.Select(p => p.Codigo).ToHashSet(); Assert.Contains("ventas:contado:crear", codigos); Assert.Contains("ventas:contado:facturar", codigos); Assert.Contains("administracion:usuarios:gestionar", codigos); Assert.Contains("administracion:auditoria:ver", codigos); } [Fact] public async Task ListAsync_AllItemsHaveNonEmptyCodigoAndNombre() { var list = await _repository.ListAsync(); foreach (var p in list) { Assert.False(string.IsNullOrWhiteSpace(p.Codigo)); Assert.False(string.IsNullOrWhiteSpace(p.Nombre)); } } // ── GetByCodigoAsync ───────────────────────────────────────────────────── [Fact] public async Task GetByCodigoAsync_ExistingCodigo_ReturnsPermiso() { var permiso = await _repository.GetByCodigoAsync("ventas:contado:crear"); Assert.NotNull(permiso); Assert.Equal("ventas:contado:crear", permiso!.Codigo); Assert.False(string.IsNullOrWhiteSpace(permiso.Nombre)); } [Fact] public async Task GetByCodigoAsync_AnotherExistingCodigo_ReturnsCorrectPermiso() { var permiso = await _repository.GetByCodigoAsync("administracion:usuarios:gestionar"); Assert.NotNull(permiso); Assert.Equal("administracion:usuarios:gestionar", permiso!.Codigo); } [Fact] public async Task GetByCodigoAsync_NonExistentCodigo_ReturnsNull() { var permiso = await _repository.GetByCodigoAsync("permiso:inexistente:xyz"); Assert.Null(permiso); } // ── GetByCodigosAsync ──────────────────────────────────────────────────── [Fact] public async Task GetByCodigosAsync_ThreeValidCodigos_ReturnsThreeEntities() { var codigos = new[] { "ventas:contado:crear", "ventas:contado:facturar", "textos:editar" }; var result = await _repository.GetByCodigosAsync(codigos); Assert.Equal(3, result.Count); var returnedCodigos = result.Select(p => p.Codigo).ToHashSet(); foreach (var c in codigos) Assert.Contains(c, returnedCodigos); } [Fact] public async Task GetByCodigosAsync_MixedExistingAndNonExisting_ReturnsOnlyExisting() { var codigos = new[] { "ventas:contado:crear", "permiso:no:existe", "textos:editar" }; var result = await _repository.GetByCodigosAsync(codigos); // Only 2 of 3 exist Assert.Equal(2, result.Count); var returnedCodigos = result.Select(p => p.Codigo).ToHashSet(); Assert.Contains("ventas:contado:crear", returnedCodigos); Assert.Contains("textos:editar", returnedCodigos); } [Fact] public async Task GetByCodigosAsync_EmptyList_ReturnsEmpty() { var result = await _repository.GetByCodigosAsync(Array.Empty()); Assert.Empty(result); } [Fact] public async Task GetByCodigosAsync_AllNonExisting_ReturnsEmpty() { var codigos = new[] { "no:existe:uno", "no:existe:dos" }; var result = await _repository.GetByCodigosAsync(codigos); Assert.Empty(result); } }