using Microsoft.Extensions.Logging.Abstractions; using NATS.Server.Configuration; namespace NATS.Server.Tests; public class RouteHandshakeTests { [Fact] public async Task Two_servers_establish_route_connection() { await using var a = await TestServerFactory.CreateClusterEnabledAsync(); await using var b = await TestServerFactory.CreateClusterEnabledAsync(seed: a.ClusterListen); await a.WaitForReadyAsync(); await b.WaitForReadyAsync(); using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5)); while (!timeout.IsCancellationRequested && (a.Stats.Routes == 0 || b.Stats.Routes == 0)) { await Task.Delay(50, timeout.Token).ContinueWith(_ => { }, TaskScheduler.Default); } a.Stats.Routes.ShouldBeGreaterThan(0); b.Stats.Routes.ShouldBeGreaterThan(0); } } internal static class TestServerFactory { public static async Task CreateClusterEnabledAsync(string? seed = null) { var options = new NatsOptions { Host = "127.0.0.1", Port = 0, Cluster = new ClusterOptions { Name = Guid.NewGuid().ToString("N"), Host = "127.0.0.1", Port = 0, Routes = seed is null ? [] : [seed], }, }; var server = new NatsServer(options, NullLoggerFactory.Instance); var cts = new CancellationTokenSource(); _ = server.StartAsync(cts.Token); await server.WaitForReadyAsync(); return new ClusterTestServer(server, cts); } } internal sealed class ClusterTestServer(NatsServer server, CancellationTokenSource cts) : IAsyncDisposable { public ServerStats Stats => server.Stats; public string ClusterListen => server.ClusterListen!; public Task WaitForReadyAsync() => server.WaitForReadyAsync(); public async ValueTask DisposeAsync() { await cts.CancelAsync(); server.Dispose(); cts.Dispose(); } }