// Copyright 2012-2026 The NATS Authors // Licensed under the Apache License, Version 2.0 using System.Reflection; using Shouldly; using ZB.MOM.NatsNet.Server; using ZB.MOM.NatsNet.Server.Internal; namespace ZB.MOM.NatsNet.Server.Tests.Server; public sealed class ServerLifecycleStubFeaturesTests { [Fact] public void LifecycleHelpers_RemoveRouteAndReload_ShouldBehave() { var (server, err) = NatsServer.NewServer(new ServerOptions()); err.ShouldBeNull(); server.ShouldNotBeNull(); var route = new ClientConnection(ClientKind.Router) { Cid = 42 }; var routes = new Dictionary> { ["pool"] = [route] }; var clients = new Dictionary { [route.Cid] = route }; SetField(server!, "_routes", routes); SetField(server!, "_clients", clients); server.ForEachRoute(_ => { }); InvokePrivate(server!, "RemoveRoute", route); ((Dictionary>)GetField(server!, "_routes")).Count.ShouldBe(0); ((Dictionary)GetField(server!, "_clients")).Count.ShouldBe(0); var nonce = new byte[16]; InvokePrivate(server!, "GenerateNonce", nonce); nonce.Any(b => b != 0).ShouldBeTrue(); var before = (DateTime)GetField(server!, "_configTime"); server.Reload(); var after = (DateTime)GetField(server!, "_configTime"); after.ShouldBeGreaterThanOrEqualTo(before); } [Fact] public void NumRemotesInternal_WhenRoutesRegistered_ReturnsRouteBucketCount() { var (server, err) = NatsServer.NewServer(new ServerOptions()); err.ShouldBeNull(); server.ShouldNotBeNull(); SetField( server!, "_routes", new Dictionary> { ["a"] = [], ["b"] = [], ["c"] = [], }); var numRemotesInternal = server.GetType() .GetMethod("NumRemotesInternal", BindingFlags.Instance | BindingFlags.NonPublic); numRemotesInternal.ShouldNotBeNull(); var count = (int)numRemotesInternal!.Invoke(server, null)!; count.ShouldBe(3); server.NumRemotes().ShouldBe(3); } [Fact] public void ReadyForConnectionsInternal_WhenRouteListenerErrorPresent_ReturnsDetailedFailure() { var opts = new ServerOptions { DontListen = true, Cluster = new ClusterOpts { Port = 6222 }, LeafNode = new LeafNodeOpts { Port = 0 }, Websocket = new WebsocketOpts { Port = 0 }, Mqtt = new MqttOpts { Port = 0 }, }; var (server, err) = NatsServer.NewServer(opts); err.ShouldBeNull(); server.ShouldNotBeNull(); SetField(server!, "_routeListenerErr", new InvalidOperationException("route listener failed")); var readyForConnectionsInternal = server.GetType() .GetMethod("ReadyForConnectionsInternal", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); readyForConnectionsInternal.ShouldNotBeNull(); var readyError = readyForConnectionsInternal!.Invoke(server, [TimeSpan.FromMilliseconds(40)]) as Exception; readyError.ShouldNotBeNull(); readyError!.Message.ShouldContain("route("); readyError.Message.ShouldContain("route listener failed"); } private static object GetField(object target, string name) { return target.GetType() .GetField(name, BindingFlags.Instance | BindingFlags.NonPublic)! .GetValue(target)!; } private static void SetField(object target, string name, object value) { target.GetType() .GetField(name, BindingFlags.Instance | BindingFlags.NonPublic)! .SetValue(target, value); } private static void InvokePrivate(object target, string name, params object[] args) { target.GetType() .GetMethod(name, BindingFlags.Instance | BindingFlags.NonPublic)! .Invoke(target, args); } }