feat: CAT-002 Regla de Oro Rama vs Hoja + validaciones #35
@@ -12,7 +12,8 @@ public static class RubroTreeBuilder
|
|||||||
{
|
{
|
||||||
public static IReadOnlyList<RubroTreeNodeDto> Build(
|
public static IReadOnlyList<RubroTreeNodeDto> Build(
|
||||||
IEnumerable<Rubro> flat,
|
IEnumerable<Rubro> flat,
|
||||||
bool incluirInactivos)
|
bool incluirInactivos,
|
||||||
|
IReadOnlyDictionary<int, int> avisoCounts)
|
||||||
{
|
{
|
||||||
var filtered = incluirInactivos
|
var filtered = incluirInactivos
|
||||||
? flat.ToList()
|
? flat.ToList()
|
||||||
@@ -36,6 +37,7 @@ public static class RubroTreeBuilder
|
|||||||
Activo: r.Activo,
|
Activo: r.Activo,
|
||||||
ParentId: r.ParentId,
|
ParentId: r.ParentId,
|
||||||
TarifarioBaseId: r.TarifarioBaseId,
|
TarifarioBaseId: r.TarifarioBaseId,
|
||||||
|
TieneAvisos: avisoCounts.GetValueOrDefault(r.Id, 0) > 0,
|
||||||
Hijos: children);
|
Hijos: children);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,4 +10,5 @@ public sealed record RubroTreeNodeDto(
|
|||||||
bool Activo,
|
bool Activo,
|
||||||
int? ParentId,
|
int? ParentId,
|
||||||
int? TarifarioBaseId,
|
int? TarifarioBaseId,
|
||||||
|
bool TieneAvisos,
|
||||||
IReadOnlyList<RubroTreeNodeDto> Hijos);
|
IReadOnlyList<RubroTreeNodeDto> Hijos);
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ public sealed class GetRubroTreeQueryHandler : ICommandHandler<GetRubroTreeQuery
|
|||||||
public async Task<IReadOnlyList<RubroTreeNodeDto>> Handle(GetRubroTreeQuery query)
|
public async Task<IReadOnlyList<RubroTreeNodeDto>> Handle(GetRubroTreeQuery query)
|
||||||
{
|
{
|
||||||
var all = await _repo.GetAllAsync(query.IncluirInactivos);
|
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>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ public class RubroTreeBuilderTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void Build_empty_returns_empty_list()
|
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();
|
result.Should().BeEmpty();
|
||||||
}
|
}
|
||||||
@@ -30,7 +30,7 @@ public class RubroTreeBuilderTests
|
|||||||
{
|
{
|
||||||
var rubros = new[] { MakeRubro(1, null, "Autos", 0) };
|
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.Should().HaveCount(1);
|
||||||
result[0].Id.Should().Be(1);
|
result[0].Id.Should().Be(1);
|
||||||
@@ -50,7 +50,7 @@ public class RubroTreeBuilderTests
|
|||||||
MakeRubro(2, null, "Camiones", 1)
|
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.Should().HaveCount(3);
|
||||||
result[0].Id.Should().Be(1); // Orden=0
|
result[0].Id.Should().Be(1); // Orden=0
|
||||||
@@ -70,7 +70,7 @@ public class RubroTreeBuilderTests
|
|||||||
MakeRubro(3, 2, "Compactos", 0),
|
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.Should().HaveCount(1);
|
||||||
result[0].Id.Should().Be(1);
|
result[0].Id.Should().Be(1);
|
||||||
@@ -91,7 +91,7 @@ public class RubroTreeBuilderTests
|
|||||||
MakeRubro(2, null, "Motos", 1, activo: false),
|
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.Should().HaveCount(1);
|
||||||
result[0].Id.Should().Be(1);
|
result[0].Id.Should().Be(1);
|
||||||
@@ -106,7 +106,7 @@ public class RubroTreeBuilderTests
|
|||||||
MakeRubro(2, null, "Motos", 1, activo: false),
|
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);
|
result.Should().HaveCount(2);
|
||||||
}
|
}
|
||||||
@@ -125,7 +125,7 @@ public class RubroTreeBuilderTests
|
|||||||
MakeRubro(5, 1, "A", 0),
|
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;
|
var hijos = result[0].Hijos;
|
||||||
hijos.Should().HaveCount(4);
|
hijos.Should().HaveCount(4);
|
||||||
@@ -135,6 +135,47 @@ public class RubroTreeBuilderTests
|
|||||||
hijos[3].Nombre.Should().Be("D"); // Orden=3
|
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 ──────────────────────────────────────────────────
|
// ── O(n) perf smoke test ──────────────────────────────────────────────────
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -148,7 +189,7 @@ public class RubroTreeBuilderTests
|
|||||||
rubros.Add(MakeRubro(i, 1, $"Child{i}", i - 2));
|
rubros.Add(MakeRubro(i, 1, $"Child{i}", i - 2));
|
||||||
|
|
||||||
var sw = System.Diagnostics.Stopwatch.StartNew();
|
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();
|
sw.Stop();
|
||||||
|
|
||||||
result.Should().HaveCount(1);
|
result.Should().HaveCount(1);
|
||||||
|
|||||||
Reference in New Issue
Block a user