using System.Text; using System.Text.Json; using NATS.Server; using NATS.Server.Events; using Microsoft.Extensions.Logging.Abstractions; namespace NATS.Server.Tests; public class SystemRequestReplyTests { [Fact] public async Task Varz_request_reply_returns_server_info() { using var server = CreateTestServer(); _ = server.StartAsync(CancellationToken.None); await server.WaitForReadyAsync(); var received = new TaskCompletionSource(); var replySubject = $"_INBOX.test.{Guid.NewGuid():N}"; server.EventSystem!.SysSubscribe(replySubject, (sub, client, acc, subject, reply, hdr, msg) => { received.TrySetResult(msg.ToArray()); }); var reqSubject = string.Format(EventSubjects.ServerReq, server.ServerId, "VARZ"); server.SendInternalMsg(reqSubject, replySubject, null); var result = await received.Task.WaitAsync(TimeSpan.FromSeconds(5)); var json = Encoding.UTF8.GetString(result); json.ShouldContain("\"server_id\""); json.ShouldContain("\"version\""); json.ShouldContain("\"host\""); json.ShouldContain("\"port\""); await server.ShutdownAsync(); } [Fact] public async Task Healthz_request_reply_returns_ok() { using var server = CreateTestServer(); _ = server.StartAsync(CancellationToken.None); await server.WaitForReadyAsync(); var received = new TaskCompletionSource(); var replySubject = $"_INBOX.test.{Guid.NewGuid():N}"; server.EventSystem!.SysSubscribe(replySubject, (sub, client, acc, subject, reply, hdr, msg) => { received.TrySetResult(msg.ToArray()); }); var reqSubject = string.Format(EventSubjects.ServerReq, server.ServerId, "HEALTHZ"); server.SendInternalMsg(reqSubject, replySubject, null); var result = await received.Task.WaitAsync(TimeSpan.FromSeconds(5)); var json = Encoding.UTF8.GetString(result); json.ShouldContain("ok"); await server.ShutdownAsync(); } [Fact] public async Task Subsz_request_reply_returns_subscription_count() { using var server = CreateTestServer(); _ = server.StartAsync(CancellationToken.None); await server.WaitForReadyAsync(); var received = new TaskCompletionSource(); var replySubject = $"_INBOX.test.{Guid.NewGuid():N}"; server.EventSystem!.SysSubscribe(replySubject, (sub, client, acc, subject, reply, hdr, msg) => { received.TrySetResult(msg.ToArray()); }); var reqSubject = string.Format(EventSubjects.ServerReq, server.ServerId, "SUBSZ"); server.SendInternalMsg(reqSubject, replySubject, null); var result = await received.Task.WaitAsync(TimeSpan.FromSeconds(5)); var json = Encoding.UTF8.GetString(result); json.ShouldContain("\"num_subscriptions\""); await server.ShutdownAsync(); } [Fact] public async Task Idz_request_reply_returns_server_identity() { using var server = CreateTestServer(); _ = server.StartAsync(CancellationToken.None); await server.WaitForReadyAsync(); var received = new TaskCompletionSource(); var replySubject = $"_INBOX.test.{Guid.NewGuid():N}"; server.EventSystem!.SysSubscribe(replySubject, (sub, client, acc, subject, reply, hdr, msg) => { received.TrySetResult(msg.ToArray()); }); var reqSubject = string.Format(EventSubjects.ServerReq, server.ServerId, "IDZ"); server.SendInternalMsg(reqSubject, replySubject, null); var result = await received.Task.WaitAsync(TimeSpan.FromSeconds(5)); var json = Encoding.UTF8.GetString(result); json.ShouldContain("\"server_id\""); json.ShouldContain("\"server_name\""); await server.ShutdownAsync(); } [Fact] public async Task Ping_varz_responds_via_wildcard_subject() { using var server = CreateTestServer(); _ = server.StartAsync(CancellationToken.None); await server.WaitForReadyAsync(); var received = new TaskCompletionSource(); var replySubject = $"_INBOX.test.{Guid.NewGuid():N}"; server.EventSystem!.SysSubscribe(replySubject, (sub, client, acc, subject, reply, hdr, msg) => { received.TrySetResult(msg.ToArray()); }); var pingSubject = string.Format(EventSubjects.ServerPing, "VARZ"); server.SendInternalMsg(pingSubject, replySubject, null); var result = await received.Task.WaitAsync(TimeSpan.FromSeconds(5)); var json = Encoding.UTF8.GetString(result); json.ShouldContain("\"server_id\""); await server.ShutdownAsync(); } [Fact] public async Task Request_without_reply_is_ignored() { using var server = CreateTestServer(); _ = server.StartAsync(CancellationToken.None); await server.WaitForReadyAsync(); // Send a request with no reply subject -- should not crash var reqSubject = string.Format(EventSubjects.ServerReq, server.ServerId, "VARZ"); server.SendInternalMsg(reqSubject, null, null); // Give it a moment to process without error await Task.Delay(200); // Server should still be running server.IsShuttingDown.ShouldBeFalse(); await server.ShutdownAsync(); } private static NatsServer CreateTestServer() { var port = GetFreePort(); return new NatsServer(new NatsOptions { Port = port }, NullLoggerFactory.Instance); } private static int GetFreePort() { using var sock = new System.Net.Sockets.Socket( System.Net.Sockets.AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp); sock.Bind(new System.Net.IPEndPoint(System.Net.IPAddress.Loopback, 0)); return ((System.Net.IPEndPoint)sock.LocalEndPoint!).Port; } }