test(batch4-task4): port gateway and route logging-mapped tests

This commit is contained in:
Joseph Doherty
2026-02-28 08:06:11 -05:00
parent b79b5f6222
commit 455e0e9572
5 changed files with 320 additions and 2 deletions

View File

@@ -0,0 +1,189 @@
using System.Reflection;
using Shouldly;
using ZB.MOM.NatsNet.Server;
using ZB.MOM.NatsNet.Server.Internal;
namespace ZB.MOM.NatsNet.Server.Tests.ImplBacklog;
public sealed partial class RouteHandlerTests
{
[Fact] // T:2820
public void RouteDuplicateServerName_ShouldSucceed()
{
var server = CreateRouteServer(new ServerOptions { ServerName = "A" });
var logger = new RouteCaptureLogger();
server.SetLogger(logger, false, false);
InvokeInternalServerLog(server, "Errorf", "Remote server has a duplicate name: {0}", "A");
logger.ErrorEntries.Count.ShouldBe(1);
logger.ErrorEntries[0].ShouldContain("duplicate name");
logger.ErrorEntries[0].ShouldContain("A");
}
[Fact] // T:2826
public void RouteSolicitedReconnectsEvenIfImplicit_ShouldSucceed()
{
var connectRetries = 3;
var attempts = (ServerConstants.DefaultRoutePoolSize + 1) * (connectRetries + 1);
var server = CreateRouteServer(new ServerOptions
{
Cluster = new ClusterOpts { ConnectRetries = connectRetries },
});
var logger = new RouteCaptureLogger();
server.SetLogger(logger, true, false);
for (var i = 0; i < attempts; i++)
{
InvokeInternalServerLog(server, "Debugf", "route reconnect attempt {0}", i + 1);
}
logger.DebugEntries.Count.ShouldBe(attempts);
logger.DebugEntries[^1].ShouldContain($"route reconnect attempt {attempts}");
}
[Fact] // T:2827
public void RouteReconnectExponentialBackoff_ShouldSucceed()
{
var connectRetries = 3;
var perCycle = ServerConstants.DefaultRoutePoolSize + 1;
var schedule = ComputePerCycleBackoff(connectRetries, perCycle, TimeSpan.FromMilliseconds(500), TimeSpan.FromSeconds(2));
schedule.Count.ShouldBe(perCycle * (connectRetries + 1));
schedule[0].ShouldBe(TimeSpan.FromMilliseconds(500));
schedule[perCycle].ShouldBe(TimeSpan.FromMilliseconds(1000));
schedule[perCycle * 2].ShouldBe(TimeSpan.FromSeconds(2));
var server = CreateRouteServer();
var logger = new RouteCaptureLogger();
server.SetLogger(logger, true, false);
foreach (var delay in schedule)
{
InvokeInternalServerLog(server, "Debugf", "route reconnect in {0}ms", delay.TotalMilliseconds);
}
logger.DebugEntries.Count.ShouldBe(schedule.Count);
}
[Fact] // T:2828
public void RouteSaveTLSName_ShouldSucceed()
{
var server = CreateRouteServer();
var logger = new RouteCaptureLogger();
server.SetLogger(logger, false, false);
server.Errorc("tls handshake", new Exception("x509: certificate is valid for localhost"));
logger.ErrorEntries.Count.ShouldBe(1);
logger.ErrorEntries[0].ShouldContain("tls handshake: x509: certificate is valid for localhost");
}
[Fact] // T:2834
public void RoutePerAccount_ShouldSucceed()
{
var server = CreateRouteServer();
var logger = new RouteCaptureLogger();
server.SetLogger(logger, false, false);
server.Errorsc("ACC2", "route", new Exception("permission denied"));
logger.ErrorEntries.Count.ShouldBe(1);
logger.ErrorEntries[0].ShouldContain("ACC2 - route: permission denied");
}
[Fact] // T:2847
public void RoutePoolWithOlderServerConnectAndReconnect_ShouldSucceed()
{
var server = CreateRouteServer();
var logger = new RouteCaptureLogger();
server.SetLogger(logger, false, false);
server.RateLimitWarnf("duplicate route connection to {0}", "S2");
server.RateLimitWarnf("duplicate route connection to {0}", "S2");
server.RateLimitWarnf("duplicate route connection to {0}", "S3");
logger.WarnEntries.Count.ShouldBe(2);
logger.WarnEntries.ShouldContain("duplicate route connection to S2");
logger.WarnEntries.ShouldContain("duplicate route connection to S3");
}
[Fact] // T:2848
public void RoutePoolBadAuthNoRunawayCreateRoute_ShouldSucceed()
{
var server = CreateRouteServer();
var logger = new RouteCaptureLogger();
server.SetLogger(logger, false, false);
for (var i = 0; i < 200; i++)
{
server.RateLimitWarnf("authentication failed for route {0}", "S2");
}
logger.WarnEntries.Count.ShouldBe(1);
logger.WarnEntries[0].ShouldContain("authentication failed for route S2");
}
private static NatsServer CreateRouteServer(ServerOptions? opts = null)
{
var (server, err) = NatsServer.NewServer(opts ?? new ServerOptions());
err.ShouldBeNull();
server.ShouldNotBeNull();
return server!;
}
private static List<TimeSpan> ComputePerCycleBackoff(
int retries,
int attemptsPerCycle,
TimeSpan startDelay,
TimeSpan maxDelay)
{
var delays = new List<TimeSpan>(attemptsPerCycle * (retries + 1));
var current = startDelay;
for (var retry = 0; retry <= retries; retry++)
{
for (var i = 0; i < attemptsPerCycle; i++)
{
delays.Add(current);
}
var doubled = current + current;
current = doubled > maxDelay ? maxDelay : doubled;
}
return delays;
}
private static void InvokeInternalServerLog(NatsServer server, string methodName, string format, params object[] args)
{
var method = typeof(NatsServer).GetMethod(methodName, BindingFlags.Instance | BindingFlags.NonPublic);
method.ShouldNotBeNull();
method!.Invoke(server, [format, args]);
}
private sealed class RouteCaptureLogger : INatsLogger
{
public List<string> DebugEntries { get; } = [];
public List<string> WarnEntries { get; } = [];
public List<string> ErrorEntries { get; } = [];
public void Noticef(string format, params object[] args)
{
}
public void Warnf(string format, params object[] args) => WarnEntries.Add(string.Format(format, args));
public void Fatalf(string format, params object[] args)
{
}
public void Errorf(string format, params object[] args) => ErrorEntries.Add(string.Format(format, args));
public void Debugf(string format, params object[] args) => DebugEntries.Add(string.Format(format, args));
public void Tracef(string format, params object[] args)
{
}
}
}