diff --git a/tests/SIGCM2.Api.Tests/Admin/V014MigrationTests.cs b/tests/SIGCM2.Api.Tests/Admin/V014MigrationTests.cs index 95b7ab1..963983e 100644 --- a/tests/SIGCM2.Api.Tests/Admin/V014MigrationTests.cs +++ b/tests/SIGCM2.Api.Tests/Admin/V014MigrationTests.cs @@ -93,6 +93,8 @@ public sealed class V014MigrationTests : IClassFixture( - "SELECT COUNT(1) FROM dbo.TipoDeIva"); + var count = await conn.ExecuteScalarAsync(""" + SELECT COUNT(1) FROM dbo.TipoDeIva + WHERE Codigo IN ('EXENTO', 'NO_GRAVADO', 'IVA_105', 'IVA_21') + AND PredecesorId IS NULL + AND VigenciaDesde = CAST('2020-01-01' AS DATE) + """); count.Should().Be(4, "El seed de V014 debe generar exactamente 4 TipoDeIva canónicos"); } @@ -112,8 +118,13 @@ public sealed class V014MigrationTests : IClassFixture( - "SELECT Codigo FROM dbo.TipoDeIva ORDER BY Codigo")).ToList(); + var codigos = (await conn.QueryAsync(""" + SELECT Codigo FROM dbo.TipoDeIva + WHERE Codigo IN ('EXENTO', 'NO_GRAVADO', 'IVA_105', 'IVA_21') + AND PredecesorId IS NULL + AND VigenciaDesde = CAST('2020-01-01' AS DATE) + ORDER BY Codigo + """)).ToList(); codigos.Should().BeEquivalentTo( new[] { "EXENTO", "IVA_105", "IVA_21", "NO_GRAVADO" }, @@ -126,8 +137,13 @@ public sealed class V014MigrationTests : IClassFixture( - "SELECT Codigo, Porcentaje FROM dbo.TipoDeIva ORDER BY Codigo")).ToList(); + var rows = (await conn.QueryAsync<(string Codigo, decimal Porcentaje)>(""" + SELECT Codigo, Porcentaje FROM dbo.TipoDeIva + WHERE Codigo IN ('EXENTO', 'NO_GRAVADO', 'IVA_105', 'IVA_21') + AND PredecesorId IS NULL + AND VigenciaDesde = CAST('2020-01-01' AS DATE) + ORDER BY Codigo + """)).ToList(); rows.Should().ContainSingle(r => r.Codigo == "EXENTO" && r.Porcentaje == 0m); rows.Should().ContainSingle(r => r.Codigo == "NO_GRAVADO" && r.Porcentaje == 0m); @@ -143,16 +159,20 @@ public sealed class V014MigrationTests : IClassFixture(""" SELECT COUNT(1) FROM dbo.TipoDeIva - WHERE Activo = 0 - OR PredecesorId IS NOT NULL - OR VigenciaHasta IS NOT NULL + WHERE Codigo IN ('EXENTO', 'NO_GRAVADO', 'IVA_105', 'IVA_21') + AND VigenciaDesde = CAST('2020-01-01' AS DATE) + AND PredecesorId IS NULL + AND (Activo = 0 OR VigenciaHasta IS NOT NULL) """); invalidRows.Should().Be(0, - "Todas las filas seed deben tener Activo=1, PredecesorId=NULL, VigenciaHasta=NULL"); + "Las 4 filas seed canónicas deben tener Activo=1, PredecesorId=NULL, VigenciaHasta=NULL"); } // ── REQ-SEED-002 ────────────────────────────────────────────────────────── + // NOTE: Filters use Alicuota=0 + PredecesorId IS NULL + VigenciaDesde='2020-01-01' + // to isolate ONLY the 24 canonical seed rows, ignoring rows inserted by repo integration tests. + // Seed provinces are stored as PascalCase matching enum ProvinciaArgentina.ToString() (T700 cleanup). [Fact] public async Task IngresosBrutos_Seed_HasExactly24Rows() @@ -160,14 +180,18 @@ public sealed class V014MigrationTests : IClassFixture( - "SELECT COUNT(1) FROM dbo.IngresosBrutos"); - // Design canónico: 23 provincias INDEC + CABA = 24 jurisdicciones. - // La lista del design incluye CABA como elemento propio junto a BUENOS_AIRES (provincia). + // La lista del design incluye CABA (CiudadAutonomaDeBuenosAires) como elemento propio. // REQ-SEED-002 especifica "25" pero la lista canónica del design tiene 24 entradas únicas. // DISCOVERY: posible discrepancia spec vs. design — anotado en apply-progress. // Implementamos lo que la lista del design establece explícitamente: 24 filas. + var count = await conn.ExecuteScalarAsync(""" + SELECT COUNT(1) FROM dbo.IngresosBrutos + WHERE Alicuota = 0 + AND PredecesorId IS NULL + AND VigenciaDesde = CAST('2020-01-01' AS DATE) + """); + count.Should().Be(24, "El seed de V014 debe generar 24 IngresosBrutos (23 provincias INDEC + CABA)"); } @@ -177,23 +201,31 @@ public sealed class V014MigrationTests : IClassFixture( - "SELECT Provincia FROM dbo.IngresosBrutos ORDER BY Provincia")).ToList(); + var provincias = (await conn.QueryAsync(""" + SELECT Provincia FROM dbo.IngresosBrutos + WHERE Alicuota = 0 + AND PredecesorId IS NULL + AND VigenciaDesde = CAST('2020-01-01' AS DATE) + ORDER BY Provincia + """)).ToList(); // Lista canónica del design ADM-009: 23 provincias argentinas INDEC + CABA = 24 + // Stored as PascalCase matching ProvinciaArgentina enum values (T700 cleanup). var expectedCanonical = new[] { - "BUENOS_AIRES", "CABA", "CATAMARCA", "CHACO", "CHUBUT", - "CORDOBA", "CORRIENTES", "ENTRE_RIOS", "FORMOSA", "JUJUY", - "LA_PAMPA", "LA_RIOJA", "MENDOZA", "MISIONES", "NEUQUEN", - "RIO_NEGRO", "SALTA", "SAN_JUAN", "SAN_LUIS", "SANTA_CRUZ", - "SANTA_FE", "SANTIAGO_DEL_ESTERO", "TIERRA_DEL_FUEGO", "TUCUMAN" + "BuenosAires", "CiudadAutonomaDeBuenosAires", "Catamarca", "Chaco", "Chubut", + "Cordoba", "Corrientes", "EntreRios", "Formosa", "Jujuy", + "LaPampa", "LaRioja", "Mendoza", "Misiones", "Neuquen", + "RioNegro", "Salta", "SanJuan", "SanLuis", "SantaCruz", + "SantaFe", "SantiagoDelEstero", "TierraDelFuego", "Tucuman" }; - provincias.Should().Contain("CABA", "CABA debe estar entre las provincias"); - provincias.Should().Contain("BUENOS_AIRES", "Buenos Aires (provincia) debe estar como BUENOS_AIRES"); + provincias.Should().Contain("CiudadAutonomaDeBuenosAires", + "CABA debe estar almacenada como CiudadAutonomaDeBuenosAires (PascalCase enum)"); + provincias.Should().Contain("BuenosAires", + "Buenos Aires (provincia) debe estar como BuenosAires (PascalCase enum)"); foreach (var prov in expectedCanonical) - provincias.Should().Contain(prov, $"Provincia {prov} debe estar en el seed"); + provincias.Should().Contain(prov, $"Provincia {prov} debe estar en el seed (PascalCase)"); } [Fact] @@ -202,10 +234,15 @@ public sealed class V014MigrationTests : IClassFixture( - "SELECT COUNT(1) FROM dbo.IngresosBrutos WHERE Alicuota <> 0"); + // Verify all 24 seed rows (VigenciaDesde='2020-01-01', PredecesorId IS NULL) have Alicuota=0. + var nonZero = await conn.ExecuteScalarAsync(""" + SELECT COUNT(1) FROM dbo.IngresosBrutos + WHERE PredecesorId IS NULL + AND VigenciaDesde = CAST('2020-01-01' AS DATE) + AND Alicuota <> 0 + """); - nonZero.Should().Be(0, "Todas las filas seed de IngresosBrutos deben tener Alicuota=0 (placeholder)"); + nonZero.Should().Be(0, "Las 24 filas seed de IngresosBrutos deben tener Alicuota=0 (placeholder)"); } [Fact] @@ -216,13 +253,14 @@ public sealed class V014MigrationTests : IClassFixture(""" SELECT COUNT(1) FROM dbo.IngresosBrutos - WHERE Activo = 0 - OR PredecesorId IS NOT NULL - OR VigenciaHasta IS NOT NULL + WHERE VigenciaDesde = CAST('2020-01-01' AS DATE) + AND PredecesorId IS NULL + AND Alicuota = 0 + AND (Activo = 0 OR VigenciaHasta IS NOT NULL) """); invalidRows.Should().Be(0, - "Todas las filas seed deben tener Activo=1, PredecesorId=NULL, VigenciaHasta=NULL"); + "Las 24 filas seed deben tener Activo=1, PredecesorId=NULL, VigenciaHasta=NULL"); } // ── REQ-FISCAL-AUTH-002 ─────────────────────────────────────────────────── @@ -283,8 +321,13 @@ public sealed class V014MigrationTests : IClassFixture( - "SELECT COUNT(1) FROM dbo.TipoDeIva"); + // Count only the 4 canonical seed rows — not test-inserted rows. + var count = await conn.ExecuteScalarAsync(""" + SELECT COUNT(1) FROM dbo.TipoDeIva + WHERE Codigo IN ('EXENTO', 'NO_GRAVADO', 'IVA_105', 'IVA_21') + AND PredecesorId IS NULL + AND VigenciaDesde = CAST('2020-01-01' AS DATE) + """); count.Should().Be(4, "Re-ejecutar el seed MERGE no debe duplicar filas en TipoDeIva"); } @@ -295,16 +338,15 @@ public sealed class V014MigrationTests : IClassFixture( - "SELECT COUNT(1) FROM dbo.IngresosBrutos"); + // Count only the 24 canonical seed rows — not test-inserted rows. + var count = await conn.ExecuteScalarAsync(""" + SELECT COUNT(1) FROM dbo.IngresosBrutos + WHERE Alicuota = 0 + AND PredecesorId IS NULL + AND VigenciaDesde = CAST('2020-01-01' AS DATE) + """); count.Should().Be(24, "Re-ejecutar el seed MERGE no debe duplicar filas en IngresosBrutos"); } diff --git a/tests/SIGCM2.TestSupport/SqlTestFixture.cs b/tests/SIGCM2.TestSupport/SqlTestFixture.cs index 99fde0b..411a78b 100644 --- a/tests/SIGCM2.TestSupport/SqlTestFixture.cs +++ b/tests/SIGCM2.TestSupport/SqlTestFixture.cs @@ -722,34 +722,35 @@ public sealed class SqlTestFixture : IAsyncLifetime // ── 4. Seed IngresosBrutos ──────────────────────────────────────────── // 24 filas: 23 provincias INDEC + CABA. Alicuota=0 placeholder. + // T700 cleanup: values in PascalCase matching ProvinciaArgentina enum.ToString(). const string seedIIBB = """ SET QUOTED_IDENTIFIER ON; MERGE dbo.IngresosBrutos AS t USING (VALUES - ('BUENOS_AIRES', N'Ingresos Brutos - Buenos Aires'), - ('CABA', N'Ingresos Brutos - Ciudad Autonoma de Buenos Aires'), - ('CATAMARCA', N'Ingresos Brutos - Catamarca'), - ('CHACO', N'Ingresos Brutos - Chaco'), - ('CHUBUT', N'Ingresos Brutos - Chubut'), - ('CORDOBA', N'Ingresos Brutos - Cordoba'), - ('CORRIENTES', N'Ingresos Brutos - Corrientes'), - ('ENTRE_RIOS', N'Ingresos Brutos - Entre Rios'), - ('FORMOSA', N'Ingresos Brutos - Formosa'), - ('JUJUY', N'Ingresos Brutos - Jujuy'), - ('LA_PAMPA', N'Ingresos Brutos - La Pampa'), - ('LA_RIOJA', N'Ingresos Brutos - La Rioja'), - ('MENDOZA', N'Ingresos Brutos - Mendoza'), - ('MISIONES', N'Ingresos Brutos - Misiones'), - ('NEUQUEN', N'Ingresos Brutos - Neuquen'), - ('RIO_NEGRO', N'Ingresos Brutos - Rio Negro'), - ('SALTA', N'Ingresos Brutos - Salta'), - ('SAN_JUAN', N'Ingresos Brutos - San Juan'), - ('SAN_LUIS', N'Ingresos Brutos - San Luis'), - ('SANTA_CRUZ', N'Ingresos Brutos - Santa Cruz'), - ('SANTA_FE', N'Ingresos Brutos - Santa Fe'), - ('SANTIAGO_DEL_ESTERO', N'Ingresos Brutos - Santiago del Estero'), - ('TIERRA_DEL_FUEGO', N'Ingresos Brutos - Tierra del Fuego'), - ('TUCUMAN', N'Ingresos Brutos - Tucuman') + ('BuenosAires', N'Ingresos Brutos - Buenos Aires'), + ('CiudadAutonomaDeBuenosAires', N'Ingresos Brutos - Ciudad Autonoma de Buenos Aires'), + ('Catamarca', N'Ingresos Brutos - Catamarca'), + ('Chaco', N'Ingresos Brutos - Chaco'), + ('Chubut', N'Ingresos Brutos - Chubut'), + ('Cordoba', N'Ingresos Brutos - Cordoba'), + ('Corrientes', N'Ingresos Brutos - Corrientes'), + ('EntreRios', N'Ingresos Brutos - Entre Rios'), + ('Formosa', N'Ingresos Brutos - Formosa'), + ('Jujuy', N'Ingresos Brutos - Jujuy'), + ('LaPampa', N'Ingresos Brutos - La Pampa'), + ('LaRioja', N'Ingresos Brutos - La Rioja'), + ('Mendoza', N'Ingresos Brutos - Mendoza'), + ('Misiones', N'Ingresos Brutos - Misiones'), + ('Neuquen', N'Ingresos Brutos - Neuquen'), + ('RioNegro', N'Ingresos Brutos - Rio Negro'), + ('Salta', N'Ingresos Brutos - Salta'), + ('SanJuan', N'Ingresos Brutos - San Juan'), + ('SanLuis', N'Ingresos Brutos - San Luis'), + ('SantaCruz', N'Ingresos Brutos - Santa Cruz'), + ('SantaFe', N'Ingresos Brutos - Santa Fe'), + ('SantiagoDelEstero', N'Ingresos Brutos - Santiago del Estero'), + ('TierraDelFuego', N'Ingresos Brutos - Tierra del Fuego'), + ('Tucuman', N'Ingresos Brutos - Tucuman') ) AS s (Provincia, Descripcion) ON t.Provincia = s.Provincia AND t.PredecesorId IS NULL WHEN NOT MATCHED BY TARGET THEN