using ZB.MOM.WW.ScadaBridge.CentralUI.Components.Shared; using ZB.MOM.WW.ScadaBridge.Commons.Entities.Instances; namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Shared; /// /// Unit tests for — the pure area/instance forest /// behind the Instance Picker combobox (design 2026-09-08). /// public class InstanceTreeBuilderTests { private static Area MakeArea(int id, string name, int? parentAreaId = null) => new(name) { Id = id, SiteId = 1, ParentAreaId = parentAreaId }; private static Instance MakeInstance(int id, string uniqueName, int? areaId = null) => new(uniqueName) { Id = id, SiteId = 1, AreaId = areaId }; [Fact] public void Build_NestsAreasByParent_AndPutsRootInstancesAfterRootAreas() { var line1 = MakeArea(1, "Line1"); var cellA = MakeArea(2, "Cell A", parentAreaId: 1); var p1 = MakeInstance(10, "P1", areaId: 2); var zulu = MakeInstance(11, "Zulu"); var alpha = MakeInstance(12, "Alpha"); var roots = InstanceTreeBuilder.Build([line1, cellA], [p1, zulu, alpha], filter: null); Assert.Equal(new[] { "a:1", "i:12", "i:11" }, roots.Select(n => n.Key)); var areaNode = roots[0]; Assert.Equal("Line1", areaNode.Label); Assert.False(areaNode.IsInstance); Assert.Same(line1, areaNode.Area); Assert.Null(areaNode.Instance); Assert.True(areaNode.HasChildren); var cellNode = Assert.Single(areaNode.Children); Assert.Equal("a:2", cellNode.Key); Assert.Equal("Cell A", cellNode.Label); Assert.Same(cellA, cellNode.Area); var p1Node = Assert.Single(cellNode.Children); Assert.Equal("i:10", p1Node.Key); Assert.Equal("P1", p1Node.Label); Assert.True(p1Node.IsInstance); Assert.Same(p1, p1Node.Instance); Assert.Null(p1Node.Area); Assert.False(p1Node.HasChildren); Assert.Equal("Alpha", roots[1].Label); Assert.Same(alpha, roots[1].Instance); Assert.Equal("Zulu", roots[2].Label); Assert.Same(zulu, roots[2].Instance); } [Fact] public void Build_SortsAreasAndInstancesCaseInsensitively() { var beta = MakeArea(1, "beta"); var alphaArea = MakeArea(2, "Alpha"); var zebra = MakeInstance(10, "zebra"); var apple = MakeInstance(11, "Apple"); var roots = InstanceTreeBuilder.Build([beta, alphaArea], [zebra, apple], filter: null); Assert.Equal(new[] { "Alpha", "beta", "Apple", "zebra" }, roots.Select(n => n.Label)); } [Fact] public void Build_OrphanedArea_BecomesARoot() { // ParentAreaId points at an area that is not in the supplied list. var orphan = MakeArea(5, "Orphan", parentAreaId: 99); var child = MakeInstance(10, "OrphanChild", areaId: 5); var roots = InstanceTreeBuilder.Build([orphan], [child], filter: null); var root = Assert.Single(roots); Assert.Equal("a:5", root.Key); var leaf = Assert.Single(root.Children); Assert.Equal("i:10", leaf.Key); } [Fact] public void Build_InstanceWithUnknownArea_SurfacesAtRoot() { // AreaId points at an area that is not in the supplied list — the instance must not // vanish; it is listed at the root like an instance with no area at all. var line1 = MakeArea(1, "Line1"); var stranded = MakeInstance(10, "Stranded", areaId: 99); var roots = InstanceTreeBuilder.Build([line1], [stranded], filter: null); Assert.Equal(new[] { "a:1", "i:10" }, roots.Select(n => n.Key)); Assert.Same(stranded, roots[1].Instance); } [Fact] public void Build_InstanceWithUnknownArea_StaysFilterable() { var line1 = MakeArea(1, "Line1"); var stranded = MakeInstance(10, "Stranded", areaId: 99); var matched = InstanceTreeBuilder.Build([line1], [stranded], filter: "rand"); Assert.Equal("i:10", Assert.Single(matched).Key); Assert.Empty(InstanceTreeBuilder.Build([line1], [stranded], filter: "nomatch")); } [Fact] public void Build_DeepNesting_WiresChildrenAtEveryLevel() { var plant = MakeArea(1, "Plant"); var line = MakeArea(2, "Line", parentAreaId: 1); var cell = MakeArea(3, "Cell", parentAreaId: 2); var deep = MakeInstance(10, "Deep", areaId: 3); var roots = InstanceTreeBuilder.Build([plant, line, cell], [deep], filter: null); var plantNode = Assert.Single(roots); Assert.Equal("a:1", plantNode.Key); var lineNode = Assert.Single(plantNode.Children); Assert.Equal("a:2", lineNode.Key); var cellNode = Assert.Single(lineNode.Children); Assert.Equal("a:3", cellNode.Key); var leaf = Assert.Single(cellNode.Children); Assert.Equal("i:10", leaf.Key); Assert.False(leaf.HasChildren); } [Fact] public void Build_CyclicParentAreaIds_DoNotLoopOrThrow() { // A bad row pair: each area names the other as its parent. Neither is reachable // from a root, so the forest is simply empty — and building it must terminate. var a = MakeArea(1, "A", parentAreaId: 2); var b = MakeArea(2, "B", parentAreaId: 1); var inA = MakeInstance(10, "InA", areaId: 1); var inB = MakeInstance(11, "InB", areaId: 2); var roots = InstanceTreeBuilder.Build([a, b], [inA, inB], filter: null); Assert.Empty(roots); } [Fact] public void Build_Filter_MatchesInstanceNamesOnly_AreaNameMatchIsNotEnough() { // "Line1" contains the needle but holds no matching instance — it is dropped. var line1 = MakeArea(1, "Line1"); var line2 = MakeArea(2, "Line2"); var other = MakeInstance(10, "Pump", areaId: 1); var hit = MakeInstance(11, "Line1Feeder", areaId: 2); var roots = InstanceTreeBuilder.Build([line1, line2], [other, hit], filter: "line1"); var root = Assert.Single(roots); Assert.Equal("a:2", root.Key); Assert.Equal("i:11", Assert.Single(root.Children).Key); } [Fact] public void Build_Filter_KeepsMatchingInstancesWithAncestors_DropsEmptyBranches() { var line1 = MakeArea(1, "Line1"); var cellA = MakeArea(2, "Cell A", parentAreaId: 1); var emptyArea = MakeArea(3, "Line2"); var p1 = MakeInstance(10, "P1", areaId: 2); var zulu = MakeInstance(11, "Zulu"); var alpha = MakeInstance(12, "Alpha"); var roots = InstanceTreeBuilder.Build([line1, cellA, emptyArea], [p1, zulu, alpha], filter: "p1"); var root = Assert.Single(roots); Assert.Equal("a:1", root.Key); var cellNode = Assert.Single(root.Children); Assert.Equal("a:2", cellNode.Key); var leaf = Assert.Single(cellNode.Children); Assert.Equal("i:10", leaf.Key); } [Fact] public void Build_Filter_WhitespaceMeansNoFilter() { var line1 = MakeArea(1, "Line1"); var p1 = MakeInstance(10, "P1", areaId: 1); var zulu = MakeInstance(11, "Zulu"); var roots = InstanceTreeBuilder.Build([line1], [p1, zulu], filter: " "); Assert.Equal(new[] { "a:1", "i:11" }, roots.Select(n => n.Key)); Assert.Single(roots[0].Children); } [Fact] public void Build_NullOrEmptyInputs_ReturnEmpty() { Assert.Empty(InstanceTreeBuilder.Build(null, null, filter: null)); Assert.Empty(InstanceTreeBuilder.Build([], [], filter: null)); Assert.Empty(InstanceTreeBuilder.Build(null, [], filter: "anything")); var orphanInstances = InstanceTreeBuilder.Build(null, [MakeInstance(1, "Solo")], filter: null); Assert.Equal("i:1", Assert.Single(orphanInstances).Key); var areasOnly = InstanceTreeBuilder.Build([MakeArea(1, "Line1")], null, filter: null); Assert.Equal("a:1", Assert.Single(areasOnly).Key); } [Fact] public void Build_AreaWithNoInstancesAndNoFilter_IsStillShown() { var empty = MakeArea(1, "Empty"); var roots = InstanceTreeBuilder.Build([empty], [], filter: null); var root = Assert.Single(roots); Assert.Equal("a:1", root.Key); Assert.False(root.HasChildren); } }