using ZB.MOM.WW.MxGateway.Contracts.Proto.Galaxy; using ZB.MOM.WW.MxGateway.Server.Galaxy; namespace ZB.MOM.WW.MxGateway.Tests.Galaxy; /// /// Coverage for — the parent→children /// index used by the lazy browse projector (Task 3). Verifies root grouping, nested /// parent→child linkage, corrupt self-parented row handling, and the areas-first /// ordering rule shared with DashboardBrowseTreeBuilder. /// public sealed class GalaxyHierarchyIndexTests { /// Verifies roots (ParentGobjectId == 0) bucket under sentinel key 0. [Fact] public void ChildrenByParent_RootsUnderSentinelZero() { GalaxyObject root1 = new() { GobjectId = 1, ParentGobjectId = 0, ContainedName = "r1" }; GalaxyObject root2 = new() { GobjectId = 2, ParentGobjectId = 0, ContainedName = "r2" }; GalaxyObject root3 = new() { GobjectId = 3, ParentGobjectId = 0, ContainedName = "r3" }; GalaxyHierarchyIndex index = GalaxyHierarchyIndex.Build([root1, root2, root3]); Assert.True(index.ChildrenByParent.TryGetValue(0, out IReadOnlyList? roots)); Assert.NotNull(roots); Assert.Equal(3, roots!.Count); Assert.Contains(roots, view => view.Object.GobjectId == 1); Assert.Contains(roots, view => view.Object.GobjectId == 2); Assert.Contains(roots, view => view.Object.GobjectId == 3); } /// Verifies a nested A→B→C chain links each parent to its single child bucket. [Fact] public void ChildrenByParent_NestedHierarchy_LinksParentToChildren() { GalaxyObject areaA = new() { GobjectId = 1, ParentGobjectId = 0, IsArea = true, ContainedName = "A" }; GalaxyObject objB = new() { GobjectId = 2, ParentGobjectId = 1, ContainedName = "B" }; GalaxyObject objC = new() { GobjectId = 3, ParentGobjectId = 2, ContainedName = "C" }; GalaxyHierarchyIndex index = GalaxyHierarchyIndex.Build([areaA, objB, objC]); Assert.True(index.ChildrenByParent.TryGetValue(0, out IReadOnlyList? underRoot)); Assert.NotNull(underRoot); Assert.Single(underRoot!); Assert.Equal(1, underRoot![0].Object.GobjectId); Assert.True(index.ChildrenByParent.TryGetValue(1, out IReadOnlyList? underA)); Assert.NotNull(underA); Assert.Single(underA!); Assert.Equal(2, underA![0].Object.GobjectId); Assert.True(index.ChildrenByParent.TryGetValue(2, out IReadOnlyList? underB)); Assert.NotNull(underB); Assert.Single(underB!); Assert.Equal(3, underB![0].Object.GobjectId); } /// Verifies a self-parented (corrupt) row appears under root, not under itself. [Fact] public void ChildrenByParent_SelfParentedObject_AppearsAsRoot() { GalaxyObject selfParented = new() { GobjectId = 5, ParentGobjectId = 5, ContainedName = "loop" }; GalaxyHierarchyIndex index = GalaxyHierarchyIndex.Build([selfParented]); Assert.True(index.ChildrenByParent.TryGetValue(0, out IReadOnlyList? roots)); Assert.NotNull(roots); Assert.Single(roots!); Assert.Equal(5, roots![0].Object.GobjectId); // The self-parented row must not appear as its own child — bucket either absent or empty. if (index.ChildrenByParent.TryGetValue(5, out IReadOnlyList? underSelf)) { Assert.Empty(underSelf!); } } /// Verifies is OrdinalIgnoreCase and supports O(1) lookups. [Fact] public void ObjectViewsByTagName_IsCaseInsensitive_AndLookupsAreO1() { GalaxyObject root = new() { GobjectId = 1, ParentGobjectId = 0, IsArea = true, ContainedName = "Plant", BrowseName = "Plant", TagName = "Plant" }; GalaxyObject mixer = new() { GobjectId = 2, ParentGobjectId = 1, ContainedName = "Mixer_001", BrowseName = "Mixer_001", TagName = "Plant.Mixer_001" }; GalaxyHierarchyIndex index = GalaxyHierarchyIndex.Build([root, mixer]); Assert.True(index.ObjectViewsByTagName.TryGetValue("Plant.Mixer_001", out GalaxyObjectView? exact)); Assert.NotNull(exact); Assert.Equal(2, exact!.Object.GobjectId); // Case-insensitive lookup must hit the same entry. Assert.True(index.ObjectViewsByTagName.TryGetValue("plant.mixer_001", out GalaxyObjectView? lower)); Assert.NotNull(lower); Assert.Same(exact, lower); Assert.False(index.ObjectViewsByTagName.ContainsKey("Plant.Missing")); } /// Verifies is OrdinalIgnoreCase. [Fact] public void ObjectViewsByContainedPath_IsCaseInsensitive() { GalaxyObject root = new() { GobjectId = 1, ParentGobjectId = 0, IsArea = true, ContainedName = "Plant", BrowseName = "Plant", TagName = "Plant" }; GalaxyObject lineA = new() { GobjectId = 2, ParentGobjectId = 1, IsArea = true, ContainedName = "Line_A", BrowseName = "Line_A", TagName = "Plant.Line_A" }; GalaxyHierarchyIndex index = GalaxyHierarchyIndex.Build([root, lineA]); Assert.True(index.ObjectViewsByContainedPath.TryGetValue("Plant/Line_A", out GalaxyObjectView? exact)); Assert.NotNull(exact); Assert.Equal(2, exact!.Object.GobjectId); Assert.True(index.ObjectViewsByContainedPath.TryGetValue("plant/line_a", out GalaxyObjectView? lower)); Assert.NotNull(lower); Assert.Same(exact, lower); Assert.False(index.ObjectViewsByContainedPath.ContainsKey("Plant/Missing")); } /// Verifies children sort areas-first, then by display name (case-insensitive). [Fact] public void ChildrenByParent_SortsAreasFirstThenByDisplayName() { GalaxyObject parent = new() { GobjectId = 1, ParentGobjectId = 0, IsArea = true, ContainedName = "Root" }; GalaxyObject zebraObj = new() { GobjectId = 10, ParentGobjectId = 1, IsArea = false, ContainedName = "zebra" }; GalaxyObject alphaArea = new() { GobjectId = 11, ParentGobjectId = 1, IsArea = true, ContainedName = "alpha" }; GalaxyObject betaArea = new() { GobjectId = 12, ParentGobjectId = 1, IsArea = true, ContainedName = "beta" }; GalaxyHierarchyIndex index = GalaxyHierarchyIndex.Build([parent, zebraObj, alphaArea, betaArea]); Assert.True(index.ChildrenByParent.TryGetValue(1, out IReadOnlyList? children)); Assert.NotNull(children); Assert.Equal(3, children!.Count); Assert.Equal(11, children[0].Object.GobjectId); Assert.Equal(12, children[1].Object.GobjectId); Assert.Equal(10, children[2].Object.GobjectId); } }