refactor: rename remaining tests to NATS.Server.Core.Tests
- Rename tests/NATS.Server.Tests -> tests/NATS.Server.Core.Tests - Update solution file, InternalsVisibleTo, and csproj references - Remove JETSTREAM_INTEGRATION_MATRIX and NATS.NKeys from csproj (moved to JetStream.Tests and Auth.Tests) - Update all namespaces from NATS.Server.Tests.* to NATS.Server.Core.Tests.* - Replace private GetFreePort/ReadUntilAsync helpers with TestUtilities calls - Fix stale namespace in Transport.Tests/NetworkingGoParityTests.cs
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
public class ClientClosedReasonTests
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
public class ClientFlagsTests
|
||||
{
|
||||
@@ -6,8 +6,9 @@ using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using NATS.Server;
|
||||
using NATS.Server.TestUtilities;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for HPUB/HMSG header support, mirroring the Go reference tests:
|
||||
@@ -23,7 +24,7 @@ public class ClientHeaderTests : IAsyncLifetime
|
||||
|
||||
public ClientHeaderTests()
|
||||
{
|
||||
_port = GetFreePort();
|
||||
_port = TestPortAllocator.GetFreePort();
|
||||
_server = new NatsServer(new NatsOptions { Port = _port }, NullLoggerFactory.Instance);
|
||||
}
|
||||
|
||||
@@ -39,30 +40,11 @@ public class ClientHeaderTests : IAsyncLifetime
|
||||
_server.Dispose();
|
||||
}
|
||||
|
||||
private static int GetFreePort()
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
sock.Bind(new IPEndPoint(IPAddress.Loopback, 0));
|
||||
return ((IPEndPoint)sock.LocalEndPoint!).Port;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads from the socket accumulating data until the accumulated string contains
|
||||
/// <paramref name="expected"/>, or the timeout elapses.
|
||||
/// </summary>
|
||||
private static async Task<string> ReadUntilAsync(Socket sock, string expected, int timeoutMs = 5000)
|
||||
{
|
||||
using var cts = new CancellationTokenSource(timeoutMs);
|
||||
var sb = new StringBuilder();
|
||||
var buf = new byte[4096];
|
||||
while (!sb.ToString().Contains(expected))
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Connect a raw TCP socket, read the INFO line, and send a CONNECT with
|
||||
@@ -72,7 +54,7 @@ public class ClientHeaderTests : IAsyncLifetime
|
||||
{
|
||||
var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
await sock.ConnectAsync(IPAddress.Loopback, _port);
|
||||
await ReadUntilAsync(sock, "\r\n"); // discard INFO
|
||||
await SocketTestHelper.ReadUntilAsync(sock, "\r\n"); // discard INFO
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes(
|
||||
"CONNECT {\"headers\":true,\"no_responders\":true}\r\n"));
|
||||
return sock;
|
||||
@@ -104,7 +86,7 @@ public class ClientHeaderTests : IAsyncLifetime
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("SUB foo 1\r\n"));
|
||||
// Flush via PING/PONG to ensure the subscription is registered before publishing
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG");
|
||||
|
||||
// Match Go reference test exactly:
|
||||
// Header block: "Name:Derek\r\n" = 12 bytes
|
||||
@@ -119,7 +101,7 @@ public class ClientHeaderTests : IAsyncLifetime
|
||||
|
||||
// Read the full HMSG on the subscriber socket (control line + header + payload + trailing CRLF)
|
||||
// The complete wire message ends with the payload followed by \r\n
|
||||
var received = await ReadUntilAsync(sub, payload + "\r\n", timeoutMs: 5000);
|
||||
var received = await SocketTestHelper.ReadUntilAsync(sub, payload + "\r\n", timeoutMs: 5000);
|
||||
|
||||
// Verify HMSG control line: HMSG foo 1 <hdrLen> <totalLen>
|
||||
received.ShouldContain($"HMSG foo 1 {hdrLen} {totalLen}\r\n");
|
||||
@@ -141,7 +123,7 @@ public class ClientHeaderTests : IAsyncLifetime
|
||||
await sock.ConnectAsync(IPAddress.Loopback, _port);
|
||||
|
||||
// Read the INFO line
|
||||
var infoLine = await ReadUntilAsync(sock, "\r\n");
|
||||
var infoLine = await SocketTestHelper.ReadUntilAsync(sock, "\r\n");
|
||||
|
||||
// INFO must start with "INFO "
|
||||
infoLine.ShouldStartWith("INFO ");
|
||||
@@ -181,13 +163,13 @@ public class ClientHeaderTests : IAsyncLifetime
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("SUB reply.inbox 1\r\n"));
|
||||
// Flush via PING/PONG to ensure SUB is registered
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
|
||||
await ReadUntilAsync(sock, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sock, "PONG");
|
||||
|
||||
// Publish to a subject with no subscribers, using reply.inbox as reply-to
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("PUB no.listeners reply.inbox 0\r\n\r\n"));
|
||||
|
||||
// The server should send back an HMSG on reply.inbox with status 503
|
||||
var received = await ReadUntilAsync(sock, "NATS/1.0 503", timeoutMs: 5000);
|
||||
var received = await SocketTestHelper.ReadUntilAsync(sock, "NATS/1.0 503", timeoutMs: 5000);
|
||||
|
||||
// Must be an HMSG (header message) on the reply subject
|
||||
received.ShouldContain("HMSG reply.inbox");
|
||||
@@ -1,6 +1,6 @@
|
||||
using NATS.Server.Protocol;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
public class ClientKindCommandMatrixTests
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using NATS.Server.Protocol;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
public class ClientKindProtocolRoutingTests
|
||||
{
|
||||
@@ -4,7 +4,7 @@
|
||||
using NATS.Server;
|
||||
using Shouldly;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
public class ClientKindTests
|
||||
{
|
||||
@@ -6,8 +6,9 @@ using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using NATS.Server;
|
||||
using NATS.Server.TestUtilities;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for client lifecycle: connection handshake, CONNECT proto parsing,
|
||||
@@ -16,26 +17,7 @@ namespace NATS.Server.Tests;
|
||||
/// </summary>
|
||||
public class ClientLifecycleTests
|
||||
{
|
||||
private static int GetFreePort()
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
sock.Bind(new IPEndPoint(IPAddress.Loopback, 0));
|
||||
return ((IPEndPoint)sock.LocalEndPoint!).Port;
|
||||
}
|
||||
|
||||
private static async Task<string> ReadUntilAsync(Socket sock, string expected, int timeoutMs = 5000)
|
||||
{
|
||||
using var cts = new CancellationTokenSource(timeoutMs);
|
||||
var sb = new StringBuilder();
|
||||
var buf = new byte[4096];
|
||||
while (!sb.ToString().Contains(expected))
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// TestClientConnectProto: Sends CONNECT with verbose:false, pedantic:false, name:"test-client"
|
||||
@@ -45,7 +27,7 @@ public class ClientLifecycleTests
|
||||
[Fact]
|
||||
public async Task Connect_proto_accepted()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
using var cts = new CancellationTokenSource();
|
||||
var server = new NatsServer(new NatsOptions { Port = port }, NullLoggerFactory.Instance);
|
||||
_ = server.StartAsync(cts.Token);
|
||||
@@ -67,7 +49,7 @@ public class ClientLifecycleTests
|
||||
await client.SendAsync(Encoding.ASCII.GetBytes(connectMsg));
|
||||
|
||||
// Should receive PONG confirming connection is accepted
|
||||
var response = await ReadUntilAsync(client, "PONG");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(client, "PONG");
|
||||
response.ShouldContain("PONG\r\n");
|
||||
}
|
||||
finally
|
||||
@@ -87,7 +69,7 @@ public class ClientLifecycleTests
|
||||
public async Task Max_subscriptions_enforced()
|
||||
{
|
||||
const int maxSubs = 10;
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
using var cts = new CancellationTokenSource();
|
||||
var server = new NatsServer(
|
||||
new NatsOptions { Port = port, MaxSubs = maxSubs },
|
||||
@@ -119,7 +101,7 @@ public class ClientLifecycleTests
|
||||
await client.SendAsync(Encoding.ASCII.GetBytes(subsBuilder.ToString()));
|
||||
|
||||
// Server should send -ERR 'Maximum Subscriptions Exceeded' and close
|
||||
var response = await ReadUntilAsync(client, "-ERR", timeoutMs: 5000);
|
||||
var response = await SocketTestHelper.ReadUntilAsync(client, "-ERR", timeoutMs: 5000);
|
||||
response.ShouldContain("-ERR 'Maximum Subscriptions Exceeded'");
|
||||
|
||||
// Connection should be closed after the error
|
||||
@@ -144,7 +126,7 @@ public class ClientLifecycleTests
|
||||
[Fact]
|
||||
public async Task Auth_timeout_closes_connection_if_no_connect()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
using var cts = new CancellationTokenSource();
|
||||
var server = new NatsServer(
|
||||
new NatsOptions
|
||||
@@ -170,7 +152,7 @@ public class ClientLifecycleTests
|
||||
|
||||
// Do NOT send CONNECT — wait for auth timeout to fire
|
||||
// AuthTimeout is 500ms; wait up to 3x that for the error
|
||||
var response = await ReadUntilAsync(client, "Authentication Timeout", timeoutMs: 3000);
|
||||
var response = await SocketTestHelper.ReadUntilAsync(client, "Authentication Timeout", timeoutMs: 3000);
|
||||
response.ShouldContain("-ERR 'Authentication Timeout'");
|
||||
|
||||
// Connection should be closed after the auth timeout error
|
||||
@@ -10,8 +10,9 @@ using Microsoft.Extensions.Logging.Abstractions;
|
||||
using NATS.Server;
|
||||
using NATS.Server.Auth;
|
||||
using NATS.Server.Protocol;
|
||||
using NATS.Server.TestUtilities;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Protocol-level parity tests ported from Go client_test.go.
|
||||
@@ -24,28 +25,6 @@ public class ClientProtocolParityTests
|
||||
// Helpers (self-contained, duplicated per task spec)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
private static int GetFreePort()
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
sock.Bind(new IPEndPoint(IPAddress.Loopback, 0));
|
||||
return ((IPEndPoint)sock.LocalEndPoint!).Port;
|
||||
}
|
||||
|
||||
private static async Task<string> ReadUntilAsync(Socket sock, string expected, int timeoutMs = 5000)
|
||||
{
|
||||
using var cts = new CancellationTokenSource(timeoutMs);
|
||||
var sb = new StringBuilder();
|
||||
var buf = new byte[8192];
|
||||
while (!sb.ToString().Contains(expected))
|
||||
{
|
||||
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 async Task<string> ReadAllAvailableAsync(Socket sock, int timeoutMs = 1000)
|
||||
{
|
||||
using var cts = new CancellationTokenSource(timeoutMs);
|
||||
@@ -87,7 +66,7 @@ public class ClientProtocolParityTests
|
||||
private static async Task<(NatsServer Server, int Port, CancellationTokenSource Cts)>
|
||||
StartServerAsync(NatsOptions? options = null)
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
options ??= new NatsOptions();
|
||||
options.Port = port;
|
||||
var cts = new CancellationTokenSource();
|
||||
@@ -104,7 +83,7 @@ public class ClientProtocolParityTests
|
||||
{
|
||||
var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
await sock.ConnectAsync(IPAddress.Loopback, port);
|
||||
await ReadUntilAsync(sock, "\r\n"); // drain INFO
|
||||
await SocketTestHelper.ReadUntilAsync(sock, "\r\n"); // drain INFO
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes($"CONNECT {connectJson}\r\n"));
|
||||
return sock;
|
||||
}
|
||||
@@ -116,7 +95,7 @@ public class ClientProtocolParityTests
|
||||
{
|
||||
var sock = await ConnectAndHandshakeAsync(port, connectJson);
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
|
||||
await ReadUntilAsync(sock, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
return sock;
|
||||
}
|
||||
|
||||
@@ -134,7 +113,7 @@ public class ClientProtocolParityTests
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
await sock.ConnectAsync(IPAddress.Loopback, port);
|
||||
|
||||
var info = await ReadUntilAsync(sock, "\r\n");
|
||||
var info = await SocketTestHelper.ReadUntilAsync(sock, "\r\n");
|
||||
info.ShouldStartWith("INFO ");
|
||||
|
||||
var jsonStart = info.IndexOf('{');
|
||||
@@ -166,7 +145,7 @@ public class ClientProtocolParityTests
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
await sock.ConnectAsync(IPAddress.Loopback, port);
|
||||
|
||||
var info = await ReadUntilAsync(sock, "\r\n");
|
||||
var info = await SocketTestHelper.ReadUntilAsync(sock, "\r\n");
|
||||
var jsonStr = info[(info.IndexOf('{'))..(info.LastIndexOf('}') + 1)];
|
||||
var serverInfo = JsonSerializer.Deserialize<ServerInfo>(jsonStr);
|
||||
serverInfo!.MaxPayload.ShouldBe(maxPayload);
|
||||
@@ -188,7 +167,7 @@ public class ClientProtocolParityTests
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
await sock.ConnectAsync(IPAddress.Loopback, port);
|
||||
|
||||
var info = await ReadUntilAsync(sock, "\r\n");
|
||||
var info = await SocketTestHelper.ReadUntilAsync(sock, "\r\n");
|
||||
info.ShouldContain("\"auth_required\":true");
|
||||
}
|
||||
finally
|
||||
@@ -208,7 +187,7 @@ public class ClientProtocolParityTests
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
await sock.ConnectAsync(IPAddress.Loopback, port);
|
||||
|
||||
var info = await ReadUntilAsync(sock, "\r\n");
|
||||
var info = await SocketTestHelper.ReadUntilAsync(sock, "\r\n");
|
||||
// auth_required should not be present (or should be false/omitted)
|
||||
info.ShouldNotContain("\"auth_required\":true");
|
||||
}
|
||||
@@ -234,7 +213,7 @@ public class ClientProtocolParityTests
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
|
||||
|
||||
// With verbose:true, the CONNECT itself triggers +OK, then PING triggers PONG + +OK
|
||||
var response = await ReadUntilAsync(sock, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
response.ShouldContain("+OK\r\n");
|
||||
response.ShouldContain("PONG\r\n");
|
||||
}
|
||||
@@ -256,7 +235,7 @@ public class ClientProtocolParityTests
|
||||
|
||||
// PUB should not trigger +OK when verbose is false
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("PUB foo 5\r\nhello\r\nPING\r\n"));
|
||||
var response = await ReadUntilAsync(sock, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
|
||||
response.ShouldNotContain("+OK");
|
||||
}
|
||||
@@ -276,10 +255,10 @@ public class ClientProtocolParityTests
|
||||
{
|
||||
using var sock = await ConnectAndHandshakeAsync(port, "{\"verbose\":true}");
|
||||
// Drain the +OK from CONNECT
|
||||
await ReadUntilAsync(sock, "+OK\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(sock, "+OK\r\n");
|
||||
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("SUB foo 1\r\nPING\r\n"));
|
||||
var response = await ReadUntilAsync(sock, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
|
||||
// SUB should trigger +OK in verbose mode
|
||||
response.ShouldContain("+OK\r\n");
|
||||
@@ -299,10 +278,10 @@ public class ClientProtocolParityTests
|
||||
try
|
||||
{
|
||||
using var sock = await ConnectAndHandshakeAsync(port, "{\"verbose\":true}");
|
||||
await ReadUntilAsync(sock, "+OK\r\n"); // drain CONNECT +OK
|
||||
await SocketTestHelper.ReadUntilAsync(sock, "+OK\r\n"); // drain CONNECT +OK
|
||||
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("SUB foo 1\r\nUNSUB 1\r\nPING\r\n"));
|
||||
var response = await ReadUntilAsync(sock, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
|
||||
// Should get two +OK (SUB + UNSUB) plus PONG
|
||||
CountOccurrences(response, "+OK\r\n").ShouldBeGreaterThanOrEqualTo(2);
|
||||
@@ -322,10 +301,10 @@ public class ClientProtocolParityTests
|
||||
try
|
||||
{
|
||||
using var sock = await ConnectAndHandshakeAsync(port, "{\"verbose\":true}");
|
||||
await ReadUntilAsync(sock, "+OK\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(sock, "+OK\r\n");
|
||||
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("PUB foo 5\r\nhello\r\nPING\r\n"));
|
||||
var response = await ReadUntilAsync(sock, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
|
||||
// PUB should trigger +OK in verbose mode
|
||||
response.ShouldContain("+OK\r\n");
|
||||
@@ -350,7 +329,7 @@ public class ClientProtocolParityTests
|
||||
using var sock = await ConnectAndHandshakeAsync(port,
|
||||
"{\"user\":\"derek\",\"pass\":\"foo\"}");
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
|
||||
var response = await ReadUntilAsync(sock, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
response.ShouldContain("PONG\r\n");
|
||||
}
|
||||
finally
|
||||
@@ -373,7 +352,7 @@ public class ClientProtocolParityTests
|
||||
using var sock = await ConnectAndHandshakeAsync(port,
|
||||
"{\"auth_token\":\"YZZ222\"}");
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
|
||||
var response = await ReadUntilAsync(sock, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
response.ShouldContain("PONG\r\n");
|
||||
}
|
||||
finally
|
||||
@@ -393,7 +372,7 @@ public class ClientProtocolParityTests
|
||||
using var sock = await ConnectAndHandshakeAsync(port,
|
||||
"{\"name\":\"my-test-client\"}");
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
|
||||
var response = await ReadUntilAsync(sock, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
response.ShouldContain("PONG\r\n");
|
||||
}
|
||||
finally
|
||||
@@ -417,7 +396,7 @@ public class ClientProtocolParityTests
|
||||
using var sock = await ConnectAndHandshakeAsync(port,
|
||||
"{\"verbose\":false,\"pedantic\":false,\"protocol\":0}");
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
|
||||
var response = await ReadUntilAsync(sock, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
response.ShouldContain("PONG\r\n");
|
||||
}
|
||||
finally
|
||||
@@ -437,7 +416,7 @@ public class ClientProtocolParityTests
|
||||
using var sock = await ConnectAndHandshakeAsync(port,
|
||||
"{\"verbose\":false,\"pedantic\":false,\"protocol\":1}");
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
|
||||
var response = await ReadUntilAsync(sock, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
response.ShouldContain("PONG\r\n");
|
||||
}
|
||||
finally
|
||||
@@ -460,7 +439,7 @@ public class ClientProtocolParityTests
|
||||
{
|
||||
using var sock = await ConnectAndHandshakeAsync(port);
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
|
||||
var response = await ReadUntilAsync(sock, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
response.ShouldContain("PONG\r\n");
|
||||
}
|
||||
finally
|
||||
@@ -535,7 +514,7 @@ public class ClientProtocolParityTests
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes(
|
||||
$"SUB foo 1\r\nPUB foo {payload.Length}\r\n{payload}\r\nPING\r\n"));
|
||||
|
||||
var response = await ReadUntilAsync(sock, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
response.ShouldContain("MSG foo 1");
|
||||
response.ShouldContain("PONG\r\n");
|
||||
}
|
||||
@@ -593,7 +572,7 @@ public class ClientProtocolParityTests
|
||||
// Publish to an invalid subject (contains space)
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("PUB foo.*.bar 5\r\nhello\r\nPING\r\n"));
|
||||
|
||||
var response = await ReadUntilAsync(sock, "PONG\r\n", 5000);
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n", 5000);
|
||||
response.ShouldContain("-ERR 'Invalid Publish Subject'");
|
||||
}
|
||||
finally
|
||||
@@ -615,7 +594,7 @@ public class ClientProtocolParityTests
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes(
|
||||
"SUB foo.bar 1\r\nPUB foo.bar 5\r\nhello\r\nPING\r\n"));
|
||||
|
||||
var response = await ReadUntilAsync(sock, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
response.ShouldContain("MSG foo.bar 1 5\r\nhello\r\n");
|
||||
response.ShouldNotContain("-ERR");
|
||||
}
|
||||
@@ -637,7 +616,7 @@ public class ClientProtocolParityTests
|
||||
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("PUB foo.> 5\r\nhello\r\nPING\r\n"));
|
||||
|
||||
var response = await ReadUntilAsync(sock, "PONG\r\n", 5000);
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n", 5000);
|
||||
response.ShouldContain("-ERR 'Invalid Publish Subject'");
|
||||
}
|
||||
finally
|
||||
@@ -663,7 +642,7 @@ public class ClientProtocolParityTests
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes(
|
||||
"SUB foo 1\r\nPUB foo 5\r\nhello\r\nPING\r\n"));
|
||||
|
||||
var response = await ReadUntilAsync(sock, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
response.ShouldContain("MSG foo 1 5\r\nhello\r\n");
|
||||
}
|
||||
finally
|
||||
@@ -685,7 +664,7 @@ public class ClientProtocolParityTests
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes(
|
||||
"SUB foo 1\r\nPUB foo 5\r\nhello\r\nPING\r\n"));
|
||||
|
||||
var response = await ReadUntilAsync(sock, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
response.ShouldNotContain("MSG");
|
||||
}
|
||||
finally
|
||||
@@ -709,9 +688,9 @@ public class ClientProtocolParityTests
|
||||
|
||||
// Both subscribe to same queue group
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("SUB foo bar 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(pub, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(pub, "PONG\r\n");
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("SUB foo bar 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG\r\n");
|
||||
|
||||
// Publish 100 messages from the echo:false client
|
||||
var sb = new StringBuilder();
|
||||
@@ -719,11 +698,11 @@ public class ClientProtocolParityTests
|
||||
sb.Append("PUB foo 5\r\nhello\r\n");
|
||||
sb.Append("PING\r\n");
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes(sb.ToString()));
|
||||
await ReadUntilAsync(pub, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(pub, "PONG\r\n");
|
||||
|
||||
// Send PING on sub to flush deliveries
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
|
||||
var response = await ReadUntilAsync(sub, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sub, "PONG\r\n");
|
||||
|
||||
// The subscriber should receive all 100 messages since the publisher
|
||||
// has echo:false (all queue messages go to the other member)
|
||||
@@ -753,12 +732,12 @@ public class ClientProtocolParityTests
|
||||
// Publish first (no subscribers), then subscribe to "foo", then publish "foo.bar"
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes(
|
||||
"PUB foo.bar 5\r\nhello\r\nSUB foo 1\r\nPING\r\n"));
|
||||
var response1 = await ReadUntilAsync(sock, "PONG\r\n");
|
||||
var response1 = await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
response1.ShouldStartWith("PONG\r\n");
|
||||
|
||||
// Now publish foo.bar again -- should NOT match "foo" subscription
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("PUB foo.bar 5\r\nhello\r\nPING\r\n"));
|
||||
var response2 = await ReadUntilAsync(sock, "PONG\r\n");
|
||||
var response2 = await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
response2.ShouldStartWith("PONG\r\n");
|
||||
response2.ShouldNotContain("MSG");
|
||||
}
|
||||
@@ -785,7 +764,7 @@ public class ClientProtocolParityTests
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
await sock.ConnectAsync(IPAddress.Loopback, port);
|
||||
await ReadUntilAsync(sock, "\r\n"); // INFO
|
||||
await SocketTestHelper.ReadUntilAsync(sock, "\r\n"); // INFO
|
||||
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes(
|
||||
"CONNECT {\"auth_token\":\"wrong_token\"}\r\n"));
|
||||
@@ -812,7 +791,7 @@ public class ClientProtocolParityTests
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
await sock.ConnectAsync(IPAddress.Loopback, port);
|
||||
await ReadUntilAsync(sock, "\r\n"); // INFO
|
||||
await SocketTestHelper.ReadUntilAsync(sock, "\r\n"); // INFO
|
||||
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes(
|
||||
"CONNECT {\"user\":\"admin\",\"pass\":\"wrongpass\"}\r\n"));
|
||||
@@ -840,7 +819,7 @@ public class ClientProtocolParityTests
|
||||
using var sock = await ConnectAndHandshakeAsync(port,
|
||||
"{\"user\":\"admin\",\"pass\":\"secret\"}");
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
|
||||
var response = await ReadUntilAsync(sock, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
response.ShouldContain("PONG\r\n");
|
||||
}
|
||||
finally
|
||||
@@ -863,10 +842,10 @@ public class ClientProtocolParityTests
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
await sock.ConnectAsync(IPAddress.Loopback, port);
|
||||
await ReadUntilAsync(sock, "\r\n"); // INFO
|
||||
await SocketTestHelper.ReadUntilAsync(sock, "\r\n"); // INFO
|
||||
|
||||
// Do NOT send CONNECT
|
||||
var response = await ReadUntilAsync(sock, "Authentication Timeout", timeoutMs: 5000);
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "Authentication Timeout", timeoutMs: 5000);
|
||||
response.ShouldContain("-ERR 'Authentication Timeout'");
|
||||
}
|
||||
finally
|
||||
@@ -906,7 +885,7 @@ public class ClientProtocolParityTests
|
||||
|
||||
// Subscribe to a denied subject
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("SUB denied.topic 1\r\nPING\r\n"));
|
||||
var response = await ReadUntilAsync(sock, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
response.ShouldContain("-ERR 'Permissions Violation for Subscription'");
|
||||
}
|
||||
finally
|
||||
@@ -941,7 +920,7 @@ public class ClientProtocolParityTests
|
||||
"{\"user\":\"limited\",\"pass\":\"pass\",\"verbose\":false}");
|
||||
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("PUB denied.topic 5\r\nhello\r\nPING\r\n"));
|
||||
var response = await ReadUntilAsync(sock, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
response.ShouldContain("-ERR 'Permissions Violation for Publish'");
|
||||
}
|
||||
finally
|
||||
@@ -978,7 +957,7 @@ public class ClientProtocolParityTests
|
||||
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes(
|
||||
"SUB allowed.topic 1\r\nPUB allowed.topic 5\r\nhello\r\nPING\r\n"));
|
||||
var response = await ReadUntilAsync(sock, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
response.ShouldContain("MSG allowed.topic 1 5\r\nhello\r\n");
|
||||
response.ShouldNotContain("-ERR");
|
||||
}
|
||||
@@ -1019,12 +998,12 @@ public class ClientProtocolParityTests
|
||||
|
||||
// Allowed
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("PUB public.topic 5\r\nhello\r\nPING\r\n"));
|
||||
var r1 = await ReadUntilAsync(sock, "PONG\r\n");
|
||||
var r1 = await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
r1.ShouldNotContain("-ERR");
|
||||
|
||||
// Denied
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("PUB secret.data 5\r\nhello\r\nPING\r\n"));
|
||||
var r2 = await ReadUntilAsync(sock, "PONG\r\n");
|
||||
var r2 = await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
r2.ShouldContain("-ERR 'Permissions Violation for Publish'");
|
||||
}
|
||||
finally
|
||||
@@ -1047,7 +1026,7 @@ public class ClientProtocolParityTests
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
await sock.ConnectAsync(IPAddress.Loopback, port);
|
||||
await ReadUntilAsync(sock, "\r\n"); // INFO
|
||||
await SocketTestHelper.ReadUntilAsync(sock, "\r\n"); // INFO
|
||||
|
||||
// no_responders without headers should fail
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes(
|
||||
@@ -1075,12 +1054,12 @@ public class ClientProtocolParityTests
|
||||
|
||||
// Subscribe on the reply inbox
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("SUB reply.inbox 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sock, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
|
||||
// Publish to a subject with no subscribers, with a reply subject
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("PUB no.listeners reply.inbox 0\r\n\r\n"));
|
||||
|
||||
var response = await ReadUntilAsync(sock, "NATS/1.0 503", timeoutMs: 5000);
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "NATS/1.0 503", timeoutMs: 5000);
|
||||
response.ShouldContain("HMSG reply.inbox");
|
||||
response.ShouldContain("NATS/1.0 503");
|
||||
}
|
||||
@@ -1104,7 +1083,7 @@ public class ClientProtocolParityTests
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
await sock.ConnectAsync(IPAddress.Loopback, port);
|
||||
var info = await ReadUntilAsync(sock, "\r\n");
|
||||
var info = await SocketTestHelper.ReadUntilAsync(sock, "\r\n");
|
||||
info.ShouldContain("\"headers\":true");
|
||||
}
|
||||
finally
|
||||
@@ -1125,7 +1104,7 @@ public class ClientProtocolParityTests
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
await sock.ConnectAsync(IPAddress.Loopback, port);
|
||||
var info = await ReadUntilAsync(sock, "\r\n");
|
||||
var info = await SocketTestHelper.ReadUntilAsync(sock, "\r\n");
|
||||
info.ShouldContain("\"headers\":true");
|
||||
}
|
||||
finally
|
||||
@@ -1146,12 +1125,12 @@ public class ClientProtocolParityTests
|
||||
using var pub = await ConnectAndPingAsync(port, "{\"headers\":true}");
|
||||
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("SUB foo 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG\r\n");
|
||||
|
||||
// HPUB foo 12 14\r\nName:Derek\r\nOK
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("HPUB foo 12 14\r\nName:Derek\r\nOK\r\n"));
|
||||
|
||||
var response = await ReadUntilAsync(sub, "OK\r\n", timeoutMs: 5000);
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sub, "OK\r\n", timeoutMs: 5000);
|
||||
response.ShouldContain("HMSG foo 1 12 14\r\n");
|
||||
response.ShouldContain("Name:Derek");
|
||||
}
|
||||
@@ -1210,7 +1189,7 @@ public class ClientProtocolParityTests
|
||||
sb.Append("PING\r\n");
|
||||
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes(sb.ToString()));
|
||||
var response = await ReadUntilAsync(sock, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
|
||||
response.ShouldNotContain("-ERR");
|
||||
response.ShouldContain("PONG\r\n");
|
||||
@@ -1235,7 +1214,7 @@ public class ClientProtocolParityTests
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
await sock.ConnectAsync(IPAddress.Loopback, port);
|
||||
var info = await ReadUntilAsync(sock, "\r\n");
|
||||
var info = await SocketTestHelper.ReadUntilAsync(sock, "\r\n");
|
||||
|
||||
var jsonStr = info[(info.IndexOf('{'))..(info.LastIndexOf('}') + 1)];
|
||||
var serverInfo = JsonSerializer.Deserialize<ServerInfo>(jsonStr);
|
||||
@@ -1285,7 +1264,7 @@ public class ClientProtocolParityTests
|
||||
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes(
|
||||
"SUB foo 1\r\nSUB bar 2\r\nSUB baz 3\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sock, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
|
||||
server.SubList.Count.ShouldBe(3u);
|
||||
|
||||
@@ -1395,7 +1374,7 @@ public class ClientProtocolParityTests
|
||||
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes(
|
||||
"SUB foo 1\r\nPUB foo reply.to 5\r\nhello\r\nPING\r\n"));
|
||||
var response = await ReadUntilAsync(sock, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
|
||||
response.ShouldContain("MSG foo 1 reply.to 5\r\nhello\r\n");
|
||||
}
|
||||
@@ -1417,7 +1396,7 @@ public class ClientProtocolParityTests
|
||||
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes(
|
||||
"SUB foo 1\r\nPUB foo reply.to 0\r\n\r\nPING\r\n"));
|
||||
var response = await ReadUntilAsync(sock, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
|
||||
response.ShouldContain("MSG foo 1 reply.to 0\r\n");
|
||||
}
|
||||
@@ -1444,13 +1423,13 @@ public class ClientProtocolParityTests
|
||||
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes(
|
||||
"SUB foo 1\r\nSUB foo 2\r\nUNSUB 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG\r\n");
|
||||
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("PUB foo 5\r\nhello\r\nPING\r\n"));
|
||||
await ReadUntilAsync(pub, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(pub, "PONG\r\n");
|
||||
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
|
||||
var response = await ReadUntilAsync(sub, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sub, "PONG\r\n");
|
||||
|
||||
response.ShouldContain("MSG foo 2 5");
|
||||
response.ShouldNotContain("MSG foo 1 5");
|
||||
@@ -1474,7 +1453,7 @@ public class ClientProtocolParityTests
|
||||
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes(
|
||||
"SUB foo 1\r\nUNSUB 1 5\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG\r\n");
|
||||
|
||||
// Publish 10 messages
|
||||
var sb = new StringBuilder();
|
||||
@@ -1482,7 +1461,7 @@ public class ClientProtocolParityTests
|
||||
sb.Append("PUB foo 1\r\nx\r\n");
|
||||
sb.Append("PING\r\n");
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes(sb.ToString()));
|
||||
await ReadUntilAsync(pub, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(pub, "PONG\r\n");
|
||||
|
||||
// Collect messages on subscriber
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
|
||||
@@ -1509,10 +1488,10 @@ public class ClientProtocolParityTests
|
||||
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes(
|
||||
"SUB foo 1\r\nUNSUB 1 100\r\nUNSUB 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG\r\n");
|
||||
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("PUB foo 5\r\nhello\r\nPING\r\n"));
|
||||
await ReadUntilAsync(pub, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(pub, "PONG\r\n");
|
||||
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
|
||||
var response = await ReadAllAvailableAsync(sub, 1000);
|
||||
@@ -1542,7 +1521,7 @@ public class ClientProtocolParityTests
|
||||
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes(
|
||||
"SUB foo g1 1\r\nSUB foo g1 2\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sock, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
|
||||
var sb = new StringBuilder();
|
||||
for (int i = 0; i < count; i++)
|
||||
@@ -1550,7 +1529,7 @@ public class ClientProtocolParityTests
|
||||
sb.Append("PING\r\n");
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes(sb.ToString()));
|
||||
|
||||
var response = await ReadUntilAsync(sock, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
|
||||
var n1 = CountOccurrences(response, "MSG foo 1 5");
|
||||
var n2 = CountOccurrences(response, "MSG foo 2 5");
|
||||
@@ -1579,7 +1558,7 @@ public class ClientProtocolParityTests
|
||||
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes(
|
||||
"PUB foo 5\r\nhello\r\nPUB foo 5\r\nhello\r\nPUB foo 5\r\nhello\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sock, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
|
||||
Interlocked.Read(ref server.Stats.InMsgs).ShouldBeGreaterThanOrEqualTo(3);
|
||||
}
|
||||
@@ -1600,7 +1579,7 @@ public class ClientProtocolParityTests
|
||||
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes(
|
||||
"PUB foo 10\r\n0123456789\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sock, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
|
||||
Interlocked.Read(ref server.Stats.InBytes).ShouldBeGreaterThanOrEqualTo(10);
|
||||
}
|
||||
@@ -1621,7 +1600,7 @@ public class ClientProtocolParityTests
|
||||
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes(
|
||||
"SUB foo 1\r\nPUB foo 5\r\nhello\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sock, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
|
||||
Interlocked.Read(ref server.Stats.OutMsgs).ShouldBeGreaterThanOrEqualTo(1);
|
||||
}
|
||||
@@ -1647,7 +1626,7 @@ public class ClientProtocolParityTests
|
||||
using var slowSub = await ConnectAndPingAsync(port, "{\"verbose\":false}");
|
||||
|
||||
await slowSub.SendAsync(Encoding.ASCII.GetBytes("SUB flood 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(slowSub, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(slowSub, "PONG\r\n");
|
||||
|
||||
using var pub = await ConnectAndPingAsync(port, "{\"verbose\":false}");
|
||||
|
||||
@@ -1658,7 +1637,7 @@ public class ClientProtocolParityTests
|
||||
sb.Append($"PUB flood {payload.Length}\r\n{payload}\r\n");
|
||||
sb.Append("PING\r\n");
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes(sb.ToString()));
|
||||
await ReadUntilAsync(pub, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(pub, "PONG\r\n");
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
@@ -1683,7 +1662,7 @@ public class ClientProtocolParityTests
|
||||
try
|
||||
{
|
||||
using var sock = await ConnectAndHandshakeAsync(port, "{\"verbose\":true}");
|
||||
await ReadUntilAsync(sock, "+OK\r\n"); // drain CONNECT +OK
|
||||
await SocketTestHelper.ReadUntilAsync(sock, "+OK\r\n"); // drain CONNECT +OK
|
||||
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
|
||||
var response = await ReadAllAvailableAsync(sock, 2000);
|
||||
@@ -1713,13 +1692,13 @@ public class ClientProtocolParityTests
|
||||
using var pub = await ConnectAndPingAsync(port);
|
||||
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("SUB foo 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG\r\n");
|
||||
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("PUB foo 5\r\nhello\r\nPING\r\n"));
|
||||
await ReadUntilAsync(pub, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(pub, "PONG\r\n");
|
||||
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
|
||||
var response = await ReadUntilAsync(sub, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sub, "PONG\r\n");
|
||||
response.ShouldContain("MSG foo 1 5\r\nhello\r\n");
|
||||
}
|
||||
finally
|
||||
@@ -1739,14 +1718,14 @@ public class ClientProtocolParityTests
|
||||
using var pub = await ConnectAndPingAsync(port);
|
||||
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("SUB foo.* 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG\r\n");
|
||||
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes(
|
||||
"PUB foo.bar 5\r\nhello\r\nPUB foo.baz 5\r\nworld\r\nPING\r\n"));
|
||||
await ReadUntilAsync(pub, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(pub, "PONG\r\n");
|
||||
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
|
||||
var response = await ReadUntilAsync(sub, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sub, "PONG\r\n");
|
||||
response.ShouldContain("MSG foo.bar 1 5\r\nhello\r\n");
|
||||
response.ShouldContain("MSG foo.baz 1 5\r\nworld\r\n");
|
||||
}
|
||||
@@ -1767,14 +1746,14 @@ public class ClientProtocolParityTests
|
||||
using var pub = await ConnectAndPingAsync(port);
|
||||
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("SUB foo.> 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG\r\n");
|
||||
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes(
|
||||
"PUB foo.bar.baz 5\r\nhello\r\nPING\r\n"));
|
||||
await ReadUntilAsync(pub, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(pub, "PONG\r\n");
|
||||
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
|
||||
var response = await ReadUntilAsync(sub, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sub, "PONG\r\n");
|
||||
response.ShouldContain("MSG foo.bar.baz 1 5\r\nhello\r\n");
|
||||
}
|
||||
finally
|
||||
@@ -1800,14 +1779,14 @@ public class ClientProtocolParityTests
|
||||
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes(
|
||||
"SUB foo 1\r\nUNSUB 1 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG\r\n");
|
||||
|
||||
var sb = new StringBuilder();
|
||||
for (int i = 0; i < 5; i++)
|
||||
sb.Append("PUB foo 2\r\nok\r\n");
|
||||
sb.Append("PING\r\n");
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes(sb.ToString()));
|
||||
await ReadUntilAsync(pub, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(pub, "PONG\r\n");
|
||||
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
|
||||
var response = await ReadAllAvailableAsync(sub, 2000);
|
||||
@@ -1833,7 +1812,7 @@ public class ClientProtocolParityTests
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
await sock.ConnectAsync(IPAddress.Loopback, port);
|
||||
await ReadUntilAsync(sock, "\r\n"); // INFO
|
||||
await SocketTestHelper.ReadUntilAsync(sock, "\r\n"); // INFO
|
||||
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes(
|
||||
"CONNECT {\"no_responders\":true,\"headers\":false}\r\n"));
|
||||
@@ -1864,13 +1843,13 @@ public class ClientProtocolParityTests
|
||||
using var sub = await ConnectAndPingAsync(port, "{\"headers\":true}");
|
||||
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("SUB foo 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG\r\n");
|
||||
|
||||
// HPUB with valid header block
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes(
|
||||
"HPUB foo 12 14\r\nName:Derek\r\nOK\r\n"));
|
||||
|
||||
var response = await ReadUntilAsync(sub, "OK\r\n", timeoutMs: 5000);
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sub, "OK\r\n", timeoutMs: 5000);
|
||||
response.ShouldContain("HMSG foo 1 12 14\r\n");
|
||||
}
|
||||
finally
|
||||
@@ -1894,7 +1873,7 @@ public class ClientProtocolParityTests
|
||||
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes(
|
||||
"SUB foo 1\r\nPUB foo 0\r\n\r\nPING\r\n"));
|
||||
var response = await ReadUntilAsync(sock, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
|
||||
response.ShouldContain("MSG foo 1 0\r\n");
|
||||
}
|
||||
@@ -1948,7 +1927,7 @@ public class ClientProtocolParityTests
|
||||
using var pub = await ConnectAndPingAsync(port);
|
||||
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("SUB foo 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG\r\n");
|
||||
|
||||
// Start publishing concurrently
|
||||
var pubTask = Task.Run(async () =>
|
||||
@@ -1990,13 +1969,13 @@ public class ClientProtocolParityTests
|
||||
using var sock = await ConnectAndHandshakeAsync(port, "{\"verbose\":true}");
|
||||
|
||||
// Drain +OK from CONNECT
|
||||
await ReadUntilAsync(sock, "+OK\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(sock, "+OK\r\n");
|
||||
|
||||
// SUB -> +OK, PUB -> +OK, UNSUB -> +OK, PING -> PONG + +OK
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes(
|
||||
"SUB foo 1\r\nPUB foo 5\r\nhello\r\nUNSUB 1\r\nPING\r\n"));
|
||||
|
||||
var response = await ReadUntilAsync(sock, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
|
||||
// At least 3 +OK (SUB, PUB, UNSUB) plus the one for PING
|
||||
CountOccurrences(response, "+OK\r\n").ShouldBeGreaterThanOrEqualTo(3);
|
||||
@@ -2024,19 +2003,19 @@ public class ClientProtocolParityTests
|
||||
using var pub = await ConnectAndPingAsync(port);
|
||||
|
||||
await sub1.SendAsync(Encoding.ASCII.GetBytes("SUB foo 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub1, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(sub1, "PONG\r\n");
|
||||
await sub2.SendAsync(Encoding.ASCII.GetBytes("SUB foo 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub2, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(sub2, "PONG\r\n");
|
||||
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("PUB foo 5\r\nhello\r\nPING\r\n"));
|
||||
await ReadUntilAsync(pub, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(pub, "PONG\r\n");
|
||||
|
||||
await sub1.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
|
||||
var r1 = await ReadUntilAsync(sub1, "PONG\r\n");
|
||||
var r1 = await SocketTestHelper.ReadUntilAsync(sub1, "PONG\r\n");
|
||||
r1.ShouldContain("MSG foo 1 5\r\nhello\r\n");
|
||||
|
||||
await sub2.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
|
||||
var r2 = await ReadUntilAsync(sub2, "PONG\r\n");
|
||||
var r2 = await SocketTestHelper.ReadUntilAsync(sub2, "PONG\r\n");
|
||||
r2.ShouldContain("MSG foo 1 5\r\nhello\r\n");
|
||||
}
|
||||
finally
|
||||
@@ -2058,7 +2037,7 @@ public class ClientProtocolParityTests
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
await sock.ConnectAsync(IPAddress.Loopback, port);
|
||||
var info = await ReadUntilAsync(sock, "\r\n");
|
||||
var info = await SocketTestHelper.ReadUntilAsync(sock, "\r\n");
|
||||
|
||||
var jsonStr = info[(info.IndexOf('{'))..(info.LastIndexOf('}') + 1)];
|
||||
var si = JsonSerializer.Deserialize<ServerInfo>(jsonStr);
|
||||
@@ -2084,7 +2063,7 @@ public class ClientProtocolParityTests
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
await sock.ConnectAsync(IPAddress.Loopback, port);
|
||||
var info = await ReadUntilAsync(sock, "\r\n");
|
||||
var info = await SocketTestHelper.ReadUntilAsync(sock, "\r\n");
|
||||
info.ShouldContain("\"proto\":");
|
||||
}
|
||||
finally
|
||||
@@ -2106,7 +2085,7 @@ public class ClientProtocolParityTests
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
await sock.ConnectAsync(IPAddress.Loopback, port);
|
||||
var info = await ReadUntilAsync(sock, "\r\n");
|
||||
var info = await SocketTestHelper.ReadUntilAsync(sock, "\r\n");
|
||||
|
||||
// host field should be present
|
||||
info.ShouldContain("\"host\":");
|
||||
@@ -2133,9 +2112,9 @@ public class ClientProtocolParityTests
|
||||
""".Trim();
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
await sock.ConnectAsync(IPAddress.Loopback, port);
|
||||
await ReadUntilAsync(sock, "\r\n"); // INFO
|
||||
await SocketTestHelper.ReadUntilAsync(sock, "\r\n"); // INFO
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes(connect + "\r\nPING\r\n"));
|
||||
var response = await ReadUntilAsync(sock, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
response.ShouldContain("PONG\r\n");
|
||||
}
|
||||
finally
|
||||
@@ -9,8 +9,9 @@ using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using NATS.Server;
|
||||
using NATS.Server.TestUtilities;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
public class ClientPubSubTests : IAsyncLifetime
|
||||
{
|
||||
@@ -20,7 +21,7 @@ public class ClientPubSubTests : IAsyncLifetime
|
||||
|
||||
public ClientPubSubTests()
|
||||
{
|
||||
_port = GetFreePort();
|
||||
_port = TestPortAllocator.GetFreePort();
|
||||
_server = new NatsServer(new NatsOptions { Port = _port }, NullLoggerFactory.Instance);
|
||||
}
|
||||
|
||||
@@ -36,12 +37,6 @@ public class ClientPubSubTests : IAsyncLifetime
|
||||
_server.Dispose();
|
||||
}
|
||||
|
||||
private static int GetFreePort()
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
sock.Bind(new IPEndPoint(IPAddress.Loopback, 0));
|
||||
return ((IPEndPoint)sock.LocalEndPoint!).Port;
|
||||
}
|
||||
|
||||
private async Task<Socket> ConnectClientAsync()
|
||||
{
|
||||
@@ -53,19 +48,6 @@ public class ClientPubSubTests : IAsyncLifetime
|
||||
/// <summary>
|
||||
/// Reads from a socket until the accumulated data contains the expected substring.
|
||||
/// </summary>
|
||||
private static async Task<string> ReadUntilAsync(Socket sock, string expected, int timeoutMs = 5000)
|
||||
{
|
||||
using var cts = new CancellationTokenSource(timeoutMs);
|
||||
var sb = new StringBuilder();
|
||||
var buf = new byte[4096];
|
||||
while (!sb.ToString().Contains(expected))
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
// Go reference: TestClientSimplePubSub (client_test.go line 666)
|
||||
// SUB foo 1, PUB foo 5\r\nhello — subscriber receives MSG foo 1 5\r\nhello
|
||||
@@ -83,7 +65,7 @@ public class ClientPubSubTests : IAsyncLifetime
|
||||
"CONNECT {}\r\nSUB foo 1\r\nPUB foo 5\r\nhello\r\nPING\r\n"));
|
||||
|
||||
// Read until we see the message payload (delivered before PONG)
|
||||
var response = await ReadUntilAsync(client, "hello\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(client, "hello\r\n");
|
||||
|
||||
// MSG line: MSG foo 1 5\r\nhello\r\n
|
||||
response.ShouldContain("MSG foo 1 5\r\nhello\r\n");
|
||||
@@ -106,7 +88,7 @@ public class ClientPubSubTests : IAsyncLifetime
|
||||
|
||||
// With echo=false the server must not deliver the message back to the publisher.
|
||||
// The first line we receive should be PONG, not MSG.
|
||||
var response = await ReadUntilAsync(client, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(client, "PONG\r\n");
|
||||
|
||||
response.ShouldStartWith("PONG\r\n");
|
||||
response.ShouldNotContain("MSG");
|
||||
@@ -127,7 +109,7 @@ public class ClientPubSubTests : IAsyncLifetime
|
||||
await client.SendAsync(Encoding.ASCII.GetBytes(
|
||||
"CONNECT {}\r\nSUB foo 1\r\nPUB foo bar 5\r\nhello\r\nPING\r\n"));
|
||||
|
||||
var response = await ReadUntilAsync(client, "hello\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(client, "hello\r\n");
|
||||
|
||||
// MSG line must include the reply subject: MSG <subject> <sid> <reply> <#bytes>
|
||||
response.ShouldContain("MSG foo 1 bar 5\r\nhello\r\n");
|
||||
@@ -149,7 +131,7 @@ public class ClientPubSubTests : IAsyncLifetime
|
||||
"CONNECT {}\r\nSUB foo 1\r\nPUB foo bar 0\r\n\r\nPING\r\n"));
|
||||
|
||||
// Read until PONG — MSG should arrive before PONG
|
||||
var response = await ReadUntilAsync(client, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(client, "PONG\r\n");
|
||||
|
||||
// MSG line: MSG foo 1 bar 0\r\n\r\n (empty body, still CRLF terminated)
|
||||
response.ShouldContain("MSG foo 1 bar 0\r\n");
|
||||
@@ -172,7 +154,7 @@ public class ClientPubSubTests : IAsyncLifetime
|
||||
// CONNECT, two queue subs with different sids, PING to confirm
|
||||
await client.SendAsync(Encoding.ASCII.GetBytes(
|
||||
"CONNECT {}\r\nSUB foo g1 1\r\nSUB foo g1 2\r\nPING\r\n"));
|
||||
await ReadUntilAsync(client, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(client, "PONG\r\n");
|
||||
|
||||
// Publish 100 messages, then PING to flush all deliveries
|
||||
var pubSb = new StringBuilder();
|
||||
@@ -182,7 +164,7 @@ public class ClientPubSubTests : IAsyncLifetime
|
||||
await client.SendAsync(Encoding.ASCII.GetBytes(pubSb.ToString()));
|
||||
|
||||
// Read until PONG — all MSGs arrive before the PONG
|
||||
var response = await ReadUntilAsync(client, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(client, "PONG\r\n");
|
||||
|
||||
// Count deliveries per sid
|
||||
var n1 = Regex.Matches(response, @"MSG foo 1 5").Count;
|
||||
@@ -10,8 +10,9 @@ using NATS.Server;
|
||||
using NATS.Server.Auth;
|
||||
using NATS.Server.Protocol;
|
||||
using NATS.Server.Server;
|
||||
using NATS.Server.TestUtilities;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for client protocol handling, matching Go client_test.go.
|
||||
@@ -22,12 +23,6 @@ public class ClientServerGoParityTests
|
||||
// Helpers shared across test methods
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
private static int GetFreePort()
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
sock.Bind(new IPEndPoint(IPAddress.Loopback, 0));
|
||||
return ((IPEndPoint)sock.LocalEndPoint!).Port;
|
||||
}
|
||||
|
||||
private static async Task<NatsServer> StartServerAsync(NatsOptions opts)
|
||||
{
|
||||
@@ -47,19 +42,6 @@ public class ClientServerGoParityTests
|
||||
/// <summary>
|
||||
/// Read until the accumulated data contains the expected substring, with timeout.
|
||||
/// </summary>
|
||||
private static async Task<string> ReadUntilAsync(Socket sock, string expected, int timeoutMs = 5000)
|
||||
{
|
||||
using var cts = new CancellationTokenSource(timeoutMs);
|
||||
var sb = new StringBuilder();
|
||||
var buf = new byte[4096];
|
||||
while (!sb.ToString().Contains(expected))
|
||||
{
|
||||
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 async Task<string> ReadLineAsync(Socket sock)
|
||||
{
|
||||
@@ -80,7 +62,7 @@ public class ClientServerGoParityTests
|
||||
[Fact]
|
||||
public async Task TestClientCreateAndInfo()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var opts = new NatsOptions { Port = port };
|
||||
var server = await StartServerAsync(opts);
|
||||
try
|
||||
@@ -121,7 +103,7 @@ public class ClientServerGoParityTests
|
||||
[Fact]
|
||||
public async Task TestClientConnect()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var server = await StartServerAsync(new NatsOptions { Port = port });
|
||||
try
|
||||
{
|
||||
@@ -131,7 +113,7 @@ public class ClientServerGoParityTests
|
||||
// Basic CONNECT with verbose and pedantic flags
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("CONNECT {\"verbose\":true,\"pedantic\":true}\r\nPING\r\n"));
|
||||
|
||||
var response = await ReadUntilAsync(sock, "PONG");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "PONG");
|
||||
// verbose=true means server sends +OK for CONNECT and PING
|
||||
response.ShouldContain("+OK");
|
||||
response.ShouldContain("PONG");
|
||||
@@ -155,7 +137,7 @@ public class ClientServerGoParityTests
|
||||
[Fact]
|
||||
public async Task TestClientConnectProto()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var server = await StartServerAsync(new NatsOptions { Port = port });
|
||||
try
|
||||
{
|
||||
@@ -163,14 +145,14 @@ public class ClientServerGoParityTests
|
||||
using var sock0 = await ConnectRawAsync(port);
|
||||
await ReadLineAsync(sock0);
|
||||
await sock0.SendAsync(Encoding.ASCII.GetBytes("CONNECT {\"protocol\":0}\r\nPING\r\n"));
|
||||
var r0 = await ReadUntilAsync(sock0, "PONG");
|
||||
var r0 = await SocketTestHelper.ReadUntilAsync(sock0, "PONG");
|
||||
r0.ShouldContain("PONG");
|
||||
|
||||
// protocol=1 (info proto)
|
||||
using var sock1 = await ConnectRawAsync(port);
|
||||
await ReadLineAsync(sock1);
|
||||
await sock1.SendAsync(Encoding.ASCII.GetBytes("CONNECT {\"protocol\":1}\r\nPING\r\n"));
|
||||
var r1 = await ReadUntilAsync(sock1, "PONG");
|
||||
var r1 = await SocketTestHelper.ReadUntilAsync(sock1, "PONG");
|
||||
r1.ShouldContain("PONG");
|
||||
}
|
||||
finally
|
||||
@@ -192,14 +174,14 @@ public class ClientServerGoParityTests
|
||||
[Fact]
|
||||
public async Task TestClientPing()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var server = await StartServerAsync(new NatsOptions { Port = port });
|
||||
try
|
||||
{
|
||||
using var sock = await ConnectRawAsync(port);
|
||||
await ReadLineAsync(sock); // INFO
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\nPING\r\n"));
|
||||
var response = await ReadUntilAsync(sock, "PONG");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "PONG");
|
||||
response.ShouldContain("PONG\r\n");
|
||||
}
|
||||
finally
|
||||
@@ -221,7 +203,7 @@ public class ClientServerGoParityTests
|
||||
[Fact]
|
||||
public async Task TestClientPub()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var server = await StartServerAsync(new NatsOptions { Port = port });
|
||||
try
|
||||
{
|
||||
@@ -232,11 +214,11 @@ public class ClientServerGoParityTests
|
||||
await ReadLineAsync(sub);
|
||||
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\nSUB foo 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG");
|
||||
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\nPUB foo 5\r\nhello\r\n"));
|
||||
|
||||
var msg = await ReadUntilAsync(sub, "hello");
|
||||
var msg = await SocketTestHelper.ReadUntilAsync(sub, "hello");
|
||||
msg.ShouldContain("MSG foo 1 5\r\nhello\r\n");
|
||||
}
|
||||
finally
|
||||
@@ -258,7 +240,7 @@ public class ClientServerGoParityTests
|
||||
[Fact]
|
||||
public async Task TestClientPubPermission()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var opts = new NatsOptions
|
||||
{
|
||||
Port = port,
|
||||
@@ -284,7 +266,7 @@ public class ClientServerGoParityTests
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes(
|
||||
"CONNECT {\"user\":\"testuser\",\"pass\":\"testpass\"}\r\nPUB denied.subject 3\r\nfoo\r\n"));
|
||||
|
||||
var response = await ReadUntilAsync(sock, "-ERR", timeoutMs: 3000);
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "-ERR", timeoutMs: 3000);
|
||||
response.ShouldContain("-ERR");
|
||||
response.ShouldContain("Permissions Violation");
|
||||
}
|
||||
@@ -307,7 +289,7 @@ public class ClientServerGoParityTests
|
||||
[Fact]
|
||||
public async Task TestClientSub()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var server = await StartServerAsync(new NatsOptions { Port = port });
|
||||
try
|
||||
{
|
||||
@@ -318,11 +300,11 @@ public class ClientServerGoParityTests
|
||||
await ReadLineAsync(pub);
|
||||
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\nSUB test.sub 42\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG");
|
||||
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\nPUB test.sub 4\r\ndata\r\n"));
|
||||
|
||||
var msg = await ReadUntilAsync(sub, "data");
|
||||
var msg = await SocketTestHelper.ReadUntilAsync(sub, "data");
|
||||
msg.ShouldContain("MSG test.sub 42 4\r\ndata\r\n");
|
||||
}
|
||||
finally
|
||||
@@ -344,7 +326,7 @@ public class ClientServerGoParityTests
|
||||
[Fact]
|
||||
public async Task TestClientSubWithQueueGroup()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var server = await StartServerAsync(new NatsOptions { Port = port });
|
||||
try
|
||||
{
|
||||
@@ -358,15 +340,15 @@ public class ClientServerGoParityTests
|
||||
|
||||
// Both subscribe to same queue group
|
||||
await sub1.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\nSUB qtest myqueue 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub1, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sub1, "PONG");
|
||||
await sub2.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\nSUB qtest myqueue 2\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub2, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sub2, "PONG");
|
||||
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\nPUB qtest 5\r\nhello\r\n"));
|
||||
|
||||
// Exactly one subscriber should receive the message
|
||||
var received1Task = ReadUntilAsync(sub1, "MSG", timeoutMs: 1000);
|
||||
var received2Task = ReadUntilAsync(sub2, "MSG", timeoutMs: 1000);
|
||||
var received1Task = SocketTestHelper.ReadUntilAsync(sub1, "MSG", timeoutMs: 1000);
|
||||
var received2Task = SocketTestHelper.ReadUntilAsync(sub2, "MSG", timeoutMs: 1000);
|
||||
|
||||
var completed = await Task.WhenAny(received1Task, received2Task);
|
||||
var result = await completed;
|
||||
@@ -391,7 +373,7 @@ public class ClientServerGoParityTests
|
||||
[Fact]
|
||||
public async Task TestClientUnsub()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var server = await StartServerAsync(new NatsOptions { Port = port });
|
||||
try
|
||||
{
|
||||
@@ -403,7 +385,7 @@ public class ClientServerGoParityTests
|
||||
|
||||
// Subscribe then immediately unsubscribe
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\nSUB unsub.test 5\r\nUNSUB 5\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG");
|
||||
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\nPUB unsub.test 3\r\nfoo\r\n"));
|
||||
|
||||
@@ -444,7 +426,7 @@ public class ClientServerGoParityTests
|
||||
[Fact]
|
||||
public async Task TestClientMsg()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var server = await StartServerAsync(new NatsOptions { Port = port });
|
||||
try
|
||||
{
|
||||
@@ -455,11 +437,11 @@ public class ClientServerGoParityTests
|
||||
await ReadLineAsync(pub);
|
||||
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\nSUB msg.test 99\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG");
|
||||
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\nPUB msg.test reply.subj 7\r\npayload\r\n"));
|
||||
|
||||
var msg = await ReadUntilAsync(sub, "payload");
|
||||
var msg = await SocketTestHelper.ReadUntilAsync(sub, "payload");
|
||||
// MSG <subject> <sid> [reply] <#bytes>
|
||||
msg.ShouldContain("MSG msg.test 99 reply.subj 7\r\npayload\r\n");
|
||||
}
|
||||
@@ -482,7 +464,7 @@ public class ClientServerGoParityTests
|
||||
[Fact]
|
||||
public async Task TestClientMaxPayload()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var server = await StartServerAsync(new NatsOptions { Port = port, MaxPayload = 16 });
|
||||
try
|
||||
{
|
||||
@@ -495,7 +477,7 @@ public class ClientServerGoParityTests
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\n"));
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("PUB foo 32\r\n01234567890123456789012345678901\r\n"));
|
||||
|
||||
var response = await ReadUntilAsync(sock, "-ERR", timeoutMs: 3000);
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "-ERR", timeoutMs: 3000);
|
||||
response.ShouldContain(NatsProtocol.ErrMaxPayloadViolation);
|
||||
}
|
||||
finally
|
||||
@@ -517,7 +499,7 @@ public class ClientServerGoParityTests
|
||||
[Fact]
|
||||
public async Task TestClientSlowConsumer()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
// Very small MaxPending to easily trigger slow consumer
|
||||
var server = await StartServerAsync(new NatsOptions { Port = port, MaxPending = 512 });
|
||||
try
|
||||
@@ -529,7 +511,7 @@ public class ClientServerGoParityTests
|
||||
await ReadLineAsync(pub);
|
||||
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\nSUB slow.test 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG");
|
||||
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\n"));
|
||||
|
||||
@@ -567,11 +549,11 @@ public class ClientServerGoParityTests
|
||||
public async Task TestClientWriteDeadline()
|
||||
{
|
||||
// Verify WriteDeadline is a configurable option and defaults to 10 seconds
|
||||
var opts = new NatsOptions { Port = GetFreePort() };
|
||||
var opts = new NatsOptions { Port = TestPortAllocator.GetFreePort() };
|
||||
opts.WriteDeadline.ShouldBe(TimeSpan.FromSeconds(10));
|
||||
|
||||
// Custom write deadline
|
||||
var customOpts = new NatsOptions { Port = GetFreePort(), WriteDeadline = TimeSpan.FromMilliseconds(500) };
|
||||
var customOpts = new NatsOptions { Port = TestPortAllocator.GetFreePort(), WriteDeadline = TimeSpan.FromMilliseconds(500) };
|
||||
customOpts.WriteDeadline.ShouldBe(TimeSpan.FromMilliseconds(500));
|
||||
|
||||
// Server starts with custom write deadline without error
|
||||
@@ -601,7 +583,7 @@ public class ClientServerGoParityTests
|
||||
[Fact]
|
||||
public async Task TestClientParseConnect()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var server = await StartServerAsync(new NatsOptions { Port = port });
|
||||
try
|
||||
{
|
||||
@@ -610,7 +592,7 @@ public class ClientServerGoParityTests
|
||||
await ReadLineAsync(sock);
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes(
|
||||
"CONNECT {\"verbose\":true,\"name\":\"my-client\",\"lang\":\"dotnet\"}\r\nPING\r\n"));
|
||||
var r = await ReadUntilAsync(sock, "PONG");
|
||||
var r = await SocketTestHelper.ReadUntilAsync(sock, "PONG");
|
||||
r.ShouldContain("+OK"); // verbose=true → +OK after CONNECT
|
||||
r.ShouldContain("PONG");
|
||||
}
|
||||
@@ -633,7 +615,7 @@ public class ClientServerGoParityTests
|
||||
[Fact]
|
||||
public async Task TestClientConnectVerbose()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var server = await StartServerAsync(new NatsOptions { Port = port });
|
||||
try
|
||||
{
|
||||
@@ -643,7 +625,7 @@ public class ClientServerGoParityTests
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes(
|
||||
"CONNECT {\"verbose\":true}\r\nSUB foo 1\r\nPING\r\n"));
|
||||
|
||||
var response = await ReadUntilAsync(sock, "PONG");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "PONG");
|
||||
// +OK for CONNECT, +OK for SUB, PONG
|
||||
var okCount = CountOccurrences(response, "+OK");
|
||||
okCount.ShouldBeGreaterThanOrEqualTo(2);
|
||||
@@ -667,7 +649,7 @@ public class ClientServerGoParityTests
|
||||
[Fact]
|
||||
public async Task TestClientParsePub()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var server = await StartServerAsync(new NatsOptions { Port = port });
|
||||
try
|
||||
{
|
||||
@@ -678,16 +660,16 @@ public class ClientServerGoParityTests
|
||||
await ReadLineAsync(pub);
|
||||
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\nSUB parse.pub 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG");
|
||||
|
||||
// PUB without reply
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\nPUB parse.pub 3\r\nfoo\r\n"));
|
||||
var msg1 = await ReadUntilAsync(sub, "foo");
|
||||
var msg1 = await SocketTestHelper.ReadUntilAsync(sub, "foo");
|
||||
msg1.ShouldContain("MSG parse.pub 1 3\r\nfoo\r\n");
|
||||
|
||||
// PUB with reply
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("PUB parse.pub _INBOX.reply 3\r\nbar\r\n"));
|
||||
var msg2 = await ReadUntilAsync(sub, "bar");
|
||||
var msg2 = await SocketTestHelper.ReadUntilAsync(sub, "bar");
|
||||
msg2.ShouldContain("MSG parse.pub 1 _INBOX.reply 3\r\nbar\r\n");
|
||||
}
|
||||
finally
|
||||
@@ -709,7 +691,7 @@ public class ClientServerGoParityTests
|
||||
[Fact]
|
||||
public async Task TestClientParseSub()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var server = await StartServerAsync(new NatsOptions { Port = port });
|
||||
try
|
||||
{
|
||||
@@ -721,11 +703,11 @@ public class ClientServerGoParityTests
|
||||
|
||||
// SUB without queue group
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\nSUB parse.sub 7\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG");
|
||||
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\nPUB parse.sub 2\r\nhi\r\n"));
|
||||
|
||||
var msg = await ReadUntilAsync(sub, "hi");
|
||||
var msg = await SocketTestHelper.ReadUntilAsync(sub, "hi");
|
||||
msg.ShouldContain("MSG parse.sub 7 2\r\nhi\r\n");
|
||||
}
|
||||
finally
|
||||
@@ -747,7 +729,7 @@ public class ClientServerGoParityTests
|
||||
[Fact]
|
||||
public async Task TestClientParseUnsub()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var server = await StartServerAsync(new NatsOptions { Port = port });
|
||||
try
|
||||
{
|
||||
@@ -759,18 +741,18 @@ public class ClientServerGoParityTests
|
||||
|
||||
// Subscribe then set max-messages=2 via UNSUB
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\nSUB autounsub 3\r\nUNSUB 3 2\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG");
|
||||
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\n"));
|
||||
|
||||
// First message — delivered
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("PUB autounsub 3\r\none\r\n"));
|
||||
var m1 = await ReadUntilAsync(sub, "one");
|
||||
var m1 = await SocketTestHelper.ReadUntilAsync(sub, "one");
|
||||
m1.ShouldContain("MSG autounsub");
|
||||
|
||||
// Second message — delivered (reaches max)
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("PUB autounsub 3\r\ntwo\r\n"));
|
||||
var m2 = await ReadUntilAsync(sub, "two");
|
||||
var m2 = await SocketTestHelper.ReadUntilAsync(sub, "two");
|
||||
m2.ShouldContain("MSG autounsub");
|
||||
|
||||
// Third message — NOT delivered (auto-unsubscribed)
|
||||
@@ -805,7 +787,7 @@ public class ClientServerGoParityTests
|
||||
[Fact]
|
||||
public async Task TestClientSplitMsg()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var server = await StartServerAsync(new NatsOptions { Port = port });
|
||||
try
|
||||
{
|
||||
@@ -816,7 +798,7 @@ public class ClientServerGoParityTests
|
||||
await ReadLineAsync(pub);
|
||||
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\nSUB split.msg 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG");
|
||||
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\n"));
|
||||
|
||||
@@ -827,7 +809,7 @@ public class ClientServerGoParityTests
|
||||
await Task.Delay(10);
|
||||
await pub.SendAsync(part2);
|
||||
|
||||
var msg = await ReadUntilAsync(sub, "world");
|
||||
var msg = await SocketTestHelper.ReadUntilAsync(sub, "world");
|
||||
msg.ShouldContain("MSG split.msg 1 11\r\nhello world\r\n");
|
||||
}
|
||||
finally
|
||||
@@ -849,7 +831,7 @@ public class ClientServerGoParityTests
|
||||
[Fact]
|
||||
public async Task TestClientAuthTimeout()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var server = await StartServerAsync(new NatsOptions
|
||||
{
|
||||
Port = port,
|
||||
@@ -864,7 +846,7 @@ public class ClientServerGoParityTests
|
||||
info.ShouldContain("\"auth_required\":true");
|
||||
|
||||
// Do NOT send CONNECT — wait for auth timeout
|
||||
var response = await ReadUntilAsync(sock, "-ERR", timeoutMs: 3000);
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "-ERR", timeoutMs: 3000);
|
||||
response.ShouldContain(NatsProtocol.ErrAuthTimeout);
|
||||
}
|
||||
finally
|
||||
@@ -886,7 +868,7 @@ public class ClientServerGoParityTests
|
||||
[Fact]
|
||||
public async Task TestClientMaxConnections()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var server = await StartServerAsync(new NatsOptions { Port = port, MaxConnections = 1 });
|
||||
try
|
||||
{
|
||||
@@ -922,7 +904,7 @@ public class ClientServerGoParityTests
|
||||
[Fact]
|
||||
public async Task TestClientNoEcho()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var server = await StartServerAsync(new NatsOptions { Port = port });
|
||||
try
|
||||
{
|
||||
@@ -932,7 +914,7 @@ public class ClientServerGoParityTests
|
||||
// Connect with echo=false, sub and pub on same connection
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes(
|
||||
"CONNECT {\"echo\":false}\r\nSUB noecho 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sock, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sock, "PONG");
|
||||
|
||||
// Publish on same connection
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("PUB noecho 5\r\nhello\r\n"));
|
||||
@@ -970,7 +952,7 @@ public class ClientServerGoParityTests
|
||||
{
|
||||
// NOTE: .NET profiling endpoint is not yet implemented; ProfPort>0 logs a warning
|
||||
// but does not fail. This test verifies the option is accepted without error.
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var server = await StartServerAsync(new NatsOptions { Port = port, ProfPort = 6060 });
|
||||
try
|
||||
{
|
||||
@@ -999,7 +981,7 @@ public class ClientServerGoParityTests
|
||||
[Fact]
|
||||
public async Task TestStartupAndShutdown()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var server = await StartServerAsync(new NatsOptions { Port = port, NoSystemAccount = true });
|
||||
try
|
||||
{
|
||||
@@ -1013,7 +995,7 @@ public class ClientServerGoParityTests
|
||||
info.ShouldStartWith("INFO ");
|
||||
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sock, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sock, "PONG");
|
||||
|
||||
server.ClientCount.ShouldBe(1);
|
||||
}
|
||||
@@ -1063,7 +1045,7 @@ public class ClientServerGoParityTests
|
||||
[Fact]
|
||||
public void TestNewServer()
|
||||
{
|
||||
var server = new NatsServer(new NatsOptions { Port = GetFreePort() }, NullLoggerFactory.Instance);
|
||||
var server = new NatsServer(new NatsOptions { Port = TestPortAllocator.GetFreePort() }, NullLoggerFactory.Instance);
|
||||
try
|
||||
{
|
||||
server.ServerId.ShouldNotBeNullOrWhiteSpace();
|
||||
@@ -1124,7 +1106,7 @@ public class ClientServerGoParityTests
|
||||
[Fact]
|
||||
public async Task TestNoDeadlockOnSlowConsumer()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var server = await StartServerAsync(new NatsOptions { Port = port, MaxPending = 256 });
|
||||
try
|
||||
{
|
||||
@@ -1135,7 +1117,7 @@ public class ClientServerGoParityTests
|
||||
await ReadLineAsync(pub);
|
||||
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\nSUB nodeadlock 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG");
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\n"));
|
||||
|
||||
var payload = new string('X', 200);
|
||||
@@ -1168,13 +1150,13 @@ public class ClientServerGoParityTests
|
||||
[Fact]
|
||||
public async Task TestServerShutdownContextTimeout()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var server = await StartServerAsync(new NatsOptions { Port = port });
|
||||
|
||||
using var client = await ConnectRawAsync(port);
|
||||
await ReadLineAsync(client);
|
||||
await client.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\nPING\r\n"));
|
||||
await ReadUntilAsync(client, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(client, "PONG");
|
||||
|
||||
// Shutdown should complete within 15 seconds even with active clients
|
||||
using var shutdownCts = new CancellationTokenSource(TimeSpan.FromSeconds(15));
|
||||
@@ -1205,7 +1187,7 @@ public class ClientServerGoParityTests
|
||||
opts.WriteDeadline.ShouldBe(TimeSpan.FromMilliseconds(250));
|
||||
|
||||
// Verify the option is honored by the running server
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var server = await StartServerAsync(new NatsOptions
|
||||
{
|
||||
Port = port,
|
||||
@@ -1222,7 +1204,7 @@ public class ClientServerGoParityTests
|
||||
await ReadLineAsync(pub);
|
||||
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\nSUB wd.test 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG");
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\n"));
|
||||
|
||||
// Don't drain sub socket — flood to exceed MaxPending and trigger slow consumer
|
||||
@@ -1254,7 +1236,7 @@ public class ClientServerGoParityTests
|
||||
[Fact]
|
||||
public async Task TestMaxPayloadVerification()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var server = await StartServerAsync(new NatsOptions { Port = port, MaxPayload = 100 });
|
||||
try
|
||||
{
|
||||
@@ -1265,7 +1247,7 @@ public class ClientServerGoParityTests
|
||||
var oversized = new string('A', 200);
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes($"PUB bigpayload {oversized.Length}\r\n{oversized}\r\n"));
|
||||
|
||||
var response = await ReadUntilAsync(sock, "-ERR", timeoutMs: 3000);
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "-ERR", timeoutMs: 3000);
|
||||
response.ShouldContain(NatsProtocol.ErrMaxPayloadViolation);
|
||||
|
||||
// Connection should be closed
|
||||
@@ -1293,7 +1275,7 @@ public class ClientServerGoParityTests
|
||||
[Fact]
|
||||
public async Task TestMaxSubscriptions()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var server = await StartServerAsync(new NatsOptions { Port = port, MaxSubs = 3 });
|
||||
try
|
||||
{
|
||||
@@ -1304,12 +1286,12 @@ public class ClientServerGoParityTests
|
||||
// Subscribe 3 times (at limit)
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes(
|
||||
"SUB sub1 1\r\nSUB sub2 2\r\nSUB sub3 3\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sock, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sock, "PONG");
|
||||
|
||||
// 4th subscription should trigger -ERR
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("SUB sub4 4\r\n"));
|
||||
|
||||
var response = await ReadUntilAsync(sock, "-ERR", timeoutMs: 3000);
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "-ERR", timeoutMs: 3000);
|
||||
response.ShouldContain(NatsProtocol.ErrMaxSubscriptionsExceeded);
|
||||
}
|
||||
finally
|
||||
@@ -1331,7 +1313,7 @@ public class ClientServerGoParityTests
|
||||
[Fact]
|
||||
public async Task TestAuthorizationTimeout()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var server = await StartServerAsync(new NatsOptions
|
||||
{
|
||||
Port = port,
|
||||
@@ -1346,7 +1328,7 @@ public class ClientServerGoParityTests
|
||||
info.ShouldContain("\"auth_required\":true");
|
||||
|
||||
// Do not send CONNECT — wait for auth timeout
|
||||
var response = await ReadUntilAsync(sock, "-ERR", timeoutMs: 3000);
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "-ERR", timeoutMs: 3000);
|
||||
response.ShouldContain(NatsProtocol.ErrAuthTimeout);
|
||||
}
|
||||
finally
|
||||
@@ -1373,7 +1355,7 @@ public class ClientServerGoParityTests
|
||||
// (TlsRateLimiter is only created when TLS is configured)
|
||||
var opts = new NatsOptions
|
||||
{
|
||||
Port = GetFreePort(),
|
||||
Port = TestPortAllocator.GetFreePort(),
|
||||
TlsRateLimit = 100,
|
||||
};
|
||||
opts.TlsRateLimit.ShouldBe(100L);
|
||||
@@ -1402,8 +1384,8 @@ public class ClientServerGoParityTests
|
||||
[Fact]
|
||||
public async Task TestMonitoringPort()
|
||||
{
|
||||
var monPort = GetFreePort();
|
||||
var natsPort = GetFreePort();
|
||||
var monPort = TestPortAllocator.GetFreePort();
|
||||
var natsPort = TestPortAllocator.GetFreePort();
|
||||
var server = await StartServerAsync(new NatsOptions { Port = natsPort, MonitorPort = monPort });
|
||||
try
|
||||
{
|
||||
@@ -1441,7 +1423,7 @@ public class ClientServerGoParityTests
|
||||
try
|
||||
{
|
||||
var pidFile = Path.Combine(tempDir, "nats.pid");
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var server = await StartServerAsync(new NatsOptions { Port = port, PidFile = pidFile });
|
||||
|
||||
File.Exists(pidFile).ShouldBeTrue();
|
||||
@@ -1472,7 +1454,7 @@ public class ClientServerGoParityTests
|
||||
[Fact]
|
||||
public async Task TestMaxControlLine()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
// Default MaxControlLine is 4096
|
||||
var server = await StartServerAsync(new NatsOptions { Port = port, MaxControlLine = 64 });
|
||||
try
|
||||
@@ -1527,7 +1509,7 @@ public class ClientServerGoParityTests
|
||||
{
|
||||
// Custom MaxPayload
|
||||
var customMax = 512 * 1024; // 512 KB
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var server = await StartServerAsync(new NatsOptions { Port = port, MaxPayload = customMax });
|
||||
try
|
||||
{
|
||||
@@ -1554,7 +1536,7 @@ public class ClientServerGoParityTests
|
||||
[Fact]
|
||||
public async Task TestServerShutdownWaitForClients()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var server = await StartServerAsync(new NatsOptions { Port = port });
|
||||
|
||||
var clients = new List<Socket>();
|
||||
@@ -1563,7 +1545,7 @@ public class ClientServerGoParityTests
|
||||
var sock = await ConnectRawAsync(port);
|
||||
await ReadLineAsync(sock);
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sock, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sock, "PONG");
|
||||
clients.Add(sock);
|
||||
}
|
||||
|
||||
@@ -1592,7 +1574,7 @@ public class ClientServerGoParityTests
|
||||
[Fact]
|
||||
public async Task TestNoRaceParallelClients()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var server = await StartServerAsync(new NatsOptions { Port = port });
|
||||
try
|
||||
{
|
||||
@@ -1603,7 +1585,7 @@ public class ClientServerGoParityTests
|
||||
var info = await ReadLineAsync(sock);
|
||||
info.ShouldStartWith("INFO ");
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sock, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sock, "PONG");
|
||||
sock.Shutdown(SocketShutdown.Both);
|
||||
}).ToArray();
|
||||
|
||||
@@ -1628,7 +1610,7 @@ public class ClientServerGoParityTests
|
||||
[Fact]
|
||||
public async Task TestServerListenRetry()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var server = await StartServerAsync(new NatsOptions { Port = port });
|
||||
try
|
||||
{
|
||||
@@ -1668,7 +1650,7 @@ public class ClientServerGoParityTests
|
||||
[Fact]
|
||||
public async Task TestServerInfoWithOperatorMode()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var serverName = "test-parity-server";
|
||||
var server = await StartServerAsync(new NatsOptions { Port = port, ServerName = serverName });
|
||||
try
|
||||
@@ -6,8 +6,9 @@ using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using NATS.Server;
|
||||
using NATS.Server.TestUtilities;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for slow consumer detection and client cleanup when pending bytes exceed MaxPending.
|
||||
@@ -15,26 +16,7 @@ namespace NATS.Server.Tests;
|
||||
/// </summary>
|
||||
public class ClientSlowConsumerTests
|
||||
{
|
||||
private static int GetFreePort()
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
sock.Bind(new IPEndPoint(IPAddress.Loopback, 0));
|
||||
return ((IPEndPoint)sock.LocalEndPoint!).Port;
|
||||
}
|
||||
|
||||
private static async Task<string> ReadUntilAsync(Socket sock, string expected, int timeoutMs = 5000)
|
||||
{
|
||||
using var cts = new CancellationTokenSource(timeoutMs);
|
||||
var sb = new StringBuilder();
|
||||
var buf = new byte[4096];
|
||||
while (!sb.ToString().Contains(expected))
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Slow_consumer_detected_when_pending_exceeds_limit: Creates a server with a small
|
||||
@@ -58,7 +40,7 @@ public class ClientSlowConsumerTests
|
||||
const int payloadSize = 512; // each message payload
|
||||
const int floodCount = 50; // enough to exceed the 1KB limit
|
||||
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
using var cts = new CancellationTokenSource();
|
||||
var server = new NatsServer(
|
||||
new NatsOptions
|
||||
@@ -81,7 +63,7 @@ public class ClientSlowConsumerTests
|
||||
|
||||
// Subscribe to "flood" subject and confirm with PING/PONG
|
||||
await slowSub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {\"verbose\":false}\r\nSUB flood 1\r\nPING\r\n"));
|
||||
var pong = await ReadUntilAsync(slowSub, "PONG");
|
||||
var pong = await SocketTestHelper.ReadUntilAsync(slowSub, "PONG");
|
||||
pong.ShouldContain("PONG");
|
||||
|
||||
// Connect the publisher
|
||||
@@ -101,7 +83,7 @@ public class ClientSlowConsumerTests
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes(pubSb.ToString()));
|
||||
|
||||
// Wait for publisher's PONG confirming all publishes were processed
|
||||
await ReadUntilAsync(pub, "PONG", timeoutMs: 5000);
|
||||
await SocketTestHelper.ReadUntilAsync(pub, "PONG", timeoutMs: 5000);
|
||||
|
||||
// Give the server time to detect and close the slow consumer
|
||||
await Task.Delay(500);
|
||||
@@ -9,7 +9,7 @@ using NATS.Server;
|
||||
using NATS.Server.Auth;
|
||||
using NATS.Server.Protocol;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
public class ClientTests : IAsyncDisposable
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
public class ClientTraceModeTests
|
||||
{
|
||||
@@ -1,7 +1,7 @@
|
||||
using NATS.Server;
|
||||
using Shouldly;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for per-client trace delivery and echo control.
|
||||
@@ -7,8 +7,9 @@ using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using NATS.Server;
|
||||
using NATS.Server.TestUtilities;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
public class ClientUnsubTests : IAsyncLifetime
|
||||
{
|
||||
@@ -18,7 +19,7 @@ public class ClientUnsubTests : IAsyncLifetime
|
||||
|
||||
public ClientUnsubTests()
|
||||
{
|
||||
_port = GetFreePort();
|
||||
_port = TestPortAllocator.GetFreePort();
|
||||
_server = new NatsServer(new NatsOptions { Port = _port }, NullLoggerFactory.Instance);
|
||||
}
|
||||
|
||||
@@ -34,12 +35,6 @@ public class ClientUnsubTests : IAsyncLifetime
|
||||
_server.Dispose();
|
||||
}
|
||||
|
||||
private static int GetFreePort()
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
sock.Bind(new IPEndPoint(IPAddress.Loopback, 0));
|
||||
return ((IPEndPoint)sock.LocalEndPoint!).Port;
|
||||
}
|
||||
|
||||
private async Task<Socket> ConnectAndHandshakeAsync()
|
||||
{
|
||||
@@ -53,19 +48,6 @@ public class ClientUnsubTests : IAsyncLifetime
|
||||
return sock;
|
||||
}
|
||||
|
||||
private static async Task<string> ReadUntilAsync(Socket sock, string expected, int timeoutMs = 5000)
|
||||
{
|
||||
using var cts = new CancellationTokenSource(timeoutMs);
|
||||
var sb = new StringBuilder();
|
||||
var buf = new byte[4096];
|
||||
while (!sb.ToString().Contains(expected))
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mirrors TestClientUnSub: subscribe twice, unsubscribe one sid, publish,
|
||||
@@ -80,17 +62,17 @@ public class ClientUnsubTests : IAsyncLifetime
|
||||
|
||||
// Subscribe to "foo" with sid 1 and sid 2
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("SUB foo 1\r\nSUB foo 2\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG");
|
||||
|
||||
// Unsubscribe sid 1
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("UNSUB 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG");
|
||||
|
||||
// Publish one message to "foo"
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("PUB foo 5\r\nHello\r\n"));
|
||||
|
||||
// Should receive exactly one MSG for sid 2; sid 1 is gone
|
||||
var response = await ReadUntilAsync(sub, "MSG foo 2 5");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sub, "MSG foo 2 5");
|
||||
response.ShouldContain("MSG foo 2 5");
|
||||
response.ShouldNotContain("MSG foo 1 5");
|
||||
}
|
||||
@@ -111,7 +93,7 @@ public class ClientUnsubTests : IAsyncLifetime
|
||||
|
||||
// Subscribe to "foo" with sid 1, limit to 5 messages
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes($"SUB foo 1\r\nUNSUB 1 {maxMessages}\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG");
|
||||
|
||||
// Publish 10 messages
|
||||
var pubData = new StringBuilder();
|
||||
@@ -156,7 +138,7 @@ public class ClientUnsubTests : IAsyncLifetime
|
||||
|
||||
// Subscribe with a large max-messages limit, then immediately UNSUB without limit
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("SUB foo 1\r\nUNSUB 1 100\r\nUNSUB 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG");
|
||||
|
||||
// Publish a message — subscription should already be gone
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("PUB foo 5\r\nHello\r\n"));
|
||||
@@ -194,7 +176,7 @@ public class ClientUnsubTests : IAsyncLifetime
|
||||
|
||||
// Subscribe to 3 distinct subjects
|
||||
await client.SendAsync(Encoding.ASCII.GetBytes("SUB foo 1\r\nSUB bar 2\r\nSUB baz 3\r\nPING\r\n"));
|
||||
await ReadUntilAsync(client, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(client, "PONG");
|
||||
|
||||
// Confirm subscriptions are registered in the server's SubList
|
||||
_server.SubList.Count.ShouldBe(3u);
|
||||
@@ -15,7 +15,7 @@ using NATS.Server.JetStream.Storage;
|
||||
using NATS.Server.Raft;
|
||||
using NATS.Server.Subscriptions;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// NORACE concurrency stress tests ported from Go's norace_test.go.
|
||||
@@ -1,6 +1,6 @@
|
||||
using NATS.Server.Configuration;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
public class ConfigIntegrationTests
|
||||
{
|
||||
@@ -1,7 +1,7 @@
|
||||
using NATS.Server;
|
||||
using NATS.Server.Configuration;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
public class ConfigProcessorTests
|
||||
{
|
||||
@@ -2,7 +2,7 @@ using NATS.Server;
|
||||
using NATS.Server.Auth;
|
||||
using NATS.Server.Configuration;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
public class ConfigReloadTests
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using NATS.Server.Configuration;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
public class ConfigRuntimeParityTests
|
||||
{
|
||||
@@ -5,7 +5,7 @@ using NATS.Server.Auth;
|
||||
using NATS.Server.Configuration;
|
||||
using Shouldly;
|
||||
|
||||
namespace NATS.Server.Tests.Configuration;
|
||||
namespace NATS.Server.Core.Tests.Configuration;
|
||||
|
||||
public sealed class AuthChangePropagationTests
|
||||
{
|
||||
@@ -9,8 +9,9 @@ using System.Text;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using NATS.Client.Core;
|
||||
using NATS.Server.Configuration;
|
||||
using NATS.Server.TestUtilities;
|
||||
|
||||
namespace NATS.Server.Tests.Configuration;
|
||||
namespace NATS.Server.Core.Tests.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for auth change propagation on config reload.
|
||||
@@ -25,13 +26,6 @@ public class AuthReloadTests
|
||||
{
|
||||
// ─── Helpers ────────────────────────────────────────────────────────────
|
||||
|
||||
private static int GetFreePort()
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
sock.Bind(new IPEndPoint(IPAddress.Loopback, 0));
|
||||
return ((IPEndPoint)sock.LocalEndPoint!).Port;
|
||||
}
|
||||
|
||||
private static async Task<Socket> RawConnectAsync(int port)
|
||||
{
|
||||
var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
@@ -51,22 +45,6 @@ public class AuthReloadTests
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes(connectJson), SocketFlags.None);
|
||||
}
|
||||
|
||||
private static async Task<string> ReadUntilAsync(Socket sock, string expected, int timeoutMs = 5000)
|
||||
{
|
||||
using var cts = new CancellationTokenSource(timeoutMs);
|
||||
var sb = new StringBuilder();
|
||||
var buf = new byte[4096];
|
||||
while (!sb.ToString().Contains(expected, StringComparison.Ordinal))
|
||||
{
|
||||
int n;
|
||||
try { n = await sock.ReceiveAsync(buf, SocketFlags.None, cts.Token); }
|
||||
catch (OperationCanceledException) { break; }
|
||||
if (n == 0) break;
|
||||
sb.Append(Encoding.ASCII.GetString(buf, 0, n));
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static void WriteConfigAndReload(NatsServer server, string configPath, string configText)
|
||||
{
|
||||
File.WriteAllText(configPath, configText);
|
||||
@@ -88,7 +66,7 @@ public class AuthReloadTests
|
||||
var configPath = Path.Combine(Path.GetTempPath(), $"natsdotnet-authdc-{Guid.NewGuid():N}.conf");
|
||||
try
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
|
||||
// Start with no auth
|
||||
File.WriteAllText(configPath, $"port: {port}\ndebug: false");
|
||||
@@ -107,7 +85,7 @@ public class AuthReloadTests
|
||||
|
||||
// Send a PING to confirm the connection is established
|
||||
await sock.SendAsync("PING\r\n"u8.ToArray(), SocketFlags.None);
|
||||
var pong = await ReadUntilAsync(sock, "PONG", timeoutMs: 3000);
|
||||
var pong = await SocketTestHelper.ReadUntilAsync(sock, "PONG", timeoutMs: 3000);
|
||||
pong.ShouldContain("PONG");
|
||||
|
||||
server.ClientCount.ShouldBeGreaterThanOrEqualTo(1);
|
||||
@@ -146,7 +124,7 @@ public class AuthReloadTests
|
||||
var configPath = Path.Combine(Path.GetTempPath(), $"natsdotnet-credchg-{Guid.NewGuid():N}.conf");
|
||||
try
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
|
||||
// Start with user/password auth
|
||||
File.WriteAllText(configPath,
|
||||
@@ -167,7 +145,7 @@ public class AuthReloadTests
|
||||
|
||||
// Verify connection works
|
||||
await sock.SendAsync("PING\r\n"u8.ToArray(), SocketFlags.None);
|
||||
var pong = await ReadUntilAsync(sock, "PONG", timeoutMs: 3000);
|
||||
var pong = await SocketTestHelper.ReadUntilAsync(sock, "PONG", timeoutMs: 3000);
|
||||
pong.ShouldContain("PONG");
|
||||
|
||||
// Change the password via reload
|
||||
@@ -202,7 +180,7 @@ public class AuthReloadTests
|
||||
var configPath = Path.Combine(Path.GetTempPath(), $"natsdotnet-authoff-{Guid.NewGuid():N}.conf");
|
||||
try
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
|
||||
// Start with auth enabled
|
||||
File.WriteAllText(configPath,
|
||||
@@ -266,7 +244,7 @@ public class AuthReloadTests
|
||||
var configPath = Path.Combine(Path.GetTempPath(), $"natsdotnet-newauth-{Guid.NewGuid():N}.conf");
|
||||
try
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
|
||||
// Start with no auth
|
||||
File.WriteAllText(configPath, $"port: {port}\ndebug: false");
|
||||
@@ -326,7 +304,7 @@ public class AuthReloadTests
|
||||
var configPath = Path.Combine(Path.GetTempPath(), $"natsdotnet-noauth-{Guid.NewGuid():N}.conf");
|
||||
try
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
File.WriteAllText(configPath, $"port: {port}\ndebug: false");
|
||||
|
||||
var options = new NatsOptions { ConfigFile = configPath, Port = port };
|
||||
@@ -341,7 +319,7 @@ public class AuthReloadTests
|
||||
using var sock = await RawConnectAsync(port);
|
||||
await SendConnectAsync(sock);
|
||||
await sock.SendAsync("PING\r\n"u8.ToArray(), SocketFlags.None);
|
||||
var pong = await ReadUntilAsync(sock, "PONG", timeoutMs: 3000);
|
||||
var pong = await SocketTestHelper.ReadUntilAsync(sock, "PONG", timeoutMs: 3000);
|
||||
pong.ShouldContain("PONG");
|
||||
|
||||
var countBefore = server.ClientCount;
|
||||
@@ -357,7 +335,7 @@ public class AuthReloadTests
|
||||
|
||||
// Client should still be responsive
|
||||
await sock.SendAsync("PING\r\n"u8.ToArray(), SocketFlags.None);
|
||||
var pong2 = await ReadUntilAsync(sock, "PONG", timeoutMs: 3000);
|
||||
var pong2 = await SocketTestHelper.ReadUntilAsync(sock, "PONG", timeoutMs: 3000);
|
||||
pong2.ShouldContain("PONG");
|
||||
}
|
||||
finally
|
||||
@@ -5,7 +5,7 @@ using NATS.Server;
|
||||
using NATS.Server.Configuration;
|
||||
using Shouldly;
|
||||
|
||||
namespace NATS.Server.Tests.Configuration;
|
||||
namespace NATS.Server.Core.Tests.Configuration;
|
||||
|
||||
public class ClusterConfigReloadTests
|
||||
{
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Reflection;
|
||||
using NATS.Server.Configuration;
|
||||
|
||||
namespace NATS.Server.Tests.Configuration;
|
||||
namespace NATS.Server.Core.Tests.Configuration;
|
||||
|
||||
public class ConfigPedanticParityBatch1Tests
|
||||
{
|
||||
@@ -12,8 +12,9 @@ using System.Text;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using NATS.Client.Core;
|
||||
using NATS.Server.Configuration;
|
||||
using NATS.Server.TestUtilities;
|
||||
|
||||
namespace NATS.Server.Tests.Configuration;
|
||||
namespace NATS.Server.Core.Tests.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Advanced configuration model and hot-reload tests ported from Go's opts_test.go
|
||||
@@ -25,13 +26,6 @@ public class ConfigReloadAdvancedTests
|
||||
{
|
||||
// ─── Helpers ────────────────────────────────────────────────────────────
|
||||
|
||||
private static int GetFreePort()
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
sock.Bind(new IPEndPoint(IPAddress.Loopback, 0));
|
||||
return ((IPEndPoint)sock.LocalEndPoint!).Port;
|
||||
}
|
||||
|
||||
private static async Task<Socket> RawConnectAsync(int port)
|
||||
{
|
||||
var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
@@ -41,22 +35,6 @@ public class ConfigReloadAdvancedTests
|
||||
return sock;
|
||||
}
|
||||
|
||||
private static async Task<string> ReadUntilAsync(Socket sock, string expected, int timeoutMs = 5000)
|
||||
{
|
||||
using var cts = new CancellationTokenSource(timeoutMs);
|
||||
var sb = new StringBuilder();
|
||||
var buf = new byte[4096];
|
||||
while (!sb.ToString().Contains(expected, StringComparison.Ordinal))
|
||||
{
|
||||
int n;
|
||||
try { n = await sock.ReceiveAsync(buf, SocketFlags.None, cts.Token); }
|
||||
catch (OperationCanceledException) { break; }
|
||||
if (n == 0) break;
|
||||
sb.Append(Encoding.ASCII.GetString(buf, 0, n));
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static void WriteConfigAndReload(NatsServer server, string configPath, string configText)
|
||||
{
|
||||
File.WriteAllText(configPath, configText);
|
||||
@@ -66,7 +44,7 @@ public class ConfigReloadAdvancedTests
|
||||
private static async Task<(NatsServer server, int port, CancellationTokenSource cts, string configPath)>
|
||||
StartServerWithConfigAsync(string configContent)
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var configPath = Path.Combine(Path.GetTempPath(), $"natsdotnet-adv-{Guid.NewGuid():N}.conf");
|
||||
var finalContent = configContent.Replace("{PORT}", port.ToString());
|
||||
File.WriteAllText(configPath, finalContent);
|
||||
@@ -545,7 +523,7 @@ public class ConfigReloadAdvancedTests
|
||||
[Fact]
|
||||
public async Task Reload_adding_cluster_block_rejected()
|
||||
{
|
||||
var clusterPort = GetFreePort();
|
||||
var clusterPort = TestPortAllocator.GetFreePort();
|
||||
var (server, port, cts, configPath) = await StartServerWithConfigAsync("port: {PORT}");
|
||||
try
|
||||
{
|
||||
@@ -12,8 +12,9 @@ using System.Text;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using NATS.Client.Core;
|
||||
using NATS.Server.Configuration;
|
||||
using NATS.Server.TestUtilities;
|
||||
|
||||
namespace NATS.Server.Tests.Configuration;
|
||||
namespace NATS.Server.Core.Tests.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Extended parity tests for config hot reload behaviour ported from Go's
|
||||
@@ -24,13 +25,6 @@ public class ConfigReloadExtendedParityTests
|
||||
{
|
||||
// ─── Helpers ────────────────────────────────────────────────────────────
|
||||
|
||||
private static int GetFreePort()
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
sock.Bind(new IPEndPoint(IPAddress.Loopback, 0));
|
||||
return ((IPEndPoint)sock.LocalEndPoint!).Port;
|
||||
}
|
||||
|
||||
private static async Task<Socket> RawConnectAsync(int port)
|
||||
{
|
||||
var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
@@ -40,28 +34,6 @@ public class ConfigReloadExtendedParityTests
|
||||
return sock;
|
||||
}
|
||||
|
||||
private static async Task<string> ReadUntilAsync(Socket sock, string expected, int timeoutMs = 5000)
|
||||
{
|
||||
using var cts = new CancellationTokenSource(timeoutMs);
|
||||
var sb = new StringBuilder();
|
||||
var buf = new byte[4096];
|
||||
while (!sb.ToString().Contains(expected, StringComparison.Ordinal))
|
||||
{
|
||||
int n;
|
||||
try
|
||||
{
|
||||
n = await sock.ReceiveAsync(buf, SocketFlags.None, cts.Token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (n == 0) break;
|
||||
sb.Append(Encoding.ASCII.GetString(buf, 0, n));
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static void WriteConfigAndReload(NatsServer server, string configPath, string configText)
|
||||
{
|
||||
File.WriteAllText(configPath, configText);
|
||||
@@ -71,7 +43,7 @@ public class ConfigReloadExtendedParityTests
|
||||
private static async Task<(NatsServer server, int port, CancellationTokenSource cts, string configPath)>
|
||||
StartServerWithConfigAsync(string configContent)
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var configPath = Path.Combine(Path.GetTempPath(), $"natsdotnet-reload-{Guid.NewGuid():N}.conf");
|
||||
var finalContent = configContent.Replace("{PORT}", port.ToString());
|
||||
File.WriteAllText(configPath, finalContent);
|
||||
@@ -112,7 +84,7 @@ public class ConfigReloadExtendedParityTests
|
||||
[Fact]
|
||||
public async Task Reload_without_config_file_throws()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var options = new NatsOptions { Port = port };
|
||||
var server = new NatsServer(options, NullLoggerFactory.Instance);
|
||||
var cts = new CancellationTokenSource();
|
||||
@@ -142,7 +114,7 @@ public class ConfigReloadExtendedParityTests
|
||||
var (server, port, cts, configPath) = await StartServerWithConfigAsync("port: {PORT}");
|
||||
try
|
||||
{
|
||||
var newPort = GetFreePort();
|
||||
var newPort = TestPortAllocator.GetFreePort();
|
||||
File.WriteAllText(configPath, $"port: {newPort}");
|
||||
Should.Throw<InvalidOperationException>(() => server.ReloadConfigOrThrow())
|
||||
.Message.ShouldContain("Port");
|
||||
@@ -633,11 +605,11 @@ public class ConfigReloadExtendedParityTests
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("CONNECT {\"verbose\":false,\"pedantic\":false}\r\n"), SocketFlags.None);
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("SUB foo 1\r\n"), SocketFlags.None);
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"), SocketFlags.None);
|
||||
await ReadUntilAsync(sock, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sock, "PONG");
|
||||
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("PUB foo 5\r\nhello\r\n"), SocketFlags.None);
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"), SocketFlags.None);
|
||||
var response = await ReadUntilAsync(sock, "PONG");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "PONG");
|
||||
response.ShouldContain("MSG foo");
|
||||
|
||||
WriteConfigAndReload(server, configPath, $"port: {port}\nmax_payload: 2");
|
||||
@@ -645,7 +617,7 @@ public class ConfigReloadExtendedParityTests
|
||||
using var sock2 = await RawConnectAsync(port);
|
||||
await sock2.SendAsync(Encoding.ASCII.GetBytes("CONNECT {\"verbose\":false,\"pedantic\":false}\r\n"), SocketFlags.None);
|
||||
await sock2.SendAsync(Encoding.ASCII.GetBytes("PUB foo 5\r\nhello\r\n"), SocketFlags.None);
|
||||
var errResponse = await ReadUntilAsync(sock2, "-ERR", timeoutMs: 5000);
|
||||
var errResponse = await SocketTestHelper.ReadUntilAsync(sock2, "-ERR", timeoutMs: 5000);
|
||||
errResponse.ShouldContain("-ERR");
|
||||
}
|
||||
finally
|
||||
@@ -983,7 +955,7 @@ public class ConfigReloadExtendedParityTests
|
||||
|
||||
using var c2 = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
await c2.ConnectAsync(IPAddress.Loopback, port);
|
||||
var response = await ReadUntilAsync(c2, "-ERR", timeoutMs: 5000);
|
||||
var response = await SocketTestHelper.ReadUntilAsync(c2, "-ERR", timeoutMs: 5000);
|
||||
response.ShouldContain("maximum connections exceeded");
|
||||
|
||||
WriteConfigAndReload(server, configPath, $"port: {port}\nmax_connections: 10");
|
||||
@@ -1016,7 +988,7 @@ public class ConfigReloadExtendedParityTests
|
||||
|
||||
using var c4 = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
await c4.ConnectAsync(IPAddress.Loopback, port);
|
||||
var response = await ReadUntilAsync(c4, "-ERR", timeoutMs: 5000);
|
||||
var response = await SocketTestHelper.ReadUntilAsync(c4, "-ERR", timeoutMs: 5000);
|
||||
response.ShouldContain("maximum connections exceeded");
|
||||
}
|
||||
finally
|
||||
@@ -1309,12 +1281,12 @@ public class ConfigReloadExtendedParityTests
|
||||
[Fact]
|
||||
public async Task Reload_cluster_port_change_rejected()
|
||||
{
|
||||
var clusterPort = GetFreePort();
|
||||
var clusterPort = TestPortAllocator.GetFreePort();
|
||||
var (server, port, cts, configPath) = await StartServerWithConfigAsync(
|
||||
$"port: {{PORT}}\ncluster {{\n host: 127.0.0.1\n port: {clusterPort}\n}}");
|
||||
try
|
||||
{
|
||||
var newClusterPort = GetFreePort();
|
||||
var newClusterPort = TestPortAllocator.GetFreePort();
|
||||
File.WriteAllText(configPath,
|
||||
$"port: {port}\ncluster {{\n host: 127.0.0.1\n port: {newClusterPort}\n}}");
|
||||
Should.Throw<InvalidOperationException>(() => server.ReloadConfigOrThrow())
|
||||
@@ -1383,7 +1355,7 @@ public class ConfigReloadExtendedParityTests
|
||||
[Fact]
|
||||
public async Task Reload_cli_overrides_preserved()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var configPath = Path.Combine(Path.GetTempPath(), $"natsdotnet-cli-{Guid.NewGuid():N}.conf");
|
||||
File.WriteAllText(configPath, $"port: {port}\ndebug: false");
|
||||
|
||||
@@ -1734,7 +1706,7 @@ public class ConfigReloadExtendedParityTests
|
||||
var (server, port, cts, configPath) = await StartServerWithConfigAsync("port: {PORT}");
|
||||
try
|
||||
{
|
||||
var monPort = GetFreePort();
|
||||
var monPort = TestPortAllocator.GetFreePort();
|
||||
WriteConfigAndReload(server, configPath, $"port: {port}\nhttp_port: {monPort}");
|
||||
|
||||
await using var client = new NatsConnection(new NatsOpts { Url = $"nats://127.0.0.1:{port}" });
|
||||
@@ -1756,7 +1728,7 @@ public class ConfigReloadExtendedParityTests
|
||||
var (server, port, cts, configPath) = await StartServerWithConfigAsync("port: {PORT}");
|
||||
try
|
||||
{
|
||||
var profPort = GetFreePort();
|
||||
var profPort = TestPortAllocator.GetFreePort();
|
||||
WriteConfigAndReload(server, configPath, $"port: {port}\nprof_port: {profPort}");
|
||||
|
||||
await using var client = new NatsConnection(new NatsOpts { Url = $"nats://127.0.0.1:{port}" });
|
||||
@@ -9,8 +9,9 @@ using System.Text;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using NATS.Client.Core;
|
||||
using NATS.Server.Configuration;
|
||||
using NATS.Server.TestUtilities;
|
||||
|
||||
namespace NATS.Server.Tests.Configuration;
|
||||
namespace NATS.Server.Core.Tests.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Parity tests for config hot reload behaviour.
|
||||
@@ -23,16 +24,9 @@ public class ConfigReloadParityTests
|
||||
{
|
||||
// ─── Helpers ────────────────────────────────────────────────────────────
|
||||
|
||||
private static int GetFreePort()
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
sock.Bind(new IPEndPoint(IPAddress.Loopback, 0));
|
||||
return ((IPEndPoint)sock.LocalEndPoint!).Port;
|
||||
}
|
||||
|
||||
private static async Task<(NatsServer server, int port, CancellationTokenSource cts)> StartServerAsync(NatsOptions options)
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
options.Port = port;
|
||||
var server = new NatsServer(options, NullLoggerFactory.Instance);
|
||||
var cts = new CancellationTokenSource();
|
||||
@@ -60,28 +54,6 @@ public class ConfigReloadParityTests
|
||||
/// Reads from <paramref name="sock"/> until the accumulated response contains
|
||||
/// <paramref name="expected"/> or the timeout elapses.
|
||||
/// </summary>
|
||||
private static async Task<string> ReadUntilAsync(Socket sock, string expected, int timeoutMs = 5000)
|
||||
{
|
||||
using var cts = new CancellationTokenSource(timeoutMs);
|
||||
var sb = new StringBuilder();
|
||||
var buf = new byte[4096];
|
||||
while (!sb.ToString().Contains(expected, StringComparison.Ordinal))
|
||||
{
|
||||
int n;
|
||||
try
|
||||
{
|
||||
n = await sock.ReceiveAsync(buf, SocketFlags.None, cts.Token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (n == 0) break;
|
||||
sb.Append(Encoding.ASCII.GetString(buf, 0, n));
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a config file, then calls <see cref="NatsServer.ReloadConfigOrThrow"/>.
|
||||
/// Mirrors the pattern from JetStreamClusterReloadTests.
|
||||
@@ -115,7 +87,7 @@ public class ConfigReloadParityTests
|
||||
// Allocate a port first so we can embed it in the config file.
|
||||
// The server will bind to this port; the config file must match
|
||||
// to avoid a non-reloadable Port-change error on reload.
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
|
||||
// Start with no connection limit.
|
||||
File.WriteAllText(configPath, $"port: {port}\nmax_connections: 65536");
|
||||
@@ -144,7 +116,7 @@ public class ConfigReloadParityTests
|
||||
await c3.ConnectAsync(IPAddress.Loopback, port);
|
||||
|
||||
// The server sends INFO then immediately -ERR and closes the socket.
|
||||
var response = await ReadUntilAsync(c3, "-ERR", timeoutMs: 5000);
|
||||
var response = await SocketTestHelper.ReadUntilAsync(c3, "-ERR", timeoutMs: 5000);
|
||||
response.ShouldContain("maximum connections exceeded");
|
||||
}
|
||||
finally
|
||||
@@ -175,7 +147,7 @@ public class ConfigReloadParityTests
|
||||
{
|
||||
// Allocate a port and embed it in every config write to prevent a
|
||||
// non-reloadable Port-change error when the config file is updated.
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
|
||||
// Start with no authentication required.
|
||||
File.WriteAllText(configPath, $"port: {port}\ndebug: false");
|
||||
@@ -252,7 +224,7 @@ public class ConfigReloadParityTests
|
||||
{
|
||||
// Allocate a port and embed it in every config write to prevent a
|
||||
// non-reloadable Port-change error when the config file is updated.
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
|
||||
// Start with debug disabled.
|
||||
File.WriteAllText(configPath, $"port: {port}\ndebug: false");
|
||||
@@ -1,6 +1,6 @@
|
||||
using NATS.Server.Configuration;
|
||||
|
||||
namespace NATS.Server.Tests.Configuration;
|
||||
namespace NATS.Server.Core.Tests.Configuration;
|
||||
|
||||
public class ConfigWarningsParityBatch1Tests
|
||||
{
|
||||
@@ -4,7 +4,7 @@
|
||||
using NATS.Server.Configuration;
|
||||
using Shouldly;
|
||||
|
||||
namespace NATS.Server.Tests.Configuration;
|
||||
namespace NATS.Server.Core.Tests.Configuration;
|
||||
|
||||
public sealed class JetStreamConfigReloadTests
|
||||
{
|
||||
@@ -5,7 +5,7 @@ using NATS.Server;
|
||||
using NATS.Server.Configuration;
|
||||
using Shouldly;
|
||||
|
||||
namespace NATS.Server.Tests.Configuration;
|
||||
namespace NATS.Server.Core.Tests.Configuration;
|
||||
|
||||
public class LoggingReloadTests
|
||||
{
|
||||
@@ -6,7 +6,7 @@ using System.Net.Sockets;
|
||||
using NATS.Server.Auth;
|
||||
using NATS.Server.Configuration;
|
||||
|
||||
namespace NATS.Server.Tests.Configuration;
|
||||
namespace NATS.Server.Core.Tests.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Parity tests ported from Go server/opts_test.go that exercise config parsing,
|
||||
@@ -15,8 +15,9 @@ using System.Text;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using NATS.Client.Core;
|
||||
using NATS.Server.Configuration;
|
||||
using NATS.Server.TestUtilities;
|
||||
|
||||
namespace NATS.Server.Tests.Configuration;
|
||||
namespace NATS.Server.Core.Tests.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Go-parity tests for config hot-reload behaviour ported from Go's reload_test.go.
|
||||
@@ -28,13 +29,6 @@ public class ReloadGoParityTests
|
||||
{
|
||||
// ─── Helpers ────────────────────────────────────────────────────────────
|
||||
|
||||
private static int GetFreePort()
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
sock.Bind(new IPEndPoint(IPAddress.Loopback, 0));
|
||||
return ((IPEndPoint)sock.LocalEndPoint!).Port;
|
||||
}
|
||||
|
||||
private static async Task<Socket> RawConnectAsync(int port)
|
||||
{
|
||||
var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
@@ -44,28 +38,6 @@ public class ReloadGoParityTests
|
||||
return sock;
|
||||
}
|
||||
|
||||
private static async Task<string> ReadUntilAsync(Socket sock, string expected, int timeoutMs = 5000)
|
||||
{
|
||||
using var cts = new CancellationTokenSource(timeoutMs);
|
||||
var sb = new StringBuilder();
|
||||
var buf = new byte[4096];
|
||||
while (!sb.ToString().Contains(expected, StringComparison.Ordinal))
|
||||
{
|
||||
int n;
|
||||
try
|
||||
{
|
||||
n = await sock.ReceiveAsync(buf, SocketFlags.None, cts.Token);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (n == 0) break;
|
||||
sb.Append(Encoding.ASCII.GetString(buf, 0, n));
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static void WriteConfigAndReload(NatsServer server, string configPath, string configText)
|
||||
{
|
||||
File.WriteAllText(configPath, configText);
|
||||
@@ -75,7 +47,7 @@ public class ReloadGoParityTests
|
||||
private static async Task<(NatsServer server, int port, CancellationTokenSource cts, string configPath)>
|
||||
StartServerWithConfigAsync(string configContent)
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var configPath = Path.Combine(Path.GetTempPath(), $"natsdotnet-reload-goparity-{Guid.NewGuid():N}.conf");
|
||||
var finalContent = configContent.Replace("{PORT}", port.ToString());
|
||||
File.WriteAllText(configPath, finalContent);
|
||||
@@ -130,7 +102,7 @@ public class ReloadGoParityTests
|
||||
// A third connection should be rejected.
|
||||
using var c3 = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
await c3.ConnectAsync(IPAddress.Loopback, port);
|
||||
var response = await ReadUntilAsync(c3, "-ERR", timeoutMs: 5000);
|
||||
var response = await SocketTestHelper.ReadUntilAsync(c3, "-ERR", timeoutMs: 5000);
|
||||
response.ShouldContain("maximum connections exceeded");
|
||||
}
|
||||
finally
|
||||
@@ -167,7 +139,7 @@ public class ReloadGoParityTests
|
||||
// A third connection should be rejected (limit=2, count=2).
|
||||
using var c3reject = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
await c3reject.ConnectAsync(IPAddress.Loopback, port);
|
||||
var r1 = await ReadUntilAsync(c3reject, "-ERR", timeoutMs: 5000);
|
||||
var r1 = await SocketTestHelper.ReadUntilAsync(c3reject, "-ERR", timeoutMs: 5000);
|
||||
r1.ShouldContain("maximum connections exceeded");
|
||||
|
||||
// Increase limit to 10.
|
||||
@@ -203,11 +175,11 @@ public class ReloadGoParityTests
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("CONNECT {\"verbose\":false,\"pedantic\":false}\r\n"), SocketFlags.None);
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("SUB foo 1\r\n"), SocketFlags.None);
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"), SocketFlags.None);
|
||||
await ReadUntilAsync(sock, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sock, "PONG");
|
||||
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("PUB foo 5\r\nhello\r\n"), SocketFlags.None);
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"), SocketFlags.None);
|
||||
var beforeResponse = await ReadUntilAsync(sock, "PONG");
|
||||
var beforeResponse = await SocketTestHelper.ReadUntilAsync(sock, "PONG");
|
||||
beforeResponse.ShouldContain("MSG foo");
|
||||
|
||||
// Reduce max_payload to 2 bytes.
|
||||
@@ -217,7 +189,7 @@ public class ReloadGoParityTests
|
||||
using var sock2 = await RawConnectAsync(port);
|
||||
await sock2.SendAsync(Encoding.ASCII.GetBytes("CONNECT {\"verbose\":false,\"pedantic\":false}\r\n"), SocketFlags.None);
|
||||
await sock2.SendAsync(Encoding.ASCII.GetBytes("PUB foo 5\r\nhello\r\n"), SocketFlags.None);
|
||||
var errResponse = await ReadUntilAsync(sock2, "-ERR", timeoutMs: 5000);
|
||||
var errResponse = await SocketTestHelper.ReadUntilAsync(sock2, "-ERR", timeoutMs: 5000);
|
||||
errResponse.ShouldContain("-ERR");
|
||||
}
|
||||
finally
|
||||
@@ -379,12 +351,12 @@ public class ReloadGoParityTests
|
||||
public async Task ReloadGoParityTests_ClusterAuthorization_ChangedClusterIsRejected()
|
||||
{
|
||||
// Go: TestConfigReloadEnableClusterAuthorization (reload_test.go:1411)
|
||||
var clusterPort = GetFreePort();
|
||||
var clusterPort = TestPortAllocator.GetFreePort();
|
||||
var (server, port, cts, configPath) = await StartServerWithConfigAsync(
|
||||
$"port: {{PORT}}\ncluster {{\n name: mycluster\n host: 127.0.0.1\n port: {clusterPort}\n}}");
|
||||
try
|
||||
{
|
||||
var newClusterPort = GetFreePort();
|
||||
var newClusterPort = TestPortAllocator.GetFreePort();
|
||||
// Changing cluster host/port must be rejected.
|
||||
File.WriteAllText(configPath,
|
||||
$"port: {port}\ncluster {{\n name: mycluster\n host: 127.0.0.1\n port: {newClusterPort}\n}}");
|
||||
@@ -413,12 +385,12 @@ public class ReloadGoParityTests
|
||||
public async Task ReloadGoParityTests_ClusterRoutes_ChangeIsRejected()
|
||||
{
|
||||
// Go: TestConfigReloadClusterRoutes (reload_test.go:1586)
|
||||
var clusterPort = GetFreePort();
|
||||
var clusterPort = TestPortAllocator.GetFreePort();
|
||||
var (server, port, cts, configPath) = await StartServerWithConfigAsync(
|
||||
$"port: {{PORT}}\ncluster {{\n name: testcluster\n host: 127.0.0.1\n port: {clusterPort}\n}}");
|
||||
try
|
||||
{
|
||||
var otherPort = GetFreePort();
|
||||
var otherPort = TestPortAllocator.GetFreePort();
|
||||
// Adding routes also changes the cluster block.
|
||||
File.WriteAllText(configPath,
|
||||
$"port: {port}\ncluster {{\n name: testcluster\n host: 127.0.0.1\n port: {clusterPort}\n routes: [\"nats://127.0.0.1:{otherPort}\"]\n}}");
|
||||
@@ -583,7 +555,7 @@ public class ReloadGoParityTests
|
||||
public async Task ReloadGoParityTests_RoutePool_ClusterBlockIsImmutable()
|
||||
{
|
||||
// Go: TestConfigReloadRoutePoolAndPerAccount (reload_test.go:5148)
|
||||
var clusterPort = GetFreePort();
|
||||
var clusterPort = TestPortAllocator.GetFreePort();
|
||||
var (server, port, cts, configPath) = await StartServerWithConfigAsync(
|
||||
$"port: {{PORT}}\ncluster {{\n name: local\n host: 127.0.0.1\n port: {clusterPort}\n pool_size: 3\n}}");
|
||||
try
|
||||
@@ -612,7 +584,7 @@ public class ReloadGoParityTests
|
||||
public async Task ReloadGoParityTests_PerAccountRoutes_ClusterBlockIsImmutable()
|
||||
{
|
||||
// Go: TestConfigReloadRoutePoolAndPerAccount (reload_test.go:5148)
|
||||
var clusterPort = GetFreePort();
|
||||
var clusterPort = TestPortAllocator.GetFreePort();
|
||||
var (server, port, cts, configPath) = await StartServerWithConfigAsync(
|
||||
$"port: {{PORT}}\naccounts {{\n A {{ users: [{{user: u1, password: pwd}}] }}\n}}\ncluster {{\n name: local\n host: 127.0.0.1\n port: {clusterPort}\n}}");
|
||||
try
|
||||
@@ -640,7 +612,7 @@ public class ReloadGoParityTests
|
||||
public async Task ReloadGoParityTests_RouteCompression_ClusterBlockIsImmutable()
|
||||
{
|
||||
// Go: TestConfigReloadRouteCompression (reload_test.go:5877)
|
||||
var clusterPort = GetFreePort();
|
||||
var clusterPort = TestPortAllocator.GetFreePort();
|
||||
var (server, port, cts, configPath) = await StartServerWithConfigAsync(
|
||||
$"port: {{PORT}}\ncluster {{\n name: local\n host: 127.0.0.1\n port: {clusterPort}\n}}");
|
||||
try
|
||||
@@ -782,7 +754,7 @@ public class ReloadGoParityTests
|
||||
{
|
||||
// Go: TestConfigReloadNoPanicOnShutdown (reload_test.go:6358)
|
||||
var configPath = Path.Combine(Path.GetTempPath(), $"natsdotnet-shutdown-{Guid.NewGuid():N}.conf");
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
|
||||
try
|
||||
{
|
||||
@@ -984,7 +956,7 @@ public class ReloadGoParityTests
|
||||
// Go: TestConfigReloadNotPreventedByGateways (reload_test.go:3445)
|
||||
// Note: server_name is non-reloadable, so we do not include it to avoid
|
||||
// conflicts between the parsed config value and the Options default.
|
||||
var gatewayPort = GetFreePort();
|
||||
var gatewayPort = TestPortAllocator.GetFreePort();
|
||||
var (server, port, cts, configPath) = await StartServerWithConfigAsync(
|
||||
$"port: {{PORT}}\ngateway {{\n name: local\n port: {gatewayPort}\n}}");
|
||||
try
|
||||
@@ -1013,7 +985,7 @@ public class ReloadGoParityTests
|
||||
public async Task ReloadGoParityTests_BoolFlags_CliOverridePreserved()
|
||||
{
|
||||
// Go: TestConfigReloadBoolFlags (reload_test.go:3480)
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var configPath = Path.Combine(Path.GetTempPath(), $"natsdotnet-boolflags-{Guid.NewGuid():N}.conf");
|
||||
File.WriteAllText(configPath, $"port: {port}\ndebug: false");
|
||||
|
||||
@@ -8,8 +8,9 @@ using System.Text;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using NATS.Client.Core;
|
||||
using NATS.Server.Configuration;
|
||||
using NATS.Server.TestUtilities;
|
||||
|
||||
namespace NATS.Server.Tests.Configuration;
|
||||
namespace NATS.Server.Core.Tests.Configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for SIGHUP-triggered config reload and the ConfigReloader async API.
|
||||
@@ -24,13 +25,6 @@ public class SignalReloadTests
|
||||
{
|
||||
// ─── Helpers ────────────────────────────────────────────────────────────
|
||||
|
||||
private static int GetFreePort()
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
sock.Bind(new IPEndPoint(IPAddress.Loopback, 0));
|
||||
return ((IPEndPoint)sock.LocalEndPoint!).Port;
|
||||
}
|
||||
|
||||
private static async Task<Socket> RawConnectAsync(int port)
|
||||
{
|
||||
var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
@@ -40,22 +34,6 @@ public class SignalReloadTests
|
||||
return sock;
|
||||
}
|
||||
|
||||
private static async Task<string> ReadUntilAsync(Socket sock, string expected, int timeoutMs = 5000)
|
||||
{
|
||||
using var cts = new CancellationTokenSource(timeoutMs);
|
||||
var sb = new StringBuilder();
|
||||
var buf = new byte[4096];
|
||||
while (!sb.ToString().Contains(expected, StringComparison.Ordinal))
|
||||
{
|
||||
int n;
|
||||
try { n = await sock.ReceiveAsync(buf, SocketFlags.None, cts.Token); }
|
||||
catch (OperationCanceledException) { break; }
|
||||
if (n == 0) break;
|
||||
sb.Append(Encoding.ASCII.GetString(buf, 0, n));
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private static void WriteConfigAndReload(NatsServer server, string configPath, string configText)
|
||||
{
|
||||
File.WriteAllText(configPath, configText);
|
||||
@@ -77,7 +55,7 @@ public class SignalReloadTests
|
||||
var configPath = Path.Combine(Path.GetTempPath(), $"natsdotnet-sighup-{Guid.NewGuid():N}.conf");
|
||||
try
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
File.WriteAllText(configPath, $"port: {port}\ndebug: false");
|
||||
|
||||
var options = new NatsOptions { ConfigFile = configPath, Port = port };
|
||||
@@ -291,7 +269,7 @@ public class SignalReloadTests
|
||||
var configPath = Path.Combine(Path.GetTempPath(), $"natsdotnet-e2e-{Guid.NewGuid():N}.conf");
|
||||
try
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
File.WriteAllText(configPath, $"port: {port}\nmax_connections: 65536");
|
||||
|
||||
var options = new NatsOptions { ConfigFile = configPath, Port = port };
|
||||
@@ -312,7 +290,7 @@ public class SignalReloadTests
|
||||
// New connection should be rejected
|
||||
using var c2 = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
await c2.ConnectAsync(IPAddress.Loopback, port);
|
||||
var response = await ReadUntilAsync(c2, "-ERR", timeoutMs: 5000);
|
||||
var response = await SocketTestHelper.ReadUntilAsync(c2, "-ERR", timeoutMs: 5000);
|
||||
response.ShouldContain("maximum connections exceeded");
|
||||
}
|
||||
finally
|
||||
@@ -336,7 +314,7 @@ public class SignalReloadTests
|
||||
var configPath = Path.Combine(Path.GetTempPath(), $"natsdotnet-throw-{Guid.NewGuid():N}.conf");
|
||||
try
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
File.WriteAllText(configPath, $"port: {port}\nserver_name: original");
|
||||
|
||||
var options = new NatsOptions { ConfigFile = configPath, Port = port, ServerName = "original" };
|
||||
@@ -8,7 +8,7 @@ using System.Security.Cryptography.X509Certificates;
|
||||
using NATS.Server.Configuration;
|
||||
using NATS.Server.Tls;
|
||||
|
||||
namespace NATS.Server.Tests.Configuration;
|
||||
namespace NATS.Server.Core.Tests.Configuration;
|
||||
|
||||
public class TlsReloadTests
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using NATS.Server.TestUtilities.Parity;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
public class DifferencesParityClosureTests
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
// Go reference: server/client.go (maxFlushPending, pcd, flush signal coalescing)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
public class GoParityRunnerTests
|
||||
{
|
||||
@@ -33,8 +33,9 @@ using NATS.Server.Configuration;
|
||||
using NATS.Server.Protocol;
|
||||
using NATS.Server.Subscriptions;
|
||||
using Serilog;
|
||||
using NATS.Server.TestUtilities;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Infrastructure parity tests covering parser utilities, logging, error wrapping,
|
||||
@@ -44,26 +45,7 @@ public class InfrastructureGoParityTests
|
||||
{
|
||||
// ─── helpers ─────────────────────────────────────────────────────────────
|
||||
|
||||
private static int GetFreePort()
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
sock.Bind(new IPEndPoint(IPAddress.Loopback, 0));
|
||||
return ((IPEndPoint)sock.LocalEndPoint!).Port;
|
||||
}
|
||||
|
||||
private static async Task<string> ReadUntilAsync(Socket sock, string expected, int timeoutMs = 5000)
|
||||
{
|
||||
using var cts = new CancellationTokenSource(timeoutMs);
|
||||
var sb = new StringBuilder();
|
||||
var buf = new byte[4096];
|
||||
while (!sb.ToString().Contains(expected))
|
||||
{
|
||||
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 async Task<List<ParsedCommand>> ParseCommandsAsync(string input)
|
||||
{
|
||||
@@ -416,7 +398,7 @@ public class InfrastructureGoParityTests
|
||||
public void Log_server_reopen_log_file_callback_is_invocable()
|
||||
{
|
||||
// Go: TestReOpenLogFile (log_test.go:84)
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
using var cts = new CancellationTokenSource();
|
||||
using var server = new NatsServer(new NatsOptions { Port = port }, NullLoggerFactory.Instance);
|
||||
|
||||
@@ -818,7 +800,7 @@ public class InfrastructureGoParityTests
|
||||
public async Task NKey_info_json_contains_nonce_when_nkeys_configured()
|
||||
{
|
||||
// Go: TestServerInfoNonceAlwaysEnabled (nkey_test.go:58)
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
using var cts = new CancellationTokenSource();
|
||||
|
||||
var kp = KeyPair.CreatePair(PrefixByte.User);
|
||||
@@ -835,7 +817,7 @@ public class InfrastructureGoParityTests
|
||||
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
await sock.ConnectAsync(IPAddress.Loopback, port);
|
||||
var info = await ReadUntilAsync(sock, "\r\n");
|
||||
var info = await SocketTestHelper.ReadUntilAsync(sock, "\r\n");
|
||||
sock.Dispose();
|
||||
await cts.CancelAsync();
|
||||
|
||||
@@ -852,7 +834,7 @@ public class InfrastructureGoParityTests
|
||||
public async Task Ping_server_sends_ping_at_interval()
|
||||
{
|
||||
// Go: TestPing (ping_test.go:34)
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
using var cts = new CancellationTokenSource();
|
||||
using var server = new NatsServer(new NatsOptions
|
||||
{
|
||||
@@ -866,14 +848,14 @@ public class InfrastructureGoParityTests
|
||||
|
||||
var conn = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
await conn.ConnectAsync(IPAddress.Loopback, port);
|
||||
await ReadUntilAsync(conn, "\r\n"); // consume INFO
|
||||
await SocketTestHelper.ReadUntilAsync(conn, "\r\n"); // consume INFO
|
||||
|
||||
// Establish the connection
|
||||
await conn.SendAsync("CONNECT {\"verbose\":false}\r\nPING\r\n"u8.ToArray());
|
||||
await ReadUntilAsync(conn, "PONG"); // initial PONG
|
||||
await SocketTestHelper.ReadUntilAsync(conn, "PONG"); // initial PONG
|
||||
|
||||
// Wait for the server to send a PING
|
||||
var received = await ReadUntilAsync(conn, "PING", 2000);
|
||||
var received = await SocketTestHelper.ReadUntilAsync(conn, "PING", 2000);
|
||||
received.ShouldContain("PING");
|
||||
|
||||
// Reply with PONG to keep alive
|
||||
@@ -4,8 +4,9 @@ using System.Text;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using NATS.Client.Core;
|
||||
using NATS.Server;
|
||||
using NATS.Server.TestUtilities;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
public class IntegrationTests : IAsyncLifetime
|
||||
{
|
||||
@@ -16,7 +17,7 @@ public class IntegrationTests : IAsyncLifetime
|
||||
|
||||
public IntegrationTests()
|
||||
{
|
||||
_port = GetFreePort();
|
||||
_port = TestPortAllocator.GetFreePort();
|
||||
_server = new NatsServer(new NatsOptions { Port = _port }, NullLoggerFactory.Instance);
|
||||
}
|
||||
|
||||
@@ -32,12 +33,6 @@ public class IntegrationTests : IAsyncLifetime
|
||||
_server.Dispose();
|
||||
}
|
||||
|
||||
private static int GetFreePort()
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
sock.Bind(new IPEndPoint(IPAddress.Loopback, 0));
|
||||
return ((IPEndPoint)sock.LocalEndPoint!).Port;
|
||||
}
|
||||
|
||||
private NatsConnection CreateClient()
|
||||
{
|
||||
@@ -14,7 +14,7 @@
|
||||
using System.Diagnostics;
|
||||
using NATS.Server.Internal.Avl;
|
||||
|
||||
namespace NATS.Server.Tests.Internal.Avl;
|
||||
namespace NATS.Server.Core.Tests.Internal.Avl;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for the AVL-backed SequenceSet, ported from Go server/avl/seqset_test.go
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
using NATS.Server.Internal.Gsl;
|
||||
|
||||
namespace NATS.Server.Tests.Internal.Gsl;
|
||||
namespace NATS.Server.Core.Tests.Internal.Gsl;
|
||||
|
||||
public class GenericSubjectListTests
|
||||
{
|
||||
@@ -5,7 +5,7 @@ using NATS.Server.Internal.SubjectTree;
|
||||
using NATS.Server.Internal.SysMem;
|
||||
using NATS.Server.Internal.TimeHashWheel;
|
||||
|
||||
namespace NATS.Server.Tests.Internal;
|
||||
namespace NATS.Server.Core.Tests.Internal;
|
||||
|
||||
public class InternalDsParityBatch2Tests
|
||||
{
|
||||
@@ -2,7 +2,7 @@ using System.Reflection;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using NATS.Server.Monitoring;
|
||||
|
||||
namespace NATS.Server.Tests.Internal;
|
||||
namespace NATS.Server.Core.Tests.Internal;
|
||||
|
||||
public class InternalDsPeriodicSamplerParityTests
|
||||
{
|
||||
@@ -3,7 +3,7 @@ using System.Text.Json;
|
||||
using NATS.Server.Events;
|
||||
using NATS.Server.Internal;
|
||||
|
||||
namespace NATS.Server.Tests.Internal;
|
||||
namespace NATS.Server.Core.Tests.Internal;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for MsgTraceContext: header parsing, event collection, trace propagation,
|
||||
@@ -3,7 +3,7 @@ using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using NATS.Server.Internal.SubjectTree;
|
||||
|
||||
namespace NATS.Server.Tests.Internal.SubjectTree;
|
||||
namespace NATS.Server.Core.Tests.Internal.SubjectTree;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for the Adaptive Radix Tree (ART) based SubjectTree.
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
using NATS.Server.Internal.TimeHashWheel;
|
||||
|
||||
namespace NATS.Server.Tests.Internal.TimeHashWheel;
|
||||
namespace NATS.Server.Core.Tests.Internal.TimeHashWheel;
|
||||
|
||||
public class HashWheelTests
|
||||
{
|
||||
@@ -1,7 +1,7 @@
|
||||
using System.Text;
|
||||
using NATS.Server.Internal;
|
||||
|
||||
namespace NATS.Server.Tests.Internal;
|
||||
namespace NATS.Server.Core.Tests.Internal;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for TraceContextPropagator: trace creation, header injection/extraction,
|
||||
@@ -1,6 +1,6 @@
|
||||
using NATS.Server.Auth;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
public class InternalClientTests
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using Serilog;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
public class LoggingTests : IDisposable
|
||||
{
|
||||
@@ -15,8 +15,9 @@ using System.Text;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using NATS.Server;
|
||||
using NATS.Server.Protocol;
|
||||
using NATS.Server.TestUtilities;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for message trace infrastructure: MessageTraceContext population,
|
||||
@@ -33,7 +34,7 @@ public class MessageTraceTests : IAsyncLifetime
|
||||
|
||||
public MessageTraceTests()
|
||||
{
|
||||
_port = GetFreePort();
|
||||
_port = TestPortAllocator.GetFreePort();
|
||||
_server = new NatsServer(new NatsOptions { Port = _port }, NullLoggerFactory.Instance);
|
||||
}
|
||||
|
||||
@@ -49,32 +50,13 @@ public class MessageTraceTests : IAsyncLifetime
|
||||
_server.Dispose();
|
||||
}
|
||||
|
||||
private static int GetFreePort()
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
sock.Bind(new IPEndPoint(IPAddress.Loopback, 0));
|
||||
return ((IPEndPoint)sock.LocalEndPoint!).Port;
|
||||
}
|
||||
|
||||
private static async Task<string> ReadUntilAsync(Socket sock, string expected, int timeoutMs = 5000)
|
||||
{
|
||||
using var cts = new CancellationTokenSource(timeoutMs);
|
||||
var sb = new StringBuilder();
|
||||
var buf = new byte[4096];
|
||||
while (!sb.ToString().Contains(expected))
|
||||
{
|
||||
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 async Task<Socket> ConnectWithHeadersAsync(string? clientName = null, string? lang = null, string? version = null)
|
||||
{
|
||||
var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
await sock.ConnectAsync(IPAddress.Loopback, _port);
|
||||
await ReadUntilAsync(sock, "\r\n"); // discard INFO
|
||||
await SocketTestHelper.ReadUntilAsync(sock, "\r\n"); // discard INFO
|
||||
|
||||
var connectJson = BuildConnectJson(headers: true, name: clientName, lang: lang, version: version);
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes($"CONNECT {connectJson}\r\n"));
|
||||
@@ -300,7 +282,7 @@ public class MessageTraceTests : IAsyncLifetime
|
||||
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("SUB trace.test 1\r\n"));
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG");
|
||||
|
||||
// Build HPUB with Nats-Trace-Dest header
|
||||
// Header block: "NATS/1.0\r\nNats-Trace-Dest: trace.inbox\r\n\r\n"
|
||||
@@ -312,7 +294,7 @@ public class MessageTraceTests : IAsyncLifetime
|
||||
var hpub = $"HPUB trace.test {hdrLen} {totalLen}\r\n{headerBlock}{payload}\r\n";
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes(hpub));
|
||||
|
||||
var received = await ReadUntilAsync(sub, "Nats-Trace-Dest");
|
||||
var received = await SocketTestHelper.ReadUntilAsync(sub, "Nats-Trace-Dest");
|
||||
|
||||
received.ShouldContain("HMSG trace.test");
|
||||
received.ShouldContain("Nats-Trace-Dest: trace.inbox");
|
||||
@@ -333,7 +315,7 @@ public class MessageTraceTests : IAsyncLifetime
|
||||
// Subscribe to wildcard
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("SUB trace.* 1\r\n"));
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG");
|
||||
|
||||
const string headerBlock = "NATS/1.0\r\nNats-Trace-Dest: t.inbox.1\r\n\r\n";
|
||||
const string payload = "wildcard-msg";
|
||||
@@ -343,7 +325,7 @@ public class MessageTraceTests : IAsyncLifetime
|
||||
var hpub = $"HPUB trace.subject {hdrLen} {totalLen}\r\n{headerBlock}{payload}\r\n";
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes(hpub));
|
||||
|
||||
var received = await ReadUntilAsync(sub, "Nats-Trace-Dest");
|
||||
var received = await SocketTestHelper.ReadUntilAsync(sub, "Nats-Trace-Dest");
|
||||
|
||||
received.ShouldContain("HMSG trace.subject");
|
||||
received.ShouldContain("Nats-Trace-Dest: t.inbox.1");
|
||||
@@ -364,7 +346,7 @@ public class MessageTraceTests : IAsyncLifetime
|
||||
// Queue group subscription
|
||||
await qsub.SendAsync(Encoding.ASCII.GetBytes("SUB trace.q workers 1\r\n"));
|
||||
await qsub.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
|
||||
await ReadUntilAsync(qsub, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(qsub, "PONG");
|
||||
|
||||
const string headerBlock = "NATS/1.0\r\nNats-Trace-Dest: qg.trace\r\n\r\n";
|
||||
const string payload = "queued";
|
||||
@@ -374,7 +356,7 @@ public class MessageTraceTests : IAsyncLifetime
|
||||
var hpub = $"HPUB trace.q {hdrLen} {totalLen}\r\n{headerBlock}{payload}\r\n";
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes(hpub));
|
||||
|
||||
var received = await ReadUntilAsync(qsub, "Nats-Trace-Dest");
|
||||
var received = await SocketTestHelper.ReadUntilAsync(qsub, "Nats-Trace-Dest");
|
||||
|
||||
received.ShouldContain("Nats-Trace-Dest: qg.trace");
|
||||
received.ShouldContain("queued");
|
||||
@@ -393,7 +375,7 @@ public class MessageTraceTests : IAsyncLifetime
|
||||
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("SUB multi.hdr 1\r\n"));
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG");
|
||||
|
||||
const string headerBlock =
|
||||
"NATS/1.0\r\n" +
|
||||
@@ -408,7 +390,7 @@ public class MessageTraceTests : IAsyncLifetime
|
||||
var hpub = $"HPUB multi.hdr {hdrLen} {totalLen}\r\n{headerBlock}{payload}\r\n";
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes(hpub));
|
||||
|
||||
var received = await ReadUntilAsync(sub, "X-Priority");
|
||||
var received = await SocketTestHelper.ReadUntilAsync(sub, "X-Priority");
|
||||
|
||||
received.ShouldContain("X-Request-Id: req-99");
|
||||
received.ShouldContain("Nats-Trace-Dest: t.multi");
|
||||
@@ -429,7 +411,7 @@ public class MessageTraceTests : IAsyncLifetime
|
||||
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("SUB trace.long 1\r\n"));
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG");
|
||||
|
||||
var longId = new string('a', 256);
|
||||
var headerBlock = $"NATS/1.0\r\nNats-Trace-Dest: {longId}\r\n\r\n";
|
||||
@@ -440,7 +422,7 @@ public class MessageTraceTests : IAsyncLifetime
|
||||
var hpub = $"HPUB trace.long {hdrLen} {totalLen}\r\n{headerBlock}{payload}\r\n";
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes(hpub));
|
||||
|
||||
var received = await ReadUntilAsync(sub, longId);
|
||||
var received = await SocketTestHelper.ReadUntilAsync(sub, longId);
|
||||
|
||||
received.ShouldContain(longId);
|
||||
}
|
||||
@@ -494,7 +476,7 @@ public class MessageTraceTests : IAsyncLifetime
|
||||
[Fact]
|
||||
public async Task Server_with_trace_enabled_starts_and_accepts_connections()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
using var cts = new CancellationTokenSource();
|
||||
using var server = new NatsServer(new NatsOptions { Port = port, Trace = true }, NullLoggerFactory.Instance);
|
||||
_ = server.StartAsync(cts.Token);
|
||||
@@ -502,7 +484,7 @@ public class MessageTraceTests : IAsyncLifetime
|
||||
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
await sock.ConnectAsync(IPAddress.Loopback, port);
|
||||
var info = await ReadUntilAsync(sock, "\r\n");
|
||||
var info = await SocketTestHelper.ReadUntilAsync(sock, "\r\n");
|
||||
|
||||
info.ShouldStartWith("INFO ");
|
||||
|
||||
@@ -17,7 +17,7 @@ using NATS.Server.Monitoring;
|
||||
using NATS.Server.Protocol;
|
||||
using NATS.Server.TestUtilities;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Go parity tests for message trace header infrastructure and closed-connection
|
||||
@@ -32,7 +32,7 @@ public class MsgTraceGoParityTests : IAsyncLifetime
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
_port = GetFreePort();
|
||||
_port = TestPortAllocator.GetFreePort();
|
||||
_server = new NatsServer(new NatsOptions { Port = _port }, NullLoggerFactory.Instance);
|
||||
_ = _server.StartAsync(_cts.Token);
|
||||
await _server.WaitForReadyAsync();
|
||||
@@ -46,32 +46,13 @@ public class MsgTraceGoParityTests : IAsyncLifetime
|
||||
|
||||
// ─── helpers ────────────────────────────────────────────────────────────
|
||||
|
||||
private static int GetFreePort()
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
sock.Bind(new IPEndPoint(IPAddress.Loopback, 0));
|
||||
return ((IPEndPoint)sock.LocalEndPoint!).Port;
|
||||
}
|
||||
|
||||
private static async Task<string> ReadUntilAsync(Socket sock, string expected, int timeoutMs = 5000)
|
||||
{
|
||||
using var cts = new CancellationTokenSource(timeoutMs);
|
||||
var sb = new StringBuilder();
|
||||
var buf = new byte[4096];
|
||||
while (!sb.ToString().Contains(expected))
|
||||
{
|
||||
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 async Task<Socket> ConnectClientAsync(bool headers = true)
|
||||
{
|
||||
var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
await sock.ConnectAsync(IPAddress.Loopback, _port);
|
||||
await ReadUntilAsync(sock, "\r\n"); // consume INFO
|
||||
await SocketTestHelper.ReadUntilAsync(sock, "\r\n"); // consume INFO
|
||||
var connectJson = headers
|
||||
? "{\"verbose\":false,\"headers\":true}"
|
||||
: "{\"verbose\":false}";
|
||||
@@ -260,7 +241,7 @@ public class MsgTraceGoParityTests : IAsyncLifetime
|
||||
using var pub = await ConnectClientAsync();
|
||||
|
||||
await sub.SendAsync("SUB trace.test 1\r\nPING\r\n"u8.ToArray());
|
||||
await ReadUntilAsync(sub, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG");
|
||||
|
||||
const string hdrBlock = "NATS/1.0\r\nNats-Trace-Dest: trace.inbox\r\n\r\n";
|
||||
const string payload = "hello";
|
||||
@@ -270,7 +251,7 @@ public class MsgTraceGoParityTests : IAsyncLifetime
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes(
|
||||
$"HPUB trace.test {hdrLen} {totalLen}\r\n{hdrBlock}{payload}\r\n"));
|
||||
|
||||
var received = await ReadUntilAsync(sub, "Nats-Trace-Dest");
|
||||
var received = await SocketTestHelper.ReadUntilAsync(sub, "Nats-Trace-Dest");
|
||||
|
||||
received.ShouldContain("HMSG trace.test");
|
||||
received.ShouldContain("Nats-Trace-Dest: trace.inbox");
|
||||
@@ -289,7 +270,7 @@ public class MsgTraceGoParityTests : IAsyncLifetime
|
||||
using var pub = await ConnectClientAsync();
|
||||
|
||||
await sub.SendAsync("SUB trace.* 1\r\nPING\r\n"u8.ToArray());
|
||||
await ReadUntilAsync(sub, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG");
|
||||
|
||||
const string hdrBlock = "NATS/1.0\r\nNats-Trace-Dest: t.inbox.1\r\n\r\n";
|
||||
const string payload = "wildcard-msg";
|
||||
@@ -299,7 +280,7 @@ public class MsgTraceGoParityTests : IAsyncLifetime
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes(
|
||||
$"HPUB trace.subject {hdrLen} {totalLen}\r\n{hdrBlock}{payload}\r\n"));
|
||||
|
||||
var received = await ReadUntilAsync(sub, "Nats-Trace-Dest");
|
||||
var received = await SocketTestHelper.ReadUntilAsync(sub, "Nats-Trace-Dest");
|
||||
|
||||
received.ShouldContain("HMSG trace.subject");
|
||||
received.ShouldContain("Nats-Trace-Dest: t.inbox.1");
|
||||
@@ -319,7 +300,7 @@ public class MsgTraceGoParityTests : IAsyncLifetime
|
||||
|
||||
// Subscribe via a queue group
|
||||
await qsub.SendAsync("SUB trace.q workers 1\r\nPING\r\n"u8.ToArray());
|
||||
await ReadUntilAsync(qsub, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(qsub, "PONG");
|
||||
|
||||
const string hdrBlock = "NATS/1.0\r\nNats-Trace-Dest: qg.trace\r\n\r\n";
|
||||
const string payload = "queued";
|
||||
@@ -330,7 +311,7 @@ public class MsgTraceGoParityTests : IAsyncLifetime
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes(
|
||||
$"HPUB trace.q {hdrLen} {totalLen}\r\n{hdrBlock}{payload}\r\n"));
|
||||
|
||||
var received = await ReadUntilAsync(qsub, "Nats-Trace-Dest", 3000);
|
||||
var received = await SocketTestHelper.ReadUntilAsync(qsub, "Nats-Trace-Dest", 3000);
|
||||
|
||||
received.ShouldContain("Nats-Trace-Dest: qg.trace");
|
||||
received.ShouldContain("queued");
|
||||
@@ -348,7 +329,7 @@ public class MsgTraceGoParityTests : IAsyncLifetime
|
||||
using var pub = await ConnectClientAsync();
|
||||
|
||||
await sub.SendAsync("SUB multi.hdr 1\r\nPING\r\n"u8.ToArray());
|
||||
await ReadUntilAsync(sub, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG");
|
||||
|
||||
const string hdrBlock =
|
||||
"NATS/1.0\r\n" +
|
||||
@@ -363,7 +344,7 @@ public class MsgTraceGoParityTests : IAsyncLifetime
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes(
|
||||
$"HPUB multi.hdr {hdrLen} {totalLen}\r\n{hdrBlock}{payload}\r\n"));
|
||||
|
||||
var received = await ReadUntilAsync(sub, "X-Priority");
|
||||
var received = await SocketTestHelper.ReadUntilAsync(sub, "X-Priority");
|
||||
|
||||
received.ShouldContain("X-Request-Id: req-99");
|
||||
received.ShouldContain("Nats-Trace-Dest: t.multi");
|
||||
@@ -414,7 +395,7 @@ public class MsgTraceGoParityTests : IAsyncLifetime
|
||||
public async Task MsgTrace_server_with_trace_enabled_starts_and_accepts_connections()
|
||||
{
|
||||
// Go: TestMsgTraceBasic (msgtrace_test.go:172)
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
using var cts = new CancellationTokenSource();
|
||||
using var server = new NatsServer(
|
||||
new NatsOptions { Port = port, Trace = true }, NullLoggerFactory.Instance);
|
||||
@@ -423,7 +404,7 @@ public class MsgTraceGoParityTests : IAsyncLifetime
|
||||
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
await sock.ConnectAsync(IPAddress.Loopback, port);
|
||||
var info = await ReadUntilAsync(sock, "\r\n");
|
||||
var info = await SocketTestHelper.ReadUntilAsync(sock, "\r\n");
|
||||
info.ShouldStartWith("INFO ");
|
||||
|
||||
await cts.CancelAsync();
|
||||
@@ -494,7 +475,7 @@ public class MsgTraceGoParityTests : IAsyncLifetime
|
||||
|
||||
// Do a full handshake so the client is accepted
|
||||
await sock.SendAsync("PING\r\n"u8.ToArray());
|
||||
await ReadUntilAsync(sock, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sock, "PONG");
|
||||
|
||||
// Close the connection
|
||||
sock.Close();
|
||||
@@ -520,7 +501,7 @@ public class MsgTraceGoParityTests : IAsyncLifetime
|
||||
{
|
||||
// Go: TestClosedConnsAccounting (closed_conns_test.go:46)
|
||||
// Build a server with a tiny ring buffer
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
using var cts = new CancellationTokenSource();
|
||||
using var server = new NatsServer(
|
||||
new NatsOptions { Port = port, MaxClosedClients = 5 },
|
||||
@@ -533,9 +514,9 @@ public class MsgTraceGoParityTests : IAsyncLifetime
|
||||
{
|
||||
using var s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
await s.ConnectAsync(IPAddress.Loopback, port);
|
||||
await ReadUntilAsync(s, "\r\n"); // INFO
|
||||
await SocketTestHelper.ReadUntilAsync(s, "\r\n"); // INFO
|
||||
await s.SendAsync(Encoding.ASCII.GetBytes("CONNECT {\"verbose\":false}\r\nPING\r\n"));
|
||||
await ReadUntilAsync(s, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(s, "PONG");
|
||||
s.Close();
|
||||
// brief pause to let server process
|
||||
await Task.Delay(5);
|
||||
@@ -587,7 +568,7 @@ public class MsgTraceGoParityTests : IAsyncLifetime
|
||||
public async Task ClosedConns_max_payload_close_reason_tracked()
|
||||
{
|
||||
// Go: TestClosedMaxPayload (closed_conns_test.go:219)
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
using var cts = new CancellationTokenSource();
|
||||
using var server = new NatsServer(
|
||||
new NatsOptions { Port = port, MaxPayload = 100 },
|
||||
@@ -597,11 +578,11 @@ public class MsgTraceGoParityTests : IAsyncLifetime
|
||||
|
||||
var conn = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
await conn.ConnectAsync(IPAddress.Loopback, port);
|
||||
await ReadUntilAsync(conn, "\r\n"); // INFO
|
||||
await SocketTestHelper.ReadUntilAsync(conn, "\r\n"); // INFO
|
||||
|
||||
// Establish connection first
|
||||
await conn.SendAsync("CONNECT {\"verbose\":false}\r\nPING\r\n"u8.ToArray());
|
||||
await ReadUntilAsync(conn, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(conn, "PONG");
|
||||
|
||||
// Send a PUB with payload > MaxPayload (200 bytes > 100 byte limit)
|
||||
// Must include the full payload so the parser yields the command to NatsClient
|
||||
@@ -638,7 +619,7 @@ public class MsgTraceGoParityTests : IAsyncLifetime
|
||||
public async Task ClosedConns_auth_timeout_close_reason_tracked()
|
||||
{
|
||||
// Go: TestClosedAuthorizationTimeout (closed_conns_test.go:143)
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
using var cts = new CancellationTokenSource();
|
||||
using var server = new NatsServer(
|
||||
new NatsOptions
|
||||
@@ -654,7 +635,7 @@ public class MsgTraceGoParityTests : IAsyncLifetime
|
||||
// Just connect without sending CONNECT — auth timeout fires
|
||||
var conn = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
await conn.ConnectAsync(IPAddress.Loopback, port);
|
||||
await ReadUntilAsync(conn, "\r\n"); // INFO
|
||||
await SocketTestHelper.ReadUntilAsync(conn, "\r\n"); // INFO
|
||||
|
||||
// Don't send CONNECT — wait for auth timeout
|
||||
var deadline = DateTime.UtcNow + TimeSpan.FromSeconds(5);
|
||||
@@ -682,7 +663,7 @@ public class MsgTraceGoParityTests : IAsyncLifetime
|
||||
public async Task ClosedConns_auth_violation_close_reason_tracked()
|
||||
{
|
||||
// Go: TestClosedAuthorizationViolation (closed_conns_test.go:164)
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
using var cts = new CancellationTokenSource();
|
||||
using var server = new NatsServer(
|
||||
new NatsOptions { Port = port, Authorization = "correct_token" },
|
||||
@@ -693,13 +674,13 @@ public class MsgTraceGoParityTests : IAsyncLifetime
|
||||
// Connect with wrong token
|
||||
var conn = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
await conn.ConnectAsync(IPAddress.Loopback, port);
|
||||
await ReadUntilAsync(conn, "\r\n"); // INFO
|
||||
await SocketTestHelper.ReadUntilAsync(conn, "\r\n"); // INFO
|
||||
|
||||
await conn.SendAsync(
|
||||
"CONNECT {\"verbose\":false,\"auth_token\":\"wrong_token\"}\r\nPING\r\n"u8.ToArray());
|
||||
|
||||
// Wait for close and error response
|
||||
await ReadUntilAsync(conn, "-ERR", 2000);
|
||||
await SocketTestHelper.ReadUntilAsync(conn, "-ERR", 2000);
|
||||
conn.Dispose();
|
||||
|
||||
var deadline = DateTime.UtcNow + TimeSpan.FromSeconds(5);
|
||||
@@ -727,7 +708,7 @@ public class MsgTraceGoParityTests : IAsyncLifetime
|
||||
public async Task ClosedConns_up_auth_violation_close_reason_tracked()
|
||||
{
|
||||
// Go: TestClosedUPAuthorizationViolation (closed_conns_test.go:187)
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
using var cts = new CancellationTokenSource();
|
||||
using var server = new NatsServer(
|
||||
new NatsOptions
|
||||
@@ -746,19 +727,19 @@ public class MsgTraceGoParityTests : IAsyncLifetime
|
||||
using (var conn1 = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
|
||||
{
|
||||
await conn1.ConnectAsync(IPAddress.Loopback, port);
|
||||
await ReadUntilAsync(conn1, "\r\n"); // INFO
|
||||
await SocketTestHelper.ReadUntilAsync(conn1, "\r\n"); // INFO
|
||||
await conn1.SendAsync("CONNECT {\"verbose\":false}\r\nPING\r\n"u8.ToArray());
|
||||
await ReadUntilAsync(conn1, "-ERR", 2000);
|
||||
await SocketTestHelper.ReadUntilAsync(conn1, "-ERR", 2000);
|
||||
}
|
||||
|
||||
// Wrong password
|
||||
using (var conn2 = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
|
||||
{
|
||||
await conn2.ConnectAsync(IPAddress.Loopback, port);
|
||||
await ReadUntilAsync(conn2, "\r\n"); // INFO
|
||||
await SocketTestHelper.ReadUntilAsync(conn2, "\r\n"); // INFO
|
||||
await conn2.SendAsync(
|
||||
"CONNECT {\"verbose\":false,\"user\":\"my_user\",\"pass\":\"wrong_pass\"}\r\nPING\r\n"u8.ToArray());
|
||||
await ReadUntilAsync(conn2, "-ERR", 2000);
|
||||
await SocketTestHelper.ReadUntilAsync(conn2, "-ERR", 2000);
|
||||
}
|
||||
|
||||
var deadline = DateTime.UtcNow + TimeSpan.FromSeconds(5);
|
||||
@@ -788,7 +769,7 @@ public class MsgTraceGoParityTests : IAsyncLifetime
|
||||
var (certPath, keyPath) = TestCertHelper.GenerateTestCertFiles();
|
||||
try
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
using var cts = new CancellationTokenSource();
|
||||
using var server = new NatsServer(
|
||||
new NatsOptions
|
||||
@@ -807,9 +788,9 @@ public class MsgTraceGoParityTests : IAsyncLifetime
|
||||
using (var conn = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
|
||||
{
|
||||
await conn.ConnectAsync(IPAddress.Loopback, port);
|
||||
await ReadUntilAsync(conn, "\r\n"); // INFO
|
||||
await SocketTestHelper.ReadUntilAsync(conn, "\r\n"); // INFO
|
||||
await conn.SendAsync("CONNECT {\"verbose\":false}\r\nPING\r\n"u8.ToArray());
|
||||
_ = await ReadUntilAsync(conn, "-ERR", 1000);
|
||||
_ = await SocketTestHelper.ReadUntilAsync(conn, "-ERR", 1000);
|
||||
}
|
||||
|
||||
var deadline = DateTime.UtcNow + TimeSpan.FromSeconds(5);
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<IsPackable>false</IsPackable>
|
||||
<DefineConstants>$(DefineConstants);JETSTREAM_INTEGRATION_MATRIX</DefineConstants>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -13,7 +12,6 @@
|
||||
<PackageReference Include="Shouldly" />
|
||||
<PackageReference Include="xunit" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" />
|
||||
<PackageReference Include="NATS.NKeys" />
|
||||
<PackageReference Include="Serilog.Sinks.File" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -22,7 +20,7 @@
|
||||
<Using Include="Shouldly" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\NATS.Server\NATS.Server.csproj" />
|
||||
<ProjectReference Include="..\NATS.Server.TestUtilities\NATS.Server.TestUtilities.csproj" />
|
||||
</ItemGroup>
|
||||
@@ -1,6 +1,6 @@
|
||||
using NATS.Server.Configuration;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
public class NatsConfLexerTests
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using NATS.Server.Configuration;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
public class NatsConfParserTests
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using NATS.Server.Protocol;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
public class NatsHeaderParserTests
|
||||
{
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
public class NatsOptionsTests
|
||||
{
|
||||
@@ -3,8 +3,9 @@ using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using NATS.Server;
|
||||
using NATS.Server.TestUtilities;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
public class NoRespondersTests : IAsyncLifetime
|
||||
{
|
||||
@@ -14,7 +15,7 @@ public class NoRespondersTests : IAsyncLifetime
|
||||
|
||||
public NoRespondersTests()
|
||||
{
|
||||
_port = GetFreePort();
|
||||
_port = TestPortAllocator.GetFreePort();
|
||||
_server = new NatsServer(new NatsOptions { Port = _port }, NullLoggerFactory.Instance);
|
||||
}
|
||||
|
||||
@@ -30,12 +31,6 @@ public class NoRespondersTests : IAsyncLifetime
|
||||
_server.Dispose();
|
||||
}
|
||||
|
||||
private static int GetFreePort()
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
sock.Bind(new IPEndPoint(IPAddress.Loopback, 0));
|
||||
return ((IPEndPoint)sock.LocalEndPoint!).Port;
|
||||
}
|
||||
|
||||
private async Task<Socket> ConnectClientAsync()
|
||||
{
|
||||
@@ -44,19 +39,6 @@ public class NoRespondersTests : IAsyncLifetime
|
||||
return sock;
|
||||
}
|
||||
|
||||
private static async Task<string> ReadUntilAsync(Socket sock, string expected, int timeoutMs = 5000)
|
||||
{
|
||||
using var cts = new CancellationTokenSource(timeoutMs);
|
||||
var sb = new StringBuilder();
|
||||
var buf = new byte[4096];
|
||||
while (!sb.ToString().Contains(expected))
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task NoResponders_without_headers_closes_connection()
|
||||
@@ -64,14 +46,14 @@ public class NoRespondersTests : IAsyncLifetime
|
||||
using var client = await ConnectClientAsync();
|
||||
|
||||
// Read INFO
|
||||
await ReadUntilAsync(client, "\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(client, "\r\n");
|
||||
|
||||
// Send CONNECT with no_responders:true but headers:false
|
||||
await client.SendAsync(Encoding.ASCII.GetBytes(
|
||||
"CONNECT {\"no_responders\":true,\"headers\":false}\r\n"));
|
||||
|
||||
// Should receive -ERR and connection should close
|
||||
var response = await ReadUntilAsync(client, "-ERR");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(client, "-ERR");
|
||||
response.ShouldContain("-ERR");
|
||||
response.ShouldContain("No Responders Requires Headers Support");
|
||||
}
|
||||
@@ -82,14 +64,14 @@ public class NoRespondersTests : IAsyncLifetime
|
||||
using var client = await ConnectClientAsync();
|
||||
|
||||
// Read INFO
|
||||
await ReadUntilAsync(client, "\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(client, "\r\n");
|
||||
|
||||
// Send CONNECT with both no_responders and headers true, then PING
|
||||
await client.SendAsync(Encoding.ASCII.GetBytes(
|
||||
"CONNECT {\"no_responders\":true,\"headers\":true}\r\nPING\r\n"));
|
||||
|
||||
// Should receive PONG (connection stays alive)
|
||||
var response = await ReadUntilAsync(client, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(client, "PONG\r\n");
|
||||
response.ShouldContain("PONG\r\n");
|
||||
}
|
||||
|
||||
@@ -99,7 +81,7 @@ public class NoRespondersTests : IAsyncLifetime
|
||||
using var client = await ConnectClientAsync();
|
||||
|
||||
// Read INFO
|
||||
await ReadUntilAsync(client, "\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(client, "\r\n");
|
||||
|
||||
// CONNECT with no_responders and headers enabled
|
||||
// SUB to the reply inbox so we can receive the 503
|
||||
@@ -110,7 +92,7 @@ public class NoRespondersTests : IAsyncLifetime
|
||||
"PUB no.subscribers _INBOX.reply 5\r\nHello\r\n"));
|
||||
|
||||
// Should receive HMSG with 503 status on the reply subject
|
||||
var response = await ReadUntilAsync(client, "HMSG");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(client, "HMSG");
|
||||
response.ShouldContain("HMSG _INBOX.reply 1");
|
||||
response.ShouldContain("503");
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using NATS.Server.TestUtilities.Parity;
|
||||
|
||||
namespace NATS.Server.Tests.Parity;
|
||||
namespace NATS.Server.Core.Tests.Parity;
|
||||
|
||||
public class JetStreamParityTruthMatrixTests
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using NATS.Server.TestUtilities.Parity;
|
||||
|
||||
namespace NATS.Server.Tests.Parity;
|
||||
namespace NATS.Server.Core.Tests.Parity;
|
||||
|
||||
public class NatsStrictCapabilityInventoryTests
|
||||
{
|
||||
@@ -3,7 +3,7 @@ using System.IO.Pipelines;
|
||||
using System.Text;
|
||||
using NATS.Server.Protocol;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
public class ParserTests
|
||||
{
|
||||
@@ -12,8 +12,9 @@ using Microsoft.Extensions.Logging.Abstractions;
|
||||
using NATS.Server;
|
||||
using NATS.Server.Protocol;
|
||||
using NATS.Server.Subscriptions;
|
||||
using NATS.Server.TestUtilities;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Go parity tests ported from client_test.go for protocol-level behaviors
|
||||
@@ -26,28 +27,6 @@ public class ClientProtocolGoParityTests
|
||||
// Helpers (self-contained per project conventions)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
private static int GetFreePort()
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
sock.Bind(new IPEndPoint(IPAddress.Loopback, 0));
|
||||
return ((IPEndPoint)sock.LocalEndPoint!).Port;
|
||||
}
|
||||
|
||||
private static async Task<string> ReadUntilAsync(Socket sock, string expected, int timeoutMs = 5000)
|
||||
{
|
||||
using var cts = new CancellationTokenSource(timeoutMs);
|
||||
var sb = new StringBuilder();
|
||||
var buf = new byte[8192];
|
||||
while (!sb.ToString().Contains(expected))
|
||||
{
|
||||
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 async Task<string> ReadAllAvailableAsync(Socket sock, int timeoutMs = 1000)
|
||||
{
|
||||
using var cts = new CancellationTokenSource(timeoutMs);
|
||||
@@ -73,7 +52,7 @@ public class ClientProtocolGoParityTests
|
||||
private static async Task<(NatsServer Server, int Port, CancellationTokenSource Cts)>
|
||||
StartServerAsync(NatsOptions? options = null)
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
options ??= new NatsOptions();
|
||||
options.Port = port;
|
||||
var cts = new CancellationTokenSource();
|
||||
@@ -87,7 +66,7 @@ public class ClientProtocolGoParityTests
|
||||
{
|
||||
var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
await sock.ConnectAsync(IPAddress.Loopback, port);
|
||||
await ReadUntilAsync(sock, "\r\n"); // drain INFO
|
||||
await SocketTestHelper.ReadUntilAsync(sock, "\r\n"); // drain INFO
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes($"CONNECT {connectJson}\r\n"));
|
||||
return sock;
|
||||
}
|
||||
@@ -96,7 +75,7 @@ public class ClientProtocolGoParityTests
|
||||
{
|
||||
var sock = await ConnectAndHandshakeAsync(port, connectJson);
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
|
||||
await ReadUntilAsync(sock, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
return sock;
|
||||
}
|
||||
|
||||
@@ -119,17 +98,17 @@ public class ClientProtocolGoParityTests
|
||||
using var pub = await ConnectAndPingAsync(port, "{\"headers\":true}");
|
||||
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("SUB foo 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG\r\n");
|
||||
|
||||
// HPUB foo 12 14\r\nName:Derek\r\nOK\r\n
|
||||
// Header block: "Name:Derek\r\n" = 12 bytes
|
||||
// Payload: "OK" = 2 bytes -> total = 14
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("HPUB foo 12 14\r\nName:Derek\r\nOK\r\n"));
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
|
||||
await ReadUntilAsync(pub, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(pub, "PONG\r\n");
|
||||
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
|
||||
var response = await ReadUntilAsync(sub, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sub, "PONG\r\n");
|
||||
|
||||
// Non-header subscriber should get a plain MSG with only the payload (2 bytes: "OK")
|
||||
response.ShouldContain("MSG foo 1 2\r\n");
|
||||
@@ -162,14 +141,14 @@ public class ClientProtocolGoParityTests
|
||||
|
||||
// Queue subscription: SUB foo bar 1
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("SUB foo bar 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG\r\n");
|
||||
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("HPUB foo 12 14\r\nName:Derek\r\nOK\r\n"));
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
|
||||
await ReadUntilAsync(pub, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(pub, "PONG\r\n");
|
||||
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
|
||||
var response = await ReadUntilAsync(sub, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sub, "PONG\r\n");
|
||||
|
||||
// Queue subscriber without headers should get MSG with only payload
|
||||
response.ShouldContain("MSG foo 1 2\r\n");
|
||||
@@ -241,7 +220,7 @@ public class ClientProtocolGoParityTests
|
||||
// (they're embedded in a comma-delimited token), so they are literal
|
||||
var subj = "foo.bar,*,>,baz";
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes($"SUB {subj} 1\r\nPUB {subj} 3\r\nmsg\r\nPING\r\n"));
|
||||
var response = await ReadUntilAsync(sock, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
|
||||
response.ShouldContain($"MSG {subj} 1 3\r\n");
|
||||
response.ShouldContain("msg\r\n");
|
||||
@@ -343,7 +322,7 @@ public class ClientProtocolGoParityTests
|
||||
using var pub = await ConnectAndPingAsync(port, "{\"headers\":true}");
|
||||
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("SUB foo 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG\r\n");
|
||||
|
||||
// Publish a message with headers
|
||||
var hdr = "NATS/1.0\r\nA: 1\r\nB: 2\r\n\r\n";
|
||||
@@ -352,10 +331,10 @@ public class ClientProtocolGoParityTests
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes(
|
||||
$"HPUB foo {hdr.Length} {totalLen}\r\n{hdr}{payload}\r\n"));
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
|
||||
await ReadUntilAsync(pub, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(pub, "PONG\r\n");
|
||||
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
|
||||
var response = await ReadUntilAsync(sub, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sub, "PONG\r\n");
|
||||
|
||||
response.ShouldContain("HMSG foo 1");
|
||||
response.ShouldContain("Hello Traced");
|
||||
@@ -383,7 +362,7 @@ public class ClientProtocolGoParityTests
|
||||
using var pub = await ConnectAndPingAsync(port, "{\"headers\":true}");
|
||||
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("SUB foo 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG\r\n");
|
||||
|
||||
var hdr = "NATS/1.0\r\nFoo: bar\r\nBaz: qux\r\n\r\n";
|
||||
var payload = "data";
|
||||
@@ -391,10 +370,10 @@ public class ClientProtocolGoParityTests
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes(
|
||||
$"HPUB foo {hdr.Length} {totalLen}\r\n{hdr}{payload}\r\n"));
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
|
||||
await ReadUntilAsync(pub, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(pub, "PONG\r\n");
|
||||
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
|
||||
var response = await ReadUntilAsync(sub, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sub, "PONG\r\n");
|
||||
|
||||
response.ShouldContain("HMSG foo 1");
|
||||
response.ShouldContain("NATS/1.0");
|
||||
@@ -452,7 +431,7 @@ public class ClientProtocolGoParityTests
|
||||
|
||||
// First sub should succeed
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("SUB foo 1\r\nPING\r\n"));
|
||||
var r1 = await ReadUntilAsync(sock, "PONG\r\n");
|
||||
var r1 = await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
r1.ShouldNotContain("-ERR");
|
||||
|
||||
// Second sub should exceed the limit
|
||||
@@ -771,7 +750,7 @@ public class ClientProtocolGoParityTests
|
||||
|
||||
// Attempt to publish to an NRG subject
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes("PUB $NRG.foo 0\r\n\r\nPING\r\n"));
|
||||
var response = await ReadUntilAsync(sock, "PONG\r\n", timeoutMs: 5000);
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n", timeoutMs: 5000);
|
||||
|
||||
// The server should reject this with a permissions violation
|
||||
// (In Go, non-system clients get a publish permission error for $NRG.*)
|
||||
@@ -800,14 +779,14 @@ public class ClientProtocolGoParityTests
|
||||
using var pub = await ConnectAndPingAsync(port, "{\"headers\":true}");
|
||||
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("SUB foo 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG\r\n");
|
||||
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("HPUB foo 12 14\r\nName:Derek\r\nOK\r\n"));
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
|
||||
await ReadUntilAsync(pub, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(pub, "PONG\r\n");
|
||||
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
|
||||
var response = await ReadUntilAsync(sub, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sub, "PONG\r\n");
|
||||
|
||||
// Header-aware subscriber should get HMSG with full headers
|
||||
response.ShouldContain("HMSG foo 1 12 14\r\n");
|
||||
@@ -841,14 +820,14 @@ public class ClientProtocolGoParityTests
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes($"SUB {subj} {i + 1}\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sock, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes($"PUB {subj} 3\r\nmsg\r\nPING\r\n"));
|
||||
var response = await ReadUntilAsync(sock, "PONG\r\n");
|
||||
var response = await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
response.ShouldContain($"MSG {subj} {i + 1} 3\r\n");
|
||||
|
||||
await sock.SendAsync(Encoding.ASCII.GetBytes($"UNSUB {i + 1}\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sock, "PONG\r\n");
|
||||
await SocketTestHelper.ReadUntilAsync(sock, "PONG\r\n");
|
||||
}
|
||||
}
|
||||
finally
|
||||
@@ -1,6 +1,6 @@
|
||||
using NATS.Server.Protocol;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
public class InterServerOpcodeRoutingTests
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using NATS.Server.Protocol;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
public class MessageTraceInitializationTests
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using NATS.Server.Protocol;
|
||||
|
||||
namespace NATS.Server.Tests.ProtocolParity;
|
||||
namespace NATS.Server.Core.Tests.ProtocolParity;
|
||||
|
||||
public class ProtoWireParityTests
|
||||
{
|
||||
@@ -1,7 +1,7 @@
|
||||
using NATS.Server;
|
||||
using NATS.Server.Protocol;
|
||||
|
||||
namespace NATS.Server.Tests.ProtocolParity;
|
||||
namespace NATS.Server.Core.Tests.ProtocolParity;
|
||||
|
||||
public class ProtocolDefaultConstantsGapParityTests
|
||||
{
|
||||
@@ -2,7 +2,7 @@ using System.Buffers;
|
||||
using System.Text;
|
||||
using NATS.Server.Protocol;
|
||||
|
||||
namespace NATS.Server.Tests.ProtocolParity;
|
||||
namespace NATS.Server.Core.Tests.ProtocolParity;
|
||||
|
||||
public class ProtocolParserSnippetGapParityTests
|
||||
{
|
||||
@@ -8,7 +8,7 @@ using System.Net;
|
||||
using System.Text;
|
||||
using NATS.Server.Protocol;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// PROXY protocol v1/v2 parser tests.
|
||||
@@ -1,7 +1,7 @@
|
||||
using NATS.Server.Auth;
|
||||
using NATS.Server.Imports;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
public class ResponseRoutingTests
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using NATS.Server.Auth;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
public class ResponseTrackerTests
|
||||
{
|
||||
@@ -3,8 +3,9 @@ using System.Net.Http.Json;
|
||||
using System.Net.Sockets;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using NATS.Server.Monitoring;
|
||||
using NATS.Server.TestUtilities;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
public class RttTests : IAsyncLifetime
|
||||
{
|
||||
@@ -16,8 +17,8 @@ public class RttTests : IAsyncLifetime
|
||||
|
||||
public RttTests()
|
||||
{
|
||||
_natsPort = GetFreePort();
|
||||
_monitorPort = GetFreePort();
|
||||
_natsPort = TestPortAllocator.GetFreePort();
|
||||
_monitorPort = TestPortAllocator.GetFreePort();
|
||||
_server = new NatsServer(
|
||||
new NatsOptions
|
||||
{
|
||||
@@ -114,10 +115,4 @@ public class RttTests : IAsyncLifetime
|
||||
response.StatusCode.ShouldBe(System.Net.HttpStatusCode.OK);
|
||||
}
|
||||
|
||||
private static int GetFreePort()
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
sock.Bind(new IPEndPoint(IPAddress.Loopback, 0));
|
||||
return ((IPEndPoint)sock.LocalEndPoint!).Port;
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ using System.Net.Sockets;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using NATS.Server.Server;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
public class AcceptLoopErrorCallbackTests
|
||||
{
|
||||
@@ -2,7 +2,7 @@ using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
public class AcceptLoopReloadLockTests
|
||||
{
|
||||
@@ -3,7 +3,7 @@ using Microsoft.Extensions.Logging.Abstractions;
|
||||
using NATS.Server.Auth;
|
||||
using NATS.Server.Protocol;
|
||||
|
||||
namespace NATS.Server.Tests.Server;
|
||||
namespace NATS.Server.Core.Tests.Server;
|
||||
|
||||
public class CoreServerClientAccessorsParityBatch2Tests
|
||||
{
|
||||
@@ -4,8 +4,9 @@ using System.Text;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using NATS.Server;
|
||||
using NATS.Server.Configuration;
|
||||
using NATS.Server.TestUtilities;
|
||||
|
||||
namespace NATS.Server.Tests.Server;
|
||||
namespace NATS.Server.Core.Tests.Server;
|
||||
|
||||
public class CoreServerGapParityTests
|
||||
{
|
||||
@@ -166,7 +167,7 @@ public class CoreServerGapParityTests
|
||||
[Fact]
|
||||
public async Task DisconnectClientByID_closes_connected_client()
|
||||
{
|
||||
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);
|
||||
@@ -185,7 +186,7 @@ public class CoreServerGapParityTests
|
||||
[Fact]
|
||||
public async Task LDMClientByID_closes_connected_client()
|
||||
{
|
||||
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);
|
||||
@@ -252,20 +253,6 @@ public class CoreServerGapParityTests
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
private static int GetFreePort()
|
||||
{
|
||||
var listener = new TcpListener(IPAddress.Loopback, 0);
|
||||
listener.Start();
|
||||
try
|
||||
{
|
||||
return ((IPEndPoint)listener.LocalEndpoint).Port;
|
||||
}
|
||||
finally
|
||||
{
|
||||
listener.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task WaitUntilAsync(Func<bool> predicate)
|
||||
{
|
||||
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));
|
||||
@@ -1,6 +1,6 @@
|
||||
using NATS.Server.Server;
|
||||
|
||||
namespace NATS.Server.Tests.Server;
|
||||
namespace NATS.Server.Core.Tests.Server;
|
||||
|
||||
public class CoreServerOptionsParityBatch3Tests
|
||||
{
|
||||
@@ -2,7 +2,7 @@ using System.Net.Sockets;
|
||||
using NATS.Server.Routes;
|
||||
using NATS.Server.Server;
|
||||
|
||||
namespace NATS.Server.Tests.Server;
|
||||
namespace NATS.Server.Core.Tests.Server;
|
||||
|
||||
public class UtilitiesAndRateCounterParityBatch1Tests
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using NATS.Server.Server;
|
||||
|
||||
namespace NATS.Server.Tests.Server;
|
||||
namespace NATS.Server.Core.Tests.Server;
|
||||
|
||||
public class UtilitiesErrorConstantsParityBatch2Tests
|
||||
{
|
||||
@@ -3,34 +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;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
// Tests ported from Go server_test.go:
|
||||
// TestRandomPorts, TestInfoServerNameDefaultsToPK, TestInfoServerNameIsSettable,
|
||||
// TestLameDuckModeInfo (simplified — no cluster, just ldm property/state)
|
||||
public class ServerConfigTests
|
||||
{
|
||||
private static int GetFreePort()
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
sock.Bind(new IPEndPoint(IPAddress.Loopback, 0));
|
||||
return ((IPEndPoint)sock.LocalEndPoint!).Port;
|
||||
}
|
||||
|
||||
private static async Task<string> ReadUntilAsync(Socket sock, string expected, int timeoutMs = 5000)
|
||||
{
|
||||
using var cts = new CancellationTokenSource(timeoutMs);
|
||||
var sb = new StringBuilder();
|
||||
var buf = new byte[4096];
|
||||
while (!sb.ToString().Contains(expected))
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
// Ref: golang/nats-server/server/server_test.go TestRandomPorts
|
||||
// The Go test uses Port=-1 (their sentinel for "random"), we use Port=0 (.NET/BSD standard).
|
||||
@@ -63,7 +45,7 @@ public class ServerConfigTests
|
||||
public async Task Server_info_contains_server_name()
|
||||
{
|
||||
const string name = "my-test-server";
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var opts = new NatsOptions { Port = port, ServerName = name };
|
||||
using var server = new NatsServer(opts, NullLoggerFactory.Instance);
|
||||
using var cts = new CancellationTokenSource();
|
||||
@@ -79,7 +61,7 @@ public class ServerConfigTests
|
||||
// Wire check — INFO line sent on connect
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
await sock.ConnectAsync(IPAddress.Loopback, port);
|
||||
var infoLine = await ReadUntilAsync(sock, "INFO");
|
||||
var infoLine = await SocketTestHelper.ReadUntilAsync(sock, "INFO");
|
||||
infoLine.ShouldContain("\"server_name\":\"my-test-server\"");
|
||||
}
|
||||
finally
|
||||
@@ -95,7 +77,7 @@ public class ServerConfigTests
|
||||
[Fact]
|
||||
public async Task Server_info_defaults_name_when_not_configured()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var opts = new NatsOptions { Port = port }; // no ServerName set
|
||||
using var server = new NatsServer(opts, NullLoggerFactory.Instance);
|
||||
using var cts = new CancellationTokenSource();
|
||||
@@ -112,7 +94,7 @@ public class ServerConfigTests
|
||||
// Wire check — INFO line includes both fields
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
await sock.ConnectAsync(IPAddress.Loopback, port);
|
||||
var infoLine = await ReadUntilAsync(sock, "INFO");
|
||||
var infoLine = await SocketTestHelper.ReadUntilAsync(sock, "INFO");
|
||||
infoLine.ShouldContain("\"server_id\":");
|
||||
infoLine.ShouldContain("\"server_name\":");
|
||||
}
|
||||
@@ -131,7 +113,7 @@ public class ServerConfigTests
|
||||
[Fact]
|
||||
public async Task Lame_duck_mode_sets_is_lame_duck_mode_and_shuts_down()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var opts = new NatsOptions
|
||||
{
|
||||
Port = port,
|
||||
@@ -2,8 +2,9 @@ using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using NATS.Server;
|
||||
using NATS.Server.TestUtilities;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
public class ServerStatsTests : IAsyncLifetime
|
||||
{
|
||||
@@ -13,7 +14,7 @@ public class ServerStatsTests : IAsyncLifetime
|
||||
|
||||
public ServerStatsTests()
|
||||
{
|
||||
_port = GetFreePort();
|
||||
_port = TestPortAllocator.GetFreePort();
|
||||
_server = new NatsServer(new NatsOptions { Port = _port }, NullLoggerFactory.Instance);
|
||||
}
|
||||
|
||||
@@ -96,10 +97,4 @@ public class ServerStatsTests : IAsyncLifetime
|
||||
stats.StaleConnectionGateways.ShouldBe(0);
|
||||
}
|
||||
|
||||
private static int GetFreePort()
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
sock.Bind(new IPEndPoint(IPAddress.Loopback, 0));
|
||||
return ((IPEndPoint)sock.LocalEndPoint!).Port;
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,9 @@ using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using NATS.Server;
|
||||
using NATS.Server.TestUtilities;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
public class ServerTests : IAsyncLifetime
|
||||
{
|
||||
@@ -15,7 +16,7 @@ public class ServerTests : IAsyncLifetime
|
||||
public ServerTests()
|
||||
{
|
||||
// Use random port
|
||||
_port = GetFreePort();
|
||||
_port = TestPortAllocator.GetFreePort();
|
||||
_server = new NatsServer(new NatsOptions { Port = _port }, NullLoggerFactory.Instance);
|
||||
}
|
||||
|
||||
@@ -31,12 +32,6 @@ public class ServerTests : IAsyncLifetime
|
||||
_server.Dispose();
|
||||
}
|
||||
|
||||
private static int GetFreePort()
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
sock.Bind(new IPEndPoint(IPAddress.Loopback, 0));
|
||||
return ((IPEndPoint)sock.LocalEndPoint!).Port;
|
||||
}
|
||||
|
||||
private async Task<Socket> ConnectClientAsync()
|
||||
{
|
||||
@@ -55,19 +50,6 @@ public class ServerTests : IAsyncLifetime
|
||||
/// <summary>
|
||||
/// Reads from a socket until the accumulated data contains the expected substring.
|
||||
/// </summary>
|
||||
private static async Task<string> ReadUntilAsync(Socket sock, string expected, int timeoutMs = 5000)
|
||||
{
|
||||
using var cts = new CancellationTokenSource(timeoutMs);
|
||||
var sb = new StringBuilder();
|
||||
var buf = new byte[4096];
|
||||
while (!sb.ToString().Contains(expected))
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Server_accepts_connection_and_sends_INFO()
|
||||
@@ -97,7 +79,7 @@ public class ServerTests : IAsyncLifetime
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\nPUB foo 5\r\nHello\r\n"));
|
||||
|
||||
// Read MSG from subscriber (may arrive across multiple TCP segments)
|
||||
var msg = await ReadUntilAsync(sub, "Hello\r\n");
|
||||
var msg = await SocketTestHelper.ReadUntilAsync(sub, "Hello\r\n");
|
||||
msg.ShouldContain("MSG foo 1 5\r\nHello\r\n");
|
||||
}
|
||||
|
||||
@@ -137,17 +119,17 @@ public class ServerTests : IAsyncLifetime
|
||||
// Connect with pedantic mode ON
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes(
|
||||
"CONNECT {\"pedantic\":true}\r\nPING\r\n"));
|
||||
var pong = await ReadUntilAsync(pub, "PONG");
|
||||
var pong = await SocketTestHelper.ReadUntilAsync(pub, "PONG");
|
||||
|
||||
// Subscribe on sub
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\nSUB foo.* 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG");
|
||||
|
||||
// PUB with wildcard subject (invalid for publish)
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("PUB foo.* 5\r\nHello\r\n"));
|
||||
|
||||
// Publisher should get -ERR
|
||||
var errResponse = await ReadUntilAsync(pub, "-ERR", timeoutMs: 3000);
|
||||
var errResponse = await SocketTestHelper.ReadUntilAsync(pub, "-ERR", timeoutMs: 3000);
|
||||
errResponse.ShouldContain("-ERR 'Invalid Publish Subject'");
|
||||
}
|
||||
|
||||
@@ -162,12 +144,12 @@ public class ServerTests : IAsyncLifetime
|
||||
|
||||
// Connect without pedantic mode (default)
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\nSUB foo.* 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG");
|
||||
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\nPUB foo.* 5\r\nHello\r\n"));
|
||||
|
||||
// Sub should still receive the message (no validation in non-pedantic mode)
|
||||
var msg = await ReadUntilAsync(sub, "Hello\r\n");
|
||||
var msg = await SocketTestHelper.ReadUntilAsync(sub, "Hello\r\n");
|
||||
msg.ShouldContain("MSG foo.* 1 5\r\nHello\r\n");
|
||||
}
|
||||
|
||||
@@ -175,7 +157,7 @@ public class ServerTests : IAsyncLifetime
|
||||
public async Task Server_rejects_max_payload_violation()
|
||||
{
|
||||
// Create server with tiny max payload
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
using var cts = new CancellationTokenSource();
|
||||
var server = new NatsServer(new NatsOptions { Port = port, MaxPayload = 10 }, NullLoggerFactory.Instance);
|
||||
_ = server.StartAsync(cts.Token);
|
||||
@@ -254,7 +236,7 @@ public class MaxConnectionsTests : IAsyncLifetime
|
||||
|
||||
public MaxConnectionsTests()
|
||||
{
|
||||
_port = GetFreePort();
|
||||
_port = TestPortAllocator.GetFreePort();
|
||||
_server = new NatsServer(new NatsOptions { Port = _port, MaxConnections = 2 }, NullLoggerFactory.Instance);
|
||||
}
|
||||
|
||||
@@ -270,12 +252,6 @@ public class MaxConnectionsTests : IAsyncLifetime
|
||||
_server.Dispose();
|
||||
}
|
||||
|
||||
private static int GetFreePort()
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
sock.Bind(new IPEndPoint(IPAddress.Loopback, 0));
|
||||
return ((IPEndPoint)sock.LocalEndPoint!).Port;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Server_rejects_connection_when_max_reached()
|
||||
@@ -320,7 +296,7 @@ public class PingKeepaliveTests : IAsyncLifetime
|
||||
|
||||
public PingKeepaliveTests()
|
||||
{
|
||||
_port = GetFreePort();
|
||||
_port = TestPortAllocator.GetFreePort();
|
||||
// Short intervals for testing: 500ms ping interval, 2 max pings out
|
||||
_server = new NatsServer(
|
||||
new NatsOptions
|
||||
@@ -344,26 +320,7 @@ public class PingKeepaliveTests : IAsyncLifetime
|
||||
_server.Dispose();
|
||||
}
|
||||
|
||||
private static int GetFreePort()
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
sock.Bind(new IPEndPoint(IPAddress.Loopback, 0));
|
||||
return ((IPEndPoint)sock.LocalEndPoint!).Port;
|
||||
}
|
||||
|
||||
private static async Task<string> ReadUntilAsync(Socket sock, string expected, int timeoutMs = 5000)
|
||||
{
|
||||
using var cts = new CancellationTokenSource(timeoutMs);
|
||||
var sb = new StringBuilder();
|
||||
var buf = new byte[4096];
|
||||
while (!sb.ToString().Contains(expected))
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Server_sends_PING_after_inactivity()
|
||||
@@ -379,7 +336,7 @@ public class PingKeepaliveTests : IAsyncLifetime
|
||||
await client.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\n"));
|
||||
|
||||
// Wait for server to send PING (should come within ~500ms)
|
||||
var response = await ReadUntilAsync(client, "PING", timeoutMs: 3000);
|
||||
var response = await SocketTestHelper.ReadUntilAsync(client, "PING", timeoutMs: 3000);
|
||||
response.ShouldContain("PING");
|
||||
|
||||
client.Dispose();
|
||||
@@ -397,14 +354,14 @@ public class PingKeepaliveTests : IAsyncLifetime
|
||||
await client.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\n"));
|
||||
|
||||
// Wait for first PING
|
||||
var response = await ReadUntilAsync(client, "PING", timeoutMs: 3000);
|
||||
var response = await SocketTestHelper.ReadUntilAsync(client, "PING", timeoutMs: 3000);
|
||||
response.ShouldContain("PING");
|
||||
|
||||
// Respond with PONG — this resets the counter
|
||||
await client.SendAsync(Encoding.ASCII.GetBytes("PONG\r\n"));
|
||||
|
||||
// Wait for next PING (counter reset, so we should get another one)
|
||||
response = await ReadUntilAsync(client, "PING", timeoutMs: 3000);
|
||||
response = await SocketTestHelper.ReadUntilAsync(client, "PING", timeoutMs: 3000);
|
||||
response.ShouldContain("PING");
|
||||
|
||||
// Respond again to keep alive
|
||||
@@ -412,7 +369,7 @@ public class PingKeepaliveTests : IAsyncLifetime
|
||||
|
||||
// Client should still be alive — send a PING and expect PONG back
|
||||
await client.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
|
||||
response = await ReadUntilAsync(client, "PONG", timeoutMs: 3000);
|
||||
response = await SocketTestHelper.ReadUntilAsync(client, "PONG", timeoutMs: 3000);
|
||||
response.ShouldContain("PONG");
|
||||
|
||||
client.Dispose();
|
||||
@@ -465,7 +422,7 @@ public class CloseReasonTests : IAsyncLifetime
|
||||
|
||||
public CloseReasonTests()
|
||||
{
|
||||
_port = GetFreePort();
|
||||
_port = TestPortAllocator.GetFreePort();
|
||||
_server = new NatsServer(new NatsOptions { Port = _port }, NullLoggerFactory.Instance);
|
||||
}
|
||||
|
||||
@@ -481,12 +438,6 @@ public class CloseReasonTests : IAsyncLifetime
|
||||
_server.Dispose();
|
||||
}
|
||||
|
||||
private static int GetFreePort()
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
sock.Bind(new IPEndPoint(IPAddress.Loopback, 0));
|
||||
return ((IPEndPoint)sock.LocalEndPoint!).Port;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Client_close_reason_set_on_normal_disconnect()
|
||||
@@ -550,31 +501,12 @@ public class ServerIdentityTests
|
||||
|
||||
public class FlushBeforeCloseTests
|
||||
{
|
||||
private static int GetFreePort()
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
sock.Bind(new IPEndPoint(IPAddress.Loopback, 0));
|
||||
return ((IPEndPoint)sock.LocalEndPoint!).Port;
|
||||
}
|
||||
|
||||
private static async Task<string> ReadUntilAsync(Socket sock, string expected, int timeoutMs = 5000)
|
||||
{
|
||||
using var cts = new CancellationTokenSource(timeoutMs);
|
||||
var sb = new StringBuilder();
|
||||
var buf = new byte[4096];
|
||||
while (!sb.ToString().Contains(expected))
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Shutdown_flushes_pending_data_to_clients()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var server = new NatsServer(new NatsOptions { Port = port }, NullLoggerFactory.Instance);
|
||||
_ = server.StartAsync(CancellationToken.None);
|
||||
await server.WaitForReadyAsync();
|
||||
@@ -591,7 +523,7 @@ public class FlushBeforeCloseTests
|
||||
|
||||
// Subscribe to "foo"
|
||||
await sub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\nSUB foo 1\r\nPING\r\n"));
|
||||
var pong = await ReadUntilAsync(sub, "PONG");
|
||||
var pong = await SocketTestHelper.ReadUntilAsync(sub, "PONG");
|
||||
pong.ShouldContain("PONG");
|
||||
|
||||
// Connect a publisher
|
||||
@@ -606,7 +538,7 @@ public class FlushBeforeCloseTests
|
||||
await Task.Delay(200);
|
||||
|
||||
// Read from subscriber to verify MSG was received
|
||||
var msg = await ReadUntilAsync(sub, "Hello\r\n");
|
||||
var msg = await SocketTestHelper.ReadUntilAsync(sub, "Hello\r\n");
|
||||
msg.ShouldContain("MSG foo 1 5\r\nHello\r\n");
|
||||
}
|
||||
finally
|
||||
@@ -619,17 +551,11 @@ public class FlushBeforeCloseTests
|
||||
|
||||
public class GracefulShutdownTests
|
||||
{
|
||||
private static int GetFreePort()
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
sock.Bind(new IPEndPoint(IPAddress.Loopback, 0));
|
||||
return ((IPEndPoint)sock.LocalEndPoint!).Port;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ShutdownAsync_disconnects_all_clients()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var server = new NatsServer(new NatsOptions { Port = port }, NullLoggerFactory.Instance);
|
||||
_ = server.StartAsync(CancellationToken.None);
|
||||
await server.WaitForReadyAsync();
|
||||
@@ -664,7 +590,7 @@ public class GracefulShutdownTests
|
||||
[Fact]
|
||||
public async Task WaitForShutdown_blocks_until_shutdown()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var server = new NatsServer(new NatsOptions { Port = port }, NullLoggerFactory.Instance);
|
||||
_ = server.StartAsync(CancellationToken.None);
|
||||
await server.WaitForReadyAsync();
|
||||
@@ -689,7 +615,7 @@ public class GracefulShutdownTests
|
||||
[Fact]
|
||||
public async Task ShutdownAsync_is_idempotent()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var server = new NatsServer(new NatsOptions { Port = port }, NullLoggerFactory.Instance);
|
||||
_ = server.StartAsync(CancellationToken.None);
|
||||
await server.WaitForReadyAsync();
|
||||
@@ -706,7 +632,7 @@ public class GracefulShutdownTests
|
||||
[Fact]
|
||||
public async Task Accept_loop_waits_for_active_clients()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var server = new NatsServer(new NatsOptions { Port = port }, NullLoggerFactory.Instance);
|
||||
_ = server.StartAsync(CancellationToken.None);
|
||||
await server.WaitForReadyAsync();
|
||||
@@ -731,17 +657,11 @@ public class GracefulShutdownTests
|
||||
|
||||
public class LameDuckTests
|
||||
{
|
||||
private static int GetFreePort()
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
sock.Bind(new IPEndPoint(IPAddress.Loopback, 0));
|
||||
return ((IPEndPoint)sock.LocalEndPoint!).Port;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task LameDuckShutdown_stops_accepting_new_connections()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var server = new NatsServer(
|
||||
new NatsOptions
|
||||
{
|
||||
@@ -795,7 +715,7 @@ public class LameDuckTests
|
||||
[Fact]
|
||||
public async Task LameDuckShutdown_eventually_closes_all_clients()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var server = new NatsServer(
|
||||
new NatsOptions
|
||||
{
|
||||
@@ -855,18 +775,12 @@ public class PidFileTests : IDisposable
|
||||
Directory.Delete(_tempDir, recursive: true);
|
||||
}
|
||||
|
||||
private static int GetFreePort()
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
sock.Bind(new IPEndPoint(IPAddress.Loopback, 0));
|
||||
return ((IPEndPoint)sock.LocalEndPoint!).Port;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Server_writes_pid_file_on_startup()
|
||||
{
|
||||
var pidFile = Path.Combine(_tempDir, "nats.pid");
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var server = new NatsServer(new NatsOptions { Port = port, PidFile = pidFile }, NullLoggerFactory.Instance);
|
||||
_ = server.StartAsync(CancellationToken.None);
|
||||
await server.WaitForReadyAsync();
|
||||
@@ -884,7 +798,7 @@ public class PidFileTests : IDisposable
|
||||
[Fact]
|
||||
public async Task Server_writes_ports_file_on_startup()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
var server = new NatsServer(new NatsOptions { Port = port, PortsFileDir = _tempDir }, NullLoggerFactory.Instance);
|
||||
_ = server.StartAsync(CancellationToken.None);
|
||||
await server.WaitForReadyAsync();
|
||||
@@ -1,6 +1,6 @@
|
||||
using NATS.Server.Configuration;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
// Go reference: server/signal_unix.go (handleSignals), server/reload.go (Reload)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Apply to a test method to suppress a specific slopwatch rule violation.
|
||||
// The justification must be 20+ characters explaining why the suppression is intentional.
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
|
||||
public sealed class SlopwatchSuppressAttribute(string ruleId, string justification) : Attribute
|
||||
@@ -2,7 +2,7 @@ using NATS.Server;
|
||||
using NATS.Server.Auth;
|
||||
using Shouldly;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Tests for <see cref="SlowConsumerTracker"/> and <see cref="Account"/> slow-consumer counters.
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
// Go reference: server/client.go (stc channel, stall gate backpressure)
|
||||
|
||||
@@ -15,7 +15,7 @@ using NATS.Server.JetStream.Publish;
|
||||
using ClusterFixture = NATS.Server.TestUtilities.JetStreamClusterFixture;
|
||||
using NATS.Server.TestUtilities;
|
||||
|
||||
namespace NATS.Server.Tests.Stress;
|
||||
namespace NATS.Server.Core.Tests.Stress;
|
||||
|
||||
/// <summary>
|
||||
/// Stress tests for clustered JetStream operations under concurrency.
|
||||
@@ -6,7 +6,7 @@
|
||||
using System.Collections.Concurrent;
|
||||
using NATS.Server.Subscriptions;
|
||||
|
||||
namespace NATS.Server.Tests.Stress;
|
||||
namespace NATS.Server.Core.Tests.Stress;
|
||||
|
||||
/// <summary>
|
||||
/// Stress tests for concurrent pub/sub operations on the in-process SubList and SubjectMatch
|
||||
@@ -8,8 +8,9 @@ using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using NATS.Server;
|
||||
using NATS.Server.TestUtilities;
|
||||
|
||||
namespace NATS.Server.Tests.Stress;
|
||||
namespace NATS.Server.Core.Tests.Stress;
|
||||
|
||||
/// <summary>
|
||||
/// Stress tests for slow consumer behaviour and connection lifecycle using real NatsServer
|
||||
@@ -24,27 +25,6 @@ public class SlowConsumerStressTests
|
||||
// Helpers
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
private static int GetFreePort()
|
||||
{
|
||||
using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
sock.Bind(new IPEndPoint(IPAddress.Loopback, 0));
|
||||
return ((IPEndPoint)sock.LocalEndPoint!).Port;
|
||||
}
|
||||
|
||||
private static async Task<string> ReadUntilAsync(Socket sock, string expected, int timeoutMs = 5000)
|
||||
{
|
||||
using var cts = new CancellationTokenSource(timeoutMs);
|
||||
var sb = new StringBuilder();
|
||||
var buf = new byte[8192];
|
||||
while (!sb.ToString().Contains(expected))
|
||||
{
|
||||
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 async Task<Socket> ConnectRawAsync(int port)
|
||||
{
|
||||
var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
|
||||
@@ -68,7 +48,7 @@ public class SlowConsumerStressTests
|
||||
const int payloadSize = 256;
|
||||
const int floodCount = 30;
|
||||
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
using var cts = new CancellationTokenSource();
|
||||
var server = new NatsServer(
|
||||
new NatsOptions { Port = port, MaxPending = maxPending },
|
||||
@@ -81,7 +61,7 @@ public class SlowConsumerStressTests
|
||||
using var slowSub = await ConnectRawAsync(port);
|
||||
await slowSub.SendAsync(
|
||||
Encoding.ASCII.GetBytes("CONNECT {\"verbose\":false}\r\nSUB sc.stat 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(slowSub, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(slowSub, "PONG");
|
||||
|
||||
using var pub = await ConnectRawAsync(port);
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {\"verbose\":false}\r\n"));
|
||||
@@ -92,7 +72,7 @@ public class SlowConsumerStressTests
|
||||
sb.Append($"PUB sc.stat {payloadSize}\r\n{payload}\r\n");
|
||||
sb.Append("PING\r\n");
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes(sb.ToString()));
|
||||
await ReadUntilAsync(pub, "PONG", 5000);
|
||||
await SocketTestHelper.ReadUntilAsync(pub, "PONG", 5000);
|
||||
|
||||
await Task.Delay(500);
|
||||
|
||||
@@ -118,7 +98,7 @@ public class SlowConsumerStressTests
|
||||
const int payloadSize = 128;
|
||||
const int floodCount = 20;
|
||||
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
using var cts = new CancellationTokenSource();
|
||||
var server = new NatsServer(
|
||||
new NatsOptions { Port = port, MaxPending = maxPending },
|
||||
@@ -134,11 +114,11 @@ public class SlowConsumerStressTests
|
||||
|
||||
await slow1.SendAsync(
|
||||
Encoding.ASCII.GetBytes("CONNECT {\"verbose\":false}\r\nSUB multi.slow 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(slow1, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(slow1, "PONG");
|
||||
|
||||
await slow2.SendAsync(
|
||||
Encoding.ASCII.GetBytes("CONNECT {\"verbose\":false}\r\nSUB multi.slow 2\r\nPING\r\n"));
|
||||
await ReadUntilAsync(slow2, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(slow2, "PONG");
|
||||
|
||||
using var pub = await ConnectRawAsync(port);
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {\"verbose\":false}\r\n"));
|
||||
@@ -149,7 +129,7 @@ public class SlowConsumerStressTests
|
||||
sb.Append($"PUB multi.slow {payloadSize}\r\n{payload}\r\n");
|
||||
sb.Append("PING\r\n");
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes(sb.ToString()));
|
||||
await ReadUntilAsync(pub, "PONG", 5000);
|
||||
await SocketTestHelper.ReadUntilAsync(pub, "PONG", 5000);
|
||||
|
||||
await Task.Delay(600);
|
||||
|
||||
@@ -173,7 +153,7 @@ public class SlowConsumerStressTests
|
||||
{
|
||||
const long maxPending = 512;
|
||||
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
using var cts = new CancellationTokenSource();
|
||||
var server = new NatsServer(
|
||||
new NatsOptions { Port = port, MaxPending = maxPending },
|
||||
@@ -186,7 +166,7 @@ public class SlowConsumerStressTests
|
||||
using var sub = await ConnectRawAsync(port);
|
||||
await sub.SendAsync(
|
||||
Encoding.ASCII.GetBytes("CONNECT {\"verbose\":false}\r\nSUB bp.test 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG");
|
||||
|
||||
using var pub = await ConnectRawAsync(port);
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {\"verbose\":false}\r\n"));
|
||||
@@ -197,7 +177,7 @@ public class SlowConsumerStressTests
|
||||
sb.Append($"PUB bp.test 400\r\n{payload}\r\n");
|
||||
sb.Append("PING\r\n");
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes(sb.ToString()));
|
||||
await ReadUntilAsync(pub, "PONG", 5000);
|
||||
await SocketTestHelper.ReadUntilAsync(pub, "PONG", 5000);
|
||||
await Task.Delay(400);
|
||||
|
||||
var stats = server.Stats;
|
||||
@@ -220,7 +200,7 @@ public class SlowConsumerStressTests
|
||||
[Trait("Category", "Stress")]
|
||||
public async Task Subscriber_receives_messages_after_100_rapid_publishes()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
using var cts = new CancellationTokenSource();
|
||||
var server = new NatsServer(
|
||||
new NatsOptions { Port = port },
|
||||
@@ -233,7 +213,7 @@ public class SlowConsumerStressTests
|
||||
using var sub = await ConnectRawAsync(port);
|
||||
await sub.SendAsync(
|
||||
Encoding.ASCII.GetBytes("CONNECT {\"verbose\":false}\r\nSUB rapid 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG");
|
||||
|
||||
using var pub = await ConnectRawAsync(port);
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {\"verbose\":false}\r\n"));
|
||||
@@ -243,9 +223,9 @@ public class SlowConsumerStressTests
|
||||
sb.Append("PUB rapid 4\r\nping\r\n");
|
||||
sb.Append("PING\r\n");
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes(sb.ToString()));
|
||||
await ReadUntilAsync(pub, "PONG", 5000);
|
||||
await SocketTestHelper.ReadUntilAsync(pub, "PONG", 5000);
|
||||
|
||||
var received = await ReadUntilAsync(sub, "MSG rapid", 5000);
|
||||
var received = await SocketTestHelper.ReadUntilAsync(sub, "MSG rapid", 5000);
|
||||
received.ShouldContain("MSG rapid");
|
||||
}
|
||||
finally
|
||||
@@ -263,7 +243,7 @@ public class SlowConsumerStressTests
|
||||
[Trait("Category", "Stress")]
|
||||
public async Task Concurrent_publish_and_subscribe_startup_does_not_crash_server()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
using var cts = new CancellationTokenSource();
|
||||
var server = new NatsServer(
|
||||
new NatsOptions { Port = port },
|
||||
@@ -278,7 +258,7 @@ public class SlowConsumerStressTests
|
||||
using var sock = await ConnectRawAsync(port);
|
||||
await sock.SendAsync(
|
||||
Encoding.ASCII.GetBytes($"CONNECT {{\"verbose\":false}}\r\nSUB conc.start.{i} {i + 1}\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sock, "PONG", 3000);
|
||||
await SocketTestHelper.ReadUntilAsync(sock, "PONG", 3000);
|
||||
});
|
||||
|
||||
await Task.WhenAll(tasks);
|
||||
@@ -302,7 +282,7 @@ public class SlowConsumerStressTests
|
||||
{
|
||||
// Use 8KB payload — large enough to span multiple TCP segments but small
|
||||
// enough to stay well within the default MaxPending limit in CI.
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
using var cts = new CancellationTokenSource();
|
||||
var server = new NatsServer(
|
||||
new NatsOptions { Port = port },
|
||||
@@ -320,20 +300,20 @@ public class SlowConsumerStressTests
|
||||
|
||||
await sub1.SendAsync(
|
||||
Encoding.ASCII.GetBytes("CONNECT {\"verbose\":false}\r\nSUB large.msg 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub1, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sub1, "PONG");
|
||||
|
||||
await sub2.SendAsync(
|
||||
Encoding.ASCII.GetBytes("CONNECT {\"verbose\":false}\r\nSUB large.msg 2\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub2, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sub2, "PONG");
|
||||
|
||||
using var pub = await ConnectRawAsync(port);
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {\"verbose\":false}\r\n"));
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes($"PUB large.msg {payloadSize}\r\n{payload}\r\nPING\r\n"));
|
||||
// Use a longer timeout for large message delivery
|
||||
await ReadUntilAsync(pub, "PONG", 10000);
|
||||
await SocketTestHelper.ReadUntilAsync(pub, "PONG", 10000);
|
||||
|
||||
var r1 = await ReadUntilAsync(sub1, "MSG large.msg", 10000);
|
||||
var r2 = await ReadUntilAsync(sub2, "MSG large.msg", 10000);
|
||||
var r1 = await SocketTestHelper.ReadUntilAsync(sub1, "MSG large.msg", 10000);
|
||||
var r2 = await SocketTestHelper.ReadUntilAsync(sub2, "MSG large.msg", 10000);
|
||||
|
||||
r1.ShouldContain("MSG large.msg");
|
||||
r2.ShouldContain("MSG large.msg");
|
||||
@@ -353,7 +333,7 @@ public class SlowConsumerStressTests
|
||||
[Trait("Category", "Stress")]
|
||||
public async Task Subscribe_unsubscribe_resubscribe_cycle_100_times_without_error()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
using var cts = new CancellationTokenSource();
|
||||
var server = new NatsServer(
|
||||
new NatsOptions { Port = port },
|
||||
@@ -373,7 +353,7 @@ public class SlowConsumerStressTests
|
||||
}
|
||||
|
||||
await client.SendAsync(Encoding.ASCII.GetBytes("PING\r\n"));
|
||||
var resp = await ReadUntilAsync(client, "PONG", 5000);
|
||||
var resp = await SocketTestHelper.ReadUntilAsync(client, "PONG", 5000);
|
||||
resp.ShouldContain("PONG");
|
||||
}
|
||||
finally
|
||||
@@ -391,7 +371,7 @@ public class SlowConsumerStressTests
|
||||
[Trait("Category", "Stress")]
|
||||
public async Task Subscriber_receives_messages_correctly_after_brief_pause()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
using var cts = new CancellationTokenSource();
|
||||
var server = new NatsServer(
|
||||
new NatsOptions { Port = port },
|
||||
@@ -404,7 +384,7 @@ public class SlowConsumerStressTests
|
||||
using var sub = await ConnectRawAsync(port);
|
||||
await sub.SendAsync(
|
||||
Encoding.ASCII.GetBytes("CONNECT {\"verbose\":false}\r\nSUB pause.sub 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG");
|
||||
|
||||
// Brief pause simulating a subscriber that drifts slightly
|
||||
await Task.Delay(100);
|
||||
@@ -412,9 +392,9 @@ public class SlowConsumerStressTests
|
||||
using var pub = await ConnectRawAsync(port);
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {\"verbose\":false}\r\n"));
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("PUB pause.sub 5\r\nhello\r\nPING\r\n"));
|
||||
await ReadUntilAsync(pub, "PONG", 5000);
|
||||
await SocketTestHelper.ReadUntilAsync(pub, "PONG", 5000);
|
||||
|
||||
var received = await ReadUntilAsync(sub, "hello", 5000);
|
||||
var received = await SocketTestHelper.ReadUntilAsync(sub, "hello", 5000);
|
||||
received.ShouldContain("hello");
|
||||
}
|
||||
finally
|
||||
@@ -432,7 +412,7 @@ public class SlowConsumerStressTests
|
||||
[Trait("Category", "Stress")]
|
||||
public async Task Multiple_client_connections_and_disconnections_leave_server_stable()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
using var cts = new CancellationTokenSource();
|
||||
var server = new NatsServer(
|
||||
new NatsOptions { Port = port },
|
||||
@@ -448,7 +428,7 @@ public class SlowConsumerStressTests
|
||||
using var sock = await ConnectRawAsync(port);
|
||||
await sock.SendAsync(
|
||||
Encoding.ASCII.GetBytes("CONNECT {\"verbose\":false}\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sock, "PONG", 3000);
|
||||
await SocketTestHelper.ReadUntilAsync(sock, "PONG", 3000);
|
||||
sock.Close();
|
||||
}
|
||||
|
||||
@@ -458,7 +438,7 @@ public class SlowConsumerStressTests
|
||||
// Server should still accept new connections
|
||||
using var final = await ConnectRawAsync(port);
|
||||
await final.SendAsync(Encoding.ASCII.GetBytes("CONNECT {\"verbose\":false}\r\nPING\r\n"));
|
||||
var resp = await ReadUntilAsync(final, "PONG", 3000);
|
||||
var resp = await SocketTestHelper.ReadUntilAsync(final, "PONG", 3000);
|
||||
resp.ShouldContain("PONG");
|
||||
}
|
||||
finally
|
||||
@@ -476,7 +456,7 @@ public class SlowConsumerStressTests
|
||||
[Trait("Category", "Stress")]
|
||||
public async Task Stats_in_and_out_bytes_increment_correctly_under_load()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
using var cts = new CancellationTokenSource();
|
||||
var server = new NatsServer(
|
||||
new NatsOptions { Port = port },
|
||||
@@ -489,7 +469,7 @@ public class SlowConsumerStressTests
|
||||
using var sub = await ConnectRawAsync(port);
|
||||
await sub.SendAsync(
|
||||
Encoding.ASCII.GetBytes("CONNECT {\"verbose\":false}\r\nSUB stats.load 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG");
|
||||
|
||||
using var pub = await ConnectRawAsync(port);
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {\"verbose\":false}\r\n"));
|
||||
@@ -499,7 +479,7 @@ public class SlowConsumerStressTests
|
||||
sb.Append("PUB stats.load 10\r\n0123456789\r\n");
|
||||
sb.Append("PING\r\n");
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes(sb.ToString()));
|
||||
await ReadUntilAsync(pub, "PONG", 5000);
|
||||
await SocketTestHelper.ReadUntilAsync(pub, "PONG", 5000);
|
||||
|
||||
await Task.Delay(200);
|
||||
|
||||
@@ -522,7 +502,7 @@ public class SlowConsumerStressTests
|
||||
[Trait("Category", "Stress")]
|
||||
public async Task Rapid_connect_disconnect_cycles_do_not_corrupt_server_state()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
using var cts = new CancellationTokenSource();
|
||||
var server = new NatsServer(
|
||||
new NatsOptions { Port = port },
|
||||
@@ -550,7 +530,7 @@ public class SlowConsumerStressTests
|
||||
// Server should still respond
|
||||
using var healthy = await ConnectRawAsync(port);
|
||||
await healthy.SendAsync(Encoding.ASCII.GetBytes("CONNECT {\"verbose\":false}\r\nPING\r\n"));
|
||||
var resp = await ReadUntilAsync(healthy, "PONG", 3000);
|
||||
var resp = await SocketTestHelper.ReadUntilAsync(healthy, "PONG", 3000);
|
||||
resp.ShouldContain("PONG");
|
||||
}
|
||||
finally
|
||||
@@ -568,7 +548,7 @@ public class SlowConsumerStressTests
|
||||
[Trait("Category", "Stress")]
|
||||
public async Task Server_accepts_connection_after_cancelled_client_task()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
using var serverCts = new CancellationTokenSource();
|
||||
var server = new NatsServer(
|
||||
new NatsOptions { Port = port },
|
||||
@@ -599,7 +579,7 @@ public class SlowConsumerStressTests
|
||||
// Server should still function
|
||||
using var good = await ConnectRawAsync(port);
|
||||
await good.SendAsync(Encoding.ASCII.GetBytes("CONNECT {\"verbose\":false}\r\nPING\r\n"));
|
||||
var resp = await ReadUntilAsync(good, "PONG", 3000);
|
||||
var resp = await SocketTestHelper.ReadUntilAsync(good, "PONG", 3000);
|
||||
resp.ShouldContain("PONG");
|
||||
}
|
||||
finally
|
||||
@@ -621,7 +601,7 @@ public class SlowConsumerStressTests
|
||||
const int payloadSize = 256;
|
||||
const int floodCount = 20;
|
||||
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
using var cts = new CancellationTokenSource();
|
||||
var server = new NatsServer(
|
||||
new NatsOptions { Port = port, MaxPending = maxPending },
|
||||
@@ -634,7 +614,7 @@ public class SlowConsumerStressTests
|
||||
using var slowSub = await ConnectRawAsync(port);
|
||||
await slowSub.SendAsync(
|
||||
Encoding.ASCII.GetBytes("CONNECT {\"verbose\":false}\r\nSUB drop.test 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(slowSub, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(slowSub, "PONG");
|
||||
|
||||
using var pub = await ConnectRawAsync(port);
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {\"verbose\":false}\r\n"));
|
||||
@@ -645,7 +625,7 @@ public class SlowConsumerStressTests
|
||||
sb.Append($"PUB drop.test {payloadSize}\r\n{payload}\r\n");
|
||||
sb.Append("PING\r\n");
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes(sb.ToString()));
|
||||
await ReadUntilAsync(pub, "PONG", 5000);
|
||||
await SocketTestHelper.ReadUntilAsync(pub, "PONG", 5000);
|
||||
|
||||
await Task.Delay(600);
|
||||
|
||||
@@ -667,7 +647,7 @@ public class SlowConsumerStressTests
|
||||
[Trait("Category", "Stress")]
|
||||
public async Task Server_delivers_to_correct_subscriber_when_multiple_subjects_active()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
using var cts = new CancellationTokenSource();
|
||||
var server = new NatsServer(
|
||||
new NatsOptions { Port = port },
|
||||
@@ -682,18 +662,18 @@ public class SlowConsumerStressTests
|
||||
|
||||
await sub1.SendAsync(
|
||||
Encoding.ASCII.GetBytes("CONNECT {\"verbose\":false}\r\nSUB target.A 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub1, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sub1, "PONG");
|
||||
|
||||
await sub2.SendAsync(
|
||||
Encoding.ASCII.GetBytes("CONNECT {\"verbose\":false}\r\nSUB target.B 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub2, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sub2, "PONG");
|
||||
|
||||
using var pub = await ConnectRawAsync(port);
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {\"verbose\":false}\r\n"));
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("PUB target.A 5\r\nhello\r\nPING\r\n"));
|
||||
await ReadUntilAsync(pub, "PONG", 5000);
|
||||
await SocketTestHelper.ReadUntilAsync(pub, "PONG", 5000);
|
||||
|
||||
var r1 = await ReadUntilAsync(sub1, "hello", 3000);
|
||||
var r1 = await SocketTestHelper.ReadUntilAsync(sub1, "hello", 3000);
|
||||
r1.ShouldContain("MSG target.A");
|
||||
|
||||
// sub2 should NOT have received the target.A message
|
||||
@@ -719,7 +699,7 @@ public class SlowConsumerStressTests
|
||||
[Trait("Category", "Stress")]
|
||||
public async Task Server_remains_stable_after_processing_many_medium_sized_messages()
|
||||
{
|
||||
var port = GetFreePort();
|
||||
var port = TestPortAllocator.GetFreePort();
|
||||
using var cts = new CancellationTokenSource();
|
||||
var server = new NatsServer(
|
||||
new NatsOptions { Port = port },
|
||||
@@ -732,7 +712,7 @@ public class SlowConsumerStressTests
|
||||
using var sub = await ConnectRawAsync(port);
|
||||
await sub.SendAsync(
|
||||
Encoding.ASCII.GetBytes("CONNECT {\"verbose\":false}\r\nSUB medium.msgs 1\r\nPING\r\n"));
|
||||
await ReadUntilAsync(sub, "PONG");
|
||||
await SocketTestHelper.ReadUntilAsync(sub, "PONG");
|
||||
|
||||
using var pub = await ConnectRawAsync(port);
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {\"verbose\":false}\r\n"));
|
||||
@@ -744,7 +724,7 @@ public class SlowConsumerStressTests
|
||||
sb.Append("PING\r\n");
|
||||
|
||||
await pub.SendAsync(Encoding.ASCII.GetBytes(sb.ToString()));
|
||||
await ReadUntilAsync(pub, "PONG", 10000);
|
||||
await SocketTestHelper.ReadUntilAsync(pub, "PONG", 10000);
|
||||
|
||||
var stats = server.Stats;
|
||||
Interlocked.Read(ref stats.InMsgs).ShouldBeGreaterThanOrEqualTo(200);
|
||||
@@ -1,6 +1,6 @@
|
||||
using NATS.Server.Subscriptions;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
public class SubListAsyncCacheSweepTests
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using NATS.Server.Subscriptions;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
public class SubListHighFanoutOptimizationTests
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using NATS.Server.Subscriptions;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
public class SubListMatchBytesTests
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using NATS.Server.Subscriptions;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
public class SubListNotificationTests
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using NATS.Server.Subscriptions;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
public class SubListQueueWeightTests
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using NATS.Server.Subscriptions;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
public class SubListRemoteFilterTests
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using NATS.Server.Subscriptions;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
public class SubListTests
|
||||
{
|
||||
@@ -1,6 +1,6 @@
|
||||
using NATS.Server.Subscriptions;
|
||||
|
||||
namespace NATS.Server.Tests;
|
||||
namespace NATS.Server.Core.Tests;
|
||||
|
||||
public class SubjectMatchTests
|
||||
{
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user