refactor: extract NATS.Server.Gateways.Tests project

Move 25 gateway-related test files from NATS.Server.Tests into a
dedicated NATS.Server.Gateways.Tests project. Update namespaces,
replace private ReadUntilAsync with SocketTestHelper from TestUtilities,
inline TestServerFactory usage, add InternalsVisibleTo, and register
the project in the solution file. All 261 tests pass.
This commit is contained in:
Joseph Doherty
2026-03-12 15:10:50 -04:00
parent a6be5e11ed
commit 9972b74bc3
29 changed files with 101 additions and 58 deletions

View File

@@ -8,6 +8,7 @@
<Project Path="tests/NATS.Server.Tests/NATS.Server.Tests.csproj" /> <Project Path="tests/NATS.Server.Tests/NATS.Server.Tests.csproj" />
<Project Path="tests/NATS.Server.Transport.Tests/NATS.Server.Transport.Tests.csproj" /> <Project Path="tests/NATS.Server.Transport.Tests/NATS.Server.Transport.Tests.csproj" />
<Project Path="tests/NATS.Server.Mqtt.Tests/NATS.Server.Mqtt.Tests.csproj" /> <Project Path="tests/NATS.Server.Mqtt.Tests/NATS.Server.Mqtt.Tests.csproj" />
<Project Path="tests/NATS.Server.Gateways.Tests/NATS.Server.Gateways.Tests.csproj" />
<Project Path="tests/NATS.E2E.Tests/NATS.E2E.Tests.csproj" /> <Project Path="tests/NATS.E2E.Tests/NATS.E2E.Tests.csproj" />
</Folder> </Folder>
</Solution> </Solution>

View File

@@ -3,6 +3,7 @@
<InternalsVisibleTo Include="NATS.Server.Tests" /> <InternalsVisibleTo Include="NATS.Server.Tests" />
<InternalsVisibleTo Include="NATS.Server.Transport.Tests" /> <InternalsVisibleTo Include="NATS.Server.Transport.Tests" />
<InternalsVisibleTo Include="NATS.Server.Mqtt.Tests" /> <InternalsVisibleTo Include="NATS.Server.Mqtt.Tests" />
<InternalsVisibleTo Include="NATS.Server.Gateways.Tests" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" /> <FrameworkReference Include="Microsoft.AspNetCore.App" />

View File

