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( $"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;