using Microsoft.Extensions.Logging.Abstractions; using NATS.Server.Configuration; namespace NATS.Server.Gateways.Tests.Gateways; public class GatewayConnectionDirectionParityBatch2Tests { [Fact] public async Task Gateway_manager_tracks_inbound_and_outbound_connection_sets() { var a = await StartServerAsync(MakeGatewayOptions("GW-A")); var b = await StartServerAsync(MakeGatewayOptions("GW-B", a.Server.GatewayListen)); try { await WaitForCondition(() => a.Server.NumInboundGateways() == 1 && b.Server.NumOutboundGateways() == 1, 10000); a.Server.NumInboundGateways().ShouldBe(1); a.Server.NumOutboundGateways().ShouldBe(0); b.Server.NumOutboundGateways().ShouldBe(1); b.Server.NumInboundGateways().ShouldBe(0); var aManager = a.Server.GatewayManager; var bManager = b.Server.GatewayManager; aManager.ShouldNotBeNull(); bManager.ShouldNotBeNull(); aManager!.HasInbound(b.Server.ServerId).ShouldBeTrue(); bManager!.HasInbound(a.Server.ServerId).ShouldBeFalse(); bManager.GetOutboundGatewayConnection(a.Server.ServerId).ShouldNotBeNull(); bManager.GetOutboundGatewayConnection("does-not-exist").ShouldBeNull(); aManager.GetInboundGatewayConnections().Count.ShouldBe(1); aManager.GetOutboundGatewayConnections().Count.ShouldBe(0); bManager.GetOutboundGatewayConnections().Count.ShouldBe(1); bManager.GetInboundGatewayConnections().Count.ShouldBe(0); } finally { await DisposeServers(a, b); } } private static NatsOptions MakeGatewayOptions(string gatewayName, string? remote = null) { return new NatsOptions { Host = "127.0.0.1", Port = 0, Gateway = new GatewayOptions { Name = gatewayName, Host = "127.0.0.1", Port = 0, Remotes = remote is null ? [] : [remote], }, }; } private static async Task<(NatsServer Server, CancellationTokenSource Cts)> StartServerAsync(NatsOptions opts) { var server = new NatsServer(opts, NullLoggerFactory.Instance); var cts = new CancellationTokenSource(); _ = server.StartAsync(cts.Token); await server.WaitForReadyAsync(); return (server, cts); } private static async Task DisposeServers(params (NatsServer Server, CancellationTokenSource Cts)[] servers) { foreach (var (server, cts) in servers) { await cts.CancelAsync(); server.Dispose(); cts.Dispose(); } } private static async Task WaitForCondition(Func predicate, int timeoutMs) { using var cts = new CancellationTokenSource(timeoutMs); while (!cts.IsCancellationRequested) { if (predicate()) return; await Task.Yield(); } throw new TimeoutException("Condition not met."); } }