using System.Reflection; using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Admin.Security; namespace ZB.MOM.WW.OtOpcUa.Admin.Tests; /// /// Deterministic unit tests for the LDAP input-sanitization and DN-parsing helpers. Live LDAP /// bind against the GLAuth dev instance is covered by the admin-browser smoke path, not here, /// because unit runs must not depend on a running external service. /// [Trait("Category", "Unit")] public sealed class LdapAuthServiceTests { private static string EscapeLdapFilter(string input) => (string)typeof(LdapAuthService) .GetMethod("EscapeLdapFilter", BindingFlags.NonPublic | BindingFlags.Static)! .Invoke(null, [input])!; private static string ExtractFirstRdnValue(string dn) => (string)typeof(LdapAuthService) .GetMethod("ExtractFirstRdnValue", BindingFlags.NonPublic | BindingFlags.Static)! .Invoke(null, [dn])!; [Theory] [InlineData("alice", "alice")] [InlineData("a(b)c", "a\\28b\\29c")] [InlineData("wildcard*", "wildcard\\2a")] [InlineData("back\\slash", "back\\5cslash")] public void Escape_filter_replaces_control_chars(string input, string expected) { EscapeLdapFilter(input).ShouldBe(expected); } [Theory] [InlineData("ou=ReadOnly,ou=groups,dc=lmxopcua,dc=local", "ReadOnly")] [InlineData("cn=admin,dc=corp,dc=com", "admin")] [InlineData("ReadOnly", "ReadOnly")] // no '=' → pass through [InlineData("ou=OnlySegment", "OnlySegment")] public void Extract_first_RDN_strips_the_first_attribute_value(string dn, string expected) { ExtractFirstRdnValue(dn).ShouldBe(expected); } }