using Microsoft.Extensions.Configuration; using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Host.Configuration; using ZB.MOM.WW.OtOpcUa.Security.Ldap; using LdapTransport = ZB.MOM.WW.Auth.Abstractions.Ldap.LdapTransport; namespace ZB.MOM.WW.OtOpcUa.Host.IntegrationTests; /// /// Regression guard for the LDAP config-section fix. The real config (admin/driver/Development /// overlays) lives under Security:Ldap, and must point /// there so the configured DevStubMode actually binds. Previously the binders used the /// nonexistent "Ldap"/"Authentication:Ldap" sections, so the dev stub never activated. /// public sealed class LdapOptionsBindingTests { /// resolves to the real overlay section. [Fact] public void SectionName_is_Security_Ldap() { LdapOptions.SectionName.ShouldBe("Security:Ldap"); } /// /// Binding from reads the configured DevStubMode /// from the real Security:Ldap section — proving the dev stub now takes effect. /// [Fact] public void Binding_from_SectionName_reads_Security_Ldap_DevStubMode() { var configuration = new ConfigurationBuilder() .AddInMemoryCollection(new Dictionary { ["Security:Ldap:DevStubMode"] = "true", }) .Build(); var options = configuration.GetSection(LdapOptions.SectionName).Get(); options.ShouldNotBeNull(); options.DevStubMode.ShouldBeTrue(); } /// /// Negative control: binding from the old (nonexistent) "Ldap" section against the same /// Security:Ldap config does NOT pick up DevStubMode — it falls back to the C# /// default (false). This is the pre-fix behaviour the change corrects. /// [Fact] public void Binding_from_old_Ldap_section_does_not_read_DevStubMode() { var configuration = new ConfigurationBuilder() .AddInMemoryCollection(new Dictionary { ["Security:Ldap:DevStubMode"] = "true", }) .Build(); var options = configuration.GetSection("Ldap").Get() ?? new LdapOptions(); options.DevStubMode.ShouldBeFalse(); } } /// /// End-to-end guard for the shipped production overlays: binds each of the three prod overlay /// files' real Security:Ldap section (the same files the host loads at boot, copied into the /// test output via the Host project reference) and runs the the /// host wires via AddValidatedOptions. Proves each prod overlay declares a TLS transport and /// therefore PASSES startup validation — i.e. the host actually boots with these overlays after the /// insecure-transport guard was added. The Development overlay (DevStubMode) is verified to /// pass via the guard exemption. /// public sealed class ProdOverlayValidationTests { private static readonly LdapOptionsValidator Sut = new(); private static LdapOptions BindOverlay(string fileName) { var path = Path.Combine(AppContext.BaseDirectory, fileName); File.Exists(path).ShouldBeTrue($"overlay '{fileName}' should be copied to the test output"); var configuration = new ConfigurationBuilder() .AddJsonFile(path, optional: false, reloadOnChange: false) .Build(); return configuration.GetSection(LdapOptions.SectionName).Get() ?? new LdapOptions(); } [Theory] [InlineData("appsettings.admin.json")] [InlineData("appsettings.driver.json")] [InlineData("appsettings.admin-driver.json")] public void Prod_overlay_declares_ldaps_transport(string fileName) { var options = BindOverlay(fileName); options.DevStubMode.ShouldBeFalse(); options.Transport.ShouldBe(LdapTransport.Ldaps); } [Theory] [InlineData("appsettings.admin.json")] [InlineData("appsettings.driver.json")] [InlineData("appsettings.admin-driver.json")] public void Prod_overlay_passes_startup_validation(string fileName) { var options = BindOverlay(fileName); // Match the host: these overlays only set Security:Ldap fields, so backfill the required // Server/SearchBase/Port the way the base C# defaults do (LdapOptions defaults are valid), // then validate exactly as AddValidatedOptions would at boot. Sut.Validate(null, options).Succeeded.ShouldBeTrue(); } [Fact] public void Development_overlay_passes_startup_validation_via_devstub_exemption() { var options = BindOverlay("appsettings.Development.json"); options.DevStubMode.ShouldBeTrue(); Sut.Validate(null, options).Succeeded.ShouldBeTrue(); } }