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.Core.Tests; public class NoRespondersTests : IAsyncLifetime { private readonly NatsServer _server; private readonly int _port; private readonly CancellationTokenSource _cts = new(); public NoRespondersTests() { _port = TestPortAllocator.GetFreePort(); _server = new NatsServer(new NatsOptions { Port = _port }, NullLoggerFactory.Instance); } public async Task InitializeAsync() { _ = _server.StartAsync(_cts.Token); await _server.WaitForReadyAsync(); } public async Task DisposeAsync() { await _cts.CancelAsync(); _server.Dispose(); } private async Task ConnectClientAsync() { var sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); await sock.ConnectAsync(IPAddress.Loopback, _port); return sock; } [Fact] public async Task NoResponders_without_headers_closes_connection() { using var client = await ConnectClientAsync(); // Read INFO await SocketTestHelper.ReadUntilAsync(client, "\r\n"); // Send CONNECT with no_responders:true but headers:false await client.SendAsync(Encoding.ASCII.GetBytes( "CONNECT {\"no_responders\":true,\"headers\":false}\r\n")); // Should receive -ERR and connection should close var response = await SocketTestHelper.ReadUntilAsync(client, "-ERR"); response.ShouldContain("-ERR"); response.ShouldContain("No Responders Requires Headers Support"); } [Fact] public async Task NoResponders_with_headers_accepted() { using var client = await ConnectClientAsync(); // Read INFO await SocketTestHelper.ReadUntilAsync(client, "\r\n"); // Send CONNECT with both no_responders and headers true, then PING await client.SendAsync(Encoding.ASCII.GetBytes( "CONNECT {\"no_responders\":true,\"headers\":true}\r\nPING\r\n")); // Should receive PONG (connection stays alive) var response = await SocketTestHelper.ReadUntilAsync(client, "PONG\r\n"); response.ShouldContain("PONG\r\n"); } [Fact] public async Task NoResponders_sends_503_when_no_subscribers() { using var client = await ConnectClientAsync(); // Read INFO await SocketTestHelper.ReadUntilAsync(client, "\r\n"); // CONNECT with no_responders and headers enabled // SUB to the reply inbox so we can receive the 503 // PUB to a subject with no subscribers, using a reply-to subject await client.SendAsync(Encoding.ASCII.GetBytes( "CONNECT {\"no_responders\":true,\"headers\":true}\r\n" + "SUB _INBOX.reply 1\r\n" + "PUB no.subscribers _INBOX.reply 5\r\nHello\r\n")); // Should receive HMSG with 503 status on the reply subject var response = await SocketTestHelper.ReadUntilAsync(client, "HMSG"); response.ShouldContain("HMSG _INBOX.reply 1"); response.ShouldContain("503"); } }