117 lines
4.1 KiB
C#
117 lines
4.1 KiB
C#
// 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<string, List<ClientConnection>> { ["pool"] = [route] };
|
|
var clients = new Dictionary<ulong, ClientConnection> { [route.Cid] = route };
|
|
|
|
SetField(server!, "_routes", routes);
|
|
SetField(server!, "_clients", clients);
|
|
|
|
server.ForEachRoute(_ => { });
|
|
|
|
InvokePrivate(server!, "RemoveRoute", route);
|
|
((Dictionary<string, List<ClientConnection>>)GetField(server!, "_routes")).Count.ShouldBe(0);
|
|
((Dictionary<ulong, ClientConnection>)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<string, List<ClientConnection>>
|
|
{
|
|
["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);
|
|
}
|
|
}
|