using System.Net; using System.Net.Sockets; using System.Text; using Microsoft.Extensions.Logging.Abstractions; using NATS.Server; namespace NATS.Server.Tests; public class ServerTests : IAsyncDisposable { private readonly NatsServer _server; private readonly int _port; private readonly CancellationTokenSource _cts = new(); public ServerTests() { // Use random port _port = GetFreePort(); _server = new NatsServer(new NatsOptions { Port = _port }, NullLoggerFactory.Instance); } public async ValueTask DisposeAsync() { await _cts.CancelAsync(); _server.Dispose(); } private static int GetFreePort() { using var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); sock.Bind(new IPEndPoint(IPAddress.Loopback, 0)); return ((IPEndPoint)sock.LocalEndPoint!).Port; } private async Task ConnectClientAsync() { var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); await sock.ConnectAsync(IPAddress.Loopback, _port); return sock; } private static async Task ReadLineAsync(Socket sock, int bufSize = 4096) { var buf = new byte[bufSize]; var n = await sock.ReceiveAsync(buf, SocketFlags.None); return Encoding.ASCII.GetString(buf, 0, n); } [Fact] public async Task Server_accepts_connection_and_sends_INFO() { var serverTask = _server.StartAsync(_cts.Token); await Task.Delay(100); // let server start using var client = await ConnectClientAsync(); var response = await ReadLineAsync(client); response.ShouldStartWith("INFO "); await _cts.CancelAsync(); } [Fact] public async Task Server_basic_pubsub() { var serverTask = _server.StartAsync(_cts.Token); await Task.Delay(100); using var pub = await ConnectClientAsync(); using var sub = await ConnectClientAsync(); // Read INFO from both await ReadLineAsync(pub); await ReadLineAsync(sub); // CONNECT + SUB on subscriber await sub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\nSUB foo 1\r\n")); await Task.Delay(50); // CONNECT + PUB on publisher await pub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\nPUB foo 5\r\nHello\r\n")); await Task.Delay(100); // Read MSG from subscriber var buf = new byte[4096]; var n = await sub.ReceiveAsync(buf, SocketFlags.None); var msg = Encoding.ASCII.GetString(buf, 0, n); msg.ShouldContain("MSG foo 1 5\r\nHello\r\n"); await _cts.CancelAsync(); } [Fact] public async Task Server_wildcard_matching() { var serverTask = _server.StartAsync(_cts.Token); await Task.Delay(100); using var pub = await ConnectClientAsync(); using var sub = await ConnectClientAsync(); await ReadLineAsync(pub); await ReadLineAsync(sub); await sub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\nSUB foo.* 1\r\n")); await Task.Delay(50); await pub.SendAsync(Encoding.ASCII.GetBytes("CONNECT {}\r\nPUB foo.bar 5\r\nHello\r\n")); await Task.Delay(100); var buf = new byte[4096]; var n = await sub.ReceiveAsync(buf, SocketFlags.None); var msg = Encoding.ASCII.GetString(buf, 0, n); msg.ShouldContain("MSG foo.bar 1 5\r\n"); await _cts.CancelAsync(); } }