docs(uns): clarify HasLazyChildren + cluster EntityId, add tie-break test (review I1/I2/M2)

This commit is contained in:
Joseph Doherty
2026-06-08 12:18:37 -04:00
parent d9082e22e3
commit 3e8941bce4
2 changed files with 15 additions and 2 deletions
@@ -40,7 +40,12 @@ public sealed class UnsNode
/// <summary>Badge count. For equipment this is tag + virtual-tag count; for container nodes it is the direct child count.</summary> /// <summary>Badge count. For equipment this is tag + virtual-tag count; for container nodes it is the direct child count.</summary>
public int ChildCount { get; set; } public int ChildCount { get; set; }
/// <summary>True when this node has children that are loaded lazily (equipment with <see cref="ChildCount"/> &gt; 0).</summary> /// <summary>
/// Gets a value indicating whether this node has children that load lazily (equipment with tags/virtual tags).
/// Structural nodes (Cluster/Area/Line) always carry <c>false</c> even when they have eager children, so a
/// renderer must decide whether to show an expand chevron with <c>Children.Count &gt; 0 || HasLazyChildren</c>,
/// not on <c>HasLazyChildren</c> alone.
/// </summary>
public bool HasLazyChildren { get; init; } public bool HasLazyChildren { get; init; }
/// <summary>Eagerly-materialised children. Empty for lazy-loaded equipment until expanded.</summary> /// <summary>Eagerly-materialised children. Empty for lazy-loaded equipment until expanded.</summary>
@@ -162,7 +167,7 @@ public static class UnsTreeAssembly
? cluster.Name ? cluster.Name
: $"{cluster.Site} ({cluster.Name})", : $"{cluster.Site} ({cluster.Name})",
ClusterId = cluster.ClusterId, ClusterId = cluster.ClusterId,
EntityId = cluster.ClusterId, EntityId = cluster.ClusterId, // Cluster's own logical id IS its ClusterId — EntityId mirrors it for uniform navigation.
HasLazyChildren = false, HasLazyChildren = false,
}; };
node.Children.AddRange(areaNodes); node.Children.AddRange(areaNodes);
@@ -43,6 +43,7 @@ public sealed class UnsTreeAssemblyTests
var cluster = tree.Single().Children.Single(); var cluster = tree.Single().Children.Single();
cluster.Key.ShouldBe("clu:c1"); cluster.Key.ShouldBe("clu:c1");
cluster.EntityId.ShouldBe("c1");
var area = cluster.Children.Single(); var area = cluster.Children.Single();
area.Kind.ShouldBe(UnsNodeKind.Area); area.Kind.ShouldBe(UnsNodeKind.Area);
@@ -120,9 +121,12 @@ public sealed class UnsTreeAssemblyTests
public void Build_orders_deterministically() public void Build_orders_deterministically()
{ {
// Two enterprises out of order; clusters/areas/lines/equipment scrambled. // Two enterprises out of order; clusters/areas/lines/equipment scrambled.
// Two clusters with identical Name but different ClusterId to verify tie-break ordering.
var clusters = new[] var clusters = new[]
{ {
new ClusterRow("cZ", "zeta", "SiteZ", "Zeta"), new ClusterRow("cZ", "zeta", "SiteZ", "Zeta"),
new ClusterRow("c9", "zeta", "SiteZ", "Zeta"),
new ClusterRow("c8", "zeta", "SiteZ", "Zeta"),
new ClusterRow("c2", "alpha", "Site2", "Bravo"), new ClusterRow("c2", "alpha", "Site2", "Bravo"),
new ClusterRow("c1", "alpha", "Site1", "Alpha"), new ClusterRow("c1", "alpha", "Site1", "Alpha"),
}; };
@@ -162,5 +166,9 @@ public sealed class UnsTreeAssemblyTests
// Equipment under l1 ordered by Name then EquipmentId: "EquipA"(e1) < "EquipB"(e2). // Equipment under l1 ordered by Name then EquipmentId: "EquipA"(e1) < "EquipB"(e2).
var l1 = a1.Children.Single(l => l.Key == "line:l1"); var l1 = a1.Children.Single(l => l.Key == "line:l1");
l1.Children.Select(eq => eq.Key).ShouldBe(new[] { "eq:e1", "eq:e2" }); l1.Children.Select(eq => eq.Key).ShouldBe(new[] { "eq:e1", "eq:e2" });
// Clusters with the same Name tie-break by ClusterId ordinal: "c8" < "c9" < "cZ".
var zetaEnt = tree.Single(e => e.Key == "ent:zeta");
zetaEnt.Children.Select(c => c.Key).ShouldBe(new[] { "clu:c8", "clu:c9", "clu:cZ" });
} }
} }