refactor: extract NATS.Server.Transport.Tests project

Move TLS, OCSP, WebSocket, Networking, and IO test files from
NATS.Server.Tests into a dedicated NATS.Server.Transport.Tests
project. Update namespaces, replace private GetFreePort/ReadUntilAsync
with shared TestUtilities helpers, extract TestCertHelper to
TestUtilities, and replace Task.Delay polling loops with
PollHelper.WaitUntilAsync/YieldForAsync for proper synchronization.
This commit is contained in:
Joseph Doherty
2026-03-12 14:57:35 -04:00
parent 5c608f07e3
commit d2c04fcca5
36 changed files with 157 additions and 152 deletions

View File

@@ -18,4 +18,18 @@ public static class SocketTestHelper
}
return sb.ToString();
}
public static async Task<string> ReadUntilAsync(Stream stream, 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))
{
var n = await stream.ReadAsync(buf, cts.Token);
if (n == 0) break;
sb.Append(Encoding.ASCII.GetString(buf, 0, n));
}
return sb.ToString();
}
}

View File

@@ -0,0 +1,31 @@
using System.Net;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
namespace NATS.Server.TestUtilities;
public static class TestCertHelper
{
public static (string certPath, string keyPath) GenerateTestCertFiles()
{
var (cert, key) = GenerateTestCert();
var certPath = Path.GetTempFileName();
var keyPath = Path.GetTempFileName();
File.WriteAllText(certPath, cert.ExportCertificatePem());
File.WriteAllText(keyPath, key.ExportPkcs8PrivateKeyPem());
return (certPath, keyPath);
}
public static (X509Certificate2 cert, RSA key) GenerateTestCert()
{
var key = RSA.Create(2048);
var req = new CertificateRequest("CN=localhost", key, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
req.CertificateExtensions.Add(new X509BasicConstraintsExtension(false, false, 0, false));
var sanBuilder = new SubjectAlternativeNameBuilder();
sanBuilder.AddIpAddress(IPAddress.Loopback);
sanBuilder.AddDnsName("localhost");
req.CertificateExtensions.Add(sanBuilder.Build());
var cert = req.CreateSelfSigned(DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddYears(1));
return (cert, key);
}
}

View File

@@ -5,6 +5,7 @@ using System.Net.Sockets;
using System.Text;
using Microsoft.Extensions.Logging.Abstractions;
using NATS.Server.Monitoring;
using NATS.Server.TestUtilities;
namespace NATS.Server.Tests;
@@ -310,7 +311,7 @@ public class MonitorTlsTests : IAsyncLifetime
{
_natsPort = GetFreePort();
_monitorPort = GetFreePort();
(_certPath, _keyPath) = TlsHelperTests.GenerateTestCertFiles();
(_certPath, _keyPath) = TestCertHelper.GenerateTestCertFiles();
_server = new NatsServer(
new NatsOptions
{

View File

@@ -15,6 +15,7 @@ using NATS.Server;
using NATS.Server.Auth;
using NATS.Server.Monitoring;
using NATS.Server.Protocol;
using NATS.Server.TestUtilities;
namespace NATS.Server.Tests;
@@ -784,7 +785,7 @@ public class MsgTraceGoParityTests : IAsyncLifetime
public async Task ClosedConns_tls_handshake_close_reason_tracked()
{
// Go: TestClosedTLSHandshake (closed_conns_test.go:247)
var (certPath, keyPath) = TlsHelperTests.GenerateTestCertFiles();
var (certPath, keyPath) = TestCertHelper.GenerateTestCertFiles();
try
{
var port = GetFreePort();

View File

@@ -1,7 +1,7 @@
using NATS.Server.IO;
using Shouldly;
namespace NATS.Server.Tests.IO;
namespace NATS.Server.Transport.Tests.IO;
/// <summary>
/// Tests for the consecutive short-read counter in AdaptiveReadBuffer.

View File

@@ -1,6 +1,6 @@
using NATS.Server.IO;
namespace NATS.Server.Tests;
namespace NATS.Server.Transport.Tests;
public class AdaptiveReadBufferTests
{

View File

@@ -4,7 +4,7 @@ using Shouldly;
// Go reference: client.go — dynamic buffer sizing and broadcast flush coalescing for fan-out.
namespace NATS.Server.Tests.IO;
namespace NATS.Server.Transport.Tests.IO;
public class DynamicBufferPoolTests
{

View File

@@ -1,6 +1,6 @@
using NATS.Server.IO;
namespace NATS.Server.Tests;
namespace NATS.Server.Transport.Tests;
public class OutboundBufferPoolTests
{

View File

@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="NATS.Client.Core" />
<PackageReference Include="NSubstitute" />
<PackageReference Include="Shouldly" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio" />
<PackageReference Include="Serilog.Sinks.File" />
</ItemGroup>
<ItemGroup>
<Using Include="Xunit" />
<Using Include="Shouldly" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\NATS.Server\NATS.Server.csproj" />
<ProjectReference Include="..\NATS.Server.TestUtilities\NATS.Server.TestUtilities.csproj" />
</ItemGroup>
</Project>

View File

@@ -10,6 +10,7 @@ using NATS.Server.Gateways;
using NATS.Server.LeafNodes;
using NATS.Server.Routes;
using NATS.Server.Subscriptions;
using NATS.Server.TestUtilities;
namespace NATS.Server.Tests.Networking;
@@ -177,9 +178,7 @@ public class NetworkingGoParityTests
await using var sub = await conn.SubscribeCoreAsync<string>("gw.interest.test");
await conn.PingAsync();
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5));
while (!timeout.IsCancellationRequested && !fixture.Local.HasRemoteInterest("gw.interest.test"))
await Task.Delay(50, timeout.Token).ContinueWith(_ => { }, TaskScheduler.Default);
await PollHelper.WaitUntilAsync(() => fixture.Local.HasRemoteInterest("gw.interest.test"));
fixture.Local.HasRemoteInterest("gw.interest.test").ShouldBeTrue();
}
@@ -204,9 +203,7 @@ public class NetworkingGoParityTests
await using var sub = await remoteConn.SubscribeCoreAsync<string>("gw.fwd.test");
await remoteConn.PingAsync();
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5));
while (!timeout.IsCancellationRequested && !fixture.Local.HasRemoteInterest("gw.fwd.test"))
await Task.Delay(50, timeout.Token).ContinueWith(_ => { }, TaskScheduler.Default);
await PollHelper.WaitUntilAsync(() => fixture.Local.HasRemoteInterest("gw.fwd.test"));
await localConn.PublishAsync("gw.fwd.test", "gateway-msg");
@@ -229,18 +226,14 @@ public class NetworkingGoParityTests
var sub = await conn.SubscribeCoreAsync<string>("gw.unsub.test");
await conn.PingAsync();
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5));
while (!timeout.IsCancellationRequested && !fixture.Local.HasRemoteInterest("gw.unsub.test"))
await Task.Delay(50, timeout.Token).ContinueWith(_ => { }, TaskScheduler.Default);
await PollHelper.WaitUntilAsync(() => fixture.Local.HasRemoteInterest("gw.unsub.test"));
fixture.Local.HasRemoteInterest("gw.unsub.test").ShouldBeTrue();
await sub.DisposeAsync();
await conn.PingAsync();
using var unsTimeout = new CancellationTokenSource(TimeSpan.FromSeconds(5));
while (!unsTimeout.IsCancellationRequested && fixture.Local.HasRemoteInterest("gw.unsub.test"))
await Task.Delay(50, unsTimeout.Token).ContinueWith(_ => { }, TaskScheduler.Default);
await PollHelper.WaitUntilAsync(() => !(fixture.Local.HasRemoteInterest("gw.unsub.test")));
fixture.Local.HasRemoteInterest("gw.unsub.test").ShouldBeFalse();
}
@@ -260,9 +253,7 @@ public class NetworkingGoParityTests
await using var sub = await conn.SubscribeCoreAsync<string>("gw.wild.>");
await conn.PingAsync();
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5));
while (!timeout.IsCancellationRequested && !fixture.Local.HasRemoteInterest("gw.wild.test"))
await Task.Delay(50, timeout.Token).ContinueWith(_ => { }, TaskScheduler.Default);
await PollHelper.WaitUntilAsync(() => fixture.Local.HasRemoteInterest("gw.wild.test"));
fixture.Local.HasRemoteInterest("gw.wild.test").ShouldBeTrue();
fixture.Local.HasRemoteInterest("gw.wild.deep.nested").ShouldBeTrue();
@@ -410,9 +401,7 @@ public class NetworkingGoParityTests
try
{
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5));
while (!timeout.IsCancellationRequested && serverA.Stats.Routes < 3)
await Task.Delay(50, timeout.Token).ContinueWith(_ => { }, TaskScheduler.Default);
await PollHelper.WaitUntilAsync(() => !(serverA.Stats.Routes < 3));
serverA.Stats.Routes.ShouldBeGreaterThanOrEqualTo(3);
}
@@ -488,9 +477,7 @@ public class NetworkingGoParityTests
try
{
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5));
while (!timeout.IsCancellationRequested && serverA.Stats.Routes < 3)
await Task.Delay(50, timeout.Token).ContinueWith(_ => { }, TaskScheduler.Default);
await PollHelper.WaitUntilAsync(() => !(serverA.Stats.Routes < 3));
await using var conn = new NatsConnection(new NatsOpts
{
@@ -501,9 +488,7 @@ public class NetworkingGoParityTests
await using var sub = await conn.SubscribeCoreAsync<string>("route.sub.test");
await conn.PingAsync();
using var interest = new CancellationTokenSource(TimeSpan.FromSeconds(5));
while (!interest.IsCancellationRequested && !serverA.HasRemoteInterest("route.sub.test"))
await Task.Delay(50, interest.Token).ContinueWith(_ => { }, TaskScheduler.Default);
await PollHelper.WaitUntilAsync(() => serverA.HasRemoteInterest("route.sub.test"));
serverA.HasRemoteInterest("route.sub.test").ShouldBeTrue();
}
@@ -582,9 +567,7 @@ public class NetworkingGoParityTests
try
{
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5));
while (!timeout.IsCancellationRequested && serverA.Stats.Routes < 3)
await Task.Delay(50, timeout.Token).ContinueWith(_ => { }, TaskScheduler.Default);
await PollHelper.WaitUntilAsync(() => !(serverA.Stats.Routes < 3));
await using var subConn = new NatsConnection(new NatsOpts
{
@@ -601,9 +584,7 @@ public class NetworkingGoParityTests
await using var sub = await subConn.SubscribeCoreAsync<string>("route.fwd.test");
await subConn.PingAsync();
using var interest = new CancellationTokenSource(TimeSpan.FromSeconds(5));
while (!interest.IsCancellationRequested && !serverA.HasRemoteInterest("route.fwd.test"))
await Task.Delay(50, interest.Token).ContinueWith(_ => { }, TaskScheduler.Default);
await PollHelper.WaitUntilAsync(() => serverA.HasRemoteInterest("route.fwd.test"));
await pubConn.PublishAsync("route.fwd.test", "routed-msg");
@@ -852,7 +833,7 @@ public class NetworkingGoParityTests
leaf.StartLoop(cts.Token);
await WriteLineAsync(remoteSocket, "LS+ $G events.>", cts.Token);
await Task.Delay(100);
await PollHelper.YieldForAsync(100);
await WriteLineAsync(remoteSocket, "LS- $G events.>", cts.Token);
var result = await received.Task.WaitAsync(cts.Token);
@@ -1132,9 +1113,7 @@ internal sealed class TwoGatewayFixture : IAsyncDisposable
_ = remote.StartAsync(remoteCts.Token);
await remote.WaitForReadyAsync();
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5));
while (!timeout.IsCancellationRequested && (local.Stats.Gateways == 0 || remote.Stats.Gateways == 0))
await Task.Delay(50, timeout.Token).ContinueWith(_ => { }, TaskScheduler.Default);
await PollHelper.WaitUntilAsync(() => !((local.Stats.Gateways == 0 || remote.Stats.Gateways == 0)));
return new TwoGatewayFixture(local, remote, localCts, remoteCts);
}
@@ -1205,37 +1184,23 @@ internal sealed class LeafFixture : IAsyncDisposable
_ = spoke.StartAsync(spokeCts.Token);
await spoke.WaitForReadyAsync();
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5));
while (!timeout.IsCancellationRequested && (hub.Stats.Leafs == 0 || spoke.Stats.Leafs == 0))
await Task.Delay(50, timeout.Token).ContinueWith(_ => { }, TaskScheduler.Default);
await PollHelper.WaitUntilAsync(() => !((hub.Stats.Leafs == 0 || spoke.Stats.Leafs == 0)));
return new LeafFixture(hub, spoke, hubCts, spokeCts);
}
public async Task WaitForRemoteInterestOnHubAsync(string subject)
{
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5));
while (!timeout.IsCancellationRequested)
{
if (Hub.HasRemoteInterest(subject))
return;
await Task.Delay(50, timeout.Token).ContinueWith(_ => { }, TaskScheduler.Default);
}
throw new TimeoutException($"Timed out waiting for remote interest on hub for '{subject}'.");
await PollHelper.WaitOrThrowAsync(
() => Hub.HasRemoteInterest(subject),
$"Timed out waiting for remote interest on hub for '{subject}'.");
}
public async Task WaitForRemoteInterestOnSpokeAsync(string subject)
{
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5));
while (!timeout.IsCancellationRequested)
{
if (Spoke.HasRemoteInterest(subject))
return;
await Task.Delay(50, timeout.Token).ContinueWith(_ => { }, TaskScheduler.Default);
}
throw new TimeoutException($"Timed out waiting for remote interest on spoke for '{subject}'.");
await PollHelper.WaitOrThrowAsync(
() => Spoke.HasRemoteInterest(subject),
$"Timed out waiting for remote interest on spoke for '{subject}'.");
}
public async ValueTask DisposeAsync()

