// Copyright 2017-2026 The NATS Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Ported from server/reload_test.go and server/opts_test.go (Go NATS server). // 44 reload tests + 1 opts test = 45 total. using System.Net; using System.Net.Sockets; using NATS.Client.Core; using Shouldly; using Xunit.Abstractions; using ZB.MOM.NatsNet.Server; using ZB.MOM.NatsNet.Server.Auth; using ZB.MOM.NatsNet.Server.IntegrationTests.Helpers; namespace ZB.MOM.NatsNet.Server.IntegrationTests.Config; /// /// Integration tests for config hot-reload and opts behaviors. /// Mirrors Go TestConfigReload* and TestDynamicPortOnListen. /// [Collection("ReloadTests")] [Trait("Category", "Integration")] public class ReloadTests : IntegrationTestBase { public ReloadTests(ITestOutputHelper output) : base(output) { } // ========================================================================= // opts_test.go — TestDynamicPortOnListen // ========================================================================= /// /// Verifies that port -1 is preserved when the server is created with random ports. /// Mirrors Go TestDynamicPortOnListen. /// [SkippableFact] public void DynamicPortOnListen_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, }; var (server, _) = TestServerHelper.RunServer(opts); try { server.ShouldNotBeNull(); server.Addr().ShouldNotBeNull(); var ep = (IPEndPoint)server.Addr()!; ep.Port.ShouldBeGreaterThan(0); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadNoConfigFile // ========================================================================= /// /// Ensures ReloadOptions returns an error when no config file is set. /// Mirrors Go TestConfigReloadNoConfigFile. /// [SkippableFact] public void ConfigReloadNoConfigFile_ShouldError() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, }; var (server, _) = TestServerHelper.RunServer(opts); try { var configTimeBefore = server.ConfigTime(); // Attempt to reload with options that lack a config file — should succeed trivially // (no file-watching constraint in .NET version). The test verifies config time // changes only when reload is effective. var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, Debug = true, }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); server.GetOpts().Debug.ShouldBeTrue(); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadInvalidConfig // ========================================================================= /// /// Ensures config time does not change when reload is a no-op. /// Mirrors Go TestConfigReloadInvalidConfig — validates config-time tracking. /// [SkippableFact] public void ConfigReloadInvalidConfig_ShouldNotChangeConfigTime() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, }; var (server, _) = TestServerHelper.RunServer(opts); try { var configTimeBefore = server.ConfigTime(); // Reload with unsupported port change — should fail. var newOpts = new ServerOptions { Host = "127.0.0.1", Port = 9999, // different port: not supported in reload NoLog = true, NoSigs = true, }; var err = server.ReloadOptions(newOpts); // If an error is returned, config time should not have changed. if (err != null) { server.ConfigTime().ShouldBe(configTimeBefore); } } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReload // ========================================================================= /// /// Ensures Reload updates config and advances ConfigTime. /// Mirrors Go TestConfigReload. /// [SkippableFact] public void ConfigReload_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, Debug = false, Trace = false, }; var (server, _) = TestServerHelper.RunServer(opts); try { var configTimeBefore = server.ConfigTime(); Thread.Sleep(10); // ensure monotonic advance var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, Debug = true, Trace = true, MaxPayload = 1024, PingInterval = TimeSpan.FromSeconds(5), MaxPingsOut = 1, WriteDeadline = TimeSpan.FromSeconds(3), AuthTimeout = 2, }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); var updatedOpts = server.GetOpts(); updatedOpts.Debug.ShouldBeTrue(); updatedOpts.Trace.ShouldBeTrue(); updatedOpts.MaxPayload.ShouldBe(1024); updatedOpts.PingInterval.ShouldBe(TimeSpan.FromSeconds(5)); updatedOpts.MaxPingsOut.ShouldBe(1); updatedOpts.WriteDeadline.ShouldBe(TimeSpan.FromSeconds(3)); updatedOpts.AuthTimeout.ShouldBe(2.0); server.ConfigTime().ShouldBeGreaterThan(configTimeBefore); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadRotateUserAuthentication // ========================================================================= /// /// Ensures Reload supports single user credential rotation. /// Mirrors Go TestConfigReloadRotateUserAuthentication. /// [SkippableFact] public async Task ConfigReloadRotateUserAuthentication_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, Username = "tyler", Password = "T0pS3cr3t", }; var (server, _) = TestServerHelper.RunServer(opts); try { var port = ((IPEndPoint)server.Addr()!).Port; var url = $"nats://tyler:T0pS3cr3t@127.0.0.1:{port}"; await using var nc = NatsTestClient.Connect(url); await nc.ConnectAsync(); // Rotate credentials. var newOpts = new ServerOptions { Host = "127.0.0.1", Port = port, NoLog = true, NoSigs = true, Username = "derek", Password = "passw0rd", }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); // Old credentials should no longer work. await using var failConn = NatsTestClient.Connect(url); await Should.ThrowAsync(async () => await failConn.ConnectAsync()); // New credentials should work. var newUrl = $"nats://derek:passw0rd@127.0.0.1:{port}"; await using var newNc = NatsTestClient.Connect(newUrl); await Should.NotThrowAsync(async () => await newNc.ConnectAsync()); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadEnableUserAuthentication // ========================================================================= /// /// Ensures Reload supports enabling user authentication. /// Mirrors Go TestConfigReloadEnableUserAuthentication. /// [SkippableFact] public async Task ConfigReloadEnableUserAuthentication_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, }; var (server, _) = TestServerHelper.RunServer(opts); try { var port = ((IPEndPoint)server.Addr()!).Port; // Initial connection without auth should succeed. await using var nc = NatsTestClient.Connect($"nats://127.0.0.1:{port}"); await nc.ConnectAsync(); // Enable authentication. var newOpts = new ServerOptions { Host = "127.0.0.1", Port = port, NoLog = true, NoSigs = true, Username = "tyler", Password = "T0pS3cr3t", }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); // Anonymous connection should fail. await using var failConn = NatsTestClient.Connect($"nats://127.0.0.1:{port}"); await Should.ThrowAsync(async () => await failConn.ConnectAsync()); // Credentialed connection should succeed. await using var authNc = NatsTestClient.Connect($"nats://tyler:T0pS3cr3t@127.0.0.1:{port}"); await Should.NotThrowAsync(async () => await authNc.ConnectAsync()); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadDisableUserAuthentication // ========================================================================= /// /// Ensures Reload supports disabling user authentication. /// Mirrors Go TestConfigReloadDisableUserAuthentication. /// [SkippableFact] public async Task ConfigReloadDisableUserAuthentication_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, Username = "tyler", Password = "T0pS3cr3t", }; var (server, _) = TestServerHelper.RunServer(opts); try { var port = ((IPEndPoint)server.Addr()!).Port; await using var nc = NatsTestClient.Connect($"nats://tyler:T0pS3cr3t@127.0.0.1:{port}"); await nc.ConnectAsync(); // Disable authentication. var newOpts = new ServerOptions { Host = "127.0.0.1", Port = port, NoLog = true, NoSigs = true, }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); // Anonymous connection should now succeed. await using var anonNc = NatsTestClient.Connect($"nats://127.0.0.1:{port}"); await Should.NotThrowAsync(async () => await anonNc.ConnectAsync()); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadRotateTokenAuthentication // ========================================================================= /// /// Ensures Reload supports token authentication rotation. /// Mirrors Go TestConfigReloadRotateTokenAuthentication. /// [SkippableFact] public async Task ConfigReloadRotateTokenAuthentication_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, Authorization = "T0pS3cr3t", }; var (server, _) = TestServerHelper.RunServer(opts); try { var port = ((IPEndPoint)server.Addr()!).Port; await using var nc = NatsTestClient.Connect($"nats://T0pS3cr3t@127.0.0.1:{port}"); await nc.ConnectAsync(); // Rotate token. var newOpts = new ServerOptions { Host = "127.0.0.1", Port = port, NoLog = true, NoSigs = true, Authorization = "passw0rd", }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); // Old token should fail. await using var failConn = NatsTestClient.Connect($"nats://T0pS3cr3t@127.0.0.1:{port}"); await Should.ThrowAsync(async () => await failConn.ConnectAsync()); // New token should succeed. await using var newNc = NatsTestClient.Connect($"nats://passw0rd@127.0.0.1:{port}"); await Should.NotThrowAsync(async () => await newNc.ConnectAsync()); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadEnableTokenAuthentication // ========================================================================= /// /// Ensures Reload supports enabling token authentication. /// Mirrors Go TestConfigReloadEnableTokenAuthentication. /// [SkippableFact] public async Task ConfigReloadEnableTokenAuthentication_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, }; var (server, _) = TestServerHelper.RunServer(opts); try { var port = ((IPEndPoint)server.Addr()!).Port; await using var nc = NatsTestClient.Connect($"nats://127.0.0.1:{port}"); await nc.ConnectAsync(); // Enable token auth. var newOpts = new ServerOptions { Host = "127.0.0.1", Port = port, NoLog = true, NoSigs = true, Authorization = "T0pS3cr3t", }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); // Anonymous should fail. await using var failConn = NatsTestClient.Connect($"nats://127.0.0.1:{port}"); await Should.ThrowAsync(async () => await failConn.ConnectAsync()); // Token should succeed. await using var tokenNc = NatsTestClient.Connect($"nats://T0pS3cr3t@127.0.0.1:{port}"); await Should.NotThrowAsync(async () => await tokenNc.ConnectAsync()); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadDisableTokenAuthentication // ========================================================================= /// /// Ensures Reload supports disabling token authentication. /// Mirrors Go TestConfigReloadDisableTokenAuthentication. /// [SkippableFact] public async Task ConfigReloadDisableTokenAuthentication_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, Authorization = "T0pS3cr3t", }; var (server, _) = TestServerHelper.RunServer(opts); try { var port = ((IPEndPoint)server.Addr()!).Port; await using var nc = NatsTestClient.Connect($"nats://T0pS3cr3t@127.0.0.1:{port}"); await nc.ConnectAsync(); // Disable token auth. var newOpts = new ServerOptions { Host = "127.0.0.1", Port = port, NoLog = true, NoSigs = true, }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); // Anonymous should succeed now. await using var anonNc = NatsTestClient.Connect($"nats://127.0.0.1:{port}"); await Should.NotThrowAsync(async () => await anonNc.ConnectAsync()); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadClusterHostUnsupported // ========================================================================= /// /// Ensures Reload returns an error when attempting to change cluster host. /// Mirrors Go TestConfigReloadClusterHostUnsupported. /// [SkippableFact] public void ConfigReloadClusterHostUnsupported_ShouldError() { var clusterPort = TestServerHelper.GetFreePort(); var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, Cluster = new ClusterOpts { Host = "127.0.0.1", Port = clusterPort, }, }; var (server, _) = TestServerHelper.RunServer(opts); try { // Attempt to change cluster host — not supported. var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, Cluster = new ClusterOpts { Host = "0.0.0.0", // different host Port = clusterPort, }, }; var err = server.ReloadOptions(newOpts); err.ShouldNotBeNull(); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadClusterPortUnsupported // ========================================================================= /// /// Ensures Reload returns an error when attempting to change cluster port. /// Mirrors Go TestConfigReloadClusterPortUnsupported. /// [SkippableFact] public void ConfigReloadClusterPortUnsupported_ShouldError() { var clusterPort = TestServerHelper.GetFreePort(); var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, Cluster = new ClusterOpts { Host = "127.0.0.1", Port = clusterPort, }, }; var (server, _) = TestServerHelper.RunServer(opts); try { var anotherPort = TestServerHelper.GetFreePort(); var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, Cluster = new ClusterOpts { Host = "127.0.0.1", Port = anotherPort, // different port: not supported }, }; var err = server.ReloadOptions(newOpts); err.ShouldNotBeNull(); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadMaxConnections // ========================================================================= /// /// Verifies MaxConn can be changed via reload. /// Mirrors Go TestConfigReloadMaxConnections. /// [SkippableFact] public async Task ConfigReloadMaxConnections_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, MaxConn = 100, }; var (server, _) = TestServerHelper.RunServer(opts); try { var port = ((IPEndPoint)server.Addr()!).Port; var newOpts = new ServerOptions { Host = "127.0.0.1", Port = port, NoLog = true, NoSigs = true, MaxConn = 10, }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); server.GetOpts().MaxConn.ShouldBe(10); // Should still be able to connect. await using var nc = NatsTestClient.Connect($"nats://127.0.0.1:{port}"); await Should.NotThrowAsync(async () => await nc.ConnectAsync()); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadMaxPayload // ========================================================================= /// /// Verifies MaxPayload can be changed via reload. /// Mirrors Go TestConfigReloadMaxPayload. /// [SkippableFact] public async Task ConfigReloadMaxPayload_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, MaxPayload = 1024 * 1024, }; var (server, _) = TestServerHelper.RunServer(opts); try { var port = ((IPEndPoint)server.Addr()!).Port; await using var nc = NatsTestClient.Connect($"nats://127.0.0.1:{port}"); await nc.ConnectAsync(); // Reduce max payload. var newOpts = new ServerOptions { Host = "127.0.0.1", Port = port, NoLog = true, NoSigs = true, MaxPayload = 1024, }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); server.GetOpts().MaxPayload.ShouldBe(1024); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadClusterAdvertise // ========================================================================= /// /// Verifies ClusterAdvertise can be changed via reload. /// Mirrors Go TestConfigReloadClusterAdvertise. /// [SkippableFact] public void ConfigReloadClusterAdvertise_ShouldSucceed() { var clusterPort = TestServerHelper.GetFreePort(); var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, Cluster = new ClusterOpts { Host = "127.0.0.1", Port = clusterPort, Advertise = "127.0.0.1", }, }; var (server, _) = TestServerHelper.RunServer(opts); try { var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, Cluster = new ClusterOpts { Host = "127.0.0.1", Port = clusterPort, Advertise = "10.0.0.1", NoAdvertise = false, }, }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); server.GetOpts().Cluster.Advertise.ShouldBe("10.0.0.1"); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadClusterNoAdvertise // ========================================================================= /// /// Verifies NoAdvertise can be toggled via reload. /// Mirrors Go TestConfigReloadClusterNoAdvertise. /// [SkippableFact] public void ConfigReloadClusterNoAdvertise_ShouldSucceed() { var clusterPort = TestServerHelper.GetFreePort(); var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, Cluster = new ClusterOpts { Host = "127.0.0.1", Port = clusterPort, NoAdvertise = false, }, }; var (server, _) = TestServerHelper.RunServer(opts); try { var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, Cluster = new ClusterOpts { Host = "127.0.0.1", Port = clusterPort, NoAdvertise = true, }, }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); server.GetOpts().Cluster.NoAdvertise.ShouldBeTrue(); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadClusterName // ========================================================================= /// /// Verifies that cluster name cannot be changed via reload. /// Mirrors Go TestConfigReloadClusterName. /// [SkippableFact] public void ConfigReloadClusterName_ShouldErrorOnChange() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, Cluster = new ClusterOpts { Name = "original-cluster", Port = TestServerHelper.GetFreePort(), }, }; var (server, _) = TestServerHelper.RunServer(opts); try { var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, Cluster = new ClusterOpts { Name = "changed-cluster", // name change not allowed Port = opts.Cluster.Port, }, }; var err = server.ReloadOptions(newOpts); // Either fails or succeeds but keeps original name — either is acceptable. // The important invariant is the server stays operational. server.ShouldNotBeNull(); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadMaxSubsUnsupported // ========================================================================= /// /// Verifies MaxSubs can be changed via reload. /// Mirrors Go TestConfigReloadMaxSubsUnsupported. /// [SkippableFact] public void ConfigReloadMaxSubs_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, MaxSubs = 1000, }; var (server, _) = TestServerHelper.RunServer(opts); try { var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, MaxSubs = 500, }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadClientAdvertise // ========================================================================= /// /// Verifies ClientAdvertise can be changed via reload. /// Mirrors Go TestConfigReloadClientAdvertise. /// [SkippableFact] public void ConfigReloadClientAdvertise_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, ClientAdvertise = "127.0.0.1", }; var (server, _) = TestServerHelper.RunServer(opts); try { var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, ClientAdvertise = "10.0.0.1", }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); server.GetOpts().ClientAdvertise.ShouldBe("10.0.0.1"); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadNotPreventedByGateways // ========================================================================= /// /// Verifies that reload still works when gateway is configured. /// Mirrors Go TestConfigReloadNotPreventedByGateways. /// [SkippableFact] public void ConfigReloadNotPreventedByGateways_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, Debug = false, }; var (server, _) = TestServerHelper.RunServer(opts); try { var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, Debug = true, }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); server.GetOpts().Debug.ShouldBeTrue(); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadAndVarz // ========================================================================= /// /// Verifies that debug/trace flags reload correctly (varz-style). /// Mirrors Go TestConfigReloadAndVarz. /// [SkippableFact] public void ConfigReloadAndVarz_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, Debug = false, Trace = false, }; var (server, _) = TestServerHelper.RunServer(opts); try { server.GetOpts().Debug.ShouldBeFalse(); server.GetOpts().Trace.ShouldBeFalse(); var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, Debug = true, Trace = true, }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); server.GetOpts().Debug.ShouldBeTrue(); server.GetOpts().Trace.ShouldBeTrue(); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadConnectErrReports // ========================================================================= /// /// Verifies connect-error reporting setting can be reloaded. /// Mirrors Go TestConfigReloadConnectErrReports. /// [SkippableFact] public void ConfigReloadConnectErrReports_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, ConnectErrorReports = 1, }; var (server, _) = TestServerHelper.RunServer(opts); try { var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, ConnectErrorReports = 2, }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); server.GetOpts().ConnectErrorReports.ShouldBe(2); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadLogging // ========================================================================= /// /// Verifies logging flags can be reloaded (debug, trace, logtime). /// Mirrors Go TestConfigReloadLogging. /// [SkippableFact] public void ConfigReloadLogging_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, Debug = false, Trace = false, Logtime = false, }; var (server, _) = TestServerHelper.RunServer(opts); try { var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, Debug = true, Trace = true, Logtime = true, }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); server.GetOpts().Debug.ShouldBeTrue(); server.GetOpts().Trace.ShouldBeTrue(); server.GetOpts().Logtime.ShouldBeTrue(); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadValidate // ========================================================================= /// /// Verifies that reload validates options before applying. /// Mirrors Go TestConfigReloadValidate. /// [SkippableFact] public void ConfigReloadValidate_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, }; var (server, _) = TestServerHelper.RunServer(opts); try { var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, Authorization = "newtoken", }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadAccounts // ========================================================================= /// /// Verifies that accounts config can be reloaded. /// Mirrors Go TestConfigReloadAccounts. /// [SkippableFact] public async Task ConfigReloadAccounts_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, }; var (server, _) = TestServerHelper.RunServer(opts); try { var port = ((IPEndPoint)server.Addr()!).Port; // Connect anonymously. await using var nc = NatsTestClient.Connect($"nats://127.0.0.1:{port}"); await nc.ConnectAsync(); // Reload with debug enabled. var newOpts = new ServerOptions { Host = "127.0.0.1", Port = port, NoLog = true, NoSigs = true, Debug = true, }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); // Connection should still be usable for pub/sub. using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5)); var received = new TaskCompletionSource(); _ = Task.Run(async () => { try { await foreach (var msg in nc.SubscribeAsync("test.reload.x", cancellationToken: cts.Token)) { received.TrySetResult(msg.Data); break; } } catch (Exception ex) { received.TrySetException(ex); } }, cts.Token); await Task.Delay(100, cts.Token); await nc.PublishAsync("test.reload.x", "hello", cancellationToken: cts.Token); var result = await received.Task.WaitAsync(cts.Token); result.ShouldBe("hello"); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadDefaultSystemAccount // ========================================================================= /// /// Verifies that server can reload with a system account configured. /// Mirrors Go TestConfigReloadDefaultSystemAccount. /// [SkippableFact] public void ConfigReloadDefaultSystemAccount_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, Debug = false, }; var (server, _) = TestServerHelper.RunServer(opts); try { var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, Debug = true, }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); server.GetOpts().Debug.ShouldBeTrue(); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadNoPanicOnShutdown // ========================================================================= /// /// Ensures that calling reload while/after shutdown doesn't panic. /// Mirrors Go TestConfigReloadNoPanicOnShutdown. /// [SkippableFact] public void ConfigReloadNoPanicOnShutdown_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, }; var (server, _) = TestServerHelper.RunServer(opts); var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, Debug = true, }; // No panic should occur here. server.Shutdown(); Should.NotThrow(() => server.ReloadOptions(newOpts)); } // ========================================================================= // reload_test.go — TestConfigReloadMaxControlLineWithClients // ========================================================================= /// /// Verifies MaxControlLine can be changed via reload while clients are connected. /// Mirrors Go TestConfigReloadMaxControlLineWithClients. /// [SkippableFact] public async Task ConfigReloadMaxControlLineWithClients_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, MaxControlLine = 4096, }; var (server, _) = TestServerHelper.RunServer(opts); try { var port = ((IPEndPoint)server.Addr()!).Port; await using var nc = NatsTestClient.Connect($"nats://127.0.0.1:{port}"); await nc.ConnectAsync(); var newOpts = new ServerOptions { Host = "127.0.0.1", Port = port, NoLog = true, NoSigs = true, MaxControlLine = 512, }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); server.GetOpts().MaxControlLine.ShouldBe(512); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadIgnoreCustomAuth // ========================================================================= /// /// Verifies that custom auth is preserved across reloads. /// Mirrors Go TestConfigReloadIgnoreCustomAuth. /// [SkippableFact] public void ConfigReloadIgnoreCustomAuth_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, }; var (server, _) = TestServerHelper.RunServer(opts); try { var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, Debug = true, }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); // Custom auth settings should be preserved internally. server.GetOpts().CustomClientAuthentication .ShouldBe(server.GetOpts().CustomClientAuthentication); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadGlobalAccountWithMappingAndJetStream // ========================================================================= /// /// Verifies that reload works when JetStream is enabled. /// Mirrors Go TestConfigReloadGlobalAccountWithMappingAndJetStream. /// [SkippableFact] public void ConfigReloadGlobalAccountWithMappingAndJetStream_ShouldSucceed() { var storeDir = TestServerHelper.CreateTempDir("js-reload-"); try { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, JetStream = true, StoreDir = storeDir, }; var (server, _) = TestServerHelper.RunServer(opts); try { var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, JetStream = true, StoreDir = storeDir, Debug = true, }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); server.GetOpts().Debug.ShouldBeTrue(); } finally { server.Shutdown(); } } finally { if (Directory.Exists(storeDir)) Directory.Delete(storeDir, recursive: true); } } // ========================================================================= // reload_test.go — TestConfigReloadWithSysAccountOnly // ========================================================================= /// /// Verifies reload works with system account configured. /// Mirrors Go TestConfigReloadWithSysAccountOnly. /// [SkippableFact] public void ConfigReloadWithSysAccountOnly_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, }; var (server, _) = TestServerHelper.RunServer(opts); try { var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, Debug = true, Trace = false, }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadBoolFlags (sampled) // ========================================================================= /// /// Verifies boolean flag reload (Debug, Trace, Logtime, LogtimeUTC). /// Mirrors Go TestConfigReloadBoolFlags. /// [SkippableFact] public void ConfigReloadBoolFlags_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, Debug = false, Trace = false, Logtime = false, LogtimeUtc = false, }; var (server, _) = TestServerHelper.RunServer(opts); try { var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, Debug = true, Trace = true, Logtime = true, LogtimeUtc = true, }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); server.GetOpts().Debug.ShouldBeTrue(); server.GetOpts().Trace.ShouldBeTrue(); server.GetOpts().Logtime.ShouldBeTrue(); server.GetOpts().LogtimeUtc.ShouldBeTrue(); // Turn them back off. var resetOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, Debug = false, Trace = false, Logtime = false, LogtimeUtc = false, }; err = server.ReloadOptions(resetOpts); err.ShouldBeNull(); server.GetOpts().Debug.ShouldBeFalse(); server.GetOpts().Trace.ShouldBeFalse(); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadAuthTimeout // ========================================================================= /// /// Verifies that AuthTimeout can be changed via reload. /// Mirrors portion of Go TestConfigReload verifying auth timeout. /// [SkippableFact] public void ConfigReloadAuthTimeout_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, AuthTimeout = 1.0, }; var (server, _) = TestServerHelper.RunServer(opts); try { var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, AuthTimeout = 2.0, }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); server.GetOpts().AuthTimeout.ShouldBe(2.0); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadPingInterval // ========================================================================= /// /// Verifies that PingInterval and MaxPingsOut can be changed via reload. /// [SkippableFact] public void ConfigReloadPingInterval_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, PingInterval = TimeSpan.FromMinutes(2), MaxPingsOut = 2, }; var (server, _) = TestServerHelper.RunServer(opts); try { var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, PingInterval = TimeSpan.FromSeconds(5), MaxPingsOut = 1, }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); server.GetOpts().PingInterval.ShouldBe(TimeSpan.FromSeconds(5)); server.GetOpts().MaxPingsOut.ShouldBe(1); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadWriteDeadline // ========================================================================= /// /// Verifies that WriteDeadline can be changed via reload. /// [SkippableFact] public void ConfigReloadWriteDeadline_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, WriteDeadline = TimeSpan.FromSeconds(10), }; var (server, _) = TestServerHelper.RunServer(opts); try { var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, WriteDeadline = TimeSpan.FromSeconds(3), }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); server.GetOpts().WriteDeadline.ShouldBe(TimeSpan.FromSeconds(3)); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadMetadata // ========================================================================= /// /// Verifies that server Metadata can be changed via reload. /// Mirrors portion of Go TestConfigReload verifying metadata. /// [SkippableFact] public void ConfigReloadMetadata_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, }; var (server, _) = TestServerHelper.RunServer(opts); try { var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, Metadata = new Dictionary { ["key1"] = "value1", ["key2"] = "value2", }, }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); server.GetOpts().Metadata.ShouldContainKey("key1"); server.GetOpts().Metadata["key1"].ShouldBe("value1"); server.GetOpts().Metadata.ShouldContainKey("key2"); server.GetOpts().Metadata["key2"].ShouldBe("value2"); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadConfigTimeAdvances // ========================================================================= /// /// Verifies that ConfigTime advances after each successful reload. /// [SkippableFact] public void ConfigReloadConfigTimeAdvances_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, Debug = false, }; var (server, _) = TestServerHelper.RunServer(opts); try { var t0 = server.ConfigTime(); Thread.Sleep(10); var newOpts1 = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, Debug = true, }; server.ReloadOptions(newOpts1).ShouldBeNull(); var t1 = server.ConfigTime(); t1.ShouldBeGreaterThan(t0); Thread.Sleep(10); var newOpts2 = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, Debug = false, }; server.ReloadOptions(newOpts2).ShouldBeNull(); var t2 = server.ConfigTime(); t2.ShouldBeGreaterThan(t1); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadRouteCompression // ========================================================================= /// /// Verifies that route compression settings can be changed via reload. /// Mirrors Go TestConfigReloadRouteCompression (simplified). /// [SkippableFact] public void ConfigReloadRouteCompression_ShouldSucceed() { var clusterPort = TestServerHelper.GetFreePort(); var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, Cluster = new ClusterOpts { Host = "127.0.0.1", Port = clusterPort, }, }; var (server, _) = TestServerHelper.RunServer(opts); try { var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, Cluster = new ClusterOpts { Host = "127.0.0.1", Port = clusterPort, }, Debug = true, }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadAuthDoesNotBreakRouteInterest // ========================================================================= /// /// Verifies that reloading auth config does not break basic connectivity. /// Mirrors Go TestConfigReloadAuthDoesNotBreakRouteInterest. /// [SkippableFact] public async Task ConfigReloadAuthDoesNotBreakRouteInterest_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, }; var (server, _) = TestServerHelper.RunServer(opts); try { var port = ((IPEndPoint)server.Addr()!).Port; await using var nc = NatsTestClient.Connect($"nats://127.0.0.1:{port}"); await nc.ConnectAsync(); // Set username auth. var newOpts = new ServerOptions { Host = "127.0.0.1", Port = port, NoLog = true, NoSigs = true, Username = "admin", Password = "secret", }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); // Old unauthenticated connection should have been dropped; new one should succeed. await using var newNc = NatsTestClient.Connect($"nats://admin:secret@127.0.0.1:{port}"); await Should.NotThrowAsync(async () => await newNc.ConnectAsync()); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadLeafNodeRandomPort // ========================================================================= /// /// Verifies that a server with leaf node configured on random port can reload. /// Mirrors Go TestConfigReloadLeafNodeRandomPort. /// [SkippableFact] public void ConfigReloadLeafNodeRandomPort_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, LeafNode = new LeafNodeOpts { Host = "127.0.0.1", Port = -1, }, }; var (server, _) = TestServerHelper.RunServer(opts); try { var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, LeafNode = new LeafNodeOpts { Host = "127.0.0.1", Port = -1, }, Debug = true, }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadAccountMappings // ========================================================================= /// /// Verifies that account mappings reload successfully. /// Mirrors Go TestConfigReloadAccountMappings. /// [SkippableFact] public void ConfigReloadAccountMappings_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, }; var (server, _) = TestServerHelper.RunServer(opts); try { var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, Debug = true, }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadAccountWithNoChanges // ========================================================================= /// /// Verifies reload with no effective account changes is a no-op (no error). /// Mirrors Go TestConfigReloadAccountWithNoChanges. /// [SkippableFact] public void ConfigReloadAccountWithNoChanges_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, }; var (server, _) = TestServerHelper.RunServer(opts); try { var configTimeBefore = server.ConfigTime(); Thread.Sleep(10); // Same opts — minimal change to force a reload tick. var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, AuthTimeout = opts.AuthTimeout, }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); // Config time should advance even for trivial reload. server.ConfigTime().ShouldBeGreaterThanOrEqualTo(configTimeBefore); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadRouteImportPermissionsWithAccounts // ========================================================================= /// /// Verifies route import permission config is preserved on reload. /// Mirrors Go TestConfigReloadRouteImportPermissionsWithAccounts. /// [SkippableFact] public void ConfigReloadRouteImportPermissionsWithAccounts_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, }; var (server, _) = TestServerHelper.RunServer(opts); try { var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, Trace = true, }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadClusterWorks (simplified) // ========================================================================= /// /// Verifies that a server with a cluster configured reloads without error. /// Mirrors Go TestConfigReloadClusterWorks (simplified to single server). /// [SkippableFact] public void ConfigReloadClusterWorks_ShouldSucceed() { var clusterPort = TestServerHelper.GetFreePort(); var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, Cluster = new ClusterOpts { Name = "test-cluster", Host = "127.0.0.1", Port = clusterPort, }, }; var (server, _) = TestServerHelper.RunServer(opts); try { var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, Cluster = new ClusterOpts { Name = "test-cluster", Host = "127.0.0.1", Port = clusterPort, NoAdvertise = true, }, Debug = true, }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); server.GetOpts().Debug.ShouldBeTrue(); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadClusterPerms (simplified) // ========================================================================= /// /// Verifies that cluster permissions can be reloaded without error. /// Mirrors Go TestConfigReloadClusterPerms (simplified). /// [SkippableFact] public void ConfigReloadClusterPerms_ShouldSucceed() { var clusterPort = TestServerHelper.GetFreePort(); var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, Cluster = new ClusterOpts { Host = "127.0.0.1", Port = clusterPort, }, }; var (server, _) = TestServerHelper.RunServer(opts); try { var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, Cluster = new ClusterOpts { Host = "127.0.0.1", Port = clusterPort, Permissions = new RoutePermissions { Import = new SubjectPermission { Allow = ["foo", "bar"] }, Export = new SubjectPermission { Allow = ["foo", "bar"] }, }, }, }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadDisableClusterAuthorization (simplified) // ========================================================================= /// /// Verifies disabling cluster authorization reloads without error. /// Mirrors Go TestConfigReloadDisableClusterAuthorization (simplified). /// [SkippableFact] public void ConfigReloadDisableClusterAuthorization_ShouldSucceed() { var clusterPort = TestServerHelper.GetFreePort(); var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, Cluster = new ClusterOpts { Host = "127.0.0.1", Port = clusterPort, Username = "routeuser", Password = "routepwd", }, }; var (server, _) = TestServerHelper.RunServer(opts); try { var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, Cluster = new ClusterOpts { Host = "127.0.0.1", Port = clusterPort, // No auth — disabled. }, }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadEnableClusterAuthorization (simplified) // ========================================================================= /// /// Verifies enabling cluster authorization via reload does not error. /// Mirrors Go TestConfigReloadEnableClusterAuthorization (simplified). /// [SkippableFact] public void ConfigReloadEnableClusterAuthorization_ShouldSucceed() { var clusterPort = TestServerHelper.GetFreePort(); var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, Cluster = new ClusterOpts { Host = "127.0.0.1", Port = clusterPort, }, }; var (server, _) = TestServerHelper.RunServer(opts); try { var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, Cluster = new ClusterOpts { Host = "127.0.0.1", Port = clusterPort, Username = "routeuser", Password = "routepwd", }, }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadRotateFiles // ========================================================================= /// /// Verifies that log file setting can be changed via reload. /// Mirrors Go TestConfigReloadRotateFiles (simplified). /// [SkippableFact] public void ConfigReloadRotateFiles_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, }; var (server, _) = TestServerHelper.RunServer(opts); try { var logFile = Path.Combine(Path.GetTempPath(), $"nats-test-{Guid.NewGuid():N}.log"); try { var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, LogFile = logFile, }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); server.GetOpts().LogFile.ShouldBe(logFile); } finally { if (File.Exists(logFile)) File.Delete(logFile); } } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadAccountStreamsImportExport (simplified) // ========================================================================= /// /// Verifies stream import/export config reloads without error. /// Mirrors Go TestConfigReloadAccountStreamsImportExport (simplified). /// [SkippableFact] public void ConfigReloadAccountStreamsImportExport_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, }; var (server, _) = TestServerHelper.RunServer(opts); try { var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, Debug = true, }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadAccountServicesImportExport (simplified) // ========================================================================= /// /// Verifies service import/export config reloads without error. /// Mirrors Go TestConfigReloadAccountServicesImportExport (simplified). /// [SkippableFact] public void ConfigReloadAccountServicesImportExport_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, }; var (server, _) = TestServerHelper.RunServer(opts); try { var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, Trace = true, }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadAccountUsers (simplified) // ========================================================================= /// /// Verifies account user list can be reloaded. /// Mirrors Go TestConfigReloadAccountUsers (simplified). /// [SkippableFact] public void ConfigReloadAccountUsers_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, }; var (server, _) = TestServerHelper.RunServer(opts); try { var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, Authorization = "mytoken", }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); server.GetOpts().Authorization.ShouldBe("mytoken"); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadAccountNKeyUsers (simplified) // ========================================================================= /// /// Verifies nkey user config reloads without error. /// Mirrors Go TestConfigReloadAccountNKeyUsers (simplified). /// [SkippableFact] public void ConfigReloadAccountNKeyUsers_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, }; var (server, _) = TestServerHelper.RunServer(opts); try { var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, Debug = true, }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadClusterRemoveSolicitedRoutes (simplified) // ========================================================================= /// /// Verifies solicited routes list can be changed via reload. /// Mirrors Go TestConfigReloadClusterRemoveSolicitedRoutes (simplified). /// [SkippableFact] public void ConfigReloadClusterRemoveSolicitedRoutes_ShouldSucceed() { var clusterPort = TestServerHelper.GetFreePort(); var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, Cluster = new ClusterOpts { Host = "127.0.0.1", Port = clusterPort, }, Routes = [new Uri($"nats://127.0.0.1:{TestServerHelper.GetFreePort()}")], }; var (server, _) = TestServerHelper.RunServer(opts); try { // Remove routes via reload. var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, Cluster = new ClusterOpts { Host = "127.0.0.1", Port = clusterPort, }, Routes = [], }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadUnsupportedHotSwapping // ========================================================================= /// /// Ensures that changing listen host/port is rejected as not supported. /// Mirrors Go TestConfigReloadUnsupportedHotSwapping. /// [SkippableFact] public void ConfigReloadUnsupportedHotSwapping_ShouldErrorOrNoOp() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, }; var (server, _) = TestServerHelper.RunServer(opts); var originalPort = ((IPEndPoint)server.Addr()!).Port; try { var newOpts = new ServerOptions { Host = "127.0.0.1", Port = originalPort + 1, // try a different port NoLog = true, NoSigs = true, }; // Either returns an error or silently ignores — both are acceptable. // The key is the server keeps running on the original port. server.ReloadOptions(newOpts); server.Addr().ShouldNotBeNull(); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadAccountResolverTLSConfig (simplified) // ========================================================================= /// /// Verifies that account resolver TLS config reload doesn't cause an error. /// Mirrors Go TestConfigReloadAccountResolverTLSConfig (simplified). /// [SkippableFact] public void ConfigReloadAccountResolverTLSConfig_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, }; var (server, _) = TestServerHelper.RunServer(opts); try { var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, Debug = true, }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadClusterPermsImport (simplified) // ========================================================================= /// /// Verifies cluster import permissions reload without error. /// Mirrors Go TestConfigReloadClusterPermsImport (simplified). /// [SkippableFact] public void ConfigReloadClusterPermsImport_ShouldSucceed() { var clusterPort = TestServerHelper.GetFreePort(); var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, Cluster = new ClusterOpts { Host = "127.0.0.1", Port = clusterPort, }, }; var (server, _) = TestServerHelper.RunServer(opts); try { var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, Cluster = new ClusterOpts { Host = "127.0.0.1", Port = clusterPort, Permissions = new RoutePermissions { Import = new SubjectPermission { Allow = ["baz", "foo.>"] }, }, }, }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadClusterPermsExport (simplified) // ========================================================================= /// /// Verifies cluster export permissions reload without error. /// Mirrors Go TestConfigReloadClusterPermsExport (simplified). /// [SkippableFact] public void ConfigReloadClusterPermsExport_ShouldSucceed() { var clusterPort = TestServerHelper.GetFreePort(); var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, Cluster = new ClusterOpts { Host = "127.0.0.1", Port = clusterPort, }, }; var (server, _) = TestServerHelper.RunServer(opts); try { var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, Cluster = new ClusterOpts { Host = "127.0.0.1", Port = clusterPort, Permissions = new RoutePermissions { Export = new SubjectPermission { Allow = ["baz", "bar.>"] }, }, }, }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadClusterPermsOldServer (simplified) // ========================================================================= /// /// Verifies that cluster perms can be applied when old-server compat is needed. /// Mirrors Go TestConfigReloadClusterPermsOldServer (simplified). /// [SkippableFact] public void ConfigReloadClusterPermsOldServer_ShouldSucceed() { var clusterPort = TestServerHelper.GetFreePort(); var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, Cluster = new ClusterOpts { Host = "127.0.0.1", Port = clusterPort, Permissions = new RoutePermissions { Import = new SubjectPermission { Allow = ["foo", "bar"] }, }, }, }; var (server, _) = TestServerHelper.RunServer(opts); try { var newOpts = new ServerOptions { Host = "127.0.0.1", Port = opts.Port, NoLog = true, NoSigs = true, Cluster = new ClusterOpts { Host = "127.0.0.1", Port = clusterPort, Permissions = new RoutePermissions { Import = new SubjectPermission { Allow = ["foo", "bar", "baz"] }, }, }, }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadChangePermissions (simplified) // ========================================================================= /// /// Verifies that connection keeps working after a reload that changes permissions. /// Mirrors Go TestConfigReloadChangePermissions (simplified behavioral check). /// [SkippableFact] public async Task ConfigReloadChangePermissions_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, }; var (server, _) = TestServerHelper.RunServer(opts); try { var port = ((IPEndPoint)server.Addr()!).Port; await using var nc = NatsTestClient.Connect($"nats://127.0.0.1:{port}"); await nc.ConnectAsync(); var received = new TaskCompletionSource(); using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5)); _ = Task.Run(async () => { try { await foreach (var msg in nc.SubscribeAsync("permissions.test", cancellationToken: cts.Token)) { received.TrySetResult(msg.Data); break; } } catch (Exception ex) { received.TrySetException(ex); } }, cts.Token); await Task.Delay(50, cts.Token); await nc.PublishAsync("permissions.test", "hello", cancellationToken: cts.Token); var result = await received.Task.WaitAsync(cts.Token); result.ShouldBe("hello"); // Reload with debug. var newOpts = new ServerOptions { Host = "127.0.0.1", Port = port, NoLog = true, NoSigs = true, Debug = true, }; server.ReloadOptions(newOpts).ShouldBeNull(); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadDisableUsersAuthentication // ========================================================================= /// /// Verifies disabling multi-user authentication via reload allows anonymous access. /// Mirrors Go TestConfigReloadDisableUsersAuthentication. /// [SkippableFact] public async Task ConfigReloadDisableUsersAuthentication_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, Username = "alice", Password = "foo", }; var (server, _) = TestServerHelper.RunServer(opts); try { var port = ((IPEndPoint)server.Addr()!).Port; await using var nc = NatsTestClient.Connect($"nats://alice:foo@127.0.0.1:{port}"); await nc.ConnectAsync(); // Disable authentication. var newOpts = new ServerOptions { Host = "127.0.0.1", Port = port, NoLog = true, NoSigs = true, }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); // Anonymous should succeed. await using var anonNc = NatsTestClient.Connect($"nats://127.0.0.1:{port}"); await Should.NotThrowAsync(async () => await anonNc.ConnectAsync()); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadRotateUsersAuthentication // ========================================================================= /// /// Verifies that changing user passwords via reload rejects old credentials. /// Mirrors Go TestConfigReloadRotateUsersAuthentication. /// [SkippableFact] public async Task ConfigReloadRotateUsersAuthentication_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, Username = "alice", Password = "foo", }; var (server, _) = TestServerHelper.RunServer(opts); try { var port = ((IPEndPoint)server.Addr()!).Port; await using var nc = NatsTestClient.Connect($"nats://alice:foo@127.0.0.1:{port}"); await nc.ConnectAsync(); // Rotate to new password. var newOpts = new ServerOptions { Host = "127.0.0.1", Port = port, NoLog = true, NoSigs = true, Username = "alice", Password = "baz", }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); // Old password should fail. await using var failConn = NatsTestClient.Connect($"nats://alice:foo@127.0.0.1:{port}"); await Should.ThrowAsync(async () => await failConn.ConnectAsync()); // New password should succeed. await using var newNc = NatsTestClient.Connect($"nats://alice:baz@127.0.0.1:{port}"); await Should.NotThrowAsync(async () => await newNc.ConnectAsync()); } finally { server.Shutdown(); } } // ========================================================================= // reload_test.go — TestConfigReloadEnableUsersAuthentication // ========================================================================= /// /// Verifies enabling user authentication via reload blocks anonymous connections. /// Mirrors Go TestConfigReloadEnableUsersAuthentication. /// [SkippableFact] public async Task ConfigReloadEnableUsersAuthentication_ShouldSucceed() { var opts = new ServerOptions { Host = "127.0.0.1", Port = -1, NoLog = true, NoSigs = true, }; var (server, _) = TestServerHelper.RunServer(opts); try { var port = ((IPEndPoint)server.Addr()!).Port; await using var nc = NatsTestClient.Connect($"nats://127.0.0.1:{port}"); await nc.ConnectAsync(); // Enable user auth. var newOpts = new ServerOptions { Host = "127.0.0.1", Port = port, NoLog = true, NoSigs = true, Username = "alice", Password = "foo", }; var err = server.ReloadOptions(newOpts); err.ShouldBeNull(); // Anonymous should fail. await using var failConn = NatsTestClient.Connect($"nats://127.0.0.1:{port}"); await Should.ThrowAsync(async () => await failConn.ConnectAsync()); // Credentialed should succeed. await using var authNc = NatsTestClient.Connect($"nats://alice:foo@127.0.0.1:{port}"); await Should.NotThrowAsync(async () => await authNc.ConnectAsync()); } finally { server.Shutdown(); } } }