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); + } }