@@ -1,6 +1,6 @@
using NATS.Server.Gateways; using NATS.Server.Gateways;
namespace NATS.Server.Tests; namespace NATS.Server.Gateways.Tests;
public class GatewayAdvancedRemapRuntimeTests public class GatewayAdvancedRemapRuntimeTests
{ {

View File

@@ -1,6 +1,6 @@
using NATS.Server.Gateways; using NATS.Server.Gateways;
namespace NATS.Server.Tests; namespace NATS.Server.Gateways.Tests;
public class GatewayAdvancedSemanticsTests public class GatewayAdvancedSemanticsTests
{ {

View File

@@ -0,0 +1,45 @@
using Microsoft.Extensions.Logging.Abstractions;
using NATS.Server.Configuration;
namespace NATS.Server.Gateways.Tests;
public class GatewayLeafBootstrapTests
{
[Fact]
public async Task Server_bootstraps_gateway_and_leaf_managers_when_configured()
{
var options = new NatsOptions
{
Host = "127.0.0.1",
Port = 0,
Gateway = new GatewayOptions
{
Name = "G1",
Host = "127.0.0.1",
Port = 0,
},
LeafNode = new LeafNodeOptions
{
Host = "127.0.0.1",
Port = 0,
},
};
var server = new NatsServer(options, NullLoggerFactory.Instance);
var cts = new CancellationTokenSource();
_ = server.StartAsync(cts.Token);
await server.WaitForReadyAsync();
try
{
server.Stats.Gateways.ShouldBeGreaterThanOrEqualTo(0);
server.Stats.Leafs.ShouldBeGreaterThanOrEqualTo(0);
}
finally
{
await cts.CancelAsync();
server.Dispose();
cts.Dispose();
}
}
}

View File

@@ -3,8 +3,9 @@ using System.Net.Sockets;
using System.Text; using System.Text;
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
using NATS.Server.Configuration; using NATS.Server.Configuration;
using NATS.Server.TestUtilities;
namespace NATS.Server.Tests; namespace NATS.Server.Gateways.Tests;
public class GatewayProtocolTests public class GatewayProtocolTests
{ {
@@ -87,7 +88,7 @@ internal sealed class GatewayFixture : IAsyncDisposable
_ = await ReadLineAsync(sock); // INFO _ = await ReadLineAsync(sock); // INFO
await sock.SendAsync(Encoding.ASCII.GetBytes($"CONNECT {{}}\r\nSUB {subject} 1\r\nPING\r\n")); await sock.SendAsync(Encoding.ASCII.GetBytes($"CONNECT {{}}\r\nSUB {subject} 1\r\nPING\r\n"));
await ReadUntilAsync(sock, "PONG"); await SocketTestHelper.ReadUntilAsync(sock, "PONG");
} }
public async Task PublishLocalClusterAsync(string subject, string payload) public async Task PublishLocalClusterAsync(string subject, string payload)
@@ -100,11 +101,11 @@ internal sealed class GatewayFixture : IAsyncDisposable
_localPublisher = sock; _localPublisher = sock;
_ = await ReadLineAsync(sock); // INFO _ = await ReadLineAsync(sock); // INFO
await sock.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\nPING\r\n")); await sock.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\nPING\r\n"));
await ReadUntilAsync(sock, "PONG"); await SocketTestHelper.ReadUntilAsync(sock, "PONG");
} }
await sock.SendAsync(Encoding.ASCII.GetBytes($"PUB {subject} {payload.Length}\r\n{payload}\r\nPING\r\n")); await sock.SendAsync(Encoding.ASCII.GetBytes($"PUB {subject} {payload.Length}\r\n{payload}\r\nPING\r\n"));
await ReadUntilAsync(sock, "PONG"); await SocketTestHelper.ReadUntilAsync(sock, "PONG");
} }
public Task<string> ReadRemoteClusterMessageAsync() public Task<string> ReadRemoteClusterMessageAsync()
@@ -112,7 +113,7 @@ internal sealed class GatewayFixture : IAsyncDisposable
if (_remoteSubscriber == null) if (_remoteSubscriber == null)
throw new InvalidOperationException("Remote subscriber was not initialized."); throw new InvalidOperationException("Remote subscriber was not initialized.");
return ReadUntilAsync(_remoteSubscriber, "MSG "); return SocketTestHelper.ReadUntilAsync(_remoteSubscriber, "MSG ");
} }
public async ValueTask DisposeAsync() public async ValueTask DisposeAsync()
@@ -134,20 +135,4 @@ internal sealed class GatewayFixture : IAsyncDisposable
return Encoding.ASCII.GetString(buf, 0, n); return Encoding.ASCII.GetString(buf, 0, n);
} }
private static async Task<string> ReadUntilAsync(Socket sock, string expected)
{
var sb = new StringBuilder();
var buf = new byte[4096];
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
while (!sb.ToString().Contains(expected, StringComparison.Ordinal))
{
var n = await sock.ReceiveAsync(buf, SocketFlags.None, cts.Token);
if (n == 0)
break;
sb.Append(Encoding.ASCII.GetString(buf, 0, n));
}
return sb.ToString();
}
} }

View File

@@ -5,7 +5,7 @@ using System.Net.Sockets;
using NATS.Server.Gateways; using NATS.Server.Gateways;
using Shouldly; using Shouldly;
namespace NATS.Server.Tests.Gateways; namespace NATS.Server.Gateways.Tests.Gateways;
/// <summary> /// <summary>
/// Unit tests for account-specific subscription tracking on GatewayConnection. /// Unit tests for account-specific subscription tracking on GatewayConnection.

View File

