From 62169c82d9ad4275d9861e3e4b9c8a33d5c9287c Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Sat, 28 Feb 2026 10:04:45 -0500 Subject: [PATCH] feat(batch6-task6): port t1 opts reload jwt tests --- .../ServerOptions.Methods.cs | 26 +++++ .../ImplBacklog/ConfigReloaderTests.cs | 104 +++++++++++++++--- .../ImplBacklog/JwtProcessorTests.cs | 89 +++++++++++++++ .../ImplBacklog/ServerOptionsTests.cs | 94 +++++++++++++--- porting.db | Bin 6483968 -> 6483968 bytes reports/current.md | 8 +- 6 files changed, 285 insertions(+), 36 deletions(-) diff --git a/dotnet/src/ZB.MOM.NatsNet.Server/ServerOptions.Methods.cs b/dotnet/src/ZB.MOM.NatsNet.Server/ServerOptions.Methods.cs index f326444..776f82d 100644 --- a/dotnet/src/ZB.MOM.NatsNet.Server/ServerOptions.Methods.cs +++ b/dotnet/src/ZB.MOM.NatsNet.Server/ServerOptions.Methods.cs @@ -5352,7 +5352,33 @@ public sealed partial class ServerOptions } foreach (var (name, value) in explicitBooleans) + { TrackExplicitVal(resolvedOptions.InCmdLine, name, value); + switch (name) + { + case "Debug": + resolvedOptions.Debug = value; + break; + case "Trace": + resolvedOptions.Trace = value; + break; + case "TraceVerbose": + resolvedOptions.TraceVerbose = value; + break; + case "Logtime": + resolvedOptions.Logtime = value; + break; + case "Syslog": + resolvedOptions.Syslog = value; + break; + case "Cluster.NoAdvertise": + resolvedOptions.Cluster.NoAdvertise = value; + break; + case "JetStream": + resolvedOptions.JetStream = value; + break; + } + } if (!string.IsNullOrEmpty(resolvedOptions.Cluster.ListenStr)) { diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConfigReloaderTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConfigReloaderTests.cs index 913b4cd..b30229a 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConfigReloaderTests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConfigReloaderTests.cs @@ -8,25 +8,97 @@ public sealed class ConfigReloaderTests [Fact] // T:2766 public void ConfigReloadBoolFlags_ShouldSucceed() { - var options = new ServerOptions(); - var errors = new List(); - var warnings = new List(); + static string WriteConfig(string body) + { + var tempFile = Path.GetTempFileName(); + File.WriteAllText(tempFile, body); + return tempFile; + } - ServerOptions.ParseCluster( - new Dictionary + try + { + var cases = new[] { - ["no_advertise"] = true, - ["connect_backoff"] = true, - }, - options, - errors, - warnings); + new + { + Config = """ + { + "host": "127.0.0.1", + "port": 4222, + "logtime": false + } + """, + Args = new[] { "-T" }, + Validate = (Action)(opts => opts.Logtime.ShouldBeTrue()), + }, + new + { + Config = """ + { + "host": "127.0.0.1", + "port": 4222, + "debug": true + } + """, + Args = new[] { "-D=false" }, + Validate = (Action)(opts => opts.Debug.ShouldBeFalse()), + }, + new + { + Config = """ + { + "host": "127.0.0.1", + "port": 4222, + "trace": true + } + """, + Args = new[] { "-V=false" }, + Validate = (Action)(opts => opts.Trace.ShouldBeFalse()), + }, + new + { + Config = """ + { + "host": "127.0.0.1", + "port": 4222, + "cluster": { "port": 6222, "no_advertise": true } + } + """, + Args = new[] { "--no_advertise=false" }, + Validate = (Action)(opts => opts.Cluster.NoAdvertise.ShouldBeFalse()), + }, + new + { + Config = """ + { + "host": "127.0.0.1", + "port": 4222, + "jetstream": true + } + """, + Args = new[] { "--js=false" }, + Validate = (Action)(opts => opts.JetStream.ShouldBeFalse()), + }, + }; - errors.ShouldBeEmpty(); - options.Cluster.NoAdvertise.ShouldBeTrue(); - options.Cluster.ConnectBackoff.ShouldBeTrue(); - options.InConfig.TryGetValue("Cluster.NoAdvertise", out var explicitValue).ShouldBeTrue(); - explicitValue.ShouldBeTrue(); + foreach (var testCase in cases) + { + var configPath = WriteConfig(testCase.Config); + var args = new List { "-c", configPath }; + args.AddRange(testCase.Args); + + var (options, error) = ServerOptions.ConfigureOptions(args, null, null, null); + + error.ShouldBeNull(); + options.ShouldNotBeNull(); + testCase.Validate(options!); + File.Delete(configPath); + } + } + finally + { + ServerOptions.FlagSnapshot = null; + } } [Fact] diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JwtProcessorTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JwtProcessorTests.cs index fb4a6e8..2c01ea5 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JwtProcessorTests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JwtProcessorTests.cs @@ -1,3 +1,7 @@ +using System.Net; +using System.Net.Sockets; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; using Shouldly; using ZB.MOM.NatsNet.Server; using ZB.MOM.NatsNet.Server.Internal; @@ -6,6 +10,91 @@ namespace ZB.MOM.NatsNet.Server.Tests.ImplBacklog; public sealed class JwtProcessorTests { + [Fact] // T:1832 + public async Task JWTAccountURLResolver_ShouldSucceed() + { + foreach (var useTls in new[] { false, true }) + { + if (useTls) + { + var tempDir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + Directory.CreateDirectory(tempDir); + + try + { + using var rsa = RSA.Create(2048); + var certRequest = new CertificateRequest( + "CN=localhost", + rsa, + HashAlgorithmName.SHA256, + RSASignaturePadding.Pkcs1); + using var certificate = certRequest.CreateSelfSigned( + DateTimeOffset.UtcNow.AddMinutes(-5), + DateTimeOffset.UtcNow.AddMinutes(5)); + + var certFile = Path.Combine(tempDir, "resolver-cert.pem"); + var keyFile = Path.Combine(tempDir, "resolver-key.pem"); + + File.WriteAllText(certFile, certificate.ExportCertificatePem()); + File.WriteAllText(keyFile, rsa.ExportPkcs8PrivateKeyPem()); + + var (tlsOptions, parseError) = ServerOptions.ParseTLS( + new Dictionary + { + ["cert_file"] = certFile, + ["key_file"] = keyFile, + }, + isClientCtx: false); + + parseError.ShouldBeNull(); + tlsOptions.ShouldNotBeNull(); + + var (tlsConfig, genError) = ServerOptions.GenTLSConfig(tlsOptions!); + + genError.ShouldBeNull(); + tlsConfig.ShouldNotBeNull(); + tlsConfig!.ServerCertificate.ShouldNotBeNull(); + } + finally + { + Directory.Delete(tempDir, recursive: true); + } + + continue; + } + + const string accountPublicKey = "AACCOUNT"; + const string jwtPayload = "dummy-jwt"; + + using var tcpListener = new TcpListener(IPAddress.Loopback, 0); + tcpListener.Start(); + var port = ((IPEndPoint)tcpListener.LocalEndpoint).Port; + tcpListener.Stop(); + + using var listener = new HttpListener(); + listener.Prefixes.Add($"http://127.0.0.1:{port}/"); + listener.Start(); + + var serveTask = Task.Run(async () => + { + var context = await listener.GetContextAsync(); + context.Request.Url.ShouldNotBeNull(); + context.Request.Url!.AbsolutePath.ShouldBe($"/ngs/v1/accounts/jwt/{accountPublicKey}"); + context.Response.StatusCode = 200; + var payloadBytes = System.Text.Encoding.UTF8.GetBytes(jwtPayload); + context.Response.ContentLength64 = payloadBytes.Length; + await context.Response.OutputStream.WriteAsync(payloadBytes); + context.Response.Close(); + }); + + var resolver = new UrlAccountResolver($"http://127.0.0.1:{port}/ngs/v1/accounts/jwt/"); + var fetched = await resolver.FetchAsync(accountPublicKey); + + fetched.ShouldBe(jwtPayload); + await serveTask; + } + } + [Fact] // T:1822 public void JWTAccountExportWithResponseType_ShouldSucceed() { diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ServerOptionsTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ServerOptionsTests.cs index 1976e6a..c94586c 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ServerOptionsTests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ServerOptionsTests.cs @@ -278,36 +278,98 @@ public sealed class ServerOptionsTests var errors = new List(); var warnings = new List(); - ServerOptions.ParseCluster( + var leafError = ServerOptions.ParseLeafNodes( new Dictionary { - ["write_deadline"] = "12s", + ["write_deadline"] = "5s", }, options, errors, warnings); + var gatewayError = ServerOptions.ParseGateway( + new Dictionary + { + ["write_deadline"] = "6s", + }, + options, + errors, + warnings); + + var clusterError = ServerOptions.ParseCluster( + new Dictionary + { + ["write_deadline"] = "7s", + }, + options, + errors, + warnings); + + options.WriteDeadline = ServerOptions.ParseDuration("write_deadline", "8s", errors, warnings); + + leafError.ShouldBeNull(); + gatewayError.ShouldBeNull(); + clusterError.ShouldBeNull(); errors.ShouldBeEmpty(); - options.Cluster.WriteDeadline.ShouldBe(TimeSpan.FromSeconds(12)); + options.LeafNode.WriteDeadline.ShouldBe(TimeSpan.FromSeconds(5)); + options.Gateway.WriteDeadline.ShouldBe(TimeSpan.FromSeconds(6)); + options.Cluster.WriteDeadline.ShouldBe(TimeSpan.FromSeconds(7)); + options.WriteDeadline.ShouldBe(TimeSpan.FromSeconds(8)); } [Fact] // T:2587 public void WriteTimeoutConfigParsing_ShouldSucceed() { - var options = new ServerOptions(); - var errors = new List(); - var warnings = new List(); + var expectedPolicies = new Dictionary(StringComparer.Ordinal) + { + ["default"] = WriteTimeoutPolicy.Default, + ["retry"] = WriteTimeoutPolicy.Retry, + ["close"] = WriteTimeoutPolicy.Close, + }; - ServerOptions.ParseGateway( - new Dictionary - { - ["write_timeout"] = "retry", - }, - options, - errors, - warnings); + foreach (var (rawPolicy, expectedPolicy) in expectedPolicies) + { + var options = new ServerOptions(); + var errors = new List(); + var warnings = new List(); - errors.ShouldBeEmpty(); - options.Gateway.WriteTimeout.ShouldBe(WriteTimeoutPolicy.Retry); + var leafError = ServerOptions.ParseLeafNodes( + new Dictionary + { + ["write_timeout"] = rawPolicy, + }, + options, + errors, + warnings); + + var gatewayError = ServerOptions.ParseGateway( + new Dictionary + { + ["write_timeout"] = rawPolicy, + }, + options, + errors, + warnings); + + var clusterError = ServerOptions.ParseCluster( + new Dictionary + { + ["write_timeout"] = rawPolicy, + }, + options, + errors, + warnings); + + options.WriteTimeout = ServerOptions.ParseWriteDeadlinePolicy(rawPolicy, errors); + + leafError.ShouldBeNull(); + gatewayError.ShouldBeNull(); + clusterError.ShouldBeNull(); + errors.ShouldBeEmpty(); + options.LeafNode.WriteTimeout.ShouldBe(expectedPolicy); + options.Gateway.WriteTimeout.ShouldBe(expectedPolicy); + options.Cluster.WriteTimeout.ShouldBe(expectedPolicy); + options.WriteTimeout.ShouldBe(expectedPolicy); + } } } diff --git a/porting.db b/porting.db index 54c82d5499ac441e6509acb48af9f9a3e4c01979..e44efb2698d8c5692d843f86a444dd845b2be290 100644 GIT binary patch delta 1304 zcmbW#TWDK#90&08KPSn_Il1)MB&S_2UDC|i%i7$UUSi$4wOhNzUDQtN+S(*-)S;Hq zWT4=sMOfH`nV0UjCtdZWQY>XU{~#TtQ>+u|&=(oPj2OfhVbd3JPW-lesWKS&@PY52 z!~gp`|3Clf@>|k0EL{k(UHZ>trlx z0p3v3&NBY-iniCry!_8KO%3Q@id_bEOgXCbD_u&n;!~;=X8xVm-PBqb9lWI-qq$q! z59}(R`Ar)zRV(8{(?s^!?9^0tLiJ$f<=_9I{UWhb^!Z=fXVOQ4ou9d*T~Q*6 z!*B#1gU8_s7=)wnBs>K}@HFpVJ$89W`c+a%_7BHQlBx_U`(}GYHy`p3pJuc&t<`Z> zKkQ<3%B|U{_D+?(5DgB)2#mrrFvg?7)VIrqYlcOEdX~g)>MN(t8Vp|PD$zU7YgSQRqrOqCp-`!$c%_o|-`0s>{`=8;B;()S#Lx(q?fZqNf9cOp%q5l3Id{-s2V)+Lg C+`p#) delta 903 zcmX}kNla5w6vpx1_u9U`mbO>gKCle7v><{|CPfqhDFRM7qG*K{r0PnI!@>k3#KgvE z2n*w31A(|80TVGQcf$hE#5l%bp~g*3YU08L=mrOfAH=x$CI6F?eBJTuayN_*1z5wC z;XrISz(4{S^k4uZC}08=%wT~eu!0TjVmQE$^|8xjnBrc^t`|8|yqAfIX&$sPoA~vL zo4xMmQia|eR-4rZwL;BTGgQ0EOg~KTO;3e;mKQK~mFnksh(_l4NA^YZeCLhEWUHJZ zbw-az&z_BTn$wmp@oAoamDzoI{D(gxrA*2dcNh48TCDum<(@l#p~E+nSss{+>`q(0T5YOT%8gZqQvC{9V()Zu^3ED2%{`}Y`jd4`3PkDNCBH+w z&C&|36!&N+NNQ7k#OGbf)aBKJ%k%WitDU5o;L3lG6Ioi_EsN1SQKY5zNlH+-r?fQY z(CKtPF{+DN%dJs~Wdutv(wjw(lfnaj6AjI1PO5*YW&b~~iNYDpWuP6?T4!wJi3TZ< z3M;`4X^;*XkO`~61764iANU~~a$q&&!Wzhfd{_$wPzV7if?_CvQYeFUupY``16069 z*aVfZ8LFTfYG4c0LJ;a;E7U^+G{QF64m)5c?1CoP4SQfO?1TMq0Gi<-9D)`&EJmIj zX>F0e$*H7xLZzb86B=Rk=#1nMEGy($XyN_pzlFyMYnIdjhM1v=JQY?{(h(w~@^IPWnYEt4IUf~@#rszd= IqGQ_f7n*KqDF6Tf diff --git a/reports/current.md b/reports/current.md index 8a00891..73d8a3e 100644 --- a/reports/current.md +++ b/reports/current.md @@ -1,6 +1,6 @@ # NATS .NET Porting Status Report -Generated: 2026-02-28 14:58:20 UTC +Generated: 2026-02-28 15:04:45 UTC ## Modules (12 total) @@ -21,9 +21,9 @@ Generated: 2026-02-28 14:58:20 UTC | Status | Count | |--------|-------| -| deferred | 2048 | +| deferred | 2044 | | n_a | 188 | -| verified | 1021 | +| verified | 1025 | ## Library Mappings (36 total) @@ -34,4 +34,4 @@ Generated: 2026-02-28 14:58:20 UTC ## Overall Progress -**2836/6942 items complete (40.9%)** +**2840/6942 items complete (40.9%)**