using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Driver.AbCip; namespace ZB.MOM.WW.OtOpcUa.Driver.AbCip.Tests; [Trait("Category", "Unit")] public sealed class AbCipHostAddressTests { [Theory] [InlineData("ab://10.0.0.5/1,0", "10.0.0.5", 44818, "1,0")] [InlineData("ab://10.0.0.5/1,4", "10.0.0.5", 44818, "1,4")] [InlineData("ab://10.0.0.5/1,2,2,192.168.50.20,1,0", "10.0.0.5", 44818, "1,2,2,192.168.50.20,1,0")] [InlineData("ab://10.0.0.5/", "10.0.0.5", 44818, "")] [InlineData("ab://plc-01.factory.internal/1,0", "plc-01.factory.internal", 44818, "1,0")] [InlineData("ab://10.0.0.5:44818/1,0", "10.0.0.5", 44818, "1,0")] [InlineData("ab://10.0.0.5:2222/1,0", "10.0.0.5", 2222, "1,0")] [InlineData("AB://10.0.0.5/1,0", "10.0.0.5", 44818, "1,0")] // case-insensitive scheme public void TryParse_accepts_valid_forms(string input, string gateway, int port, string cipPath) { var parsed = AbCipHostAddress.TryParse(input); parsed.ShouldNotBeNull(); parsed.Gateway.ShouldBe(gateway); parsed.Port.ShouldBe(port); parsed.CipPath.ShouldBe(cipPath); } [Theory] [InlineData(null)] [InlineData("")] [InlineData(" ")] [InlineData("http://10.0.0.5/1,0")] // wrong scheme [InlineData("ab:10.0.0.5/1,0")] // missing // [InlineData("ab://10.0.0.5")] // no path slash [InlineData("ab:///1,0")] // no gateway [InlineData("ab://10.0.0.5:0/1,0")] // invalid port [InlineData("ab://10.0.0.5:65536/1,0")] // port out of range [InlineData("ab://10.0.0.5:abc/1,0")] // non-numeric port public void TryParse_rejects_invalid_forms(string? input) { AbCipHostAddress.TryParse(input).ShouldBeNull(); } [Theory] [InlineData("10.0.0.5", 44818, "1,0", "ab://10.0.0.5/1,0")] [InlineData("10.0.0.5", 2222, "1,0", "ab://10.0.0.5:2222/1,0")] [InlineData("10.0.0.5", 44818, "", "ab://10.0.0.5/")] public void ToString_canonicalises(string gateway, int port, string path, string expected) { var addr = new AbCipHostAddress(gateway, port, path); addr.ToString().ShouldBe(expected); } [Fact] public void RoundTrip_is_stable() { const string input = "ab://plc-01:44818/1,2,2,10.0.0.10,1,0"; var parsed = AbCipHostAddress.TryParse(input)!; // Default port is stripped in canonical form; explicit 44818 → becomes default form. parsed.ToString().ShouldBe("ab://plc-01/1,2,2,10.0.0.10,1,0"); var parsedAgain = AbCipHostAddress.TryParse(parsed.ToString())!; parsedAgain.ShouldBe(parsed); } }