test(adm-009): TipoDeIva entity validation tests (Red)
This commit is contained in:
351
tests/SIGCM2.Application.Tests/Domain/Fiscal/TipoDeIvaTests.cs
Normal file
351
tests/SIGCM2.Application.Tests/Domain/Fiscal/TipoDeIvaTests.cs
Normal file
@@ -0,0 +1,351 @@
|
||||
using System.Reflection;
|
||||
using FluentAssertions;
|
||||
using SIGCM2.Domain.Entities;
|
||||
using SIGCM2.Domain.Exceptions;
|
||||
|
||||
namespace SIGCM2.Application.Tests.Domain.Fiscal;
|
||||
|
||||
public class TipoDeIvaTests
|
||||
{
|
||||
private static readonly DateOnly Desde2020 = new(2020, 1, 1);
|
||||
private static readonly DateOnly Desde2026 = new(2026, 6, 1);
|
||||
|
||||
private static TipoDeIva MakeTipoDeIva(
|
||||
int id = 1,
|
||||
string codigo = "IVA_21",
|
||||
string descripcion = "IVA 21%",
|
||||
decimal porcentaje = 21m,
|
||||
bool aplicaIVA = true,
|
||||
bool activo = true,
|
||||
DateOnly? vigenciaDesde = null,
|
||||
DateOnly? vigenciaHasta = null,
|
||||
int? predecesorId = null)
|
||||
=> TipoDeIva.FromDb(
|
||||
id: id,
|
||||
codigo: codigo,
|
||||
descripcion: descripcion,
|
||||
porcentaje: porcentaje,
|
||||
aplicaIVA: aplicaIVA,
|
||||
activo: activo,
|
||||
vigenciaDesde: vigenciaDesde ?? Desde2020,
|
||||
vigenciaHasta: vigenciaHasta,
|
||||
predecesorId: predecesorId,
|
||||
fechaCreacion: DateTime.UtcNow,
|
||||
fechaModificacion: null);
|
||||
|
||||
// ── T200.10: ForCreation validations (RED) ────────────────────────────────
|
||||
|
||||
[Fact]
|
||||
public void ForCreation_ValidArgs_ReturnsEntity()
|
||||
{
|
||||
var iva = TipoDeIva.ForCreation("IVA_21", "IVA 21%", 21m, true, Desde2020);
|
||||
|
||||
iva.Codigo.Should().Be("IVA_21");
|
||||
iva.Descripcion.Should().Be("IVA 21%");
|
||||
iva.Porcentaje.Should().Be(21m);
|
||||
iva.AplicaIVA.Should().BeTrue();
|
||||
iva.Activo.Should().BeTrue();
|
||||
iva.Id.Should().Be(0);
|
||||
iva.PredecesorId.Should().BeNull();
|
||||
iva.VigenciaHasta.Should().BeNull();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("INVALIDO")]
|
||||
[InlineData("IVA21")]
|
||||
[InlineData("iva_21")]
|
||||
[InlineData("IVA-21")]
|
||||
[InlineData("")]
|
||||
[InlineData(" ")]
|
||||
public void ForCreation_CodigoInvalido_ThrowsArgumentException(string codigoInvalido)
|
||||
{
|
||||
var act = () => TipoDeIva.ForCreation(codigoInvalido, "desc", 21m, true, Desde2020);
|
||||
|
||||
act.Should().Throw<ArgumentException>()
|
||||
.WithParameterName("codigo");
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("EXENTO")]
|
||||
[InlineData("NO_GRAVADO")]
|
||||
[InlineData("IVA_21")]
|
||||
[InlineData("IVA_105")]
|
||||
[InlineData("IVA_0")]
|
||||
public void ForCreation_CodigoValido_NoLanza(string codigoValido)
|
||||
{
|
||||
var act = () => TipoDeIva.ForCreation(codigoValido, "desc", 0m, false, Desde2020);
|
||||
|
||||
act.Should().NotThrow();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ForCreation_PorcentajeNegativo_ThrowsArgumentException()
|
||||
{
|
||||
var act = () => TipoDeIva.ForCreation("IVA_21", "desc", -5m, true, Desde2020);
|
||||
|
||||
act.Should().Throw<ArgumentException>()
|
||||
.WithParameterName("porcentaje");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ForCreation_PorcentajeMayorA100_ThrowsArgumentException()
|
||||
{
|
||||
var act = () => TipoDeIva.ForCreation("IVA_21", "desc", 150m, true, Desde2020);
|
||||
|
||||
act.Should().Throw<ArgumentException>()
|
||||
.WithParameterName("porcentaje");
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0)]
|
||||
[InlineData(10.5)]
|
||||
[InlineData(21)]
|
||||
[InlineData(100)]
|
||||
public void ForCreation_PorcentajeEnRango_NoLanza(double porcentaje)
|
||||
{
|
||||
var act = () => TipoDeIva.ForCreation("IVA_21", "desc", (decimal)porcentaje, true, Desde2020);
|
||||
|
||||
act.Should().NotThrow();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ForCreation_VigenciaHastaMenorQueDesde_ThrowsArgumentException()
|
||||
{
|
||||
var desde = new DateOnly(2026, 6, 1);
|
||||
var hasta = new DateOnly(2026, 1, 1); // antes que desde
|
||||
|
||||
var act = () => TipoDeIva.ForCreation("IVA_21", "desc", 21m, true, desde, hasta);
|
||||
|
||||
act.Should().Throw<ArgumentException>()
|
||||
.WithParameterName("vigenciaHasta");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ForCreation_VigenciaHastaIgualQueDesde_NoLanza()
|
||||
{
|
||||
var fecha = new DateOnly(2026, 1, 1);
|
||||
var act = () => TipoDeIva.ForCreation("IVA_21", "desc", 21m, true, fecha, fecha);
|
||||
|
||||
act.Should().NotThrow();
|
||||
}
|
||||
|
||||
// ── T200.12: ForCreation sets all properties correctly ────────────────────
|
||||
|
||||
[Fact]
|
||||
public void ForCreation_SetsAllProps_Correctly()
|
||||
{
|
||||
var iva = TipoDeIva.ForCreation("EXENTO", "Exento de IVA", 0m, false, Desde2020);
|
||||
|
||||
iva.Id.Should().Be(0);
|
||||
iva.Activo.Should().BeTrue();
|
||||
iva.VigenciaHasta.Should().BeNull();
|
||||
iva.VigenciaDesde.Should().Be(Desde2020);
|
||||
iva.PredecesorId.Should().BeNull();
|
||||
}
|
||||
|
||||
// ── T200.11: With* methods ────────────────────────────────────────────────
|
||||
|
||||
[Fact]
|
||||
public void WithDescripcion_ReturnsNewInstanceWithUpdatedDescripcion()
|
||||
{
|
||||
var original = MakeTipoDeIva(descripcion: "Original");
|
||||
|
||||
var updated = original.WithDescripcion("Nueva descripcion");
|
||||
|
||||
updated.Should().NotBeSameAs(original);
|
||||
updated.Descripcion.Should().Be("Nueva descripcion");
|
||||
updated.Porcentaje.Should().Be(original.Porcentaje, "Porcentaje es inmutable");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WithCodigo_ReturnsNewInstanceWithUpdatedCodigo()
|
||||
{
|
||||
var original = MakeTipoDeIva(codigo: "IVA_21");
|
||||
|
||||
var updated = original.WithCodigo("NO_GRAVADO");
|
||||
|
||||
updated.Codigo.Should().Be("NO_GRAVADO");
|
||||
updated.Porcentaje.Should().Be(original.Porcentaje);
|
||||
updated.Id.Should().Be(original.Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void WithAplicaIVA_ReturnsNewInstanceWithUpdatedAplicaIVA()
|
||||
{
|
||||
var original = MakeTipoDeIva(aplicaIVA: true);
|
||||
|
||||
var updated = original.WithAplicaIVA(false);
|
||||
|
||||
updated.AplicaIVA.Should().BeFalse();
|
||||
updated.Porcentaje.Should().Be(original.Porcentaje);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Deactivate_ReturnsNewInstanceWithActivoFalse()
|
||||
{
|
||||
var original = MakeTipoDeIva(activo: true);
|
||||
|
||||
var deactivated = original.Deactivate();
|
||||
|
||||
deactivated.Activo.Should().BeFalse();
|
||||
deactivated.Porcentaje.Should().Be(original.Porcentaje);
|
||||
deactivated.Id.Should().Be(original.Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Reactivate_ReturnsNewInstanceWithActivoTrue()
|
||||
{
|
||||
var original = MakeTipoDeIva(activo: false);
|
||||
|
||||
var reactivated = original.Reactivate();
|
||||
|
||||
reactivated.Activo.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CerrarVigencia_SetsVigenciaHasta()
|
||||
{
|
||||
var original = MakeTipoDeIva(vigenciaHasta: null);
|
||||
var hasta = new DateOnly(2026, 5, 31);
|
||||
|
||||
var cerrado = original.CerrarVigencia(hasta);
|
||||
|
||||
cerrado.VigenciaHasta.Should().Be(hasta);
|
||||
cerrado.Porcentaje.Should().Be(original.Porcentaje);
|
||||
cerrado.Id.Should().Be(original.Id);
|
||||
}
|
||||
|
||||
// ── T200.13 & T200.15: NuevaVersion tuple (RED then GREEN) ───────────────
|
||||
|
||||
[Fact]
|
||||
public void NuevaVersion_ReturnsPredecesoraCerradaYNuevaVersion()
|
||||
{
|
||||
var predecesora = MakeTipoDeIva(id: 5, porcentaje: 21m, vigenciaDesde: Desde2020, vigenciaHasta: null);
|
||||
|
||||
var (cerrada, nueva) = predecesora.NuevaVersion(23.5m, Desde2026);
|
||||
|
||||
cerrada.Id.Should().Be(5);
|
||||
cerrada.VigenciaHasta.Should().Be(Desde2026.AddDays(-1), "predecesora queda cerrada el día anterior");
|
||||
cerrada.Porcentaje.Should().Be(21m, "porcentaje predecesora no cambia");
|
||||
|
||||
nueva.Id.Should().Be(0, "ID lo asigna la BD");
|
||||
nueva.PredecesorId.Should().Be(5);
|
||||
nueva.Porcentaje.Should().Be(23.5m);
|
||||
nueva.VigenciaDesde.Should().Be(Desde2026);
|
||||
nueva.VigenciaHasta.Should().BeNull("nueva versión nace abierta");
|
||||
nueva.Codigo.Should().Be(predecesora.Codigo, "hereda el código");
|
||||
nueva.Descripcion.Should().Be(predecesora.Descripcion, "hereda la descripción");
|
||||
nueva.Activo.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NuevaVersion_PorcentajeDistinto_EsIndependiente()
|
||||
{
|
||||
var predecesora = MakeTipoDeIva(porcentaje: 10.5m, vigenciaDesde: Desde2020);
|
||||
var nuevaVigencia = new DateOnly(2025, 1, 1);
|
||||
|
||||
var (_, nueva) = predecesora.NuevaVersion(21m, nuevaVigencia);
|
||||
|
||||
nueva.Porcentaje.Should().Be(21m);
|
||||
predecesora.Porcentaje.Should().Be(10.5m, "predecesora no muta");
|
||||
}
|
||||
|
||||
// ── T200.13: NuevaVersion validations ────────────────────────────────────
|
||||
|
||||
[Fact]
|
||||
public void NuevaVersion_PredecesoraConVigenciaHasta_ThrowsInvalidOperationException()
|
||||
{
|
||||
var predecesora = MakeTipoDeIva(
|
||||
vigenciaDesde: Desde2020,
|
||||
vigenciaHasta: new DateOnly(2025, 12, 31)); // ya cerrada
|
||||
|
||||
var act = () => predecesora.NuevaVersion(23.5m, Desde2026);
|
||||
|
||||
act.Should().Throw<InvalidOperationException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NuevaVersion_VigenciaDesdeIgualAPredecesora_ThrowsArgumentException()
|
||||
{
|
||||
var predecesora = MakeTipoDeIva(vigenciaDesde: Desde2020, vigenciaHasta: null);
|
||||
|
||||
var act = () => predecesora.NuevaVersion(23.5m, Desde2020); // igual a VigenciaDesde predecesora
|
||||
|
||||
act.Should().Throw<ArgumentException>()
|
||||
.WithParameterName("vigenciaDesde");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NuevaVersion_VigenciaDesDeMenorQuePredecesora_ThrowsArgumentException()
|
||||
{
|
||||
var predecesora = MakeTipoDeIva(vigenciaDesde: Desde2026, vigenciaHasta: null);
|
||||
var vigenciaAnterior = new DateOnly(2020, 1, 1);
|
||||
|
||||
var act = () => predecesora.NuevaVersion(23.5m, vigenciaAnterior);
|
||||
|
||||
act.Should().Throw<ArgumentException>()
|
||||
.WithParameterName("vigenciaDesde");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NuevaVersion_NuevoPorcentajeNegativo_ThrowsArgumentException()
|
||||
{
|
||||
var predecesora = MakeTipoDeIva(vigenciaDesde: Desde2020, vigenciaHasta: null);
|
||||
|
||||
var act = () => predecesora.NuevaVersion(-1m, Desde2026);
|
||||
|
||||
act.Should().Throw<ArgumentException>()
|
||||
.WithParameterName("nuevoPorcentaje");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NuevaVersion_NuevoPorcentajeMayorA100_ThrowsArgumentException()
|
||||
{
|
||||
var predecesora = MakeTipoDeIva(vigenciaDesde: Desde2020, vigenciaHasta: null);
|
||||
|
||||
var act = () => predecesora.NuevaVersion(101m, Desde2026);
|
||||
|
||||
act.Should().Throw<ArgumentException>()
|
||||
.WithParameterName("nuevoPorcentaje");
|
||||
}
|
||||
|
||||
// ── T200.16: reflection — NO debe existir WithPorcentaje ─────────────────
|
||||
|
||||
[Fact]
|
||||
public void TipoDeIva_No_Debe_Exponer_WithPorcentaje()
|
||||
{
|
||||
var method = typeof(TipoDeIva).GetMethod("WithPorcentaje", BindingFlags.Public | BindingFlags.Instance);
|
||||
|
||||
method.Should().BeNull("Porcentaje es inmutable — usar NuevaVersion");
|
||||
}
|
||||
|
||||
// ── Immutable fields preserved across With* ───────────────────────────────
|
||||
|
||||
[Fact]
|
||||
public void WithDescripcion_PreservesImmutableFields()
|
||||
{
|
||||
var original = MakeTipoDeIva(id: 99, porcentaje: 21m, vigenciaDesde: Desde2020);
|
||||
|
||||
var updated = original.WithDescripcion("Nueva");
|
||||
|
||||
updated.Id.Should().Be(99);
|
||||
updated.Porcentaje.Should().Be(21m);
|
||||
updated.VigenciaDesde.Should().Be(Desde2020);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void FromDb_SetsAllProperties()
|
||||
{
|
||||
var fechaCreacion = DateTime.UtcNow;
|
||||
var iva = TipoDeIva.FromDb(
|
||||
id: 7, codigo: "IVA_21", descripcion: "IVA 21%",
|
||||
porcentaje: 21m, aplicaIVA: true, activo: true,
|
||||
vigenciaDesde: Desde2020, vigenciaHasta: null,
|
||||
predecesorId: null, fechaCreacion: fechaCreacion, fechaModificacion: null);
|
||||
|
||||
iva.Id.Should().Be(7);
|
||||
iva.Codigo.Should().Be("IVA_21");
|
||||
iva.Porcentaje.Should().Be(21m);
|
||||
iva.FechaCreacion.Should().Be(fechaCreacion);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user