@@ -3,7 +3,7 @@ using NATS.Client.Core;
using NATS.Server.Auth; using NATS.Server.Auth;
using NATS.Server.Configuration; using NATS.Server.Configuration;
namespace NATS.Server.Tests.Gateways; namespace NATS.Server.Gateways.Tests.Gateways;
public class GatewayAccountScopedDeliveryTests public class GatewayAccountScopedDeliveryTests
{ {

View File

@@ -2,7 +2,7 @@ using Microsoft.Extensions.Logging.Abstractions;
using NATS.Client.Core; using NATS.Client.Core;
using NATS.Server.Configuration; using NATS.Server.Configuration;
namespace NATS.Server.Tests.Gateways; namespace NATS.Server.Gateways.Tests.Gateways;
/// <summary> /// <summary>
/// Ports TestGatewayBasic and TestGatewayDoesntSendBackToItself from /// Ports TestGatewayBasic and TestGatewayDoesntSendBackToItself from

View File

@@ -2,7 +2,7 @@ using System.Text;
using NATS.Server.Gateways; using NATS.Server.Gateways;
using Shouldly; using Shouldly;
namespace NATS.Server.Tests.Gateways; namespace NATS.Server.Gateways.Tests.Gateways;
// Go reference: gateway.go:90-120 — gateway protocol constants and command formatting. // Go reference: gateway.go:90-120 — gateway protocol constants and command formatting.

View File

@@ -3,7 +3,7 @@ using NATS.Server.Configuration;
using NATS.Server.Gateways; using NATS.Server.Gateways;
using NATS.Server.Monitoring; using NATS.Server.Monitoring;
namespace NATS.Server.Tests.Gateways; namespace NATS.Server.Gateways.Tests.Gateways;
/// <summary> /// <summary>
/// Gateway configuration validation, options parsing, monitoring endpoint, /// Gateway configuration validation, options parsing, monitoring endpoint,

View File

@@ -1,7 +1,7 @@
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
using NATS.Server.Configuration; using NATS.Server.Configuration;
namespace NATS.Server.Tests.Gateways; namespace NATS.Server.Gateways.Tests.Gateways;
public class GatewayConnectionDirectionParityBatch2Tests public class GatewayConnectionDirectionParityBatch2Tests
{ {

View File

@@ -7,7 +7,7 @@ using NATS.Server.Configuration;
using NATS.Server.Gateways; using NATS.Server.Gateways;
using NATS.Server.Subscriptions; using NATS.Server.Subscriptions;
namespace NATS.Server.Tests.Gateways; namespace NATS.Server.Gateways.Tests.Gateways;
/// <summary> /// <summary>
/// Gateway connection establishment, handshake, lifecycle, and reconnection tests. /// Gateway connection establishment, handshake, lifecycle, and reconnection tests.

View File

@@ -8,7 +8,7 @@ using NATS.Server.Configuration;
using NATS.Server.Gateways; using NATS.Server.Gateways;
using NATS.Server.Subscriptions; using NATS.Server.Subscriptions;
namespace NATS.Server.Tests.Gateways; namespace NATS.Server.Gateways.Tests.Gateways;
/// <summary> /// <summary>
/// Gateway message forwarding, reply mapping, queue subscription delivery, /// Gateway message forwarding, reply mapping, queue subscription delivery,

View File

@@ -7,7 +7,7 @@ using NATS.Server.Configuration;
using NATS.Server.Gateways; using NATS.Server.Gateways;
using NATS.Server.Subscriptions; using NATS.Server.Subscriptions;
namespace NATS.Server.Tests.Gateways; namespace NATS.Server.Gateways.Tests.Gateways;
/// <summary> /// <summary>
/// Go-parity tests for gateway functionality, ported from /// Go-parity tests for gateway functionality, ported from

View File

@@ -4,7 +4,7 @@ using System.Text;
using NATS.Server.Gateways; using NATS.Server.Gateways;
using NATS.Server.Subscriptions; using NATS.Server.Subscriptions;
namespace NATS.Server.Tests.Gateways; namespace NATS.Server.Gateways.Tests.Gateways;
public class GatewayInterestIdempotencyTests public class GatewayInterestIdempotencyTests
{ {

View File

@@ -8,7 +8,7 @@ using NATS.Server.Configuration;
using NATS.Server.Gateways; using NATS.Server.Gateways;
using NATS.Server.Subscriptions; using NATS.Server.Subscriptions;
namespace NATS.Server.Tests.Gateways; namespace NATS.Server.Gateways.Tests.Gateways;
/// <summary> /// <summary>
/// Gateway interest-only mode, account interest, subject interest propagation, /// Gateway interest-only mode, account interest, subject interest propagation,

View File

@@ -1,7 +1,7 @@
using NATS.Server.Gateways; using NATS.Server.Gateways;
using NATS.Server.Subscriptions; using NATS.Server.Subscriptions;
namespace NATS.Server.Tests; namespace NATS.Server.Gateways.Tests;
public class GatewayInterestOnlyParityTests public class GatewayInterestOnlyParityTests
{ {

View File

@@ -1,7 +1,7 @@
// Go: gateway.go:100-150 (InterestMode enum), gateway.go:1500-1600 (switchToInterestOnlyMode) // Go: gateway.go:100-150 (InterestMode enum), gateway.go:1500-1600 (switchToInterestOnlyMode)
using NATS.Server.Gateways; using NATS.Server.Gateways;
namespace NATS.Server.Tests.Gateways; namespace NATS.Server.Gateways.Tests.Gateways;
/// <summary> /// <summary>
/// Unit tests for GatewayInterestTracker — the per-connection interest mode state machine. /// Unit tests for GatewayInterestTracker — the per-connection interest mode state machine.

View File

@@ -3,7 +3,7 @@ using NATS.Server.Configuration;
using NATS.Server.Gateways; using NATS.Server.Gateways;
using Shouldly; using Shouldly;
namespace NATS.Server.Tests.Gateways; namespace NATS.Server.Gateways.Tests.Gateways;
/// <summary> /// <summary>
/// Tests for GatewayReconnectPolicy and GatewayManager reconnection tracking. /// Tests for GatewayReconnectPolicy and GatewayManager reconnection tracking.

View File

@@ -3,7 +3,7 @@ using NATS.Server.Configuration;
using NATS.Server.Gateways; using NATS.Server.Gateways;
using Shouldly; using Shouldly;
namespace NATS.Server.Tests.Gateways; namespace NATS.Server.Gateways.Tests.Gateways;
/// <summary> /// <summary>
/// Tests for gateway connection registration and state tracking (Gap 11.7). /// Tests for gateway connection registration and state tracking (Gap 11.7).

View File

@@ -1,6 +1,6 @@
using NATS.Server.Configuration; using NATS.Server.Configuration;
namespace NATS.Server.Tests.Gateways; namespace NATS.Server.Gateways.Tests.Gateways;
public class GatewayRemoteConfigParityBatch3Tests public class GatewayRemoteConfigParityBatch3Tests
{ {

View File

@@ -1,7 +1,7 @@
using NATS.Server.Configuration; using NATS.Server.Configuration;
using NATS.Server.Gateways; using NATS.Server.Gateways;
namespace NATS.Server.Tests.Gateways; namespace NATS.Server.Gateways.Tests.Gateways;
public class GatewayReplyAndConfigParityBatch1Tests public class GatewayReplyAndConfigParityBatch1Tests
{ {

View File

@@ -1,7 +1,7 @@
using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Logging.Abstractions;
using NATS.Server.Configuration; using NATS.Server.Configuration;
namespace NATS.Server.Tests.Gateways; namespace NATS.Server.Gateways.Tests.Gateways;
public class GatewayServerAccessorParityBatch4Tests public class GatewayServerAccessorParityBatch4Tests
{ {

View File

@@ -3,7 +3,7 @@ using System.Net.Sockets;
using NATS.Server.Gateways; using NATS.Server.Gateways;
using Shouldly; using Shouldly;
namespace NATS.Server.Tests.Gateways; namespace NATS.Server.Gateways.Tests.Gateways;
/// <summary> /// <summary>
/// Tests for queue group subscription tracking on GatewayConnection. /// Tests for queue group subscription tracking on GatewayConnection.

View File

@@ -1,7 +1,7 @@
using NATS.Server.Gateways; using NATS.Server.Gateways;
using Shouldly; using Shouldly;
namespace NATS.Server.Tests.Gateways; namespace NATS.Server.Gateways.Tests.Gateways;
/// <summary> /// <summary>
/// Tests for the ReplyMapCache LRU cache with TTL expiration. /// Tests for the ReplyMapCache LRU cache with TTL expiration.

View File

@@ -1,6 +1,6 @@
using NATS.Server.Gateways; using NATS.Server.Gateways;
namespace NATS.Server.Tests.Gateways; namespace NATS.Server.Gateways.Tests.Gateways;
/// <summary> /// <summary>
/// Tests for the expanded ReplyMapper with hash support. /// Tests for the expanded ReplyMapper with hash support.

View File

@@ -0,0 +1,25 @@
<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" />
</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

@@ -1,14 +0,0 @@
namespace NATS.Server.Tests;
public class GatewayLeafBootstrapTests
{
[Fact]
public async Task Server_bootstraps_gateway_and_leaf_managers_when_configured()
{
await using var server = await TestServerFactory.CreateWithGatewayAndLeafAsync();
await server.WaitForReadyAsync();
server.Stats.Gateways.ShouldBeGreaterThanOrEqualTo(0);
server.Stats.Leafs.ShouldBeGreaterThanOrEqualTo(0);
}
}