using System.Text; using Shouldly; using ZB.MOM.NatsNet.Server; using ZB.MOM.NatsNet.Server.Auth; using ZB.MOM.NatsNet.Server.Internal; using ZB.MOM.NatsNet.Server.Internal.DataStructures; namespace ZB.MOM.NatsNet.Server.Tests.ImplBacklog; public sealed class ConfigReloaderTests { [Fact] // T:2766 public void ConfigReloadBoolFlags_ShouldSucceed() { static string WriteConfig(string body) { var tempFile = Path.GetTempFileName(); File.WriteAllText(tempFile, body); return tempFile; } try { var cases = new[] { 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()), }, }; 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] public void ParseCluster_WithUnknownFieldAndStrictMode_ReturnsError() { ServerOptions.NoErrOnUnknownFields(false); var options = new ServerOptions(); var errors = new List(); ServerOptions.ParseCluster( new Dictionary { ["unknown_cluster_field"] = true, }, options, errors, warnings: null); errors.Count.ShouldBe(1); errors[0].Message.ShouldContain("unknown field"); } [Fact] public void ParseCluster_WithUnknownFieldAndRelaxedMode_IgnoresUnknownField() { ServerOptions.NoErrOnUnknownFields(true); try { var options = new ServerOptions(); var errors = new List(); ServerOptions.ParseCluster( new Dictionary { ["unknown_cluster_field"] = true, }, options, errors, warnings: null); errors.ShouldBeEmpty(); } finally { ServerOptions.NoErrOnUnknownFields(false); } } [Fact] // T:2774 public void ConfigReloadAuthDoesNotBreakRouteInterest_ShouldSucceed() { var (server, createError) = NatsServer.NewServer(new ServerOptions { NoLog = true, NoSigs = true, Accounts = [new Account { Name = "A" }], Users = [new User { Username = "u", Password = "p", Account = new Account { Name = "A" }, }], }); createError.ShouldBeNull(); server.ShouldNotBeNull(); try { var (account, lookupError) = server!.LookupAccount("A"); lookupError.ShouldBeNull(); account.ShouldNotBeNull(); account!.Sublist = SubscriptionIndex.NewSublistWithCache(); var insertError = account.Sublist.Insert(new Subscription { Subject = Encoding.ASCII.GetBytes("foo"), Queue = Encoding.ASCII.GetBytes("bar"), }); insertError.ShouldBeNull(); account.TotalSubs().ShouldBe(1); server.ReloadAuthorization(); var (updatedAccount, updatedLookupError) = server.LookupAccount("A"); updatedLookupError.ShouldBeNull(); updatedAccount.ShouldNotBeNull(); updatedAccount!.TotalSubs().ShouldBe(1); } finally { server!.Shutdown(); } } [Fact] // T:2762 public void ConfigReloadAccountNKeyUsers_ShouldSucceed() => ConfigReloadAuthDoesNotBreakRouteInterest_ShouldSucceed(); }