using System.Net; using System.Net.Sockets; using System.Text; using Microsoft.Extensions.Logging.Abstractions; using NATS.Server; using NATS.Server.TestUtilities; namespace NATS.Server.Clustering.Tests.Routes; public class RouteInfoBroadcastParityBatch4Tests { [Fact] public async Task UpdateServerINFOAndSendINFOToClients_broadcasts_INFO_to_connected_clients() { var port = TestPortAllocator.GetFreePort(); using var server = new NatsServer(new NatsOptions { Host = "127.0.0.1", Port = port }, NullLoggerFactory.Instance); using var cts = new CancellationTokenSource(); _ = server.StartAsync(cts.Token); await server.WaitForReadyAsync(); using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); await socket.ConnectAsync(IPAddress.Loopback, port); _ = await ReadLineAsync(socket, CancellationToken.None); // initial INFO await socket.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\nPING\r\n"), SocketFlags.None); _ = await ReadUntilContainsAsync(socket, "PONG", CancellationToken.None); server.UpdateServerINFOAndSendINFOToClients(); var info = await ReadLineAsync(socket, CancellationToken.None); info.ShouldStartWith("INFO "); await server.ShutdownAsync(); } private static async Task ReadUntilContainsAsync(Socket socket, string token, CancellationToken ct) { var end = DateTime.UtcNow.AddSeconds(3); var builder = new StringBuilder(); while (DateTime.UtcNow < end) { var line = await ReadLineAsync(socket, ct); if (line.Length == 0) continue; builder.AppendLine(line); if (builder.ToString().Contains(token, StringComparison.Ordinal)) return builder.ToString(); } return builder.ToString(); } private static async Task ReadLineAsync(Socket socket, CancellationToken ct) { var buffer = new List(256); var one = new byte[1]; while (true) { var n = await socket.ReceiveAsync(one.AsMemory(0, 1), SocketFlags.None, ct); if (n == 0) break; if (one[0] == '\n') break; if (one[0] != '\r') buffer.Add(one[0]); } return Encoding.ASCII.GetString([.. buffer]); } }