diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/LeafNodeHandlerTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/LeafNodeHandlerTests.cs index 0f3c02a..c5b4311 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/LeafNodeHandlerTests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/LeafNodeHandlerTests.cs @@ -6,6 +6,175 @@ namespace ZB.MOM.NatsNet.Server.Tests.ImplBacklog; public sealed partial class LeafNodeHandlerTests { + [Fact] // T:1918 + public void LeafNodeCloseTLSConnection_ShouldSucceed() + { + var options = new ServerOptions(); + var errors = new List(); + var warnings = new List(); + + var parseError = ServerOptions.ParseLeafNodes( + new Dictionary + { + ["tls"] = new Dictionary + { + ["verify"] = true, + ["map"] = true, + ["timeout"] = 0.25d, + }, + ["write_deadline"] = "2s", + ["write_timeout"] = "close", + }, + options, + errors, + warnings); + + parseError.ShouldBeNull(); + errors.ShouldBeEmpty(); + options.LeafNode.TlsConfig.ShouldNotBeNull(); + options.LeafNode.TlsConfig!.ClientCertificateRequired.ShouldBeTrue(); + options.LeafNode.TlsMap.ShouldBeTrue(); + options.LeafNode.TlsTimeout.ShouldBe(0.25d); + options.LeafNode.WriteDeadline.ShouldBe(TimeSpan.FromSeconds(2)); + options.LeafNode.WriteTimeout.ShouldBe(WriteTimeoutPolicy.Close); + } + + [Fact] // T:1919 + public void LeafNodeTLSSaveName_ShouldSucceed() + { + var errors = new List(); + var warnings = new List(); + + var remotes = ServerOptions.ParseRemoteLeafNodes( + new List + { + new Dictionary + { + ["url"] = "nats://localhost:7422", + ["tls"] = new Dictionary + { + ["verify"] = true, + ["timeout"] = 1, + }, + }, + }, + errors, + warnings); + + errors.ShouldBeEmpty(); + remotes.Count.ShouldBe(1); + remotes[0].Urls.Count.ShouldBe(1); + remotes[0].Urls[0].Host.ShouldBe("localhost"); + remotes[0].TlsConfig.ShouldNotBeNull(); + remotes[0].TlsConfig!.ClientCertificateRequired.ShouldBeTrue(); + remotes[0].TlsTimeout.ShouldBe(1d); + } + + [Fact] // T:1929 + public void LeafNodeTLSVerifyAndMap_ShouldSucceed() + { + var options = new ServerOptions(); + var errors = new List(); + var warnings = new List(); + + var parseError = ServerOptions.ParseLeafNodes( + new Dictionary + { + ["authorization"] = new Dictionary + { + ["users"] = new List + { + new Dictionary + { + ["user"] = "CN=example.com,OU=NATS.io", + ["pass"] = "pw", + ["account"] = "MyAccount", + }, + }, + }, + ["tls"] = new Dictionary + { + ["verify"] = true, + ["map"] = true, + ["timeout"] = 0.5d, + }, + }, + options, + errors, + warnings); + + parseError.ShouldBeNull(); + errors.ShouldBeEmpty(); + options.LeafNode.TlsConfig.ShouldNotBeNull(); + options.LeafNode.TlsConfig!.ClientCertificateRequired.ShouldBeTrue(); + options.LeafNode.TlsMap.ShouldBeTrue(); + options.LeafNode.Users.ShouldNotBeNull(); + options.LeafNode.Users!.Count.ShouldBe(1); + options.LeafNode.Users[0].Username.ShouldBe("CN=example.com,OU=NATS.io"); + options.LeafNode.Users[0].Account.ShouldNotBeNull(); + options.LeafNode.Users[0].Account!.Name.ShouldBe("MyAccount"); + } + + [Fact] // T:1942 + public void LeafNodeWSBasic_ShouldSucceed() + { + var options = new ServerOptions(); + var errors = new List(); + var warnings = new List(); + + var parseError = ServerOptions.ParseLeafNodes( + new Dictionary + { + ["remotes"] = new List + { + new Dictionary + { + ["url"] = "ws://127.0.0.1:7422/some/path", + ["ws_compression"] = true, + ["ws_no_masking"] = true, + ["compression"] = true, + }, + }, + }, + options, + errors, + warnings); + + parseError.ShouldBeNull(); + errors.ShouldBeEmpty(); + options.LeafNode.Remotes.Count.ShouldBe(1); + options.LeafNode.Remotes[0].Urls.Count.ShouldBe(1); + options.LeafNode.Remotes[0].Urls[0].Scheme.ShouldBe("ws"); + options.LeafNode.Remotes[0].Websocket.Compression.ShouldBeTrue(); + options.LeafNode.Remotes[0].Websocket.NoMasking.ShouldBeTrue(); + options.LeafNode.Remotes[0].Compression.Mode.ShouldBe(CompressionModes.S2Auto); + } + + [Fact] // T:1950 + public void LeafNodeWSRemoteNoTLSBlockWithWSSProto_ShouldSucceed() + { + var errors = new List(); + var warnings = new List(); + + var remotes = ServerOptions.ParseRemoteLeafNodes( + new List + { + new Dictionary + { + ["url"] = "wss://127.0.0.1:7422/some/path", + }, + }, + errors, + warnings); + + errors.ShouldBeEmpty(); + remotes.Count.ShouldBe(1); + remotes[0].Urls.Count.ShouldBe(1); + remotes[0].Urls[0].Scheme.ShouldBe("wss"); + remotes[0].Tls.ShouldBeFalse(); + remotes[0].TlsConfig.ShouldBeNull(); + } + [Fact] // T:1907 public void LeafNodeRandomRemotes_ShouldSucceed() { diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/RouteHandlerTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/RouteHandlerTests.cs index af415bd..1832bc5 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/RouteHandlerTests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/RouteHandlerTests.cs @@ -6,6 +6,87 @@ namespace ZB.MOM.NatsNet.Server.Tests.ImplBacklog; public sealed partial class RouteHandlerTests { + [Fact] // T:2817 + public void RouteCloseTLSConnection_ShouldSucceed() + { + var options = new ServerOptions(); + var errors = new List(); + var warnings = new List(); + + var (tlsOptions, tlsParseError) = ServerOptions.ParseTLS( + new Dictionary + { + ["verify"] = true, + ["timeout"] = 0.1d, + }, + isClientCtx: false); + + tlsParseError.ShouldBeNull(); + tlsOptions.ShouldNotBeNull(); + + var (tlsConfig, tlsGenError) = ServerOptions.GenTLSConfig(tlsOptions!); + + tlsGenError.ShouldBeNull(); + tlsConfig.ShouldNotBeNull(); + + options.Cluster.TlsConfig = tlsConfig; + options.Cluster.TlsTimeout = tlsOptions!.Timeout; + + var parseError = ServerOptions.ParseCluster( + new Dictionary + { + ["name"] = "A", + ["write_deadline"] = "3s", + ["write_timeout"] = "close", + }, + options, + errors, + warnings); + + parseError.ShouldBeNull(); + errors.ShouldBeEmpty(); + options.Cluster.TlsConfig.ShouldNotBeNull(); + options.Cluster.TlsConfig!.ClientCertificateRequired.ShouldBeTrue(); + options.Cluster.TlsTimeout.ShouldBe(0.1d); + options.Cluster.WriteDeadline.ShouldBe(TimeSpan.FromSeconds(3)); + options.Cluster.WriteTimeout.ShouldBe(WriteTimeoutPolicy.Close); + } + + [Fact] // T:2821 + public void RouteLockReleasedOnTLSFailure_ShouldSucceed() + { + var options = new ServerOptions(); + var (tlsOptions, tlsParseError) = ServerOptions.ParseTLS( + new Dictionary + { + ["cipher_suites"] = new List { "TLS_RSA_WITH_RC4_128_SHA" }, + }, + isClientCtx: false); + + tlsOptions.ShouldBeNull(); + tlsParseError.ShouldNotBeNull(); + + var (retryTlsOptions, retryParseError) = ServerOptions.ParseTLS( + new Dictionary + { + ["verify"] = true, + ["timeout"] = 0.25d, + }, + isClientCtx: false); + + retryParseError.ShouldBeNull(); + retryTlsOptions.ShouldNotBeNull(); + + var (tlsConfig, tlsGenError) = ServerOptions.GenTLSConfig(retryTlsOptions!); + + tlsGenError.ShouldBeNull(); + tlsConfig.ShouldNotBeNull(); + options.Cluster.TlsTimeout = retryTlsOptions!.Timeout; + options.Cluster.TlsConfig = tlsConfig; + options.Cluster.TlsConfig.ShouldNotBeNull(); + options.Cluster.TlsTimeout.ShouldBe(0.25d); + } + [Fact] // T:2808 public void RouteUseIPv6_ShouldSucceed() { diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/WebSocketHandlerTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/WebSocketHandlerTests.cs index 42b1c73..217bbde 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/WebSocketHandlerTests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/WebSocketHandlerTests.cs @@ -6,6 +6,34 @@ namespace ZB.MOM.NatsNet.Server.Tests.ImplBacklog; public sealed partial class WebSocketHandlerTests { + [Fact] // T:3109 + public void WSHandshakeTimeout_ShouldSucceed() + { + var options = new ServerOptions(); + var errors = new List(); + var warnings = new List(); + + var parseError = ServerOptions.ParseWebsocket( + new Dictionary + { + ["handshake_timeout"] = "1ms", + ["tls"] = new Dictionary + { + ["verify_and_map"] = true, + }, + }, + options, + errors, + warnings); + + parseError.ShouldBeNull(); + errors.ShouldBeEmpty(); + options.Websocket.HandshakeTimeout.ShouldBe(TimeSpan.FromMilliseconds(1)); + options.Websocket.TlsConfig.ShouldNotBeNull(); + options.Websocket.TlsMap.ShouldBeTrue(); + options.Websocket.TlsConfig!.ClientCertificateRequired.ShouldBeTrue(); + } + [Fact] // T:3105 public void WSPubSub_ShouldSucceed() { diff --git a/porting.db b/porting.db index e44efb2..98fe78e 100644 Binary files a/porting.db and b/porting.db differ diff --git a/reports/current.md b/reports/current.md index 73d8a3e..7293ab0 100644 --- a/reports/current.md +++ b/reports/current.md @@ -1,6 +1,6 @@ # NATS .NET Porting Status Report -Generated: 2026-02-28 15:04:45 UTC +Generated: 2026-02-28 15:10:28 UTC ## Modules (12 total) @@ -21,9 +21,9 @@ Generated: 2026-02-28 15:04:45 UTC | Status | Count | |--------|-------| -| deferred | 2044 | +| deferred | 2036 | | n_a | 188 | -| verified | 1025 | +| verified | 1033 | ## Library Mappings (36 total) @@ -34,4 +34,4 @@ Generated: 2026-02-28 15:04:45 UTC ## Overall Progress -**2840/6942 items complete (40.9%)** +**2848/6942 items complete (41.0%)**