227 lines
7.9 KiB
C#
227 lines
7.9 KiB
C#
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)
|
|
{
|
|
}
|
|
}
|
|
}
|