using Microsoft.Extensions.Options; using ZB.MOM.WW.MxGateway.Server.Configuration; namespace ZB.MOM.WW.MxGateway.Tests.Configuration; /// /// Covers the projection makes from bound /// onto the effective-configuration record the dashboard renders. /// /// /// The projection is hand-written member by member, so an option that exists and validates can /// still be invisible on the settings page — which is exactly what happened to the two /// dashboard-ACL options. These tests pin the dashboard-tag members to the options they come /// from; the redaction assertions pin the opposite invariant, that the two secret-bearing members /// are the only ones masked. /// public sealed class GatewayConfigurationProviderTests { /// The group → tag map reaches the projection with its groups, tags and ordering intact. [Fact] public void GetEffectiveConfiguration_CopiesGroupToTagMapping() { GatewayOptions options = new() { Dashboard = new DashboardOptions { GroupToTag = new Dictionary(StringComparer.OrdinalIgnoreCase) { // A multi-tag group is the interesting case: a projection that flattened the // array to its first element, or to a joined string, would still satisfy a // single-tag fixture. ["GwOps"] = ["team-a", "team-b"], ["GwViewers"] = ["team-a"], }, }, }; EffectiveDashboardConfiguration dashboard = Project(options).Dashboard; Assert.Equal(2, dashboard.GroupToTag.Count); Assert.Equal(["team-a", "team-b"], dashboard.GroupToTag["GwOps"]); Assert.Equal(["team-a"], dashboard.GroupToTag["GwViewers"]); } /// /// The map's case-insensitive lookup survives the projection. LDAP group names arrive in /// whatever case the directory returns them, so a projection that rebuilt the dictionary with /// the default ordinal comparer would silently stop matching. /// [Fact] public void GetEffectiveConfiguration_KeepsGroupToTagLookupCaseInsensitive() { GatewayOptions options = new() { Dashboard = new DashboardOptions { GroupToTag = new Dictionary(StringComparer.OrdinalIgnoreCase) { ["GwOps"] = ["team-a"], }, }, }; EffectiveDashboardConfiguration dashboard = Project(options).Dashboard; Assert.True(dashboard.GroupToTag.ContainsKey("gwops")); } /// /// An unconfigured map projects as empty rather than null — the settings page renders the row /// either way, and "none configured" is the operationally interesting answer. /// [Fact] public void GetEffectiveConfiguration_WhenNoTagsConfigured_ProjectsEmptyMapping() { EffectiveDashboardConfiguration dashboard = Project(new GatewayOptions()).Dashboard; Assert.Empty(dashboard.GroupToTag); Assert.Empty(dashboard.GroupToRole); } /// The untagged-session visibility policy is projected, defaulting to the fail-closed value. [Fact] public void GetEffectiveConfiguration_CopiesUntaggedSessionVisibility() { Assert.Equal( UntaggedSessionVisibility.AdminOnly, Project(new GatewayOptions()).Dashboard.UntaggedSessionVisibility); GatewayOptions widened = new() { Dashboard = new DashboardOptions { UntaggedSessionVisibility = UntaggedSessionVisibility.AllViewers, }, }; Assert.Equal( UntaggedSessionVisibility.AllViewers, Project(widened).Dashboard.UntaggedSessionVisibility); } /// The pre-existing dashboard members keep coming from the options they always did. [Fact] public void GetEffectiveConfiguration_CopiesTheOtherDashboardMembers() { GatewayOptions options = new() { Dashboard = new DashboardOptions { Enabled = false, AllowAnonymousLocalhost = false, SnapshotIntervalMilliseconds = 2_500, RecentFaultLimit = 7, RecentSessionLimit = 11, ShowTagValues = true, GroupToRole = new Dictionary(StringComparer.OrdinalIgnoreCase) { ["GwAdmins"] = "Administrator", }, }, }; EffectiveDashboardConfiguration dashboard = Project(options).Dashboard; Assert.False(dashboard.Enabled); Assert.False(dashboard.AllowAnonymousLocalhost); Assert.Equal(2_500, dashboard.SnapshotIntervalMilliseconds); Assert.Equal(7, dashboard.RecentFaultLimit); Assert.Equal(11, dashboard.RecentSessionLimit); Assert.True(dashboard.ShowTagValues); Assert.Equal("Administrator", dashboard.GroupToRole["GwAdmins"]); } /// /// The masking boundary. Only the pepper name and the LDAP bind password are redacted; the /// tag mapping is configuration, not a secret, so masking it would hide the very thing the /// settings page was extended to show. /// [Fact] public void GetEffectiveConfiguration_RedactsOnlyTheSecretBearingMembers() { GatewayOptions options = new() { Ldap = new LdapOptions { ServiceAccountPassword = "bind-password" }, Dashboard = new DashboardOptions { GroupToTag = new Dictionary(StringComparer.OrdinalIgnoreCase) { ["GwOps"] = ["team-a"], }, }, }; EffectiveGatewayConfiguration configuration = Project(options); Assert.Equal(GatewayConfigurationProvider.RedactedValue, configuration.Authentication.PepperSecretName); Assert.Equal(GatewayConfigurationProvider.RedactedValue, configuration.Ldap.ServiceAccountPassword); Assert.Equal(["team-a"], configuration.Dashboard.GroupToTag["GwOps"]); } private static EffectiveGatewayConfiguration Project(GatewayOptions options) => new GatewayConfigurationProvider(Options.Create(options)).GetEffectiveConfiguration(); }