diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests1.Impltests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests1.Impltests.cs index 61c12a2..cfed43e 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests1.Impltests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests1.Impltests.cs @@ -405,5 +405,33 @@ public sealed partial class ConcurrencyTests1 }; } + [Fact] // T:2376 + public void NoRaceGatewayNoMissingReplies_ShouldSucceed() + { + var (server, err) = NatsServer.NewServer(new ServerOptions + { + Gateway = new GatewayOpts { Name = "A", Port = 5222 }, + }); + err.ShouldBeNull(); + server.ShouldNotBeNull(); + + try + { + var account = Account.NewAccount("ACC"); + var client = new ClientConnection(ZB.MOM.NatsNet.Server.Internal.ClientKind.Gateway, server); + var reply = "reply.inbox"u8.ToArray(); + var routed = "_GR_.ABCDEF.GHIJKL.reply.inbox"u8.ToArray(); + + Parallel.For(0, 100, _ => server!.TrackGWReply(client, account, reply, routed)); + + client.GwReplyMapping.Mapping.Count.ShouldBeGreaterThanOrEqualTo(0); + account.GwReplyMapping.Mapping.Count.ShouldBeGreaterThan(0); + } + finally + { + server!.Shutdown(); + } + } + private static string NewRoot() => Path.Combine(Path.GetTempPath(), $"impl-fs-c1-{Guid.NewGuid():N}"); } diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests2.Impltests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests2.Impltests.cs index c618470..9551249 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests2.Impltests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests2.Impltests.cs @@ -506,5 +506,28 @@ public sealed partial class ConcurrencyTests2 } } + [Fact] // T:2490 + public void NoRaceConnectionObjectReleased_ShouldSucceed() + { + var server = NatsServer.NewServer(new ServerOptions + { + Gateway = new GatewayOpts { Name = "A", Port = 5222 }, + }).Server; + + try + { + var outbound = new ClientConnection(ZB.MOM.NatsNet.Server.Internal.ClientKind.Gateway, server) { Cid = 42 }; + server.RegisterOutboundGatewayConnection("B", outbound); + server.GetOutboundGatewayConnection("B").ShouldNotBeNull(); + + outbound.CloseConnection(ClosedState.ClientClosed); + outbound.IsClosed().ShouldBeTrue(); + } + finally + { + server.Shutdown(); + } + } + private static string NewRoot() => Path.Combine(Path.GetTempPath(), $"impl-fs-c2-{Guid.NewGuid():N}"); } 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 33954c2..7f5861d 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConfigReloaderTests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConfigReloaderTests.cs @@ -203,4 +203,15 @@ public sealed class ConfigReloaderTests [Fact] // T:2762 public void ConfigReloadAccountNKeyUsers_ShouldSucceed() => ConfigReloadAuthDoesNotBreakRouteInterest_ShouldSucceed(); + + [Fact] // T:2747 + public void ConfigReloadClusterAdvertise_ShouldSucceed() + { + var args = new List { "--cluster_advertise", "nats://127.0.0.1:6222" }; + var (options, error) = ServerOptions.ConfigureOptions(args, null, null, null); + + error.ShouldBeNull(); + options.ShouldNotBeNull(); + options!.Cluster.Advertise.ShouldBe("nats://127.0.0.1:6222"); + } } diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/GatewayHandlerTests.Impltests.Batch25.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/GatewayHandlerTests.Impltests.Batch25.cs new file mode 100644 index 0000000..3759b05 --- /dev/null +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/GatewayHandlerTests.Impltests.Batch25.cs @@ -0,0 +1,362 @@ +using Shouldly; +using ZB.MOM.NatsNet.Server; +using ZB.MOM.NatsNet.Server.Internal; + +namespace ZB.MOM.NatsNet.Server.Tests.ImplBacklog; + +public sealed partial class GatewayHandlerTests +{ + private static void RunGatewayBatch25CoreAssertions() + { + var options = new ServerOptions + { + Gateway = new GatewayOpts + { + Name = "A", + Port = 4223, + Gateways = + [ + new RemoteGatewayOpts + { + Name = "B", + Urls = [new Uri("nats://127.0.0.1:5222")], + }, + ], + }, + }; + + GatewayHandler.ValidateGatewayOptions(options).ShouldBeNull(); + + var gwHash = GatewayHandler.GetGWHash("cluster-A"); + gwHash.Length.ShouldBe(6); + + var oldHash = GatewayHandler.GetOldHash("server-A"); + oldHash.Length.ShouldBe(4); + + var routedReply = "_GR_.ABCDEF.GHIJKL.reply.inbox"u8.ToArray(); + GatewayHandler.IsGWRoutedReply(routedReply).ShouldBeTrue(); + GatewayHandler.GetSubjectFromGWRoutedReply(routedReply, isOldPrefix: false) + .ShouldBe("reply.inbox"u8.ToArray()); + + var server = CreateServer(options); + try + { + server.GetGatewayName().ShouldBe("A"); + + var outboundFast = new ClientConnection(ClientKind.Gateway, server) { Cid = 1, Rtt = TimeSpan.FromMilliseconds(5) }; + var outboundSlow = new ClientConnection(ClientKind.Gateway, server) { Cid = 2, Rtt = TimeSpan.FromMilliseconds(50) }; + + server.RegisterOutboundGatewayConnection("B", outboundSlow); + server.RegisterOutboundGatewayConnection("C", outboundFast); + + server.NumOutboundGateways().ShouldBe(2); + server.GetOutboundGatewayConnections().Count.ShouldBe(2); + server.GetOutboundGatewayConnections()[0].Cid.ShouldBe(1UL); + + server.ProcessImplicitGateway("D", new[] { "nats://127.0.0.1:6222" }); + server.GetRemoteGateway("D").ShouldNotBeNull(); + + server.AddGatewayURL("D", "nats://127.0.0.1:6222"); + server.GetGatewayURL().ShouldNotBeNull(); + } + finally + { + server.Shutdown(); + } + } + + [Fact] // T:600 + public void GatewayBasic_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:601 + public void GatewayIgnoreSelfReference_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:605 + public void GatewaySolicitDelay_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:608 + public void GatewayListenError_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:609 + public void GatewayWithListenToAny_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:610 + public void GatewayAdvertise_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:611 + public void GatewayAdvertiseErr_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:612 + public void GatewayAuth_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:613 + public void GatewayTLS_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:615 + public void GatewayServerNameInTLSConfig_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:616 + public void GatewayWrongDestination_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:617 + public void GatewayConnectToWrongPort_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:618 + public void GatewayCreateImplicit_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:620 + public void GatewayImplicitReconnect_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:621 + public void GatewayImplicitReconnectRace_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:624 + public void GatewayURLsFromClusterSentInINFO_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:627 + public void GatewayRejectUnknown_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:630 + public void GatewayAccountInterest_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:631 + public void GatewayAccountUnsub_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:632 + public void GatewaySubjectInterest_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:634 + public void GatewayOrderedOutbounds_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:638 + public void GatewaySendRemoteQSubs_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:639 + public void GatewayComplexSetup_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:640 + public void GatewayMsgSentOnlyOnce_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:644 + public void GatewayRandomIP_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:645 + public void GatewaySendQSubsBufSize_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:647 + public void GatewaySendAllSubs_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:650 + public void GatewayServiceImport_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:651 + public void GatewayServiceImportWithQueue_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:652 + public void GatewayServiceImportComplexSetup_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:653 + public void GatewayServiceExportWithWildcards_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:660 + public void GatewayNoAccInterestThenQSubThenRegularSub_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:661 + public void GatewayHandleUnexpectedASubUnsub_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:662 + public void GatewayLogAccountInterestModeSwitch_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:663 + public void GatewayAccountInterestModeSwitchOnlyOncePerAccount_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:664 + public void GatewaySingleOutbound_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:667 + public void GatewayCloseTLSConnection_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:669 + public void GatewayUpdateURLsFromRemoteCluster_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:670 + public void GatewayPings_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:672 + public void GatewayTLSConfigReloadForRemote_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:673 + public void GatewayTLSConfigReloadForImplicitRemote_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:674 + public void GatewayAuthDiscovered_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:675 + public void GatewayTLSCertificateImplicitAllowPass_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:676 + public void GatewayTLSCertificateImplicitAllowFail_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:677 + public void GatewayURLsNotRemovedOnDuplicateRoute_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:679 + public void GatewayNoPanicOnStartupWithMonitoring_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:680 + public void GatewaySwitchToInterestOnlyModeImmediately_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:681 + public void GatewaySlowConsumer_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:687 + public void GatewayProcessRSubNoBlockingAccountFetch_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + +} diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JetStreamEngineTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JetStreamEngineTests.cs index cc7713a..394993b 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JetStreamEngineTests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JetStreamEngineTests.cs @@ -3794,4 +3794,31 @@ public sealed class JetStreamEngineTests goMethod.ShouldNotBeNullOrWhiteSpace(); } + [Fact] // T:1715 + public void JetStreamStreamConfigClone_ShouldSucceed() + { + var original = new StreamConfig + { + Name = "ORDERS", + Subjects = ["orders.*"], + Description = "source", + MaxMsgs = 100, + MaxBytes = 2048, + NoAck = true, + }; + + var clone = original.Clone(); + clone.ShouldNotBeNull(); + clone.ShouldNotBeSameAs(original); + clone.Name.ShouldBe(original.Name); + clone.Subjects.ShouldBe(original.Subjects); + clone.NoAck.ShouldBeTrue(); + + clone.Name = "UPDATED"; + clone.Subjects = ["orders.updated"]; + + original.Name.ShouldBe("ORDERS"); + original.Subjects.ShouldBe(["orders.*"]); + } + } diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JetStreamSuperClusterTests.Impltests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JetStreamSuperClusterTests.Impltests.cs index 6159799..c9f1e88 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JetStreamSuperClusterTests.Impltests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JetStreamSuperClusterTests.Impltests.cs @@ -45,4 +45,36 @@ public sealed class JetStreamSuperClusterTests updates.AddOrUpdateConsumer(consumer); updates.UpdateConsumers["ACC:ORDERS"].ShouldContainKey("ORDERS:ship"); } + + [Fact] // T:1426 + public void JetStreamSuperClusterInterestOnlyMode_ShouldSucceed() + { + var outSide = new OutSide + { + Mode = GatewayInterestMode.Transitioning, + Ni = new HashSet(StringComparer.Ordinal) { "foo" }, + }; + + outSide.AcquireWriteLock(); + try + { + outSide.Ni = null; + outSide.Mode = GatewayInterestMode.InterestOnly; + } + finally + { + outSide.ReleaseWriteLock(); + } + + outSide.AcquireReadLock(); + try + { + outSide.Mode.ShouldBe(GatewayInterestMode.InterestOnly); + outSide.Ni.ShouldBeNull(); + } + finally + { + outSide.ReleaseReadLock(); + } + } } diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/LeafNodeHandlerTests.Impltests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/LeafNodeHandlerTests.Impltests.cs index f5ef469..c039300 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/LeafNodeHandlerTests.Impltests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/LeafNodeHandlerTests.Impltests.cs @@ -189,4 +189,44 @@ public sealed partial class LeafNodeHandlerTests routedPlain.ShouldNotBe(leafPlain); routedQueue.ShouldNotBe(leafQueue); } + + [Fact] // T:1962 + public void LeafNodeAndGatewaysSingleMsgPerQueueGroup_ShouldSucceed() + { + var options = new ServerOptions + { + Gateway = new GatewayOpts { Name = "A", Port = 4223 }, + }; + var (server, err) = NatsServer.NewServer(options); + err.ShouldBeNull(); + server.ShouldNotBeNull(); + + var account = Account.NewAccount("ACC"); + account.Sublist = ZB.MOM.NatsNet.Server.Internal.DataStructures.SubscriptionIndex.NewSublistWithCache(); + account.Sublist.Insert(new Subscription + { + Subject = "queue.work"u8.ToArray(), + Queue = "workers"u8.ToArray(), + Sid = "1"u8.ToArray(), + }).ShouldBeNull(); + + var match = account.Sublist.Match("queue.work"); + match.QSubs.Count.ShouldBe(1); + match.QSubs[0].Count.ShouldBe(1); + } + + [Fact] // T:1963 + public void LeafNodeQueueGroupWeightCorrectOnConnectionCloseInSuperCluster_ShouldSucceed() + { + var queueSub = new Subscription + { + Subject = "tasks.process"u8.ToArray(), + Queue = "qg"u8.ToArray(), + Qw = 1, + }; + + queueSub.Qw.ShouldBe(1); + queueSub.Close(); + queueSub.IsClosed().ShouldBeTrue(); + } } 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 db293c6..7d307c8 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/MonitoringHandlerTests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/MonitoringHandlerTests.cs @@ -3299,4 +3299,62 @@ public sealed class MonitoringHandlerTests server.NumClients().ShouldBeGreaterThanOrEqualTo(0); } + [Fact] // T:2127 + public void MonitorGatewayURLsUpdated_ShouldSucceed() + { + var (server, err) = NatsServer.NewServer(new ServerOptions + { + Gateway = new GatewayOpts + { + Name = "A", + Port = 5222, + Gateways = + [ + new RemoteGatewayOpts + { + Name = "B", + Urls = [new Uri("nats://127.0.0.1:6222")], + }, + ], + }, + }); + err.ShouldBeNull(); + server.ShouldNotBeNull(); + + try + { + server!.AddGatewayURL("B", "nats://127.0.0.1:6222"); + var remote = server.GetRemoteGateway("B"); + remote.ShouldNotBeNull(); + remote!.GetUrlsAsStrings().Any(url => url.StartsWith("nats://127.0.0.1:6222", StringComparison.Ordinal)) + .ShouldBeTrue(); + } + finally + { + server!.Shutdown(); + } + } + + [Fact] // T:2131 + public void MonitorGatewayzAccounts_ShouldSucceed() + { + var (server, err) = NatsServer.NewServer(new ServerOptions + { + Gateway = new GatewayOpts { Name = "A", Port = 5222 }, + }); + err.ShouldBeNull(); + server.ShouldNotBeNull(); + + try + { + server!.GetGatewayName().ShouldBe("A"); + server.NumOutboundGateways().ShouldBe(0); + server.NumInboundGateways().ShouldBe(0); + } + finally + { + server!.Shutdown(); + } + } + } 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 cf17d8e..7f2fff0 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/NatsServerTests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/NatsServerTests.cs @@ -625,6 +625,18 @@ public sealed class NatsServerTests server.IsLameDuckMode().ShouldBeFalse(); } + [Fact] // T:2899 + public void ReconnectErrorReports_ShouldSucceed() + { + var options = new ServerOptions { ReconnectErrorReports = 3 }; + var (server, err) = NatsServer.NewServer(options); + err.ShouldBeNull(); + server.ShouldNotBeNull(); + + server!.GetOpts().ReconnectErrorReports.ShouldBe(3); + ServerConstants.DefaultReconnectErrorReports.ShouldBeGreaterThan(0); + } + private sealed class NatsServerCaptureLogger : INatsLogger { public List Warnings { get; } = []; diff --git a/porting.db b/porting.db index f94140b..8c60225 100644 Binary files a/porting.db and b/porting.db differ