using System.Text.Json; using NATS.Server; using NATS.Server.Events; using Microsoft.Extensions.Logging.Abstractions; using NATS.Server.TestUtilities; namespace NATS.Server.Monitoring.Tests; public class SystemEventsTests { [Fact] public async Task Server_publishes_connect_event_on_client_auth() { using var server = CreateTestServer(); _ = server.StartAsync(CancellationToken.None); await server.WaitForReadyAsync(); var received = new TaskCompletionSource(); server.EventSystem!.SysSubscribe("$SYS.ACCOUNT.*.CONNECT", (sub, client, acc, subject, reply, hdr, msg) => { received.TrySetResult(subject); }); // Connect a real client using var sock = new System.Net.Sockets.Socket( System.Net.Sockets.AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp); await sock.ConnectAsync(System.Net.IPAddress.Loopback, server.Port); // Read INFO var buf = new byte[4096]; await sock.ReceiveAsync(buf); // Send CONNECT var connect = System.Text.Encoding.ASCII.GetBytes("CONNECT {}\r\n"); await sock.SendAsync(connect); var result = await received.Task.WaitAsync(TimeSpan.FromSeconds(5)); result.ShouldStartWith("$SYS.ACCOUNT."); result.ShouldEndWith(".CONNECT"); await server.ShutdownAsync(); } [Fact] public async Task Server_publishes_disconnect_event_on_client_close() { using var server = CreateTestServer(); _ = server.StartAsync(CancellationToken.None); await server.WaitForReadyAsync(); var received = new TaskCompletionSource(); server.EventSystem!.SysSubscribe("$SYS.ACCOUNT.*.DISCONNECT", (sub, client, acc, subject, reply, hdr, msg) => { received.TrySetResult(subject); }); // Connect and then disconnect using var sock = new System.Net.Sockets.Socket( System.Net.Sockets.AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp); await sock.ConnectAsync(System.Net.IPAddress.Loopback, server.Port); var buf = new byte[4096]; await sock.ReceiveAsync(buf); await sock.SendAsync(System.Text.Encoding.ASCII.GetBytes("CONNECT {}\r\n")); await Task.Delay(100); sock.Shutdown(System.Net.Sockets.SocketShutdown.Both); var result = await received.Task.WaitAsync(TimeSpan.FromSeconds(5)); result.ShouldStartWith("$SYS.ACCOUNT."); result.ShouldEndWith(".DISCONNECT"); await server.ShutdownAsync(); } [Fact] public async Task Server_publishes_statsz_periodically() { using var server = CreateTestServer(); _ = server.StartAsync(CancellationToken.None); await server.WaitForReadyAsync(); var received = new TaskCompletionSource(); server.EventSystem!.SysSubscribe("$SYS.SERVER.*.STATSZ", (sub, client, acc, subject, reply, hdr, msg) => { received.TrySetResult(subject); }); // Trigger a manual stats publish (don't wait 10s) server.EventSystem!.PublishServerStats(); var result = await received.Task.WaitAsync(TimeSpan.FromSeconds(5)); result.ShouldContain(".STATSZ"); await server.ShutdownAsync(); } [Fact] public async Task Server_publishes_shutdown_event() { using var server = CreateTestServer(); _ = server.StartAsync(CancellationToken.None); await server.WaitForReadyAsync(); var received = new TaskCompletionSource(); server.EventSystem!.SysSubscribe("$SYS.SERVER.*.SHUTDOWN", (sub, client, acc, subject, reply, hdr, msg) => { received.TrySetResult(subject); }); await server.ShutdownAsync(); var result = await received.Task.WaitAsync(TimeSpan.FromSeconds(5)); result.ShouldContain(".SHUTDOWN"); } private static NatsServer CreateTestServer() { var port = TestPortAllocator.GetFreePort(); return new NatsServer(new NatsOptions { Port = port }, NullLoggerFactory.Instance); } }