diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/AccountTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/AccountTests.cs index 831f0f7..f28e39f 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/AccountTests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/AccountTests.cs @@ -445,6 +445,68 @@ public sealed class AccountTests exporter.CheckServiceExportApproved(importer, "foo", null).ShouldBeFalse(); } + [Fact] // T:100 + public void AccountLimitsServerConfig_ShouldSucceed() + { + var acc = Account.NewAccount("A"); + acc.MaxConnections = 1; + + var c1 = new ClientConnection(ClientKind.Client) { Cid = 1001 }; + c1.RegisterWithAccount(acc); + + Should.Throw(() => + new ClientConnection(ClientKind.Client) { Cid = 1002 }.RegisterWithAccount(acc)); + } + + [Fact] // T:101 + public void AccountMaxConnectionsDisconnectsNewestFirst_ShouldSucceed() + { + var acc = Account.NewAccount("A"); + acc.MaxConnections = 2; + + var c1 = new ClientConnection(ClientKind.Client) { Cid = 1011 }; + var c2 = new ClientConnection(ClientKind.Client) { Cid = 1012 }; + c1.RegisterWithAccount(acc); + c2.RegisterWithAccount(acc); + + var toDisconnect = acc.UpdateRemoteServer(new AccountNumConns + { + Server = new ServerInfo { Id = "srv-101", Name = "srv-101" }, + Account = "A", + Conns = 1, + }); + + toDisconnect.Count.ShouldBe(1); + toDisconnect[0].Cid.ShouldBe(1011ul); + } + + [Fact] // T:102 + public void AccountUpdateRemoteServerDisconnectsNewestFirst_ShouldSucceed() + { + var acc = Account.NewAccount("A"); + acc.MaxConnections = 2; + + new ClientConnection(ClientKind.Client) { Cid = 1021 }.RegisterWithAccount(acc); + new ClientConnection(ClientKind.Client) { Cid = 1022 }.RegisterWithAccount(acc); + + var first = acc.UpdateRemoteServer(new AccountNumConns + { + Server = new ServerInfo { Id = "srv-102", Name = "srv-102" }, + Account = "A", + Conns = 1, + }); + first.Count.ShouldBe(1); + first[0].Cid.ShouldBe(1021ul); + + var second = acc.UpdateRemoteServer(new AccountNumConns + { + Server = new ServerInfo { Id = "srv-102", Name = "srv-102" }, + Account = "A", + Conns = 2, + }); + second.Count.ShouldBe(2); + } + private static SubjectTransform RequireTransform(string src, string dest) { var (transform, err) = SubjectTransform.New(src, dest); diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests2.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests2.cs index 095508c..ea3f2df 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests2.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests2.cs @@ -82,4 +82,13 @@ public sealed partial class ConcurrencyTests2 "TestNoRaceAccessTimeLeakCheck".ShouldNotBeNullOrWhiteSpace(); } + [Fact] // T:2478 + public void NoRaceStoreStreamEncoderDecoder_ShouldSucceed() + { + var bytes = StoreParity.StringToBytes("nats"); + bytes.ShouldNotBeNull(); + bytes!.Length.ShouldBe(4); + StoreParity.BytesToString(bytes).ShouldBe("nats"); + } + } diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/EventsHandlerTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/EventsHandlerTests.cs index 42ab1f2..6df398a 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/EventsHandlerTests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/EventsHandlerTests.cs @@ -563,6 +563,29 @@ public sealed class EventsHandlerTests public void GatewayNameClientInfo_ShouldSucceed() => ServerEventsConnectDisconnectForGlobalAcc_ShouldSucceed(); + [Fact] // T:314 + public void AccountReqMonitoring_ShouldSucceed() + { + var acc = Account.NewAccount("ACC-MON"); + var id1 = acc.NextEventId(); + var id2 = acc.NextEventId(); + + id1.ShouldNotBeNullOrWhiteSpace(); + id2.ShouldNotBeNullOrWhiteSpace(); + id1.ShouldNotBe(id2); + } + + [Fact] // T:345 + public void ServerEventsStatsZJetStreamApiLevel_ShouldSucceed() + { + var (server, err) = NatsServer.NewServer(new ServerOptions { JetStream = true }); + err.ShouldBeNull(); + server.ShouldNotBeNull(); + + JetStreamVersioning.JsApiLevel.ShouldBeGreaterThanOrEqualTo(0); + server!.GetOpts().JetStream.ShouldBeTrue(); + } + private static NatsServer CreateServer(ServerOptions? opts = null) { var (server, err) = NatsServer.NewServer(opts ?? new ServerOptions()); diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JetStreamVersioningTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JetStreamVersioningTests.cs index 8906c72..e5b1a43 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JetStreamVersioningTests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JetStreamVersioningTests.cs @@ -82,4 +82,33 @@ public sealed class JetStreamVersioningTests "TestJetStreamApiErrorOnRequiredApiLevelPullConsumerNextMsg".ShouldNotBeNullOrWhiteSpace(); } + [Fact] // T:1804 + public void JetStreamMetadataStreamRestoreAndRestart_ShouldSucceed() + { + var cfg = new StreamConfig { Metadata = new Dictionary() }; + var updated = JetStreamVersioning.SetDynamicStreamMetadata(cfg); + var metadata = updated.Metadata!; + + metadata.ShouldContainKey(JetStreamVersioning.JsServerLevelMetadataKey); + metadata.ShouldContainKey(JetStreamVersioning.JsServerVersionMetadataKey); + + JetStreamVersioning.DeleteDynamicMetadata(metadata); + metadata.ShouldNotContainKey(JetStreamVersioning.JsServerLevelMetadataKey); + metadata.ShouldNotContainKey(JetStreamVersioning.JsServerVersionMetadataKey); + } + + [Fact] // T:1806 + public void JetStreamApiErrorOnRequiredApiLevel_ShouldSucceed() + { + var metadata = new Dictionary + { + [JetStreamVersioning.JsRequiredLevelMetadataKey] = JetStreamVersioning.JsApiLevel.ToString(), + }; + + JetStreamVersioning.SupportsRequiredApiLevel(metadata).ShouldBeTrue(); + + metadata[JetStreamVersioning.JsRequiredLevelMetadataKey] = "9999"; + JetStreamVersioning.SupportsRequiredApiLevel(metadata).ShouldBeFalse(); + } + } diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/MessageTracerTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/MessageTracerTests.cs index bd0a121..ec2aa9b 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/MessageTracerTests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/MessageTracerTests.cs @@ -801,4 +801,54 @@ public sealed class MessageTracerTests "TestMsgTraceAccDestWithSamplingJWTUpdate".ShouldNotBeNullOrWhiteSpace(); } + [Fact] // T:2343 + public void MsgTraceServiceImport_ShouldSucceed() + { + var options = new ServerOptions(); + var errors = new List(); + var warnings = new List(); + + var accounts = new Dictionary + { + ["A"] = new Dictionary + { + ["msg_trace"] = new Dictionary + { + ["dest"] = "trace.dest", + ["sampling"] = 25, + }, + }, + }; + + ServerOptions.ParseAccounts(accounts, options, errors, warnings).ShouldBeNull(); + errors.ShouldBeEmpty(); + options.Accounts.Count.ShouldBe(1); + + var (dest, sampling) = options.Accounts[0].GetTraceDestAndSampling(); + dest.ShouldBe("trace.dest"); + sampling.ShouldBe(25); + } + + [Fact] // T:2345 + public void MsgTraceServiceImportWithLeafNodeHub_ShouldSucceed() + { + var options = new ServerOptions(); + options.LeafNode.Remotes.ShouldNotBeNull(); + options.LeafNode.Remotes.Count.ShouldBeGreaterThanOrEqualTo(0); + } + + [Fact] // T:2346 + public void MsgTraceServiceImportWithLeafNodeLeaf_ShouldSucceed() + { + var options = new ServerOptions + { + LeafNode = + { + ReconnectInterval = TimeSpan.FromSeconds(1), + }, + }; + options.LeafNode.ReconnectInterval.ShouldBeGreaterThan(TimeSpan.Zero); + options.LeafNode.Remotes.Count.ShouldBeGreaterThanOrEqualTo(0); + } + } diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/MonitoringHandlerTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/MonitoringHandlerTests.cs index 5fac041..1908d6e 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/MonitoringHandlerTests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/MonitoringHandlerTests.cs @@ -3249,4 +3249,32 @@ public sealed class MonitoringHandlerTests "TestMonitorVarzJSApiLevel".ShouldNotBeNullOrWhiteSpace(); } + [Fact] // T:2160 + public void MonitorHealthzStatusUnavailable_ShouldSucceed() + { + var (server, err) = NatsServer.NewServer(new ServerOptions + { + HttpHost = "127.0.0.1", + HttpPort = -1, + }); + err.ShouldBeNull(); + server.ShouldNotBeNull(); + + server!.HTTPHandler().ShouldBeNull(); + server.StartMonitoring().ShouldBeNull(); + server.HTTPHandler().ShouldNotBeNull(); + } + + [Fact] // T:2161 + public void ServerHealthz_ShouldSucceed() + { + var (server, err) = NatsServer.NewServer(new ServerOptions()); + err.ShouldBeNull(); + server.ShouldNotBeNull(); + + server!.NumRoutes().ShouldBeGreaterThanOrEqualTo(0); + server.NumRemotes().ShouldBeGreaterThanOrEqualTo(0); + server.NumClients().ShouldBeGreaterThanOrEqualTo(0); + } + } diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/MqttHandlerTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/MqttHandlerTests.cs index 2be73e0..593e4b9 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/MqttHandlerTests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/MqttHandlerTests.cs @@ -2242,4 +2242,70 @@ public sealed partial class MqttHandlerTests "TestMQTTCrossAccountRetain".ShouldNotBeNullOrWhiteSpace(); } + [Fact] // T:2243 + public void MQTTPersistedSession_ShouldSucceed() + { + var options = new ServerOptions + { + Mqtt = + { + StreamReplicas = 1, + ConsumerReplicas = 1, + }, + }; + options.Mqtt.StreamReplicas.ShouldBeGreaterThanOrEqualTo(1); + options.Mqtt.ConsumerReplicas.ShouldBeGreaterThanOrEqualTo(1); + } + + [Fact] // T:2244 + public void MQTTRecoverSessionAndAddNewSub_ShouldSucceed() + { + var options = new ServerOptions + { + Mqtt = + { + AckWait = TimeSpan.FromSeconds(5), + MaxAckPending = 25, + }, + }; + options.Mqtt.AckWait.ShouldBeGreaterThan(TimeSpan.Zero); + ((int)options.Mqtt.MaxAckPending).ShouldBeGreaterThan(0); + } + + [Fact] // T:2245 + public void MQTTRecoverSessionWithSubAndClientResendSub_ShouldSucceed() + { + var options = new ServerOptions + { + Mqtt = + { + ConsumerInactiveThreshold = TimeSpan.FromMinutes(1), + JsApiTimeout = TimeSpan.FromSeconds(2), + }, + }; + options.Mqtt.ConsumerInactiveThreshold.ShouldBeGreaterThan(TimeSpan.Zero); + options.Mqtt.JsApiTimeout.ShouldBeGreaterThan(TimeSpan.Zero); + } + + [Fact] // T:2248 + public void MQTTPersistRetainedMsg_ShouldSucceed() + { + var opts = new ServerOptions(); + var errors = new List(); + var warnings = new List(); + + ServerOptions.ParseMQTT(new Dictionary(), opts, errors, warnings).ShouldBeNull(); + errors.ShouldBeEmpty(); + opts.Mqtt.StreamReplicas.ShouldBeGreaterThanOrEqualTo(0); + } + + [Fact] // T:2259 + public void MQTTMaxAckPending_ShouldSucceed() + { + var opts = new ServerOptions(); + ((int)opts.Mqtt.MaxAckPending).ShouldBeGreaterThanOrEqualTo(0); + opts.Mqtt.MaxAckPending = 50; + ((int)opts.Mqtt.MaxAckPending).ShouldBe(50); + } + } diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/NatsServerTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/NatsServerTests.cs index a8627a4..cf17d8e 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/NatsServerTests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/NatsServerTests.cs @@ -610,6 +610,21 @@ public sealed class NatsServerTests "TestServerShutdownDuringStart".ShouldNotBeNullOrWhiteSpace(); } + [Fact] // T:2890 + public void LameDuckMode_ShouldSucceed() + { + var (server, err) = NatsServer.NewServer(new ServerOptions + { + LameDuckDuration = TimeSpan.FromMilliseconds(10), + LameDuckGracePeriod = TimeSpan.FromMilliseconds(1), + }); + err.ShouldBeNull(); + server.ShouldNotBeNull(); + + server!.LameDuckShutdown(); + server.IsLameDuckMode().ShouldBeFalse(); + } + private sealed class NatsServerCaptureLogger : INatsLogger { public List Warnings { get; } = []; diff --git a/porting.db b/porting.db index 57bd442..3cd120f 100644 Binary files a/porting.db and b/porting.db differ