test: add E2E gateway tests (cross-gateway messaging, interest-only)
- GatewayFixture: polls /gatewayz monitoring endpoint until both servers report num_gateways >= 1, replacing Task.Delay with proper synchronization - GatewayTests: two tests covering cross-gateway delivery and interest-only no-delivery behaviour, using double PingAsync() instead of Task.Delay - LeafNodeTests: replace Task.Delay(500) with sub.PingAsync()+pub.PingAsync() to properly fence subscription propagation without timing dependencies - Fix GatewayManager.StartAsync to read remotes from RemoteGateways (config- parsed) in addition to the legacy Remotes list, enabling config-file-driven outbound gateway connections
This commit is contained in:
111
tests/NATS.E2E.Tests/Infrastructure/GatewayFixture.cs
Normal file
111
tests/NATS.E2E.Tests/Infrastructure/GatewayFixture.cs
Normal file
@@ -0,0 +1,111 @@
|
||||
using System.Net.Http.Json;
|
||||
using NATS.Client.Core;
|
||||
|
||||
namespace NATS.E2E.Tests.Infrastructure;
|
||||
|
||||
public sealed class GatewayFixture : IAsyncLifetime
|
||||
{
|
||||
private NatsServerProcess _serverA = null!;
|
||||
private NatsServerProcess _serverB = null!;
|
||||
|
||||
public int PortA => _serverA.Port;
|
||||
public int PortB => _serverB.Port;
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
var gwPortA = NatsServerProcess.AllocateFreePort();
|
||||
var gwPortB = NatsServerProcess.AllocateFreePort();
|
||||
|
||||
var configA = $$"""
|
||||
server_name: gw-a
|
||||
gateway {
|
||||
name: cluster-a
|
||||
listen: 127.0.0.1:{{gwPortA}}
|
||||
gateways: [
|
||||
{ name: cluster-b, url: nats://127.0.0.1:{{gwPortB}} }
|
||||
]
|
||||
}
|
||||
""";
|
||||
|
||||
var configB = $$"""
|
||||
server_name: gw-b
|
||||
gateway {
|
||||
name: cluster-b
|
||||
listen: 127.0.0.1:{{gwPortB}}
|
||||
gateways: [
|
||||
{ name: cluster-a, url: nats://127.0.0.1:{{gwPortA}} }
|
||||
]
|
||||
}
|
||||
""";
|
||||
|
||||
_serverA = NatsServerProcess.WithConfig(configA, enableMonitoring: true);
|
||||
_serverB = NatsServerProcess.WithConfig(configB, enableMonitoring: true);
|
||||
|
||||
await Task.WhenAll(_serverA.StartAsync(), _serverB.StartAsync());
|
||||
|
||||
// Poll until both gateways report a connected outbound gateway
|
||||
await WaitForGatewayConnectionAsync();
|
||||
}
|
||||
|
||||
private async Task WaitForGatewayConnectionAsync()
|
||||
{
|
||||
using var http = new HttpClient();
|
||||
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(30));
|
||||
using var timer = new PeriodicTimer(TimeSpan.FromMilliseconds(200));
|
||||
|
||||
var monitorPorts = new[] { _serverA.MonitorPort!.Value, _serverB.MonitorPort!.Value };
|
||||
|
||||
while (await timer.WaitForNextTickAsync(timeout.Token).ConfigureAwait(false))
|
||||
{
|
||||
var allConnected = true;
|
||||
foreach (var monitorPort in monitorPorts)
|
||||
{
|
||||
try
|
||||
{
|
||||
var gatewayz = await http.GetFromJsonAsync<Gatewayz>(
|
||||
$"http://127.0.0.1:{monitorPort}/gatewayz",
|
||||
timeout.Token);
|
||||
|
||||
if (gatewayz?.NumGateways < 1)
|
||||
{
|
||||
allConnected = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (HttpRequestException)
|
||||
{
|
||||
// monitor not yet ready — retry on next tick
|
||||
allConnected = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (allConnected)
|
||||
return;
|
||||
}
|
||||
|
||||
throw new TimeoutException("Gateways did not connect to each other within 30s.");
|
||||
}
|
||||
|
||||
public async Task DisposeAsync()
|
||||
{
|
||||
await Task.WhenAll(
|
||||
_serverA.DisposeAsync().AsTask(),
|
||||
_serverB.DisposeAsync().AsTask());
|
||||
}
|
||||
|
||||
public NatsConnection CreateClientA()
|
||||
=> new(new NatsOpts { Url = $"nats://127.0.0.1:{PortA}" });
|
||||
|
||||
public NatsConnection CreateClientB()
|
||||
=> new(new NatsOpts { Url = $"nats://127.0.0.1:{PortB}" });
|
||||
|
||||
private sealed class Gatewayz
|
||||
{
|
||||
[System.Text.Json.Serialization.JsonPropertyName("num_gateways")]
|
||||
public int NumGateways { get; init; }
|
||||
}
|
||||
}
|
||||
|
||||
[CollectionDefinition("E2E-Gateway")]
|
||||
public class GatewayCollection : ICollectionFixture<GatewayFixture>;
|
||||
Reference in New Issue
Block a user