using Shouldly; using Xunit; namespace ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests; /// /// Gitea #676 — the driver connects to the endpoint URL the server ADVERTISES in GetEndpoints. A server behind NAT, /// a port map or a proxy advertises an address the client cannot reach (or one that bypasses the proxy), so the /// driver must rewrite host AND port to the configured URL when the scheme matches — what the SDK's own /// CoreClientUtils.SelectEndpoint does. /// public sealed class OpcUaClientEndpointRewriteTests { [Fact] public void Host_and_port_of_the_advertised_url_are_replaced_by_the_configured_ones() { OpcUaClientDriver.RewriteEndpointUrl("opc.tcp://internal-host:4840/OtOpcUa", "opc.tcp://10.1.2.3:54840/OtOpcUa") .ShouldBe("opc.tcp://10.1.2.3:54840/OtOpcUa"); } [Fact] public void The_advertised_path_is_kept() { // The path is the server's to choose (several endpoints may share a host:port); only the address is ours. OpcUaClientDriver.RewriteEndpointUrl("opc.tcp://plc-a:4840/Server/Ua", "opc.tcp://gateway:6000/") .ShouldBe("opc.tcp://gateway:6000/Server/Ua"); } [Fact] public void Only_the_port_differs_still_rewrites() { // The in-process stall proxy shape: same host, the configured port is the proxy's. OpcUaClientDriver.RewriteEndpointUrl("opc.tcp://127.0.0.1:4840/OtOpcUa", "opc.tcp://127.0.0.1:61000/OtOpcUa") .ShouldBe("opc.tcp://127.0.0.1:61000/OtOpcUa"); } [Fact] public void A_configured_url_without_a_port_keeps_the_advertised_port() { // Review of 3b88056f: Uri.Port is -1 for a port-less opc.tcp URL; copying it would drop the advertised port and // the SDK would fall back to 4840. OpcUaClientDriver.RewriteEndpointUrl("opc.tcp://internal-host:62541/Server", "opc.tcp://gateway/Server") .ShouldBe("opc.tcp://gateway:62541/Server"); } [Fact] public void An_IPv6_configured_host_is_bracketed() { OpcUaClientDriver.RewriteEndpointUrl("opc.tcp://internal-host:4840/OtOpcUa", "opc.tcp://[fd00::1]:54840/OtOpcUa") .ShouldBe("opc.tcp://[fd00::1]:54840/OtOpcUa"); } [Fact] public void A_different_scheme_is_left_alone() { // An https endpoint advertised next to an opc.tcp one is a different transport, not a NAT artefact. OpcUaClientDriver.RewriteEndpointUrl("https://srv:443/ua", "opc.tcp://srv:4840/ua") .ShouldBe("https://srv:443/ua"); } [Theory] [InlineData(null)] [InlineData("")] [InlineData("not a url")] public void An_unusable_advertised_url_falls_back_to_the_configured_one(string? advertised) { OpcUaClientDriver.RewriteEndpointUrl(advertised, "opc.tcp://srv:4840/ua").ShouldBe("opc.tcp://srv:4840/ua"); } }