using ZB.MOM.WW.GalaxyRepository; using ZB.MOM.WW.GalaxyRepository.Grpc; namespace ZB.MOM.WW.GalaxyRepository.Tests; /// /// GalaxyRepository-001 (scadaproj#41): the per-entry filter memo was unbounded, so a client /// issuing distinct tag_name_glob values (*, **, ...) retained one full /// result list per glob until the next Galaxy deploy. The memo must stay capped. /// public sealed class ProjectionMemoBoundTests { private const int DistinctFilters = 500; [Fact] public void DiscoverHierarchy_memo_is_bounded_under_distinct_client_globs() { GalaxyHierarchyCacheEntry entry = CreateEntry(); for (int i = 1; i <= DistinctFilters; i++) { GalaxyHierarchyProjector.Project(entry, new DiscoverHierarchyRequest { TagNameGlob = new string('*', i) }, null, 0, 1); } Assert.InRange(GalaxyHierarchyProjector.MemoCount(entry), 1, ProjectionMemo.Capacity); } [Fact] public void BrowseChildren_memo_is_bounded_under_distinct_client_globs() { GalaxyHierarchyCacheEntry entry = CreateEntry(); for (int i = 1; i <= DistinctFilters; i++) { GalaxyBrowseProjector.ProjectChildren(entry, new BrowseChildrenRequest { TagNameGlob = new string('*', i) }, null, 0, 1); } Assert.InRange(GalaxyBrowseProjector.MemoCount(entry), 1, ProjectionMemo.Capacity); } [Fact] public void Evicted_signature_is_recomputed_with_the_same_answer() { GalaxyHierarchyCacheEntry entry = CreateEntry(); DiscoverHierarchyRequest first = new() { TagNameGlob = "Plant.*" }; GalaxyHierarchyQueryResult before = GalaxyHierarchyProjector.Project(entry, first, null, 0, 10); for (int i = 1; i <= DistinctFilters; i++) { GalaxyHierarchyProjector.Project(entry, new DiscoverHierarchyRequest { TagNameGlob = new string('*', i) }, null, 0, 1); } GalaxyHierarchyQueryResult after = GalaxyHierarchyProjector.Project(entry, first, null, 0, 10); Assert.Equal(before.TotalObjectCount, after.TotalObjectCount); Assert.Equal(before.FilterSignature, after.FilterSignature); Assert.Equal(before.Objects.Select(o => o.GobjectId), after.Objects.Select(o => o.GobjectId)); } private static GalaxyHierarchyCacheEntry CreateEntry() { GalaxyObject[] objects = [ new() { GobjectId = 1, ParentGobjectId = 0, IsArea = true, ContainedName = "Plant", BrowseName = "Plant", TagName = "Plant" }, new() { GobjectId = 2, ParentGobjectId = 1, ContainedName = "Pump", BrowseName = "Pump", TagName = "Plant.Pump" }, new() { GobjectId = 3, ParentGobjectId = 1, ContainedName = "Valve", BrowseName = "Valve", TagName = "Plant.Valve" }, ]; return GalaxyHierarchyCacheEntry.Empty with { Status = GalaxyCacheStatus.Healthy, Sequence = 1, LastSuccessAt = DateTimeOffset.UtcNow, Objects = objects, Index = GalaxyHierarchyIndex.Build(objects), ObjectCount = objects.Length, }; } }