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); } public static async Task CreateWithGatewayAndLeafAsync() { var options = new NatsOptions { Host = "127.0.0.1", Port = 0, Gateway = new GatewayOptions { Name = "G1", Host = "127.0.0.1", Port = 0, }, LeafNode = new LeafNodeOptions { Host = "127.0.0.1", Port = 0, }, }; var server = new NatsServer(options, NullLoggerFactory.Instance); var cts = new CancellationTokenSource(); _ = server.StartAsync(cts.Token); await server.WaitForReadyAsync(); return new ClusterTestServer(server, cts); } public static async Task CreateJetStreamEnabledAsync() { var options = new NatsOptions { Host = "127.0.0.1", Port = 0, JetStream = new JetStreamOptions { StoreDir = Path.Combine(Path.GetTempPath(), $"nats-js-{Guid.NewGuid():N}"), MaxMemoryStore = 1024 * 1024, MaxFileStore = 10 * 1024 * 1024, }, }; 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(); } }