From 615752cdc28a5d2a16b927308072964f31b75586 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Thu, 12 Mar 2026 15:31:58 -0400 Subject: [PATCH] refactor: extract NATS.Server.Clustering.Tests project Move 29 clustering/routing test files from NATS.Server.Tests to a dedicated NATS.Server.Clustering.Tests project. Update namespaces, replace private GetFreePort/ReadUntilAsync helpers with TestUtilities calls, and extract TestServerFactory/ClusterTestServer to TestUtilities to fix cross-project reference from JetStreamStartupTests. --- NatsDotNet.slnx | 1 + src/NATS.Server/NATS.Server.csproj | 1 + .../ImplicitDiscoveryTests.cs | 2 +- .../InterServerAccountProtocolTests.cs | 2 +- .../NATS.Server.Clustering.Tests.csproj | 27 +++++++++++++++ .../Route/RouteGoParityTests.cs | 2 +- .../RouteHandshakeTests.cs | 25 ++++++++++++++ .../RoutePoolTests.cs | 2 +- .../RouteRmsgForwardingTests.cs | 2 +- .../RouteSubscriptionPropagationTests.cs | 27 ++++----------- .../RouteWireSubscriptionProtocolTests.cs | 2 +- .../Routes/AccountRouteTests.cs | 2 +- .../Routes/ClusterSplitTests.cs | 2 +- .../Routes/NoPoolFallbackTests.cs | 2 +- .../Routes/PoolSizeNegotiationTests.cs | 2 +- .../Routes/RouteAccountScopedDeliveryTests.cs | 2 +- .../Routes/RouteAccountScopedTests.cs | 2 +- .../RouteBatchProtoParityBatch3Tests.cs | 2 +- .../Routes/RouteCompressionTests.cs | 2 +- .../Routes/RouteConfigTests.cs | 2 +- .../Routes/RouteConfigValidationTests.cs | 2 +- .../Routes/RouteConnectionTests.cs | 2 +- .../Routes/RouteForwardingTests.cs | 33 ++++++------------- .../Routes/RouteHashStorageTests.cs | 2 +- .../RouteInfoBroadcastParityBatch4Tests.cs | 18 ++-------- .../Routes/RouteInterestIdempotencyTests.cs | 2 +- .../Routes/RouteParityHelpersBatch1Tests.cs | 2 +- .../Routes/RoutePoolAccountTests.cs | 2 +- .../RouteRemoteSubCleanupParityBatch2Tests.cs | 2 +- .../Routes/RouteS2CompressionTests.cs | 2 +- .../Routes/RouteSubscriptionTests.cs | 19 ++--------- .../Routes/RouteTopologyGossipTests.cs | 2 +- .../TestServerFactory.cs} | 28 ++-------------- .../JetStreamStartupTests.cs | 1 + 34 files changed, 104 insertions(+), 124 deletions(-) rename tests/{NATS.Server.Tests => NATS.Server.Clustering.Tests}/ImplicitDiscoveryTests.cs (99%) rename tests/{NATS.Server.Tests => NATS.Server.Clustering.Tests}/InterServerAccountProtocolTests.cs (98%) create mode 100644 tests/NATS.Server.Clustering.Tests/NATS.Server.Clustering.Tests.csproj rename tests/{NATS.Server.Tests => NATS.Server.Clustering.Tests}/Route/RouteGoParityTests.cs (99%) create mode 100644 tests/NATS.Server.Clustering.Tests/RouteHandshakeTests.cs rename tests/{NATS.Server.Tests => NATS.Server.Clustering.Tests}/RoutePoolTests.cs (87%) rename tests/{NATS.Server.Tests => NATS.Server.Clustering.Tests}/RouteRmsgForwardingTests.cs (91%) rename tests/{NATS.Server.Tests => NATS.Server.Clustering.Tests}/RouteSubscriptionPropagationTests.cs (89%) rename tests/{NATS.Server.Tests => NATS.Server.Clustering.Tests}/RouteWireSubscriptionProtocolTests.cs (92%) rename tests/{NATS.Server.Tests => NATS.Server.Clustering.Tests}/Routes/AccountRouteTests.cs (99%) rename tests/{NATS.Server.Tests => NATS.Server.Clustering.Tests}/Routes/ClusterSplitTests.cs (99%) rename tests/{NATS.Server.Tests => NATS.Server.Clustering.Tests}/Routes/NoPoolFallbackTests.cs (99%) rename tests/{NATS.Server.Tests => NATS.Server.Clustering.Tests}/Routes/PoolSizeNegotiationTests.cs (99%) rename tests/{NATS.Server.Tests => NATS.Server.Clustering.Tests}/Routes/RouteAccountScopedDeliveryTests.cs (99%) rename tests/{NATS.Server.Tests => NATS.Server.Clustering.Tests}/Routes/RouteAccountScopedTests.cs (89%) rename tests/{NATS.Server.Tests => NATS.Server.Clustering.Tests}/Routes/RouteBatchProtoParityBatch3Tests.cs (98%) rename tests/{NATS.Server.Tests => NATS.Server.Clustering.Tests}/Routes/RouteCompressionTests.cs (91%) rename tests/{NATS.Server.Tests => NATS.Server.Clustering.Tests}/Routes/RouteConfigTests.cs (99%) rename tests/{NATS.Server.Tests => NATS.Server.Clustering.Tests}/Routes/RouteConfigValidationTests.cs (99%) rename tests/{NATS.Server.Tests => NATS.Server.Clustering.Tests}/Routes/RouteConnectionTests.cs (99%) rename tests/{NATS.Server.Tests => NATS.Server.Clustering.Tests}/Routes/RouteForwardingTests.cs (96%) rename tests/{NATS.Server.Tests => NATS.Server.Clustering.Tests}/Routes/RouteHashStorageTests.cs (99%) rename tests/{NATS.Server.Tests => NATS.Server.Clustering.Tests}/Routes/RouteInfoBroadcastParityBatch4Tests.cs (86%) rename tests/{NATS.Server.Tests => NATS.Server.Clustering.Tests}/Routes/RouteInterestIdempotencyTests.cs (98%) rename tests/{NATS.Server.Tests => NATS.Server.Clustering.Tests}/Routes/RouteParityHelpersBatch1Tests.cs (99%) rename tests/{NATS.Server.Tests => NATS.Server.Clustering.Tests}/Routes/RoutePoolAccountTests.cs (99%) rename tests/{NATS.Server.Tests => NATS.Server.Clustering.Tests}/Routes/RouteRemoteSubCleanupParityBatch2Tests.cs (99%) rename tests/{NATS.Server.Tests => NATS.Server.Clustering.Tests}/Routes/RouteS2CompressionTests.cs (99%) rename tests/{NATS.Server.Tests => NATS.Server.Clustering.Tests}/Routes/RouteSubscriptionTests.cs (97%) rename tests/{NATS.Server.Tests => NATS.Server.Clustering.Tests}/Routes/RouteTopologyGossipTests.cs (94%) rename tests/{NATS.Server.Tests/RouteHandshakeTests.cs => NATS.Server.TestUtilities/TestServerFactory.cs} (73%) diff --git a/NatsDotNet.slnx b/NatsDotNet.slnx index 8c9e61e..0fcfe13 100644 --- a/NatsDotNet.slnx +++ b/NatsDotNet.slnx @@ -10,6 +10,7 @@ + diff --git a/src/NATS.Server/NATS.Server.csproj b/src/NATS.Server/NATS.Server.csproj index 253b7bc..6132fcd 100644 --- a/src/NATS.Server/NATS.Server.csproj +++ b/src/NATS.Server/NATS.Server.csproj @@ -5,6 +5,7 @@ + diff --git a/tests/NATS.Server.Tests/ImplicitDiscoveryTests.cs b/tests/NATS.Server.Clustering.Tests/ImplicitDiscoveryTests.cs similarity index 99% rename from tests/NATS.Server.Tests/ImplicitDiscoveryTests.cs rename to tests/NATS.Server.Clustering.Tests/ImplicitDiscoveryTests.cs index 36ca02e..f3d1d9f 100644 --- a/tests/NATS.Server.Tests/ImplicitDiscoveryTests.cs +++ b/tests/NATS.Server.Clustering.Tests/ImplicitDiscoveryTests.cs @@ -5,7 +5,7 @@ using NATS.Server.Gateways; using NATS.Server.Protocol; using NATS.Server.Routes; -namespace NATS.Server.Tests; +namespace NATS.Server.Clustering.Tests; // Go reference: server/route.go processImplicitRoute, server/gateway.go processImplicitGateway diff --git a/tests/NATS.Server.Tests/InterServerAccountProtocolTests.cs b/tests/NATS.Server.Clustering.Tests/InterServerAccountProtocolTests.cs similarity index 98% rename from tests/NATS.Server.Tests/InterServerAccountProtocolTests.cs rename to tests/NATS.Server.Clustering.Tests/InterServerAccountProtocolTests.cs index b4be42e..a59b26e 100644 --- a/tests/NATS.Server.Tests/InterServerAccountProtocolTests.cs +++ b/tests/NATS.Server.Clustering.Tests/InterServerAccountProtocolTests.cs @@ -4,7 +4,7 @@ using System.Text; using NATS.Server.Gateways; using NATS.Server.Subscriptions; -namespace NATS.Server.Tests; +namespace NATS.Server.Clustering.Tests; public class InterServerAccountProtocolTests { diff --git a/tests/NATS.Server.Clustering.Tests/NATS.Server.Clustering.Tests.csproj b/tests/NATS.Server.Clustering.Tests/NATS.Server.Clustering.Tests.csproj new file mode 100644 index 0000000..c886361 --- /dev/null +++ b/tests/NATS.Server.Clustering.Tests/NATS.Server.Clustering.Tests.csproj @@ -0,0 +1,27 @@ + + + + false + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/NATS.Server.Tests/Route/RouteGoParityTests.cs b/tests/NATS.Server.Clustering.Tests/Route/RouteGoParityTests.cs similarity index 99% rename from tests/NATS.Server.Tests/Route/RouteGoParityTests.cs rename to tests/NATS.Server.Clustering.Tests/Route/RouteGoParityTests.cs index a407ee4..d6aa7ae 100644 --- a/tests/NATS.Server.Tests/Route/RouteGoParityTests.cs +++ b/tests/NATS.Server.Clustering.Tests/Route/RouteGoParityTests.cs @@ -8,7 +8,7 @@ using Microsoft.Extensions.Logging.Abstractions; using NATS.Server.Configuration; using NATS.Server.Routes; -namespace NATS.Server.Tests.Route; +namespace NATS.Server.Clustering.Tests.Route; /// /// Go parity tests for the .NET route subsystem ported from diff --git a/tests/NATS.Server.Clustering.Tests/RouteHandshakeTests.cs b/tests/NATS.Server.Clustering.Tests/RouteHandshakeTests.cs new file mode 100644 index 0000000..f2a7a22 --- /dev/null +++ b/tests/NATS.Server.Clustering.Tests/RouteHandshakeTests.cs @@ -0,0 +1,25 @@ +using NATS.Server.TestUtilities; + +namespace NATS.Server.Clustering.Tests; + +public class RouteHandshakeTests +{ + [Fact] + public async Task Two_servers_establish_route_connection() + { + await using var a = await TestServerFactory.CreateClusterEnabledAsync(); + await using var b = await TestServerFactory.CreateClusterEnabledAsync(seed: a.ClusterListen); + + await a.WaitForReadyAsync(); + await b.WaitForReadyAsync(); + + using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5)); + while (!timeout.IsCancellationRequested && (a.Stats.Routes == 0 || b.Stats.Routes == 0)) + { + await Task.Delay(50, timeout.Token).ContinueWith(_ => { }, TaskScheduler.Default); + } + + a.Stats.Routes.ShouldBeGreaterThan(0); + b.Stats.Routes.ShouldBeGreaterThan(0); + } +} diff --git a/tests/NATS.Server.Tests/RoutePoolTests.cs b/tests/NATS.Server.Clustering.Tests/RoutePoolTests.cs similarity index 87% rename from tests/NATS.Server.Tests/RoutePoolTests.cs rename to tests/NATS.Server.Clustering.Tests/RoutePoolTests.cs index d43dc7e..a84c6b4 100644 --- a/tests/NATS.Server.Tests/RoutePoolTests.cs +++ b/tests/NATS.Server.Clustering.Tests/RoutePoolTests.cs @@ -1,4 +1,4 @@ -namespace NATS.Server.Tests; +namespace NATS.Server.Clustering.Tests; public class RoutePoolTests { diff --git a/tests/NATS.Server.Tests/RouteRmsgForwardingTests.cs b/tests/NATS.Server.Clustering.Tests/RouteRmsgForwardingTests.cs similarity index 91% rename from tests/NATS.Server.Tests/RouteRmsgForwardingTests.cs rename to tests/NATS.Server.Clustering.Tests/RouteRmsgForwardingTests.cs index ff91cf0..427e1c9 100644 --- a/tests/NATS.Server.Tests/RouteRmsgForwardingTests.cs +++ b/tests/NATS.Server.Clustering.Tests/RouteRmsgForwardingTests.cs @@ -1,4 +1,4 @@ -namespace NATS.Server.Tests; +namespace NATS.Server.Clustering.Tests; public class RouteRmsgForwardingTests { diff --git a/tests/NATS.Server.Tests/RouteSubscriptionPropagationTests.cs b/tests/NATS.Server.Clustering.Tests/RouteSubscriptionPropagationTests.cs similarity index 89% rename from tests/NATS.Server.Tests/RouteSubscriptionPropagationTests.cs rename to tests/NATS.Server.Clustering.Tests/RouteSubscriptionPropagationTests.cs index 0f0173a..5f248e2 100644 --- a/tests/NATS.Server.Tests/RouteSubscriptionPropagationTests.cs +++ b/tests/NATS.Server.Clustering.Tests/RouteSubscriptionPropagationTests.cs @@ -3,8 +3,9 @@ using System.Net.Sockets; using System.Text; using Microsoft.Extensions.Logging.Abstractions; using NATS.Server.Configuration; +using NATS.Server.TestUtilities; -namespace NATS.Server.Tests; +namespace NATS.Server.Clustering.Tests; public class RouteSubscriptionPropagationTests { @@ -90,7 +91,7 @@ internal sealed class RouteFixture : IAsyncDisposable await ReadLineAsync(sock); // INFO await sock.SendAsync(Encoding.ASCII.GetBytes($"CONNECT {{}}\r\nSUB {subject} 1\r\nPING\r\n")); - await ReadUntilAsync(sock, "PONG"); + await SocketTestHelper.ReadUntilAsync(sock, "PONG"); } public async Task SendRouteSubFrameAsync(string subject) @@ -123,11 +124,11 @@ internal sealed class RouteFixture : IAsyncDisposable _publisherOnA = sock; _ = await ReadLineAsync(sock); // INFO await sock.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\nPING\r\n")); - await ReadUntilAsync(sock, "PONG"); + await SocketTestHelper.ReadUntilAsync(sock, "PONG"); } await sock.SendAsync(Encoding.ASCII.GetBytes($"PUB {subject} {payload.Length}\r\n{payload}\r\nPING\r\n")); - await ReadUntilAsync(sock, "PONG"); + await SocketTestHelper.ReadUntilAsync(sock, "PONG"); } public async Task ReadServerBMessageAsync() @@ -135,7 +136,7 @@ internal sealed class RouteFixture : IAsyncDisposable if (_subscriberOnB == null) throw new InvalidOperationException("No subscriber socket on server B."); - return await ReadUntilAsync(_subscriberOnB, "MSG "); + return await SocketTestHelper.ReadUntilAsync(_subscriberOnB, "MSG "); } public async Task ServerAHasRemoteInterestAsync(string subject, bool expected = true) @@ -184,22 +185,6 @@ internal sealed class RouteFixture : IAsyncDisposable return Encoding.ASCII.GetString(buf, 0, n); } - private static async Task ReadUntilAsync(Socket sock, string expected) - { - var sb = new StringBuilder(); - var buf = new byte[4096]; - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5)); - - while (!sb.ToString().Contains(expected, StringComparison.Ordinal)) - { - var n = await sock.ReceiveAsync(buf, SocketFlags.None, cts.Token); - if (n == 0) - break; - sb.Append(Encoding.ASCII.GetString(buf, 0, n)); - } - - return sb.ToString(); - } private static (string Host, int Port) ParseHostPort(string endpoint) { diff --git a/tests/NATS.Server.Tests/RouteWireSubscriptionProtocolTests.cs b/tests/NATS.Server.Clustering.Tests/RouteWireSubscriptionProtocolTests.cs similarity index 92% rename from tests/NATS.Server.Tests/RouteWireSubscriptionProtocolTests.cs rename to tests/NATS.Server.Clustering.Tests/RouteWireSubscriptionProtocolTests.cs index 0002553..1653f45 100644 --- a/tests/NATS.Server.Tests/RouteWireSubscriptionProtocolTests.cs +++ b/tests/NATS.Server.Clustering.Tests/RouteWireSubscriptionProtocolTests.cs @@ -1,4 +1,4 @@ -namespace NATS.Server.Tests; +namespace NATS.Server.Clustering.Tests; public class RouteWireSubscriptionProtocolTests { diff --git a/tests/NATS.Server.Tests/Routes/AccountRouteTests.cs b/tests/NATS.Server.Clustering.Tests/Routes/AccountRouteTests.cs similarity index 99% rename from tests/NATS.Server.Tests/Routes/AccountRouteTests.cs rename to tests/NATS.Server.Clustering.Tests/Routes/AccountRouteTests.cs index b94de27..5ba5f72 100644 --- a/tests/NATS.Server.Tests/Routes/AccountRouteTests.cs +++ b/tests/NATS.Server.Clustering.Tests/Routes/AccountRouteTests.cs @@ -7,7 +7,7 @@ using Microsoft.Extensions.Logging.Abstractions; using NATS.Server.Configuration; using NATS.Server.Routes; -namespace NATS.Server.Tests.Routes; +namespace NATS.Server.Clustering.Tests.Routes; /// /// Tests for per-account dedicated route connections (Gap 13.2). diff --git a/tests/NATS.Server.Tests/Routes/ClusterSplitTests.cs b/tests/NATS.Server.Clustering.Tests/Routes/ClusterSplitTests.cs similarity index 99% rename from tests/NATS.Server.Tests/Routes/ClusterSplitTests.cs rename to tests/NATS.Server.Clustering.Tests/Routes/ClusterSplitTests.cs index 7ea73dc..a7f4f5a 100644 --- a/tests/NATS.Server.Tests/Routes/ClusterSplitTests.cs +++ b/tests/NATS.Server.Clustering.Tests/Routes/ClusterSplitTests.cs @@ -8,7 +8,7 @@ using Microsoft.Extensions.Logging.Abstractions; using NATS.Server.Configuration; using NATS.Server.Routes; -namespace NATS.Server.Tests.Routes; +namespace NATS.Server.Clustering.Tests.Routes; /// /// Tests for , diff --git a/tests/NATS.Server.Tests/Routes/NoPoolFallbackTests.cs b/tests/NATS.Server.Clustering.Tests/Routes/NoPoolFallbackTests.cs similarity index 99% rename from tests/NATS.Server.Tests/Routes/NoPoolFallbackTests.cs rename to tests/NATS.Server.Clustering.Tests/Routes/NoPoolFallbackTests.cs index 24a65b5..e70a202 100644 --- a/tests/NATS.Server.Tests/Routes/NoPoolFallbackTests.cs +++ b/tests/NATS.Server.Clustering.Tests/Routes/NoPoolFallbackTests.cs @@ -7,7 +7,7 @@ using Microsoft.Extensions.Logging.Abstractions; using NATS.Server.Configuration; using NATS.Server.Routes; -namespace NATS.Server.Tests.Routes; +namespace NATS.Server.Clustering.Tests.Routes; /// /// Tests for no-pool (legacy) route fallback — Gap 13.6. diff --git a/tests/NATS.Server.Tests/Routes/PoolSizeNegotiationTests.cs b/tests/NATS.Server.Clustering.Tests/Routes/PoolSizeNegotiationTests.cs similarity index 99% rename from tests/NATS.Server.Tests/Routes/PoolSizeNegotiationTests.cs rename to tests/NATS.Server.Clustering.Tests/Routes/PoolSizeNegotiationTests.cs index 2f65daa..dee3f0b 100644 --- a/tests/NATS.Server.Tests/Routes/PoolSizeNegotiationTests.cs +++ b/tests/NATS.Server.Clustering.Tests/Routes/PoolSizeNegotiationTests.cs @@ -7,7 +7,7 @@ using Microsoft.Extensions.Logging.Abstractions; using NATS.Server.Configuration; using NATS.Server.Routes; -namespace NATS.Server.Tests.Routes; +namespace NATS.Server.Clustering.Tests.Routes; /// /// Tests for route pool size negotiation (Gap 13.3). diff --git a/tests/NATS.Server.Tests/Routes/RouteAccountScopedDeliveryTests.cs b/tests/NATS.Server.Clustering.Tests/Routes/RouteAccountScopedDeliveryTests.cs similarity index 99% rename from tests/NATS.Server.Tests/Routes/RouteAccountScopedDeliveryTests.cs rename to tests/NATS.Server.Clustering.Tests/Routes/RouteAccountScopedDeliveryTests.cs index 34efb55..129939e 100644 --- a/tests/NATS.Server.Tests/Routes/RouteAccountScopedDeliveryTests.cs +++ b/tests/NATS.Server.Clustering.Tests/Routes/RouteAccountScopedDeliveryTests.cs @@ -3,7 +3,7 @@ using NATS.Client.Core; using NATS.Server.Auth; using NATS.Server.Configuration; -namespace NATS.Server.Tests.Routes; +namespace NATS.Server.Clustering.Tests.Routes; public class RouteAccountScopedDeliveryTests { diff --git a/tests/NATS.Server.Tests/Routes/RouteAccountScopedTests.cs b/tests/NATS.Server.Clustering.Tests/Routes/RouteAccountScopedTests.cs similarity index 89% rename from tests/NATS.Server.Tests/Routes/RouteAccountScopedTests.cs rename to tests/NATS.Server.Clustering.Tests/Routes/RouteAccountScopedTests.cs index c0d6968..cabe5ed 100644 --- a/tests/NATS.Server.Tests/Routes/RouteAccountScopedTests.cs +++ b/tests/NATS.Server.Clustering.Tests/Routes/RouteAccountScopedTests.cs @@ -1,6 +1,6 @@ using NATS.Server.Routes; -namespace NATS.Server.Tests; +namespace NATS.Server.Clustering.Tests; public class RouteAccountScopedTests { diff --git a/tests/NATS.Server.Tests/Routes/RouteBatchProtoParityBatch3Tests.cs b/tests/NATS.Server.Clustering.Tests/Routes/RouteBatchProtoParityBatch3Tests.cs similarity index 98% rename from tests/NATS.Server.Tests/Routes/RouteBatchProtoParityBatch3Tests.cs rename to tests/NATS.Server.Clustering.Tests/Routes/RouteBatchProtoParityBatch3Tests.cs index b39c17f..6258f45 100644 --- a/tests/NATS.Server.Tests/Routes/RouteBatchProtoParityBatch3Tests.cs +++ b/tests/NATS.Server.Clustering.Tests/Routes/RouteBatchProtoParityBatch3Tests.cs @@ -4,7 +4,7 @@ using System.Text; using NATS.Server.Routes; using NATS.Server.Subscriptions; -namespace NATS.Server.Tests.Routes; +namespace NATS.Server.Clustering.Tests.Routes; public class RouteBatchProtoParityBatch3Tests { diff --git a/tests/NATS.Server.Tests/Routes/RouteCompressionTests.cs b/tests/NATS.Server.Clustering.Tests/Routes/RouteCompressionTests.cs similarity index 91% rename from tests/NATS.Server.Tests/Routes/RouteCompressionTests.cs rename to tests/NATS.Server.Clustering.Tests/Routes/RouteCompressionTests.cs index 676c65c..36ed96f 100644 --- a/tests/NATS.Server.Tests/Routes/RouteCompressionTests.cs +++ b/tests/NATS.Server.Clustering.Tests/Routes/RouteCompressionTests.cs @@ -1,7 +1,7 @@ using System.Text; using NATS.Server.Routes; -namespace NATS.Server.Tests; +namespace NATS.Server.Clustering.Tests; public class RouteCompressionTests { diff --git a/tests/NATS.Server.Tests/Routes/RouteConfigTests.cs b/tests/NATS.Server.Clustering.Tests/Routes/RouteConfigTests.cs similarity index 99% rename from tests/NATS.Server.Tests/Routes/RouteConfigTests.cs rename to tests/NATS.Server.Clustering.Tests/Routes/RouteConfigTests.cs index 4135a9f..87d9b43 100644 --- a/tests/NATS.Server.Tests/Routes/RouteConfigTests.cs +++ b/tests/NATS.Server.Clustering.Tests/Routes/RouteConfigTests.cs @@ -2,7 +2,7 @@ using Microsoft.Extensions.Logging.Abstractions; using NATS.Client.Core; using NATS.Server.Configuration; -namespace NATS.Server.Tests.Routes; +namespace NATS.Server.Clustering.Tests.Routes; /// /// Tests cluster route formation and message forwarding between servers. diff --git a/tests/NATS.Server.Tests/Routes/RouteConfigValidationTests.cs b/tests/NATS.Server.Clustering.Tests/Routes/RouteConfigValidationTests.cs similarity index 99% rename from tests/NATS.Server.Tests/Routes/RouteConfigValidationTests.cs rename to tests/NATS.Server.Clustering.Tests/Routes/RouteConfigValidationTests.cs index debffaa..036588e 100644 --- a/tests/NATS.Server.Tests/Routes/RouteConfigValidationTests.cs +++ b/tests/NATS.Server.Clustering.Tests/Routes/RouteConfigValidationTests.cs @@ -7,7 +7,7 @@ using NATS.Server.Auth; using NATS.Server.Configuration; using NATS.Server.Routes; -namespace NATS.Server.Tests.Routes; +namespace NATS.Server.Clustering.Tests.Routes; /// /// Tests for route configuration validation, compression options, topology gossip, diff --git a/tests/NATS.Server.Tests/Routes/RouteConnectionTests.cs b/tests/NATS.Server.Clustering.Tests/Routes/RouteConnectionTests.cs similarity index 99% rename from tests/NATS.Server.Tests/Routes/RouteConnectionTests.cs rename to tests/NATS.Server.Clustering.Tests/Routes/RouteConnectionTests.cs index 04e19d6..c15e29e 100644 --- a/tests/NATS.Server.Tests/Routes/RouteConnectionTests.cs +++ b/tests/NATS.Server.Clustering.Tests/Routes/RouteConnectionTests.cs @@ -7,7 +7,7 @@ using NATS.Server.Configuration; using NATS.Server.Routes; using NATS.Server.Subscriptions; -namespace NATS.Server.Tests.Routes; +namespace NATS.Server.Clustering.Tests.Routes; /// /// Tests for route connection establishment, handshake, reconnection, and lifecycle. diff --git a/tests/NATS.Server.Tests/Routes/RouteForwardingTests.cs b/tests/NATS.Server.Clustering.Tests/Routes/RouteForwardingTests.cs similarity index 96% rename from tests/NATS.Server.Tests/Routes/RouteForwardingTests.cs rename to tests/NATS.Server.Clustering.Tests/Routes/RouteForwardingTests.cs index 258c707..78e8de6 100644 --- a/tests/NATS.Server.Tests/Routes/RouteForwardingTests.cs +++ b/tests/NATS.Server.Clustering.Tests/Routes/RouteForwardingTests.cs @@ -7,8 +7,9 @@ using NATS.Server.Auth; using NATS.Server.Configuration; using NATS.Server.Routes; using NATS.Server.Subscriptions; +using NATS.Server.TestUtilities; -namespace NATS.Server.Tests.Routes; +namespace NATS.Server.Clustering.Tests.Routes; /// /// Tests for route message forwarding (RMSG), reply propagation, payload delivery, @@ -140,7 +141,7 @@ public class RouteForwardingTests var buf = new byte[4096]; _ = await responderSock.ReceiveAsync(buf); // INFO await responderSock.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\nSUB service.echo 1\r\nPING\r\n")); - await ReadUntilAsync(responderSock, "PONG"); + await SocketTestHelper.ReadUntilAsync(responderSock, "PONG"); await WaitForCondition(() => b.Server.HasRemoteInterest("service.echo")); // Requester on server B: subscribe to reply inbox via raw socket @@ -150,26 +151,26 @@ public class RouteForwardingTests var replyInbox = $"_INBOX.{Guid.NewGuid():N}"; await requesterSock.SendAsync(Encoding.ASCII.GetBytes( $"CONNECT {{}}\r\nSUB {replyInbox} 2\r\nPING\r\n")); - await ReadUntilAsync(requesterSock, "PONG"); + await SocketTestHelper.ReadUntilAsync(requesterSock, "PONG"); await WaitForCondition(() => a.Server.HasRemoteInterest(replyInbox)); // Publish request with reply-to from B await requesterSock.SendAsync(Encoding.ASCII.GetBytes( $"PUB service.echo {replyInbox} 4\r\nping\r\nPING\r\n")); - await ReadUntilAsync(requesterSock, "PONG"); + await SocketTestHelper.ReadUntilAsync(requesterSock, "PONG"); // Read the request on A, verify reply-to - var requestData = await ReadUntilAsync(responderSock, "ping"); + var requestData = await SocketTestHelper.ReadUntilAsync(responderSock, "ping"); requestData.ShouldContain($"MSG service.echo 1 {replyInbox} 4"); requestData.ShouldContain("ping"); // Publish reply from A to the reply-to subject await responderSock.SendAsync(Encoding.ASCII.GetBytes( $"PUB {replyInbox} 4\r\npong\r\nPING\r\n")); - await ReadUntilAsync(responderSock, "PONG"); + await SocketTestHelper.ReadUntilAsync(responderSock, "PONG"); // Read the reply on B - var replyData = await ReadUntilAsync(requesterSock, "pong"); + var replyData = await SocketTestHelper.ReadUntilAsync(requesterSock, "pong"); replyData.ShouldContain($"MSG {replyInbox} 2 4"); replyData.ShouldContain("pong"); } @@ -595,7 +596,7 @@ public class RouteForwardingTests _ = await sock.ReceiveAsync(buf); // INFO await sock.SendAsync(Encoding.ASCII.GetBytes( "CONNECT {}\r\nPUB reply.subject.test _INBOX.reply123 5\r\nHello\r\nPING\r\n")); - await ReadUntilAsync(sock, "PONG"); + await SocketTestHelper.ReadUntilAsync(sock, "PONG"); using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5)); var msg = await sub.Msgs.ReadAsync(timeout.Token); @@ -684,7 +685,7 @@ public class RouteForwardingTests await route.SendRmsgAsync("$G", "subject.test", "_INBOX.reply", payload, timeout.Token); // Read the RMSG frame from the remote side, waiting until expected content arrives - var data = await ReadUntilAsync(remote, "test-payload"); + var data = await SocketTestHelper.ReadUntilAsync(remote, "test-payload"); data.ShouldContain("RMSG $G subject.test _INBOX.reply 12"); data.ShouldContain("test-payload"); } @@ -803,18 +804,4 @@ public class RouteForwardingTests return sb.ToString(); } - private static async Task ReadUntilAsync(Socket sock, string expected) - { - var sb = new StringBuilder(); - var buf = new byte[4096]; - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5)); - while (!sb.ToString().Contains(expected, StringComparison.Ordinal)) - { - var n = await sock.ReceiveAsync(buf, SocketFlags.None, cts.Token); - if (n == 0) break; - sb.Append(Encoding.ASCII.GetString(buf, 0, n)); - } - - return sb.ToString(); - } } diff --git a/tests/NATS.Server.Tests/Routes/RouteHashStorageTests.cs b/tests/NATS.Server.Clustering.Tests/Routes/RouteHashStorageTests.cs similarity index 99% rename from tests/NATS.Server.Tests/Routes/RouteHashStorageTests.cs rename to tests/NATS.Server.Clustering.Tests/Routes/RouteHashStorageTests.cs index e61df9f..4b7a90c 100644 --- a/tests/NATS.Server.Tests/Routes/RouteHashStorageTests.cs +++ b/tests/NATS.Server.Clustering.Tests/Routes/RouteHashStorageTests.cs @@ -8,7 +8,7 @@ using NATS.Server; using NATS.Server.Configuration; using NATS.Server.Routes; -namespace NATS.Server.Tests.Routes; +namespace NATS.Server.Clustering.Tests.Routes; /// /// Tests for the FNV-1a hash-based route storage on . diff --git a/tests/NATS.Server.Tests/Routes/RouteInfoBroadcastParityBatch4Tests.cs b/tests/NATS.Server.Clustering.Tests/Routes/RouteInfoBroadcastParityBatch4Tests.cs similarity index 86% rename from tests/NATS.Server.Tests/Routes/RouteInfoBroadcastParityBatch4Tests.cs rename to tests/NATS.Server.Clustering.Tests/Routes/RouteInfoBroadcastParityBatch4Tests.cs index e92cc24..89f9fc7 100644 --- a/tests/NATS.Server.Tests/Routes/RouteInfoBroadcastParityBatch4Tests.cs +++ b/tests/NATS.Server.Clustering.Tests/Routes/RouteInfoBroadcastParityBatch4Tests.cs @@ -3,15 +3,16 @@ using System.Net.Sockets; using System.Text; using Microsoft.Extensions.Logging.Abstractions; using NATS.Server; +using NATS.Server.TestUtilities; -namespace NATS.Server.Tests.Routes; +namespace NATS.Server.Clustering.Tests.Routes; public class RouteInfoBroadcastParityBatch4Tests { [Fact] public async Task UpdateServerINFOAndSendINFOToClients_broadcasts_INFO_to_connected_clients() { - var port = GetFreePort(); + var port = TestPortAllocator.GetFreePort(); using var server = new NatsServer(new NatsOptions { Host = "127.0.0.1", Port = port }, NullLoggerFactory.Instance); using var cts = new CancellationTokenSource(); _ = server.StartAsync(cts.Token); @@ -68,17 +69,4 @@ public class RouteInfoBroadcastParityBatch4Tests return Encoding.ASCII.GetString([.. buffer]); } - private static int GetFreePort() - { - var listener = new TcpListener(IPAddress.Loopback, 0); - listener.Start(); - try - { - return ((IPEndPoint)listener.LocalEndpoint).Port; - } - finally - { - listener.Stop(); - } - } } diff --git a/tests/NATS.Server.Tests/Routes/RouteInterestIdempotencyTests.cs b/tests/NATS.Server.Clustering.Tests/Routes/RouteInterestIdempotencyTests.cs similarity index 98% rename from tests/NATS.Server.Tests/Routes/RouteInterestIdempotencyTests.cs rename to tests/NATS.Server.Clustering.Tests/Routes/RouteInterestIdempotencyTests.cs index c596cab..bba3307 100644 --- a/tests/NATS.Server.Tests/Routes/RouteInterestIdempotencyTests.cs +++ b/tests/NATS.Server.Clustering.Tests/Routes/RouteInterestIdempotencyTests.cs @@ -4,7 +4,7 @@ using System.Text; using NATS.Server.Routes; using NATS.Server.Subscriptions; -namespace NATS.Server.Tests.Routes; +namespace NATS.Server.Clustering.Tests.Routes; public class RouteInterestIdempotencyTests { diff --git a/tests/NATS.Server.Tests/Routes/RouteParityHelpersBatch1Tests.cs b/tests/NATS.Server.Clustering.Tests/Routes/RouteParityHelpersBatch1Tests.cs similarity index 99% rename from tests/NATS.Server.Tests/Routes/RouteParityHelpersBatch1Tests.cs rename to tests/NATS.Server.Clustering.Tests/Routes/RouteParityHelpersBatch1Tests.cs index 19e4198..934aa06 100644 --- a/tests/NATS.Server.Tests/Routes/RouteParityHelpersBatch1Tests.cs +++ b/tests/NATS.Server.Clustering.Tests/Routes/RouteParityHelpersBatch1Tests.cs @@ -5,7 +5,7 @@ using NATS.Server.Configuration; using NATS.Server.Protocol; using NATS.Server.Routes; -namespace NATS.Server.Tests.Routes; +namespace NATS.Server.Clustering.Tests.Routes; public class RouteParityHelpersBatch1Tests { diff --git a/tests/NATS.Server.Tests/Routes/RoutePoolAccountTests.cs b/tests/NATS.Server.Clustering.Tests/Routes/RoutePoolAccountTests.cs similarity index 99% rename from tests/NATS.Server.Tests/Routes/RoutePoolAccountTests.cs rename to tests/NATS.Server.Clustering.Tests/Routes/RoutePoolAccountTests.cs index 2135646..56909cf 100644 --- a/tests/NATS.Server.Tests/Routes/RoutePoolAccountTests.cs +++ b/tests/NATS.Server.Clustering.Tests/Routes/RoutePoolAccountTests.cs @@ -6,7 +6,7 @@ using Microsoft.Extensions.Logging.Abstractions; using NATS.Server.Configuration; using NATS.Server.Routes; -namespace NATS.Server.Tests.Routes; +namespace NATS.Server.Clustering.Tests.Routes; /// /// Tests for route pool accounting per account, matching Go's diff --git a/tests/NATS.Server.Tests/Routes/RouteRemoteSubCleanupParityBatch2Tests.cs b/tests/NATS.Server.Clustering.Tests/Routes/RouteRemoteSubCleanupParityBatch2Tests.cs similarity index 99% rename from tests/NATS.Server.Tests/Routes/RouteRemoteSubCleanupParityBatch2Tests.cs rename to tests/NATS.Server.Clustering.Tests/Routes/RouteRemoteSubCleanupParityBatch2Tests.cs index 9d7d36d..684a69c 100644 --- a/tests/NATS.Server.Tests/Routes/RouteRemoteSubCleanupParityBatch2Tests.cs +++ b/tests/NATS.Server.Clustering.Tests/Routes/RouteRemoteSubCleanupParityBatch2Tests.cs @@ -5,7 +5,7 @@ using Microsoft.Extensions.Logging.Abstractions; using NATS.Server.Configuration; using NATS.Server.Subscriptions; -namespace NATS.Server.Tests.Routes; +namespace NATS.Server.Clustering.Tests.Routes; public class RouteRemoteSubCleanupParityBatch2Tests { diff --git a/tests/NATS.Server.Tests/Routes/RouteS2CompressionTests.cs b/tests/NATS.Server.Clustering.Tests/Routes/RouteS2CompressionTests.cs similarity index 99% rename from tests/NATS.Server.Tests/Routes/RouteS2CompressionTests.cs rename to tests/NATS.Server.Clustering.Tests/Routes/RouteS2CompressionTests.cs index 235fdb4..efa35f8 100644 --- a/tests/NATS.Server.Tests/Routes/RouteS2CompressionTests.cs +++ b/tests/NATS.Server.Clustering.Tests/Routes/RouteS2CompressionTests.cs @@ -4,7 +4,7 @@ using System.Text; using NATS.Server.Routes; -namespace NATS.Server.Tests.Routes; +namespace NATS.Server.Clustering.Tests.Routes; /// /// Tests for route S2/Snappy compression codec, matching Go's route compression diff --git a/tests/NATS.Server.Tests/Routes/RouteSubscriptionTests.cs b/tests/NATS.Server.Clustering.Tests/Routes/RouteSubscriptionTests.cs similarity index 97% rename from tests/NATS.Server.Tests/Routes/RouteSubscriptionTests.cs rename to tests/NATS.Server.Clustering.Tests/Routes/RouteSubscriptionTests.cs index b702fe8..051de09 100644 --- a/tests/NATS.Server.Tests/Routes/RouteSubscriptionTests.cs +++ b/tests/NATS.Server.Clustering.Tests/Routes/RouteSubscriptionTests.cs @@ -7,8 +7,9 @@ using NATS.Server.Auth; using NATS.Server.Configuration; using NATS.Server.Routes; using NATS.Server.Subscriptions; +using NATS.Server.TestUtilities; -namespace NATS.Server.Tests.Routes; +namespace NATS.Server.Clustering.Tests.Routes; /// /// Tests for route subscription propagation: RS+/RS-, wildcard subs, queue subs, @@ -357,7 +358,7 @@ public class RouteSubscriptionTests await sock.ConnectAsync(IPAddress.Loopback, a.Server.Port); _ = await ReadLineAsync(sock, default); await sock.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\nSUB foo queue1 1\r\nPING\r\n")); - await ReadUntilAsync(sock, "PONG"); + await SocketTestHelper.ReadUntilAsync(sock, "PONG"); await WaitForCondition(() => b.Server.HasRemoteInterest("foo")); b.Server.HasRemoteInterest("foo").ShouldBeTrue(); @@ -829,20 +830,6 @@ public class RouteSubscriptionTests private static Task WriteLineAsync(Socket socket, string line, CancellationToken ct) => socket.SendAsync(Encoding.ASCII.GetBytes($"{line}\r\n"), SocketFlags.None, ct).AsTask(); - private static async Task ReadUntilAsync(Socket sock, string expected) - { - var sb = new StringBuilder(); - var buf = new byte[4096]; - using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5)); - while (!sb.ToString().Contains(expected, StringComparison.Ordinal)) - { - var n = await sock.ReceiveAsync(buf, SocketFlags.None, cts.Token); - if (n == 0) break; - sb.Append(Encoding.ASCII.GetString(buf, 0, n)); - } - - return sb.ToString(); - } } file static class CancellationTokenExtensions diff --git a/tests/NATS.Server.Tests/Routes/RouteTopologyGossipTests.cs b/tests/NATS.Server.Clustering.Tests/Routes/RouteTopologyGossipTests.cs similarity index 94% rename from tests/NATS.Server.Tests/Routes/RouteTopologyGossipTests.cs rename to tests/NATS.Server.Clustering.Tests/Routes/RouteTopologyGossipTests.cs index 6cae4aa..01b318f 100644 --- a/tests/NATS.Server.Tests/Routes/RouteTopologyGossipTests.cs +++ b/tests/NATS.Server.Clustering.Tests/Routes/RouteTopologyGossipTests.cs @@ -2,7 +2,7 @@ using Microsoft.Extensions.Logging.Abstractions; using NATS.Server.Configuration; using NATS.Server.Routes; -namespace NATS.Server.Tests; +namespace NATS.Server.Clustering.Tests; public class RouteTopologyGossipTests { diff --git a/tests/NATS.Server.Tests/RouteHandshakeTests.cs b/tests/NATS.Server.TestUtilities/TestServerFactory.cs similarity index 73% rename from tests/NATS.Server.Tests/RouteHandshakeTests.cs rename to tests/NATS.Server.TestUtilities/TestServerFactory.cs index 8c5a7f8..ef83a4b 100644 --- a/tests/NATS.Server.Tests/RouteHandshakeTests.cs +++ b/tests/NATS.Server.TestUtilities/TestServerFactory.cs @@ -1,31 +1,9 @@ using Microsoft.Extensions.Logging.Abstractions; using NATS.Server.Configuration; -namespace NATS.Server.Tests; +namespace NATS.Server.TestUtilities; -public class RouteHandshakeTests -{ - [Fact] - public async Task Two_servers_establish_route_connection() - { - await using var a = await TestServerFactory.CreateClusterEnabledAsync(); - await using var b = await TestServerFactory.CreateClusterEnabledAsync(seed: a.ClusterListen); - - await a.WaitForReadyAsync(); - await b.WaitForReadyAsync(); - - using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5)); - while (!timeout.IsCancellationRequested && (a.Stats.Routes == 0 || b.Stats.Routes == 0)) - { - await Task.Delay(50, timeout.Token).ContinueWith(_ => { }, TaskScheduler.Default); - } - - a.Stats.Routes.ShouldBeGreaterThan(0); - b.Stats.Routes.ShouldBeGreaterThan(0); - } -} - -internal static class TestServerFactory +public static class TestServerFactory { public static async Task CreateClusterEnabledAsync(string? seed = null) { @@ -100,7 +78,7 @@ internal static class TestServerFactory } } -internal sealed class ClusterTestServer(NatsServer server, CancellationTokenSource cts) : IAsyncDisposable +public sealed class ClusterTestServer(NatsServer server, CancellationTokenSource cts) : IAsyncDisposable { public ServerStats Stats => server.Stats; public string ClusterListen => server.ClusterListen!; diff --git a/tests/NATS.Server.Tests/JetStreamStartupTests.cs b/tests/NATS.Server.Tests/JetStreamStartupTests.cs index b0bfc7d..b7ddadb 100644 --- a/tests/NATS.Server.Tests/JetStreamStartupTests.cs +++ b/tests/NATS.Server.Tests/JetStreamStartupTests.cs @@ -1,3 +1,4 @@ +using NATS.Server.TestUtilities; namespace NATS.Server.Tests; public class JetStreamStartupTests