using System.Text.Json; using NATS.Server.Events; namespace NATS.Server.Monitoring.Tests.Events; /// /// Tests for remote server and leaf node event DTOs and subject constants. /// Go reference: events.go — remote server lifecycle, leaf node advisory subjects. /// Gap 10.8: RemoteServerShutdown, RemoteServerUpdate, LeafNodeConnected events. /// public class RemoteServerEventTests { // --- RemoteServerShutdownEvent --- [Fact] public void RemoteServerShutdownEvent_HasCorrectEventType() { RemoteServerShutdownEvent.EventType.ShouldBe("io.nats.server.advisory.v1.remote_shutdown"); var ev = new RemoteServerShutdownEvent(); ev.Type.ShouldBe(RemoteServerShutdownEvent.EventType); } [Fact] public void RemoteServerShutdownEvent_GeneratesUniqueId() { var ev1 = new RemoteServerShutdownEvent(); var ev2 = new RemoteServerShutdownEvent(); ev1.Id.ShouldNotBeNullOrEmpty(); ev2.Id.ShouldNotBeNullOrEmpty(); ev1.Id.ShouldNotBe(ev2.Id); } // --- RemoteServerUpdateEvent --- [Fact] public void RemoteServerUpdateEvent_HasCorrectEventType() { RemoteServerUpdateEvent.EventType.ShouldBe("io.nats.server.advisory.v1.remote_update"); var ev = new RemoteServerUpdateEvent(); ev.Type.ShouldBe(RemoteServerUpdateEvent.EventType); } [Fact] public void RemoteServerUpdateEvent_AllFieldsSettable() { var server = new EventServerInfo { Id = "srv1", Name = "my-server" }; var ev = new RemoteServerUpdateEvent { Server = server, RemoteServerId = "remote-id-123", RemoteServerName = "remote-server", UpdateType = "routes_changed", }; ev.Server.ShouldBeSameAs(server); ev.RemoteServerId.ShouldBe("remote-id-123"); ev.RemoteServerName.ShouldBe("remote-server"); ev.UpdateType.ShouldBe("routes_changed"); } // --- LeafNodeConnectEvent --- [Fact] public void LeafNodeConnectEvent_HasCorrectEventType() { LeafNodeConnectEvent.EventType.ShouldBe("io.nats.server.advisory.v1.leafnode_connect"); var ev = new LeafNodeConnectEvent(); ev.Type.ShouldBe(LeafNodeConnectEvent.EventType); } [Fact] public void LeafNodeConnectEvent_AllFieldsSettable() { var server = new EventServerInfo { Id = "srv1", Name = "hub" }; var ev = new LeafNodeConnectEvent { Server = server, LeafNodeId = "leaf-id-abc", LeafNodeName = "leaf-node-1", RemoteUrl = "nats://10.0.0.1:7422", Account = "ACC", }; ev.Server.ShouldBeSameAs(server); ev.LeafNodeId.ShouldBe("leaf-id-abc"); ev.LeafNodeName.ShouldBe("leaf-node-1"); ev.RemoteUrl.ShouldBe("nats://10.0.0.1:7422"); ev.Account.ShouldBe("ACC"); } // --- LeafNodeDisconnectEvent --- [Fact] public void LeafNodeDisconnectEvent_HasCorrectEventType() { LeafNodeDisconnectEvent.EventType.ShouldBe("io.nats.server.advisory.v1.leafnode_disconnect"); var ev = new LeafNodeDisconnectEvent(); ev.Type.ShouldBe(LeafNodeDisconnectEvent.EventType); } [Fact] public void LeafNodeDisconnectEvent_AllFieldsSettable() { var server = new EventServerInfo { Id = "srv1", Name = "hub" }; var ev = new LeafNodeDisconnectEvent { Server = server, LeafNodeId = "leaf-id-xyz", Reason = "connection closed", }; ev.Server.ShouldBeSameAs(server); ev.LeafNodeId.ShouldBe("leaf-id-xyz"); ev.Reason.ShouldBe("connection closed"); } // --- EventSubjects --- [Fact] public void EventSubjects_RemoteShutdown_HasPlaceholder() { EventSubjects.RemoteServerShutdown.ShouldContain("{0}"); EventSubjects.RemoteServerUpdate.ShouldContain("{0}"); EventSubjects.LeafNodeConnected.ShouldContain("{0}"); EventSubjects.LeafNodeDisconnected.ShouldContain("{0}"); // Verify format strings produce expected subjects when formatted var serverId = "ABCDEF123456"; string.Format(EventSubjects.RemoteServerShutdown, serverId) .ShouldBe($"$SYS.SERVER.{serverId}.REMOTE.SHUTDOWN"); string.Format(EventSubjects.LeafNodeConnected, serverId) .ShouldBe($"$SYS.ACCOUNT.{serverId}.LEAFNODE.CONNECT"); } // --- JSON serialization --- [Fact] public void AllRemoteEvents_SerializeToJson() { var server = new EventServerInfo { Id = "srv-id", Name = "test-server" }; var shutdown = new RemoteServerShutdownEvent { Server = server, RemoteServerId = "r1", RemoteServerName = "remote", Reason = "graceful", }; var shutdownJson = JsonSerializer.Serialize(shutdown); var shutdownDoc = JsonDocument.Parse(shutdownJson).RootElement; shutdownDoc.GetProperty("type").GetString().ShouldBe(RemoteServerShutdownEvent.EventType); shutdownDoc.GetProperty("id").GetString().ShouldNotBeNullOrEmpty(); shutdownDoc.GetProperty("remote_server_id").GetString().ShouldBe("r1"); shutdownDoc.GetProperty("reason").GetString().ShouldBe("graceful"); var update = new RemoteServerUpdateEvent { Server = server, RemoteServerId = "r2", RemoteServerName = "remote2", UpdateType = "config_updated", }; var updateJson = JsonSerializer.Serialize(update); var updateDoc = JsonDocument.Parse(updateJson).RootElement; updateDoc.GetProperty("type").GetString().ShouldBe(RemoteServerUpdateEvent.EventType); updateDoc.GetProperty("update_type").GetString().ShouldBe("config_updated"); var connect = new LeafNodeConnectEvent { Server = server, LeafNodeId = "leaf1", LeafNodeName = "leaf-node", RemoteUrl = "nats://10.0.0.1:7422", Account = "ACC", }; var connectJson = JsonSerializer.Serialize(connect); var connectDoc = JsonDocument.Parse(connectJson).RootElement; connectDoc.GetProperty("type").GetString().ShouldBe(LeafNodeConnectEvent.EventType); connectDoc.GetProperty("leaf_node_id").GetString().ShouldBe("leaf1"); connectDoc.GetProperty("account").GetString().ShouldBe("ACC"); var disconnect = new LeafNodeDisconnectEvent { Server = server, LeafNodeId = "leaf1", Reason = "timeout", }; var disconnectJson = JsonSerializer.Serialize(disconnect); var disconnectDoc = JsonDocument.Parse(disconnectJson).RootElement; disconnectDoc.GetProperty("type").GetString().ShouldBe(LeafNodeDisconnectEvent.EventType); disconnectDoc.GetProperty("leaf_node_id").GetString().ShouldBe("leaf1"); disconnectDoc.GetProperty("reason").GetString().ShouldBe("timeout"); // Roundtrip: deserialize back and verify type field survives var shutdownRt = JsonSerializer.Deserialize(shutdownJson); shutdownRt.ShouldNotBeNull(); shutdownRt!.Type.ShouldBe(RemoteServerShutdownEvent.EventType); shutdownRt.RemoteServerId.ShouldBe("r1"); } }