193 lines
6.9 KiB
C#
193 lines
6.9 KiB
C#
using Shouldly;
|
|
using ZB.MOM.NatsNet.Server;
|
|
using ZB.MOM.NatsNet.Server.Internal;
|
|
using ZB.MOM.NatsNet.Server.WebSocket;
|
|
|
|
namespace ZB.MOM.NatsNet.Server.Tests.ImplBacklog;
|
|
|
|
public sealed partial class LeafNodeHandlerTests
|
|
{
|
|
[Fact] // T:1975
|
|
public void LeafNodeTLSHandshakeFirstVerifyNoInfoSent_ShouldSucceed()
|
|
{
|
|
var errors = new List<Exception>();
|
|
var warnings = new List<Exception>();
|
|
|
|
var remotes = ServerOptions.ParseRemoteLeafNodes(
|
|
new List<object?>
|
|
{
|
|
new Dictionary<string, object?>
|
|
{
|
|
["url"] = "wss://127.0.0.1:7422",
|
|
["first_info_timeout"] = "2s",
|
|
["tls"] = new Dictionary<string, object?>
|
|
{
|
|
["verify"] = true,
|
|
["timeout"] = 1,
|
|
},
|
|
},
|
|
},
|
|
errors,
|
|
warnings);
|
|
|
|
errors.ShouldBeEmpty();
|
|
remotes.Count.ShouldBe(1);
|
|
remotes[0].FirstInfoTimeout.ShouldBe(TimeSpan.FromSeconds(2));
|
|
remotes[0].TlsConfig.ShouldNotBeNull();
|
|
remotes[0].TlsTimeout.ShouldBe(1d);
|
|
}
|
|
|
|
[Fact] // T:1986
|
|
public void LeafNodeCompressionWithWSGetNeedsData_ShouldSucceed()
|
|
{
|
|
var frame = new byte[] { WsConstants.FinalBit, (byte)(WsConstants.MaskBit | 5), 1, 2, 3, 4, 0x31, 0x32, 0x33, 0x34, 0x35 };
|
|
var initial = frame[..1];
|
|
using var remainder = new MemoryStream(frame[1..]);
|
|
|
|
var (b1, nextPos) = WebSocketHelpers.WsGet(remainder, initial, 1, 1);
|
|
b1[0].ShouldBe((byte)(WsConstants.MaskBit | 5));
|
|
nextPos.ShouldBe(1);
|
|
|
|
var (mask, _) = WebSocketHelpers.WsGet(remainder, initial, 1, 4);
|
|
var (payload, _) = WebSocketHelpers.WsGet(remainder, initial, 1, 5);
|
|
WebSocketHelpers.WsMaskBuf(mask, payload);
|
|
|
|
payload.ShouldBe(new byte[] { 0x30, 0x30, 0x30, 0x30, 0x34 });
|
|
}
|
|
|
|
[Fact] // T:1984
|
|
public void LeafNodeCompressionAuto_ShouldSucceed()
|
|
{
|
|
var options = new ServerOptions();
|
|
var errors = new List<Exception>();
|
|
var warnings = new List<Exception>();
|
|
|
|
var parseError = ServerOptions.ParseLeafNodes(
|
|
new Dictionary<string, object?>
|
|
{
|
|
["remotes"] = new List<object?>
|
|
{
|
|
new Dictionary<string, object?>
|
|
{
|
|
["url"] = "nats://127.0.0.1:7422",
|
|
["compression"] = new Dictionary<string, object?>
|
|
{
|
|
["mode"] = CompressionModes.S2Auto,
|
|
["rtt_thresholds"] = new List<object?> { "10ms", "20ms", "30ms" },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
options,
|
|
errors,
|
|
warnings);
|
|
|
|
parseError.ShouldBeNull();
|
|
errors.ShouldBeEmpty();
|
|
options.LeafNode.Remotes.Count.ShouldBe(1);
|
|
options.LeafNode.Remotes[0].Compression.Mode.ShouldBe(CompressionModes.S2Auto);
|
|
options.LeafNode.Remotes[0].Compression.RttThresholds.Count.ShouldBe(3);
|
|
options.LeafNode.Remotes[0].Compression.RttThresholds[0].ShouldBe(TimeSpan.FromMilliseconds(10));
|
|
options.LeafNode.Remotes[0].Compression.RttThresholds[1].ShouldBe(TimeSpan.FromMilliseconds(20));
|
|
options.LeafNode.Remotes[0].Compression.RttThresholds[2].ShouldBe(TimeSpan.FromMilliseconds(30));
|
|
}
|
|
|
|
[Fact] // T:2001
|
|
public void LeafNodeConnectionSucceedsEvenWithDelayedFirstINFO_ShouldSucceed()
|
|
{
|
|
var errors = new List<Exception>();
|
|
var warnings = new List<Exception>();
|
|
|
|
var remotes = ServerOptions.ParseRemoteLeafNodes(
|
|
new List<object?>
|
|
{
|
|
new Dictionary<string, object?>
|
|
{
|
|
["url"] = "nats://127.0.0.1:7422",
|
|
["first_info_timeout"] = "3s",
|
|
},
|
|
new Dictionary<string, object?>
|
|
{
|
|
["url"] = "ws://127.0.0.1:7423",
|
|
["first_info_timeout"] = "3s",
|
|
},
|
|
},
|
|
errors,
|
|
warnings);
|
|
|
|
errors.ShouldBeEmpty();
|
|
remotes.Count.ShouldBe(2);
|
|
remotes[0].FirstInfoTimeout.ShouldBe(TimeSpan.FromSeconds(3));
|
|
remotes[1].FirstInfoTimeout.ShouldBe(TimeSpan.FromSeconds(3));
|
|
remotes[1].Urls[0].Scheme.ShouldBe("ws");
|
|
}
|
|
|
|
[Fact] // T:1935
|
|
public void LeafNodeNoDuplicateWithinCluster_ShouldSucceed()
|
|
{
|
|
var account = Account.NewAccount("$G");
|
|
var leaf1 = new ClientConnection(ClientKind.Leaf);
|
|
var leaf2 = new ClientConnection(ClientKind.Leaf);
|
|
|
|
((INatsAccount)account).AddClient(leaf1);
|
|
((INatsAccount)account).AddClient(leaf2);
|
|
|
|
account.RegisterLeafNodeCluster("xyz");
|
|
account.RegisterLeafNodeCluster("xyz");
|
|
|
|
account.NumLocalLeafNodes().ShouldBe(2);
|
|
account.HasLeafNodeCluster("xyz").ShouldBeTrue();
|
|
account.IsLeafNodeClusterIsolated("xyz").ShouldBeTrue();
|
|
|
|
account.RegisterLeafNodeCluster("abc");
|
|
account.IsLeafNodeClusterIsolated("xyz").ShouldBeFalse();
|
|
}
|
|
|
|
[Fact] // T:1952
|
|
public void LeafNodeStreamImport_ShouldSucceed()
|
|
{
|
|
var exporter = Account.NewAccount("B");
|
|
var importer = Account.NewAccount("C");
|
|
|
|
exporter.AddStreamExport(">", null).ShouldBeNull();
|
|
importer.AddStreamImport(exporter, ">", string.Empty).ShouldBeNull();
|
|
|
|
importer.Imports.Streams.ShouldNotBeNull();
|
|
importer.Imports.Streams!.Count.ShouldBe(1);
|
|
importer.Imports.Streams[0].From.ShouldBe(">");
|
|
importer.Imports.Streams[0].To.ShouldBe(">");
|
|
exporter.CheckStreamExportApproved(importer, "a", null).ShouldBeTrue();
|
|
}
|
|
|
|
[Fact] // T:1955
|
|
public void LeafNodeUnsubOnRouteDisconnect_ShouldSucceed()
|
|
{
|
|
var account = Account.NewAccount("$G");
|
|
var leaf = new ClientConnection(ClientKind.Leaf);
|
|
|
|
((INatsAccount)account).AddClient(leaf);
|
|
account.NumLocalLeafNodes().ShouldBe(1);
|
|
|
|
((INatsAccount)account).RemoveClient(leaf);
|
|
account.NumLocalLeafNodes().ShouldBe(0);
|
|
}
|
|
|
|
[Fact] // T:1966
|
|
public void LeafNodeRoutedSubKeyDifferentBetweenLeafSubAndRoutedSub_ShouldSucceed()
|
|
{
|
|
var plain = new Subscription { Subject = "foo"u8.ToArray() };
|
|
var queue = new Subscription { Subject = "XYZ"u8.ToArray(), Queue = "foo"u8.ToArray() };
|
|
|
|
var routedPlain = LeafNodeHandler.KeyFromSubWithOrigin(plain);
|
|
var routedQueue = LeafNodeHandler.KeyFromSubWithOrigin(queue);
|
|
var leafPlain = LeafNodeHandler.KeyFromSubWithOrigin(plain, "XYZ");
|
|
var leafQueue = LeafNodeHandler.KeyFromSubWithOrigin(queue, "XYZ");
|
|
|
|
routedPlain.ShouldNotBe(routedQueue);
|
|
routedQueue.ShouldNotBe(leafPlain);
|
|
leafPlain.ShouldNotBe(leafQueue);
|
|
routedPlain.ShouldNotBe(leafPlain);
|
|
routedQueue.ShouldNotBe(leafQueue);
|
|
}
|
|
}
|