feat: CAT-002 Regla de Oro Rama vs Hoja + validaciones #35

Merged
dmolinari merged 9 commits from feature/CAT-002 into main 2026-04-19 11:56:32 +00:00
4 changed files with 55 additions and 10 deletions
Showing only changes of commit 9e50a929ae - Show all commits

View File

@@ -12,7 +12,8 @@ public static class RubroTreeBuilder
{
public static IReadOnlyList<RubroTreeNodeDto> Build(
IEnumerable<Rubro> flat,
bool incluirInactivos)
bool incluirInactivos,
IReadOnlyDictionary<int, int> avisoCounts)
{
var filtered = incluirInactivos
? flat.ToList()
@@ -36,6 +37,7 @@ public static class RubroTreeBuilder
Activo: r.Activo,
ParentId: r.ParentId,
TarifarioBaseId: r.TarifarioBaseId,
TieneAvisos: avisoCounts.GetValueOrDefault(r.Id, 0) > 0,
Hijos: children);
}

View File

@@ -10,4 +10,5 @@ public sealed record RubroTreeNodeDto(
bool Activo,
int? ParentId,
int? TarifarioBaseId,
bool TieneAvisos,
IReadOnlyList<RubroTreeNodeDto> Hijos);

View File

@@ -17,6 +17,7 @@ public sealed class GetRubroTreeQueryHandler : ICommandHandler<GetRubroTreeQuery
public async Task<IReadOnlyList<RubroTreeNodeDto>> Handle(GetRubroTreeQuery query)
{
var all = await _repo.GetAllAsync(query.IncluirInactivos);
return RubroTreeBuilder.Build(all, query.IncluirInactivos);
// CAT-002: avisoCounts injected via IAvisoQueryRepository (wired in Batch 6)
return RubroTreeBuilder.Build(all, query.IncluirInactivos, new Dictionary<int, int>());
}
}

View File

@@ -18,7 +18,7 @@ public class RubroTreeBuilderTests
[Fact]
public void Build_empty_returns_empty_list()
{
var result = RubroTreeBuilder.Build([], incluirInactivos: false);
var result = RubroTreeBuilder.Build([], incluirInactivos: false, new Dictionary<int, int>());
result.Should().BeEmpty();
}
@@ -30,7 +30,7 @@ public class RubroTreeBuilderTests
{
var rubros = new[] { MakeRubro(1, null, "Autos", 0) };
var result = RubroTreeBuilder.Build(rubros, incluirInactivos: false);
var result = RubroTreeBuilder.Build(rubros, incluirInactivos: false, new Dictionary<int, int>());
result.Should().HaveCount(1);
result[0].Id.Should().Be(1);
@@ -50,7 +50,7 @@ public class RubroTreeBuilderTests
MakeRubro(2, null, "Camiones", 1)
};
var result = RubroTreeBuilder.Build(rubros, incluirInactivos: false);
var result = RubroTreeBuilder.Build(rubros, incluirInactivos: false, new Dictionary<int, int>());
result.Should().HaveCount(3);
result[0].Id.Should().Be(1); // Orden=0
@@ -70,7 +70,7 @@ public class RubroTreeBuilderTests
MakeRubro(3, 2, "Compactos", 0),
};
var result = RubroTreeBuilder.Build(rubros, incluirInactivos: false);
var result = RubroTreeBuilder.Build(rubros, incluirInactivos: false, new Dictionary<int, int>());
result.Should().HaveCount(1);
result[0].Id.Should().Be(1);
@@ -91,7 +91,7 @@ public class RubroTreeBuilderTests
MakeRubro(2, null, "Motos", 1, activo: false),
};
var result = RubroTreeBuilder.Build(rubros, incluirInactivos: false);
var result = RubroTreeBuilder.Build(rubros, incluirInactivos: false, new Dictionary<int, int>());
result.Should().HaveCount(1);
result[0].Id.Should().Be(1);
@@ -106,7 +106,7 @@ public class RubroTreeBuilderTests
MakeRubro(2, null, "Motos", 1, activo: false),
};
var result = RubroTreeBuilder.Build(rubros, incluirInactivos: true);
var result = RubroTreeBuilder.Build(rubros, incluirInactivos: true, new Dictionary<int, int>());
result.Should().HaveCount(2);
}
@@ -125,7 +125,7 @@ public class RubroTreeBuilderTests
MakeRubro(5, 1, "A", 0),
};
var result = RubroTreeBuilder.Build(rubros, incluirInactivos: false);
var result = RubroTreeBuilder.Build(rubros, incluirInactivos: false, new Dictionary<int, int>());
var hijos = result[0].Hijos;
hijos.Should().HaveCount(4);
@@ -135,6 +135,47 @@ public class RubroTreeBuilderTests
hijos[3].Nombre.Should().Be("D"); // Orden=3
}
// ── TieneAvisos from avisoCounts dict ────────────────────────────────────
[Fact]
public void Build_SetsTieneAvisos_True_WhenCountGreaterThanZero()
{
var rubros = new[]
{
MakeRubro(1, null, "Autos", 0),
MakeRubro(2, null, "Motos", 1),
};
var avisoCounts = new Dictionary<int, int> { { 1, 2 }, { 2, 0 } };
var result = RubroTreeBuilder.Build(rubros, incluirInactivos: false, avisoCounts);
result.Single(n => n.Id == 1).TieneAvisos.Should().BeTrue();
result.Single(n => n.Id == 2).TieneAvisos.Should().BeFalse();
}
[Fact]
public void Build_SetsTieneAvisos_False_WhenCountIsZero()
{
var rubros = new[] { MakeRubro(1, null, "Autos", 0) };
var avisoCounts = new Dictionary<int, int> { { 1, 0 } };
var result = RubroTreeBuilder.Build(rubros, incluirInactivos: false, avisoCounts);
result[0].TieneAvisos.Should().BeFalse();
}
[Fact]
public void Build_SetsTieneAvisos_False_WhenIdMissingFromDict()
{
// Stub semantics: missing key = 0 = false
var rubros = new[] { MakeRubro(1, null, "Autos", 0) };
var avisoCounts = new Dictionary<int, int>(); // empty dict
var result = RubroTreeBuilder.Build(rubros, incluirInactivos: false, avisoCounts);
result[0].TieneAvisos.Should().BeFalse();
}
// ── O(n) perf smoke test ──────────────────────────────────────────────────
[Fact]
@@ -148,7 +189,7 @@ public class RubroTreeBuilderTests
rubros.Add(MakeRubro(i, 1, $"Child{i}", i - 2));
var sw = System.Diagnostics.Stopwatch.StartNew();
var result = RubroTreeBuilder.Build(rubros, incluirInactivos: false);
var result = RubroTreeBuilder.Build(rubros, incluirInactivos: false, new Dictionary<int, int>());
sw.Stop();
result.Should().HaveCount(1);