diff --git a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Deployment/DebugTreeBuilder.cs b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Deployment/DebugTreeBuilder.cs
index e746d423..42dc7ec8 100644
--- a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Deployment/DebugTreeBuilder.cs
+++ b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Deployment/DebugTreeBuilder.cs
@@ -21,6 +21,13 @@ public static class DebugTreeBuilder
/// ). Null/empty/whitespace
/// keeps everything; matching leaves carry along their ancestor branches.
///
+ ///
+ /// The caller is expected to pass at most one
+ /// per . The DebugView page
+ /// enforces this by keying a dictionary on the attribute name before calling this
+ /// method. Passing duplicate names would produce sibling leaves with identical
+ /// keys under the same parent branch.
+ ///
public static IReadOnlyList BuildAttributeTree(
IEnumerable attributes, string? filter)
{
@@ -71,7 +78,7 @@ public static class DebugTreeBuilder
}
SortAndRollUp(roots);
- return roots;
+ return roots.AsReadOnly();
}
///
diff --git a/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Deployment/DebugTreeBuilderTests.cs b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Deployment/DebugTreeBuilderTests.cs
index 9ee4ce24..45aa2705 100644
--- a/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Deployment/DebugTreeBuilderTests.cs
+++ b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/Deployment/DebugTreeBuilderTests.cs
@@ -203,4 +203,33 @@ public class DebugTreeBuilderTests
var tree = DebugTreeBuilder.BuildAttributeTree(Array.Empty(), null);
Assert.Empty(tree);
}
+
+ [Fact]
+ public void RollUp_FourLevelDeepBadQuality_ReachesRoot()
+ {
+ var tree = DebugTreeBuilder.BuildAttributeTree(
+ new[] { Attr("A.B.C.D", quality: "Bad") }, null);
+
+ var a = Assert.Single(tree);
+ Assert.True(a.HasBadQuality);
+ var b = Assert.Single(a.Children);
+ Assert.True(b.HasBadQuality);
+ var c = Assert.Single(b.Children);
+ Assert.True(c.HasBadQuality);
+ var d = Assert.Single(c.Children);
+ Assert.True(d.HasBadQuality);
+ }
+
+ [Fact]
+ public void Filter_DeepLeafMatch_RetainsAllAncestorBranches()
+ {
+ var tree = DebugTreeBuilder.BuildAttributeTree(
+ new[] { Attr("Motor1.Compressor.Pump"), Attr("Motor1.Speed") }, "Pump");
+
+ var motor = Assert.Single(tree);
+ Assert.Equal("Motor1", motor.Key);
+ var compressor = Assert.Single(motor.Children);
+ Assert.Equal("Motor1.Compressor", compressor.Key);
+ Assert.Equal("Motor1.Compressor.Pump", Assert.Single(compressor.Children).Key);
+ }
}