View File

@@ -1,6 +1,6 @@
using NATS.Server.Tls;
namespace NATS.Server.Tests;
namespace NATS.Server.Transport.Tests;
public class OcspConfigTests
{

View File

@@ -1,6 +1,6 @@
using NATS.Server.Tls;
namespace NATS.Server.Tests;
namespace NATS.Server.Transport.Tests;
public class OcspStaplingTests
{

View File

@@ -6,9 +6,10 @@ using System.Security.Cryptography.X509Certificates;
using Microsoft.Extensions.Logging.Abstractions;
using NATS.Server;
using NATS.Server.Protocol;
using NATS.Server.TestUtilities;
using NATS.Server.Tls;
namespace NATS.Server.Tests;
namespace NATS.Server.Transport.Tests;
public class TlsConnectionWrapperTests
{
@@ -32,7 +33,7 @@ public class TlsConnectionWrapperTests
[Fact]
public async Task TlsRequired_upgrades_to_ssl()
{
var (cert, _) = TlsHelperTests.GenerateTestCert();
var (cert, _) = TestCertHelper.GenerateTestCert();
var (serverSocket, clientSocket) = await CreateSocketPairAsync();
using var clientNetStream = new NetworkStream(clientSocket, ownsSocket: true);
@@ -85,7 +86,7 @@ public class TlsConnectionWrapperTests
[Fact]
public async Task MixedMode_allows_plaintext_when_AllowNonTls()
{
var (cert, _) = TlsHelperTests.GenerateTestCert();
var (cert, _) = TestCertHelper.GenerateTestCert();
var (serverSocket, clientSocket) = await CreateSocketPairAsync();
using var clientNetStream = new NetworkStream(clientSocket, ownsSocket: true);
@@ -133,7 +134,7 @@ public class TlsConnectionWrapperTests
[Fact]
public async Task TlsRequired_rejects_plaintext()
{
var (cert, _) = TlsHelperTests.GenerateTestCert();
var (cert, _) = TestCertHelper.GenerateTestCert();
var (serverSocket, clientSocket) = await CreateSocketPairAsync();
using var clientNetStream = new NetworkStream(clientSocket, ownsSocket: true);
@@ -180,7 +181,7 @@ public class TlsConnectionWrapperTests
[Fact]
public async Task TlsFirst_handshakes_before_sending_info()
{
var (cert, _) = TlsHelperTests.GenerateTestCert();
var (cert, _) = TestCertHelper.GenerateTestCert();
var (serverSocket, clientSocket) = await CreateSocketPairAsync();
using var clientNetStream = new NetworkStream(clientSocket, ownsSocket: true);

View File

@@ -2,16 +2,17 @@ using System.Net;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using NATS.Server;
using NATS.Server.TestUtilities;
using NATS.Server.Tls;
namespace NATS.Server.Tests;
namespace NATS.Server.Transport.Tests;
public class TlsHelperTests
{
[Fact]
public void LoadCertificate_loads_pem_cert_and_key()
{
var (certPath, keyPath) = GenerateTestCertFiles();
var (certPath, keyPath) = TestCertHelper.GenerateTestCertFiles();
try
{
var cert = TlsHelper.LoadCertificate(certPath, keyPath);
@@ -24,7 +25,7 @@ public class TlsHelperTests
[Fact]
public void BuildServerAuthOptions_creates_valid_options()
{
var (certPath, keyPath) = GenerateTestCertFiles();
var (certPath, keyPath) = TestCertHelper.GenerateTestCertFiles();
try
{
var opts = new NatsOptions { TlsCert = certPath, TlsKey = keyPath };
@@ -38,7 +39,7 @@ public class TlsHelperTests
[Fact]
public void LoadCaCertificates_rejects_non_certificate_pem_block()
{
var (_, key) = GenerateTestCert();
var (_, key) = TestCertHelper.GenerateTestCert();
var pemPath = Path.GetTempFileName();
try
{
@@ -123,27 +124,10 @@ public class TlsHelperTests
await limiter.WaitAsync(cts.Token);
}
// Public helper methods used by other test classes
// Delegate to shared TestCertHelper in TestUtilities
public static (string certPath, string keyPath) GenerateTestCertFiles()
{
var (cert, key) = GenerateTestCert();
var certPath = Path.GetTempFileName();
var keyPath = Path.GetTempFileName();
File.WriteAllText(certPath, cert.ExportCertificatePem());
File.WriteAllText(keyPath, key.ExportPkcs8PrivateKeyPem());
return (certPath, keyPath);
}
=> TestCertHelper.GenerateTestCertFiles();
public static (X509Certificate2 cert, RSA key) GenerateTestCert()
{
var key = RSA.Create(2048);
var req = new CertificateRequest("CN=localhost", key, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
req.CertificateExtensions.Add(new X509BasicConstraintsExtension(false, false, 0, false));
var sanBuilder = new SubjectAlternativeNameBuilder();
sanBuilder.AddIpAddress(IPAddress.Loopback);
sanBuilder.AddDnsName("localhost");
req.CertificateExtensions.Add(sanBuilder.Build());
var cert = req.CreateSelfSigned(DateTimeOffset.UtcNow, DateTimeOffset.UtcNow.AddYears(1));
return (cert, key);
}
=> TestCertHelper.GenerateTestCert();
}

View File

@@ -2,7 +2,7 @@ using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using NATS.Server.Auth;
namespace NATS.Server.Tests;
namespace NATS.Server.Transport.Tests;
public class TlsMapAuthenticatorTests
{

View File

@@ -1,9 +1,10 @@
using System.Security.Cryptography;
using System.Text.Json;
using NATS.Server.Configuration;
using NATS.Server.TestUtilities;
using NATS.Server.Tls;
namespace NATS.Server.Tests;
namespace NATS.Server.Transport.Tests;
public class TlsOcspParityBatch1Tests
{
@@ -84,7 +85,7 @@ public class TlsOcspParityBatch1Tests
[Fact]
public void GenerateFingerprint_uses_raw_certificate_sha256()
{
var (cert, _) = TlsHelperTests.GenerateTestCert();
var (cert, _) = TestCertHelper.GenerateTestCert();
var expected = Convert.ToBase64String(SHA256.HashData(cert.RawData));
TlsHelper.GenerateFingerprint(cert).ShouldBe(expected);
@@ -104,7 +105,7 @@ public class TlsOcspParityBatch1Tests
[Fact]
public void Subject_and_issuer_dn_helpers_return_values_and_empty_for_null()
{
var (cert, _) = TlsHelperTests.GenerateTestCert();
var (cert, _) = TestCertHelper.GenerateTestCert();
TlsHelper.GetSubjectDNForm(cert).ShouldNotBeNullOrWhiteSpace();
TlsHelper.GetIssuerDNForm(cert).ShouldNotBeNullOrWhiteSpace();

View File

@@ -1,9 +1,10 @@
using System.Formats.Asn1;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using NATS.Server.TestUtilities;
using NATS.Server.Tls;
namespace NATS.Server.Tests;
namespace NATS.Server.Transport.Tests;
public class TlsOcspParityBatch2Tests
{
@@ -24,7 +25,7 @@ public class TlsOcspParityBatch2Tests
[Fact]
public void CertOCSPEligible_returns_false_when_leaf_has_no_ocsp_servers()
{
var (leaf, _) = TlsHelperTests.GenerateTestCert();
var (leaf, _) = TestCertHelper.GenerateTestCert();
var link = new ChainLink { Leaf = leaf };
TlsHelper.CertOCSPEligible(link).ShouldBeFalse();

View File

@@ -1,6 +1,7 @@
using NATS.Server.TestUtilities;
using NATS.Server.Tls;
namespace NATS.Server.Tests;
namespace NATS.Server.Transport.Tests;
public class TlsRateLimiterTests
{
@@ -39,8 +40,8 @@ public class TlsRateLimiterTests
await limiter.WaitAsync(CancellationToken.None);
await limiter.WaitAsync(CancellationToken.None);
// Wait for refill
await Task.Delay(1200);
// Wait for refill (rate limiter refills tokens after 1 second)
await PollHelper.YieldForAsync(1200);
// Should have tokens again
using var cts = new CancellationTokenSource(200);

View File

@@ -4,8 +4,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.Transport.Tests;
public class TlsServerTests : IAsyncLifetime
{
@@ -17,8 +18,8 @@ public class TlsServerTests : IAsyncLifetime
public TlsServerTests()
{
_port = GetFreePort();
(_certPath, _keyPath) = TlsHelperTests.GenerateTestCertFiles();
_port = TestPortAllocator.GetFreePort();
(_certPath, _keyPath) = TestCertHelper.GenerateTestCertFiles();
_server = new NatsServer(
new NatsOptions
{
@@ -98,25 +99,11 @@ public class TlsServerTests : IAsyncLifetime
await ssl2.FlushAsync();
// Client 1 should receive MSG (may arrive across multiple TLS records)
var msg = await ReadUntilAsync(ssl1, "hello");
var msg = await SocketTestHelper.ReadUntilAsync(ssl1, "hello");
msg.ShouldContain("MSG test 1 5");
msg.ShouldContain("hello");
}
private static async Task<string> ReadUntilAsync(Stream stream, 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 stream.ReadAsync(buf, cts.Token);
if (n == 0) break;
sb.Append(Encoding.ASCII.GetString(buf, 0, n));
}
return sb.ToString();
}
private static async Task<SslStream> UpgradeToTlsAsync(TcpClient tcp)
{
var netStream = tcp.GetStream();
@@ -127,13 +114,6 @@ public class TlsServerTests : IAsyncLifetime
await ssl.AuthenticateAsClientAsync("localhost");
return ssl;
}
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;
}
}
public class TlsMixedModeTests : IAsyncLifetime
@@ -146,8 +126,8 @@ public class TlsMixedModeTests : IAsyncLifetime
public TlsMixedModeTests()
{
_port = GetFreePort();
(_certPath, _keyPath) = TlsHelperTests.GenerateTestCertFiles();
_port = TestPortAllocator.GetFreePort();
(_certPath, _keyPath) = TestCertHelper.GenerateTestCertFiles();
_server = new NatsServer(
new NatsOptions
{
@@ -215,11 +195,4 @@ public class TlsMixedModeTests : IAsyncLifetime
var pong = Encoding.ASCII.GetString(pongBuf, 0, read);
pong.ShouldContain("PONG");
}
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;
}
}

View File

@@ -1,7 +1,7 @@
using Shouldly;
using NATS.Server.WebSocket;
namespace NATS.Server.Tests.WebSocket;
namespace NATS.Server.Transport.Tests.WebSocket;
public class WebSocketOptionsTests
{

View File

@@ -1,7 +1,7 @@
using NATS.Server.Auth;
using NATS.Server.WebSocket;
namespace NATS.Server.Tests.WebSocket;
namespace NATS.Server.Transport.Tests.WebSocket;
public class WebSocketOptionsValidatorParityBatch2Tests
{

View File

@@ -3,7 +3,7 @@
using NATS.Server.WebSocket;
using Shouldly;
namespace NATS.Server.Tests.WebSocket;
namespace NATS.Server.Transport.Tests.WebSocket;
/// <summary>
/// Tests for <see cref="WebSocketTlsConfig"/> — WebSocket-specific TLS configuration

View File

@@ -6,7 +6,7 @@
using System.Text;
using NATS.Server.WebSocket;
namespace NATS.Server.Tests.WebSocket;
namespace NATS.Server.Transport.Tests.WebSocket;
public class WsCompressionNegotiationTests
{

View File

@@ -1,7 +1,7 @@
using NATS.Server.WebSocket;
using Shouldly;
namespace NATS.Server.Tests.WebSocket;
namespace NATS.Server.Transport.Tests.WebSocket;
public class WsCompressionTests
{

View File

@@ -1,7 +1,7 @@
using System.Buffers.Binary;
using NATS.Server.WebSocket;
namespace NATS.Server.Tests.WebSocket;
namespace NATS.Server.Transport.Tests.WebSocket;
public class WsConnectionTests
{

View File

@@ -1,7 +1,7 @@
using NATS.Server.WebSocket;
using Shouldly;
namespace NATS.Server.Tests.WebSocket;
namespace NATS.Server.Transport.Tests.WebSocket;
public class WsConstantsTests
{

View File

@@ -2,7 +2,7 @@ using System.Buffers.Binary;
using NATS.Server.WebSocket;
using Shouldly;
namespace NATS.Server.Tests.WebSocket;
namespace NATS.Server.Transport.Tests.WebSocket;
public class WsFrameReadTests
{

View File

@@ -2,7 +2,7 @@ using System.Buffers.Binary;
using NATS.Server.WebSocket;
using Shouldly;
namespace NATS.Server.Tests.WebSocket;
namespace NATS.Server.Transport.Tests.WebSocket;
public class WsFrameWriterTests
{

View File

@@ -8,7 +8,7 @@ using System.Buffers.Binary;
using System.Text;
using NATS.Server.WebSocket;
namespace NATS.Server.Tests.WebSocket;
namespace NATS.Server.Transport.Tests.WebSocket;
/// <summary>
/// Parity tests ported from Go server/websocket_test.go exercising WebSocket

View File

@@ -5,7 +5,7 @@ using System.Security.Cryptography;
using System.Text;
using NATS.Server.WebSocket;
namespace NATS.Server.Tests.WebSocket;
namespace NATS.Server.Transport.Tests.WebSocket;
public class WsIntegrationTests : IAsyncLifetime
{
@@ -67,8 +67,12 @@ public class WsIntegrationTests : IAsyncLifetime
using var sub = await ConnectWsClient();
using var pub = await ConnectWsClient();
await SendWsText(sub, "CONNECT {}\r\nSUB test.ws 1\r\n");
await Task.Delay(200);
await SendWsText(sub, "CONNECT {}\r\nSUB test.ws 1\r\nPING\r\n");
// Wait for PONG to confirm subscription is registered
using var subCts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
var pong = await ReadWsFrameAsync(sub, subCts.Token);
Encoding.ASCII.GetString(pong).ShouldContain("PONG");
await SendWsText(pub, "CONNECT {}\r\nPUB test.ws 5\r\nHello\r\n");

View File

@@ -6,7 +6,7 @@
using System.Text;
using NATS.Server.WebSocket;
namespace NATS.Server.Tests.WebSocket;
namespace NATS.Server.Transport.Tests.WebSocket;
public class WsJwtAuthTests
{

View File

@@ -1,7 +1,7 @@
using NATS.Server.WebSocket;
using Shouldly;
namespace NATS.Server.Tests.WebSocket;
namespace NATS.Server.Transport.Tests.WebSocket;
public class WsOriginCheckerTests
{

View File

@@ -1,7 +1,7 @@
using System.Text;
using NATS.Server.WebSocket;
namespace NATS.Server.Tests.WebSocket;
namespace NATS.Server.Transport.Tests.WebSocket;
public class WsUpgradeHelperParityBatch1Tests
{

View File

@@ -1,7 +1,7 @@
using System.Text;
using NATS.Server.WebSocket;
namespace NATS.Server.Tests.WebSocket;
namespace NATS.Server.Transport.Tests.WebSocket;
public class WsUpgradeTests
{