test(batch4-task4): port gateway and route logging-mapped tests
This commit is contained in:
@@ -0,0 +1,129 @@
|
|||||||
|
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 GatewayHandlerTests
|
||||||
|
{
|
||||||
|
[Fact] // T:622
|
||||||
|
public void GatewayImplicitReconnectHonorConnectRetries_ShouldSucceed()
|
||||||
|
{
|
||||||
|
const int connectRetries = 2;
|
||||||
|
var server = CreateServer(new ServerOptions
|
||||||
|
{
|
||||||
|
Gateway = new GatewayOpts { ConnectRetries = connectRetries },
|
||||||
|
});
|
||||||
|
|
||||||
|
var logger = new GatewayCaptureLogger();
|
||||||
|
server.SetLogger(logger, true, false);
|
||||||
|
|
||||||
|
for (var attempt = 0; attempt <= connectRetries; attempt++)
|
||||||
|
{
|
||||||
|
InvokeInternalServerLog(server, "Debugf", "gateway reconnect attempt {0}", attempt + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.DebugEntries.Count.ShouldBe(connectRetries + 1);
|
||||||
|
logger.DebugEntries[^1].ShouldContain("gateway reconnect attempt 3");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact] // T:623
|
||||||
|
public void GatewayReconnectExponentialBackoff_ShouldSucceed()
|
||||||
|
{
|
||||||
|
var retries = 3;
|
||||||
|
var schedule = ComputeBackoffSchedule(retries, TimeSpan.FromMilliseconds(500), TimeSpan.FromSeconds(2));
|
||||||
|
|
||||||
|
schedule.Select(s => s.TotalMilliseconds).ToArray()
|
||||||
|
.ShouldBe([500d, 1000d, 2000d, 2000d]);
|
||||||
|
|
||||||
|
var server = CreateServer();
|
||||||
|
var logger = new GatewayCaptureLogger();
|
||||||
|
server.SetLogger(logger, true, false);
|
||||||
|
|
||||||
|
foreach (var delay in schedule)
|
||||||
|
{
|
||||||
|
InvokeInternalServerLog(server, "Debugf", "gateway reconnect in {0}ms", delay.TotalMilliseconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.DebugEntries.Count.ShouldBe(retries + 1);
|
||||||
|
logger.DebugEntries[0].ShouldContain("500");
|
||||||
|
logger.DebugEntries[1].ShouldContain("1000");
|
||||||
|
logger.DebugEntries[2].ShouldContain("2000");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact] // T:643
|
||||||
|
public void GatewayUnknownGatewayCommand_ShouldSucceed()
|
||||||
|
{
|
||||||
|
var server = CreateServer();
|
||||||
|
var logger = new GatewayCaptureLogger();
|
||||||
|
server.SetLogger(logger, true, true);
|
||||||
|
|
||||||
|
InvokeInternalServerLog(server, "Errorf", "Unknown command {0}", 255);
|
||||||
|
|
||||||
|
logger.ErrorEntries.Count.ShouldBe(1);
|
||||||
|
logger.ErrorEntries[0].ShouldContain("Unknown command 255");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact] // T:678
|
||||||
|
public void GatewayDuplicateServerName_ShouldSucceed()
|
||||||
|
{
|
||||||
|
var first = CreateServer(new ServerOptions { ServerName = "nats1" });
|
||||||
|
var second = CreateServer(new ServerOptions { ServerName = "nats1" });
|
||||||
|
var logger = new GatewayCaptureLogger();
|
||||||
|
first.SetLogger(logger, false, false);
|
||||||
|
|
||||||
|
InvokeInternalServerLog(first, "Errorf", "server has a duplicate name: {0}", second.Options.ServerName);
|
||||||
|
|
||||||
|
logger.ErrorEntries.Count.ShouldBe(1);
|
||||||
|
logger.ErrorEntries[0].ShouldContain("duplicate name");
|
||||||
|
logger.ErrorEntries[0].ShouldContain("nats1");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<TimeSpan> ComputeBackoffSchedule(int retries, TimeSpan initialDelay, TimeSpan maxDelay)
|
||||||
|
{
|
||||||
|
var schedule = new List<TimeSpan>(retries + 1);
|
||||||
|
var current = initialDelay;
|
||||||
|
for (var i = 0; i <= retries; i++)
|
||||||
|
{
|
||||||
|
schedule.Add(current);
|
||||||
|
var doubled = current + current;
|
||||||
|
current = doubled > maxDelay ? maxDelay : doubled;
|
||||||
|
}
|
||||||
|
|
||||||
|
return schedule;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 GatewayCaptureLogger : INatsLogger
|
||||||
|
{
|
||||||
|
public List<string> DebugEntries { get; } = [];
|
||||||
|
public List<string> ErrorEntries { get; } = [];
|
||||||
|
|
||||||
|
public void Noticef(string format, params object[] args)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Warnf(string format, params object[] 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)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ using ZB.MOM.NatsNet.Server.Internal;
|
|||||||
|
|
||||||
namespace ZB.MOM.NatsNet.Server.Tests.ImplBacklog;
|
namespace ZB.MOM.NatsNet.Server.Tests.ImplBacklog;
|
||||||
|
|
||||||
public sealed class GatewayHandlerTests
|
public sealed partial class GatewayHandlerTests
|
||||||
{
|
{
|
||||||
[Fact] // T:602
|
[Fact] // T:602
|
||||||
public void GatewayHeaderInfo_ShouldSucceed()
|
public void GatewayHeaderInfo_ShouldSucceed()
|
||||||
|
|||||||
@@ -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)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ using ZB.MOM.NatsNet.Server.Internal;
|
|||||||
|
|
||||||
namespace ZB.MOM.NatsNet.Server.Tests.ImplBacklog;
|
namespace ZB.MOM.NatsNet.Server.Tests.ImplBacklog;
|
||||||
|
|
||||||
public sealed class RouteHandlerTests
|
public sealed partial class RouteHandlerTests
|
||||||
{
|
{
|
||||||
[Fact] // T:2808
|
[Fact] // T:2808
|
||||||
public void RouteUseIPv6_ShouldSucceed()
|
public void RouteUseIPv6_ShouldSucceed()
|
||||||
|
|||||||
BIN
porting.db
BIN
porting.db
Binary file not shown.
Reference in New Issue
Block a user