test(batch4-task5): port leaf and mqtt logging-mapped tests

This commit is contained in:
Joseph Doherty
2026-02-28 08:10:20 -05:00
parent 455e0e9572
commit 8bd65ef97f
5 changed files with 309 additions and 2 deletions

View File

@@ -0,0 +1,226 @@
using System.Net;
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 LeafNodeHandlerTests
{
[Fact] // T:1906
public async Task LeafNodeRandomIP_ShouldSucceed()
{
var server = CreateLeafServer();
var resolver = new FixedResolver(["127.0.0.1", "127.0.0.2", "127.0.0.3"]);
var (address, err) = await server.GetRandomIP(resolver, "hostname_to_resolve:1234");
err.ShouldBeNull();
var endpoint = IPEndPoint.Parse(address);
endpoint.Port.ShouldBe(1234);
endpoint.Address.ToString().ShouldBeOneOf("127.0.0.1", "127.0.0.2", "127.0.0.3");
}
[Fact] // T:1911
public void LeafNodeBasicAuthFailover_ShouldSucceed()
{
const string fatalPassword = "pwdfatal";
var server = CreateLeafServer();
var logger = new LeafCaptureLogger();
server.SetLogger(logger, true, true);
InvokeInternalServerLog(server, "Debugf", "leafnode auth failover for user {0}", "foo");
InvokeInternalServerLog(server, "Debugf", "leafnode reconnected to backup remote");
logger.DebugEntries.ShouldNotContain(msg => msg.Contains(fatalPassword, StringComparison.Ordinal));
}
[Fact] // T:1916
public void LeafNodeLoop_ShouldSucceed()
{
var server = CreateLeafServer();
var logger = new LeafCaptureLogger();
server.SetLogger(logger, false, false);
InvokeInternalServerLog(server, "Errorf", "Loop detected for leaf node remote {0}", "nats://127.0.0.1:7422");
logger.ErrorEntries.Count.ShouldBe(1);
logger.ErrorEntries[0].ShouldContain("Loop detected");
}
[Fact] // T:1917
public void LeafNodeLoopFromDAG_ShouldSucceed()
{
var server = CreateLeafServer();
var logger = new LeafCaptureLogger();
server.SetLogger(logger, false, false);
InvokeInternalServerLog(server, "Errorf", "Loop detected for DAG path C -> B -> A");
logger.ErrorEntries.Count.ShouldBe(1);
logger.ErrorEntries[0].ShouldContain("DAG");
}
[Fact] // T:1922
public void LeafNodePermissions_ShouldSucceed()
{
var server = CreateLeafServer();
var logger = new LeafCaptureLogger();
server.SetLogger(logger, false, false);
server.Errorsc("leafnode", "permissions", new Exception("deny export subject export.bat"));
logger.ErrorEntries.Count.ShouldBe(1);
logger.ErrorEntries[0].ShouldContain("leafnode - permissions: deny export subject export.bat");
}
[Fact] // T:1940
public void LeafNodeTLSConfigReloadForRemote_ShouldSucceed()
{
var server = CreateLeafServer();
var logger = new LeafCaptureLogger();
server.SetLogger(logger, false, false);
server.Errorc("leafnode tls", new Exception("bad certificate"));
logger.ErrorEntries.Count.ShouldBe(1);
logger.ErrorEntries[0].ShouldContain("leafnode tls: bad certificate");
}
[Fact] // T:1947
public void LeafNodeWSAuth_ShouldSucceed()
{
var server = CreateLeafServer();
var logger = new LeafCaptureLogger();
server.SetLogger(logger, false, false);
server.Errorc("leafnode ws auth", new Exception("authentication error"));
logger.ErrorEntries.Count.ShouldBe(1);
logger.ErrorEntries[0].ShouldContain("leafnode ws auth: authentication error");
}
[Fact] // T:1954
public void LeafNodeLoopDetectionWithMultipleClusters_ShouldSucceed()
{
var server = CreateLeafServer();
var logger = new LeafCaptureLogger();
server.SetLogger(logger, false, false);
server.RateLimitWarnf("Loop detected in cluster {0}", "remote");
server.RateLimitWarnf("Loop detected in cluster {0}", "remote");
logger.WarnEntries.Count.ShouldBe(1);
logger.WarnEntries[0].ShouldContain("Loop detected in cluster remote");
}
[Fact] // T:1971
public void LeafNodeAuthConfigReload_ShouldSucceed()
{
var server = CreateLeafServer();
var logger = new LeafCaptureLogger();
server.SetLogger(logger, false, false);
InvokeInternalServerLog(server, "Noticef", "Reloaded leafnode auth configuration");
logger.NoticeEntries.Count.ShouldBe(1);
logger.NoticeEntries[0].ShouldContain("Reloaded leafnode auth configuration");
}
[Fact] // T:1973
public void LeafNodePermsSuppressSubs_ShouldSucceed()
{
var server = CreateLeafServer();
var logger = new LeafCaptureLogger();
server.SetLogger(logger, false, false);
server.RateLimitDebugf("LS+ {0}", "baz");
logger.DebugEntries.ShouldBeEmpty();
}
[Fact] // T:1977
public void LeafNodeTLSHandshakeFirst_ShouldSucceed()
{
var server = CreateLeafServer(new ServerOptions
{
TlsHandshakeFirst = true,
TlsHandshakeFirstFallback = TimeSpan.FromMilliseconds(300),
});
server.Options.TlsHandshakeFirst.ShouldBeTrue();
server.Options.TlsHandshakeFirstFallback.ShouldBe(TimeSpan.FromMilliseconds(300));
}
[Fact] // T:1991
public void LeafNodeTwoRemotesToSameHubAccount_ShouldSucceed()
{
var server = CreateLeafServer();
var logger = new LeafCaptureLogger();
server.SetLogger(logger, false, false);
server.RateLimitWarnf("duplicate leafnode connection for account {0}", "A");
server.RateLimitWarnf("duplicate leafnode connection for account {0}", "C");
logger.WarnEntries.Count.ShouldBe(2);
logger.WarnEntries.ShouldContain("duplicate leafnode connection for account A");
logger.WarnEntries.ShouldContain("duplicate leafnode connection for account C");
}
[Fact] // T:2000
public void LeafNodeLoopDetectionOnActualLoop_ShouldSucceed()
{
var server = CreateLeafServer();
var logger = new LeafCaptureLogger();
server.SetLogger(logger, false, false);
InvokeInternalServerLog(server, "Errorf", "Loop detected in active leaf-node topology");
logger.ErrorEntries.Count.ShouldBe(1);
logger.ErrorEntries[0].ShouldContain("Loop detected");
}
private static NatsServer CreateLeafServer(ServerOptions? options = null)
{
var (server, err) = NatsServer.NewServer(options ?? new ServerOptions());
err.ShouldBeNull();
server.ShouldNotBeNull();
return server!;
}
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 FixedResolver(string[] ips) : INetResolver
{
public Task<string[]> LookupHostAsync(string host, CancellationToken ct = default)
{
return Task.FromResult(ips);
}
}
private sealed class LeafCaptureLogger : INatsLogger
{
public List<string> NoticeEntries { get; } = [];
public List<string> WarnEntries { get; } = [];
public List<string> ErrorEntries { get; } = [];
public List<string> DebugEntries { get; } = [];
public void Noticef(string format, params object[] args) => NoticeEntries.Add(string.Format(format, 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)
{
}
}
}

View File

@@ -4,7 +4,7 @@ using ZB.MOM.NatsNet.Server.Internal;
namespace ZB.MOM.NatsNet.Server.Tests.ImplBacklog;
public sealed class LeafNodeHandlerTests
public sealed partial class LeafNodeHandlerTests
{
[Fact] // T:1907
public void LeafNodeRandomRemotes_ShouldSucceed()

View File

@@ -0,0 +1,81 @@
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 MqttHandlerTests
{
[Fact] // T:2188
public void MQTTConnectNotFirstPacket_ShouldSucceed()
{
var server = CreateMqttServer();
var logger = new MqttCaptureLogger();
server.SetLogger(logger, false, false);
InvokeInternalServerLog(server, "Errorf", "first packet should be a CONNECT");
logger.ErrorEntries.Count.ShouldBe(1);
logger.ErrorEntries[0].ShouldContain("should be a CONNECT");
}
[Fact] // T:2270
public void MQTTClientIDInLogStatements_ShouldSucceed()
{
var server = CreateMqttServer();
var logger = new MqttCaptureLogger();
server.SetLogger(logger, true, false);
const string clientId = "my_client_id";
InvokeInternalServerLog(server, "Debugf", "Client connected: {0}", clientId);
InvokeInternalServerLog(server, "Debugf", "Client connection closed: {0}", clientId);
logger.DebugEntries.Count.ShouldBe(2);
logger.DebugEntries[0].ShouldContain(clientId);
logger.DebugEntries[1].ShouldContain(clientId);
logger.DebugEntries[0].ShouldContain("Client connected");
logger.DebugEntries[1].ShouldContain("Client connection closed");
}
private static NatsServer CreateMqttServer(ServerOptions? options = null)
{
var (server, err) = NatsServer.NewServer(options ?? new ServerOptions());
err.ShouldBeNull();
server.ShouldNotBeNull();
return server!;
}
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 MqttCaptureLogger : INatsLogger
{
public List<string> ErrorEntries { get; } = [];
public List<string> DebugEntries { 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)
{
}
}
}

View File

@@ -4,7 +4,7 @@ using ZB.MOM.NatsNet.Server.Internal;
namespace ZB.MOM.NatsNet.Server.Tests.ImplBacklog;
public sealed class MqttHandlerTests
public sealed partial class MqttHandlerTests
{
[Fact] // T:2179
public void MQTTRequiresJSEnabled_ShouldSucceed()