using ZB.MOM.WW.GalaxyRepository; using ZB.MOM.WW.GalaxyRepository.Grpc; using ZB.MOM.WW.MxGateway.Server.Dashboard; namespace ZB.MOM.WW.MxGateway.Tests.Gateway.Dashboard; /// /// Unit tests for , the host-side /// projection that turns a shared-library /// into the dashboard's . /// public sealed class DashboardGalaxySummaryProjectorTests { /// /// Verifies that a healthy entry maps status, copies timestamps/counts, and /// groups objects into template usage (ordered by instance count) and /// category counts with resolved names. /// [Fact] public void Project_HealthyEntry_MapsStatusCopiesCountsAndGroupsObjects() { DateTimeOffset queriedAt = new(2026, 6, 25, 10, 0, 0, TimeSpan.Zero); DateTimeOffset successAt = new(2026, 6, 25, 10, 0, 1, TimeSpan.Zero); DateTimeOffset deployAt = new(2026, 6, 24, 8, 30, 0, TimeSpan.Zero); // Two templates (PumpTemplate x2, ValveTemplate x1) and two categories // (10 = UserDefined x3, 13 = Area x1). GalaxyObject area = new() { GobjectId = 1, BrowseName = "AreaA", IsArea = true, CategoryId = 13, }; GalaxyObject pumpA = new() { GobjectId = 2, BrowseName = "Pump01", CategoryId = 10, }; pumpA.TemplateChain.Add("$PumpTemplate"); GalaxyObject pumpB = new() { GobjectId = 3, BrowseName = "Pump02", CategoryId = 10, }; pumpB.TemplateChain.Add("$PumpTemplate"); GalaxyObject valve = new() { GobjectId = 4, BrowseName = "Valve01", CategoryId = 10, }; valve.TemplateChain.Add("$ValveTemplate"); GalaxyObject[] objects = [area, pumpA, pumpB, valve]; GalaxyHierarchyCacheEntry entry = new( Status: GalaxyCacheStatus.Healthy, Sequence: 7, LastQueriedAt: queriedAt, LastSuccessAt: successAt, LastDeployTime: deployAt, LastError: null, Objects: objects, Index: GalaxyHierarchyIndex.Build(objects), ObjectCount: 4, AreaCount: 1, AttributeCount: 12, HistorizedAttributeCount: 5, AlarmAttributeCount: 2); DashboardGalaxySummary summary = DashboardGalaxySummaryProjector.Project(entry); Assert.Equal(DashboardGalaxyStatus.Healthy, summary.Status); Assert.Equal(queriedAt, summary.LastQueriedAt); Assert.Equal(successAt, summary.LastSuccessAt); Assert.Equal(deployAt, summary.LastDeployTime); Assert.Null(summary.LastError); Assert.Equal(4, summary.ObjectCount); Assert.Equal(1, summary.AreaCount); Assert.Equal(12, summary.AttributeCount); Assert.Equal(5, summary.HistorizedAttributeCount); Assert.Equal(2, summary.AlarmAttributeCount); // Templates ordered by instance count descending: PumpTemplate (2) before ValveTemplate (1). Assert.Equal(2, summary.TopTemplates.Count); Assert.Equal("$PumpTemplate", summary.TopTemplates[0].TemplateName); Assert.Equal(2, summary.TopTemplates[0].InstanceCount); Assert.Equal("$ValveTemplate", summary.TopTemplates[1].TemplateName); Assert.Equal(1, summary.TopTemplates[1].InstanceCount); // Categories ordered by object count descending: UserDefined (3) before Area (1). Assert.Equal(2, summary.ObjectCategories.Count); Assert.Equal(10, summary.ObjectCategories[0].CategoryId); Assert.Equal("UserDefined", summary.ObjectCategories[0].CategoryName); Assert.Equal(3, summary.ObjectCategories[0].ObjectCount); Assert.Equal(13, summary.ObjectCategories[1].CategoryId); Assert.Equal("Area", summary.ObjectCategories[1].CategoryName); Assert.Equal(1, summary.ObjectCategories[1].ObjectCount); } /// /// Verifies that an unknown entry with no objects projects to the /// -equivalent summary. /// [Fact] public void Project_UnknownEmptyEntry_ProducesUnknownEquivalentSummary() { GalaxyHierarchyCacheEntry entry = GalaxyHierarchyCacheEntry.Empty; DashboardGalaxySummary summary = DashboardGalaxySummaryProjector.Project(entry); Assert.Equal(DashboardGalaxyStatus.Unknown, summary.Status); Assert.Null(summary.LastQueriedAt); Assert.Null(summary.LastSuccessAt); Assert.Null(summary.LastDeployTime); Assert.Null(summary.LastError); Assert.Equal(0, summary.ObjectCount); Assert.Equal(0, summary.AreaCount); Assert.Equal(0, summary.AttributeCount); Assert.Equal(0, summary.HistorizedAttributeCount); Assert.Equal(0, summary.AlarmAttributeCount); Assert.Empty(summary.TopTemplates); Assert.Empty(summary.ObjectCategories); } /// /// Verifies that the cache status enum is faithfully mapped to the dashboard /// status enum across every defined value. /// [Theory] [InlineData(GalaxyCacheStatus.Healthy, DashboardGalaxyStatus.Healthy)] [InlineData(GalaxyCacheStatus.Stale, DashboardGalaxyStatus.Stale)] [InlineData(GalaxyCacheStatus.Unavailable, DashboardGalaxyStatus.Unavailable)] [InlineData(GalaxyCacheStatus.Unknown, DashboardGalaxyStatus.Unknown)] public void Project_MapsCacheStatusToDashboardStatus( GalaxyCacheStatus cacheStatus, DashboardGalaxyStatus expected) { GalaxyHierarchyCacheEntry entry = GalaxyHierarchyCacheEntry.Empty with { Status = cacheStatus }; DashboardGalaxySummary summary = DashboardGalaxySummaryProjector.Project(entry); Assert.Equal(expected, summary.Status); } }