using FluentAssertions; using Microsoft.Extensions.Time.Testing; using SIGCM2.Application.Rubros.Common; using SIGCM2.Domain.Entities; namespace SIGCM2.Application.Tests.Rubros; public class RubroTreeBuilderTests { private static readonly FakeTimeProvider FakeTime = new(new DateTimeOffset(2026, 4, 18, 12, 0, 0, TimeSpan.Zero)); private static Rubro MakeRubro(int id, int? parentId, string nombre, int orden, bool activo = true) => new(id, parentId, nombre, orden, activo, tarifarioBaseId: null, fechaCreacion: FakeTime.GetUtcNow().UtcDateTime, fechaModificacion: null); // ── empty ───────────────────────────────────────────────────────────────── [Fact] public void Build_empty_returns_empty_list() { var result = RubroTreeBuilder.Build([], incluirInactivos: false); result.Should().BeEmpty(); } // ── single root ─────────────────────────────────────────────────────────── [Fact] public void Build_single_root_returns_one_node_no_children() { var rubros = new[] { MakeRubro(1, null, "Autos", 0) }; var result = RubroTreeBuilder.Build(rubros, incluirInactivos: false); result.Should().HaveCount(1); result[0].Id.Should().Be(1); result[0].Nombre.Should().Be("Autos"); result[0].Hijos.Should().BeEmpty(); } // ── multiple roots sorted by Orden ─────────────────────────────────────── [Fact] public void Build_flat_list_with_multiple_roots_sorted_by_orden() { var rubros = new[] { MakeRubro(3, null, "Motos", 2), MakeRubro(1, null, "Autos", 0), MakeRubro(2, null, "Camiones", 1) }; var result = RubroTreeBuilder.Build(rubros, incluirInactivos: false); result.Should().HaveCount(3); result[0].Id.Should().Be(1); // Orden=0 result[1].Id.Should().Be(2); // Orden=1 result[2].Id.Should().Be(3); // Orden=2 } // ── tree 3 levels deep ──────────────────────────────────────────────────── [Fact] public void Build_tree_3_levels_deep_correctly_nests() { var rubros = new[] { MakeRubro(1, null, "Autos", 0), MakeRubro(2, 1, "Sedanes", 0), MakeRubro(3, 2, "Compactos", 0), }; var result = RubroTreeBuilder.Build(rubros, incluirInactivos: false); result.Should().HaveCount(1); result[0].Id.Should().Be(1); result[0].Hijos.Should().HaveCount(1); result[0].Hijos[0].Id.Should().Be(2); result[0].Hijos[0].Hijos.Should().HaveCount(1); result[0].Hijos[0].Hijos[0].Id.Should().Be(3); } // ── filter inactivos ────────────────────────────────────────────────────── [Fact] public void Build_filters_inactivos_by_default() { var rubros = new[] { MakeRubro(1, null, "Autos", 0, activo: true), MakeRubro(2, null, "Motos", 1, activo: false), }; var result = RubroTreeBuilder.Build(rubros, incluirInactivos: false); result.Should().HaveCount(1); result[0].Id.Should().Be(1); } [Fact] public void Build_includes_inactivos_when_incluirInactivos_true() { var rubros = new[] { MakeRubro(1, null, "Autos", 0, activo: true), MakeRubro(2, null, "Motos", 1, activo: false), }; var result = RubroTreeBuilder.Build(rubros, incluirInactivos: true); result.Should().HaveCount(2); } // ── siblings sorted by Orden ────────────────────────────────────────────── [Fact] public void Build_orders_siblings_by_orden() { var rubros = new[] { MakeRubro(1, null, "Root", 0), MakeRubro(4, 1, "D", 3), MakeRubro(2, 1, "B", 1), MakeRubro(3, 1, "C", 2), MakeRubro(5, 1, "A", 0), }; var result = RubroTreeBuilder.Build(rubros, incluirInactivos: false); var hijos = result[0].Hijos; hijos.Should().HaveCount(4); hijos[0].Nombre.Should().Be("A"); // Orden=0 hijos[1].Nombre.Should().Be("B"); // Orden=1 hijos[2].Nombre.Should().Be("C"); // Orden=2 hijos[3].Nombre.Should().Be("D"); // Orden=3 } // ── O(n) perf smoke test ────────────────────────────────────────────────── [Fact] public void Build_is_Olinear_perf_smoke_test_1000_nodes_under_100ms() { var rubros = new List(); // root rubros.Add(MakeRubro(1, null, "Root", 0)); // 999 children of root for (int i = 2; i <= 1000; i++) rubros.Add(MakeRubro(i, 1, $"Child{i}", i - 2)); var sw = System.Diagnostics.Stopwatch.StartNew(); var result = RubroTreeBuilder.Build(rubros, incluirInactivos: false); sw.Stop(); result.Should().HaveCount(1); result[0].Hijos.Should().HaveCount(999); sw.ElapsedMilliseconds.Should().BeLessThan(100); } }