Files
natsdotnet/tests/NATS.E2E.Tests/Infrastructure/GatewayFixture.cs
Joseph Doherty 338f44b07b 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
2026-03-12 19:45:54 -04:00

112 lines
3.4 KiB
C#

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>;