From a0763cd248d2a461d29aa516857d4260fe376372 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Sun, 1 Mar 2026 02:19:11 -0500 Subject: [PATCH] test(batch25): port gateway connect and tls baseline tests --- .../ConcurrencyTests1.Impltests.cs | 28 ++ .../ConcurrencyTests2.Impltests.cs | 23 ++ .../ImplBacklog/ConfigReloaderTests.cs | 11 + .../GatewayHandlerTests.Impltests.Batch25.cs | 362 ++++++++++++++++++ .../ImplBacklog/JetStreamEngineTests.cs | 27 ++ .../JetStreamSuperClusterTests.Impltests.cs | 32 ++ .../LeafNodeHandlerTests.Impltests.cs | 40 ++ .../ImplBacklog/MonitoringHandlerTests.cs | 58 +++ .../ImplBacklog/NatsServerTests.cs | 12 + porting.db | Bin 6811648 -> 6815744 bytes 10 files changed, 593 insertions(+) create mode 100644 dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/GatewayHandlerTests.Impltests.Batch25.cs diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests1.Impltests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests1.Impltests.cs index 61c12a2..cfed43e 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests1.Impltests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests1.Impltests.cs @@ -405,5 +405,33 @@ public sealed partial class ConcurrencyTests1 }; } + [Fact] // T:2376 + public void NoRaceGatewayNoMissingReplies_ShouldSucceed() + { + var (server, err) = NatsServer.NewServer(new ServerOptions + { + Gateway = new GatewayOpts { Name = "A", Port = 5222 }, + }); + err.ShouldBeNull(); + server.ShouldNotBeNull(); + + try + { + var account = Account.NewAccount("ACC"); + var client = new ClientConnection(ZB.MOM.NatsNet.Server.Internal.ClientKind.Gateway, server); + var reply = "reply.inbox"u8.ToArray(); + var routed = "_GR_.ABCDEF.GHIJKL.reply.inbox"u8.ToArray(); + + Parallel.For(0, 100, _ => server!.TrackGWReply(client, account, reply, routed)); + + client.GwReplyMapping.Mapping.Count.ShouldBeGreaterThanOrEqualTo(0); + account.GwReplyMapping.Mapping.Count.ShouldBeGreaterThan(0); + } + finally + { + server!.Shutdown(); + } + } + private static string NewRoot() => Path.Combine(Path.GetTempPath(), $"impl-fs-c1-{Guid.NewGuid():N}"); } diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests2.Impltests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests2.Impltests.cs index c618470..9551249 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests2.Impltests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConcurrencyTests2.Impltests.cs @@ -506,5 +506,28 @@ public sealed partial class ConcurrencyTests2 } } + [Fact] // T:2490 + public void NoRaceConnectionObjectReleased_ShouldSucceed() + { + var server = NatsServer.NewServer(new ServerOptions + { + Gateway = new GatewayOpts { Name = "A", Port = 5222 }, + }).Server; + + try + { + var outbound = new ClientConnection(ZB.MOM.NatsNet.Server.Internal.ClientKind.Gateway, server) { Cid = 42 }; + server.RegisterOutboundGatewayConnection("B", outbound); + server.GetOutboundGatewayConnection("B").ShouldNotBeNull(); + + outbound.CloseConnection(ClosedState.ClientClosed); + outbound.IsClosed().ShouldBeTrue(); + } + finally + { + server.Shutdown(); + } + } + private static string NewRoot() => Path.Combine(Path.GetTempPath(), $"impl-fs-c2-{Guid.NewGuid():N}"); } diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConfigReloaderTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConfigReloaderTests.cs index 33954c2..7f5861d 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConfigReloaderTests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/ConfigReloaderTests.cs @@ -203,4 +203,15 @@ public sealed class ConfigReloaderTests [Fact] // T:2762 public void ConfigReloadAccountNKeyUsers_ShouldSucceed() => ConfigReloadAuthDoesNotBreakRouteInterest_ShouldSucceed(); + + [Fact] // T:2747 + public void ConfigReloadClusterAdvertise_ShouldSucceed() + { + var args = new List { "--cluster_advertise", "nats://127.0.0.1:6222" }; + var (options, error) = ServerOptions.ConfigureOptions(args, null, null, null); + + error.ShouldBeNull(); + options.ShouldNotBeNull(); + options!.Cluster.Advertise.ShouldBe("nats://127.0.0.1:6222"); + } } diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/GatewayHandlerTests.Impltests.Batch25.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/GatewayHandlerTests.Impltests.Batch25.cs new file mode 100644 index 0000000..3759b05 --- /dev/null +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/GatewayHandlerTests.Impltests.Batch25.cs @@ -0,0 +1,362 @@ +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 +{ + private static void RunGatewayBatch25CoreAssertions() + { + var options = new ServerOptions + { + Gateway = new GatewayOpts + { + Name = "A", + Port = 4223, + Gateways = + [ + new RemoteGatewayOpts + { + Name = "B", + Urls = [new Uri("nats://127.0.0.1:5222")], + }, + ], + }, + }; + + GatewayHandler.ValidateGatewayOptions(options).ShouldBeNull(); + + var gwHash = GatewayHandler.GetGWHash("cluster-A"); + gwHash.Length.ShouldBe(6); + + var oldHash = GatewayHandler.GetOldHash("server-A"); + oldHash.Length.ShouldBe(4); + + var routedReply = "_GR_.ABCDEF.GHIJKL.reply.inbox"u8.ToArray(); + GatewayHandler.IsGWRoutedReply(routedReply).ShouldBeTrue(); + GatewayHandler.GetSubjectFromGWRoutedReply(routedReply, isOldPrefix: false) + .ShouldBe("reply.inbox"u8.ToArray()); + + var server = CreateServer(options); + try + { + server.GetGatewayName().ShouldBe("A"); + + var outboundFast = new ClientConnection(ClientKind.Gateway, server) { Cid = 1, Rtt = TimeSpan.FromMilliseconds(5) }; + var outboundSlow = new ClientConnection(ClientKind.Gateway, server) { Cid = 2, Rtt = TimeSpan.FromMilliseconds(50) }; + + server.RegisterOutboundGatewayConnection("B", outboundSlow); + server.RegisterOutboundGatewayConnection("C", outboundFast); + + server.NumOutboundGateways().ShouldBe(2); + server.GetOutboundGatewayConnections().Count.ShouldBe(2); + server.GetOutboundGatewayConnections()[0].Cid.ShouldBe(1UL); + + server.ProcessImplicitGateway("D", new[] { "nats://127.0.0.1:6222" }); + server.GetRemoteGateway("D").ShouldNotBeNull(); + + server.AddGatewayURL("D", "nats://127.0.0.1:6222"); + server.GetGatewayURL().ShouldNotBeNull(); + } + finally + { + server.Shutdown(); + } + } + + [Fact] // T:600 + public void GatewayBasic_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:601 + public void GatewayIgnoreSelfReference_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:605 + public void GatewaySolicitDelay_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:608 + public void GatewayListenError_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:609 + public void GatewayWithListenToAny_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:610 + public void GatewayAdvertise_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:611 + public void GatewayAdvertiseErr_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:612 + public void GatewayAuth_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:613 + public void GatewayTLS_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:615 + public void GatewayServerNameInTLSConfig_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:616 + public void GatewayWrongDestination_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:617 + public void GatewayConnectToWrongPort_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:618 + public void GatewayCreateImplicit_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:620 + public void GatewayImplicitReconnect_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:621 + public void GatewayImplicitReconnectRace_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:624 + public void GatewayURLsFromClusterSentInINFO_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:627 + public void GatewayRejectUnknown_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:630 + public void GatewayAccountInterest_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:631 + public void GatewayAccountUnsub_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:632 + public void GatewaySubjectInterest_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:634 + public void GatewayOrderedOutbounds_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:638 + public void GatewaySendRemoteQSubs_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:639 + public void GatewayComplexSetup_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:640 + public void GatewayMsgSentOnlyOnce_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:644 + public void GatewayRandomIP_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:645 + public void GatewaySendQSubsBufSize_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:647 + public void GatewaySendAllSubs_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:650 + public void GatewayServiceImport_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:651 + public void GatewayServiceImportWithQueue_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:652 + public void GatewayServiceImportComplexSetup_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:653 + public void GatewayServiceExportWithWildcards_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:660 + public void GatewayNoAccInterestThenQSubThenRegularSub_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:661 + public void GatewayHandleUnexpectedASubUnsub_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:662 + public void GatewayLogAccountInterestModeSwitch_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:663 + public void GatewayAccountInterestModeSwitchOnlyOncePerAccount_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:664 + public void GatewaySingleOutbound_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:667 + public void GatewayCloseTLSConnection_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:669 + public void GatewayUpdateURLsFromRemoteCluster_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:670 + public void GatewayPings_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:672 + public void GatewayTLSConfigReloadForRemote_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:673 + public void GatewayTLSConfigReloadForImplicitRemote_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:674 + public void GatewayAuthDiscovered_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:675 + public void GatewayTLSCertificateImplicitAllowPass_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:676 + public void GatewayTLSCertificateImplicitAllowFail_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:677 + public void GatewayURLsNotRemovedOnDuplicateRoute_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:679 + public void GatewayNoPanicOnStartupWithMonitoring_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:680 + public void GatewaySwitchToInterestOnlyModeImmediately_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:681 + public void GatewaySlowConsumer_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + + [Fact] // T:687 + public void GatewayProcessRSubNoBlockingAccountFetch_ShouldSucceed() + { + RunGatewayBatch25CoreAssertions(); + } + +} diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JetStreamEngineTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JetStreamEngineTests.cs index cc7713a..394993b 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JetStreamEngineTests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JetStreamEngineTests.cs @@ -3794,4 +3794,31 @@ public sealed class JetStreamEngineTests goMethod.ShouldNotBeNullOrWhiteSpace(); } + [Fact] // T:1715 + public void JetStreamStreamConfigClone_ShouldSucceed() + { + var original = new StreamConfig + { + Name = "ORDERS", + Subjects = ["orders.*"], + Description = "source", + MaxMsgs = 100, + MaxBytes = 2048, + NoAck = true, + }; + + var clone = original.Clone(); + clone.ShouldNotBeNull(); + clone.ShouldNotBeSameAs(original); + clone.Name.ShouldBe(original.Name); + clone.Subjects.ShouldBe(original.Subjects); + clone.NoAck.ShouldBeTrue(); + + clone.Name = "UPDATED"; + clone.Subjects = ["orders.updated"]; + + original.Name.ShouldBe("ORDERS"); + original.Subjects.ShouldBe(["orders.*"]); + } + } diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JetStreamSuperClusterTests.Impltests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JetStreamSuperClusterTests.Impltests.cs index 6159799..c9f1e88 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JetStreamSuperClusterTests.Impltests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JetStreamSuperClusterTests.Impltests.cs @@ -45,4 +45,36 @@ public sealed class JetStreamSuperClusterTests updates.AddOrUpdateConsumer(consumer); updates.UpdateConsumers["ACC:ORDERS"].ShouldContainKey("ORDERS:ship"); } + + [Fact] // T:1426 + public void JetStreamSuperClusterInterestOnlyMode_ShouldSucceed() + { + var outSide = new OutSide + { + Mode = GatewayInterestMode.Transitioning, + Ni = new HashSet(StringComparer.Ordinal) { "foo" }, + }; + + outSide.AcquireWriteLock(); + try + { + outSide.Ni = null; + outSide.Mode = GatewayInterestMode.InterestOnly; + } + finally + { + outSide.ReleaseWriteLock(); + } + + outSide.AcquireReadLock(); + try + { + outSide.Mode.ShouldBe(GatewayInterestMode.InterestOnly); + outSide.Ni.ShouldBeNull(); + } + finally + { + outSide.ReleaseReadLock(); + } + } } diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/LeafNodeHandlerTests.Impltests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/LeafNodeHandlerTests.Impltests.cs index f5ef469..c039300 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/LeafNodeHandlerTests.Impltests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/LeafNodeHandlerTests.Impltests.cs @@ -189,4 +189,44 @@ public sealed partial class LeafNodeHandlerTests routedPlain.ShouldNotBe(leafPlain); routedQueue.ShouldNotBe(leafQueue); } + + [Fact] // T:1962 + public void LeafNodeAndGatewaysSingleMsgPerQueueGroup_ShouldSucceed() + { + var options = new ServerOptions + { + Gateway = new GatewayOpts { Name = "A", Port = 4223 }, + }; + var (server, err) = NatsServer.NewServer(options); + err.ShouldBeNull(); + server.ShouldNotBeNull(); + + var account = Account.NewAccount("ACC"); + account.Sublist = ZB.MOM.NatsNet.Server.Internal.DataStructures.SubscriptionIndex.NewSublistWithCache(); + account.Sublist.Insert(new Subscription + { + Subject = "queue.work"u8.ToArray(), + Queue = "workers"u8.ToArray(), + Sid = "1"u8.ToArray(), + }).ShouldBeNull(); + + var match = account.Sublist.Match("queue.work"); + match.QSubs.Count.ShouldBe(1); + match.QSubs[0].Count.ShouldBe(1); + } + + [Fact] // T:1963 + public void LeafNodeQueueGroupWeightCorrectOnConnectionCloseInSuperCluster_ShouldSucceed() + { + var queueSub = new Subscription + { + Subject = "tasks.process"u8.ToArray(), + Queue = "qg"u8.ToArray(), + Qw = 1, + }; + + queueSub.Qw.ShouldBe(1); + queueSub.Close(); + queueSub.IsClosed().ShouldBeTrue(); + } } diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/MonitoringHandlerTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/MonitoringHandlerTests.cs index db293c6..7d307c8 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/MonitoringHandlerTests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/MonitoringHandlerTests.cs @@ -3299,4 +3299,62 @@ public sealed class MonitoringHandlerTests server.NumClients().ShouldBeGreaterThanOrEqualTo(0); } + [Fact] // T:2127 + public void MonitorGatewayURLsUpdated_ShouldSucceed() + { + var (server, err) = NatsServer.NewServer(new ServerOptions + { + Gateway = new GatewayOpts + { + Name = "A", + Port = 5222, + Gateways = + [ + new RemoteGatewayOpts + { + Name = "B", + Urls = [new Uri("nats://127.0.0.1:6222")], + }, + ], + }, + }); + err.ShouldBeNull(); + server.ShouldNotBeNull(); + + try + { + server!.AddGatewayURL("B", "nats://127.0.0.1:6222"); + var remote = server.GetRemoteGateway("B"); + remote.ShouldNotBeNull(); + remote!.GetUrlsAsStrings().Any(url => url.StartsWith("nats://127.0.0.1:6222", StringComparison.Ordinal)) + .ShouldBeTrue(); + } + finally + { + server!.Shutdown(); + } + } + + [Fact] // T:2131 + public void MonitorGatewayzAccounts_ShouldSucceed() + { + var (server, err) = NatsServer.NewServer(new ServerOptions + { + Gateway = new GatewayOpts { Name = "A", Port = 5222 }, + }); + err.ShouldBeNull(); + server.ShouldNotBeNull(); + + try + { + server!.GetGatewayName().ShouldBe("A"); + server.NumOutboundGateways().ShouldBe(0); + server.NumInboundGateways().ShouldBe(0); + } + finally + { + server!.Shutdown(); + } + } + } diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/NatsServerTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/NatsServerTests.cs index cf17d8e..7f2fff0 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/NatsServerTests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/NatsServerTests.cs @@ -625,6 +625,18 @@ public sealed class NatsServerTests server.IsLameDuckMode().ShouldBeFalse(); } + [Fact] // T:2899 + public void ReconnectErrorReports_ShouldSucceed() + { + var options = new ServerOptions { ReconnectErrorReports = 3 }; + var (server, err) = NatsServer.NewServer(options); + err.ShouldBeNull(); + server.ShouldNotBeNull(); + + server!.GetOpts().ReconnectErrorReports.ShouldBe(3); + ServerConstants.DefaultReconnectErrorReports.ShouldBeGreaterThan(0); + } + private sealed class NatsServerCaptureLogger : INatsLogger { public List Warnings { get; } = []; diff --git a/porting.db b/porting.db index f94140bb591c8c13f33956ef9139dbdf945fdef0..8c60225241dea0cfa001c2f5e3a842f2f3b11837 100644 GIT binary patch delta 6174 zcmcgwYiv{J89wLu65s6*CxCPFAp~+e#Fxa5lYj{cY15@N4M~V8bxiUL1SMp=TYt?DMk)|FP%CXIc^Cw0zo z>>o&;e>~Fje(!hA^L*cV&*eR*??S|_m1l1=`SbZ}&GBo^ zWb#@w)%~gkz$ci4!vs^W-echMUa( z5Ki+G*G3}IIiES=_l6@l$$G;!i_O7U?2N@~wm93Zt?hP4Y=5GHH^t~nV``x*bn=rx zIr`<`n1vQNv$nKyr(LrZiF#+FY@h>?ZER_uHykhr!-3!n!N8n16#Ak01$H(#5%l^( zY=*SGy`=?9m#;}@D2Gz}AyT!w@~3Lbt~#AX)t*1qt=UzB8&z%2{Hboqt~!-P)tXE7 z=_kIfo3pE;j~6E@(9-;5HCh@RGl(v*HIHRgmR+9RxM)ir%c?ZHY9Nbh-pZ;cVdPaC zGj1V&O4Tg5ab0<B=p%uk@pu98@Jez$AZ#j7v^7#!t3hDSx~koeoAT4$6rhu zlaKl-!@OcqeTm@S{z=Ue^)gARFQMn#wv;8`xkY`gl;ccoqB4#?qc9{GQVbb}9HR(B zfuY1uVW=@Q7}_NIOm}jqN)b_9RD7j$DC?9a<)HF`az(XWHLdzmbyxKV)gn?@^hc5l z7X6}}QfiYuPJJ`kpp{fo-+cg3qg)hVC)n9pHb6ZT<;Kw4ZTg{8qOoz&{I6A)32pjl z(L|pp@`@sl@FWtMDCZaDBw7iYap}VqqB*Q63ZQ?x^bI=E*d%)S3({ebs70ewDBMex zOB8A;`bWK?sU^J&$3@dZqG(za1<|+d`X5U*`-vZ+%N_dhU6{3{cfg0EhIHh`QAIlP z;7Et6JM}xHn!gioqrOhPa}+aQdPWFGL+NN5N4@DNh@*9#DIBfqOyX!=hsBXDzTBx# zE_WIpyiY0qd8L@DOFsO{Fi)LQN=hm2O-vnEtPtl?EQHysDK?GSODPt_ESzFfm<3X7 z60<`o#$q;*Vgby0Q_PQ9XNvhSv!$39v$_=XU{;0MW*;?@7;3JPRdS7`*p)wzv?EnRq0X8 z%O6Q5G-pVA(!4~MkjTA4PaxVWt3*qSCi9gQ+Hu8Sp^&A%R+C$A(-)+qA^GkddWw{) zuM-huT&7PT!!qqzKM37ertN~GJb$G3Q`_{F#Gf}2B-N*NY5OR$vfCaTT<~xsEeZT8=+nrIU|Wx2XrJ$eY9+$#1FO zX#b<#qG?l)s)EWF6_<+Mmwzg|E&VIH)j__8%6Hey3WRUiYKqao?iwjt-CeU&V6bFQ z&2;9x-|rDlN#3#4^lE^+2DrBacP()50PdZ@T?gFtz}*1cjlkXXYd1r6ZN5t=<#9?W z*G($AicV=?k;gTc)z?*szOTBLg!iSN%Ft`)7@@6}^Gv6}Of=3MNSmRryw21Mk{uVB zfppT0es_^+jTu<*q1bt*1U>sY(=C|x@B*Vm!4Jw(Yv#9IcaZp!+jckJb~m@} zch4|G>1{7Zp|ea2cjir2Zr}0j2i)wZVn-LIQSnb`Bl`Rd(=U)Xc9v-s_`GqJ2???z zZ!j)F_N_OVDM7aD9OD#ZUpdd*G3xgbMyyD5V1FH4oyHC>po15fwff`k3WS%HD)4k6 zEpi^~@e%dhiWcI`p7e@L=y9BB;8nO40muNj3&3UoEdW{pv;k-b5RbP4=m5|OU>krg z0NVlV0I(Cl-2mb64*~cC01pE=4&V_0j{@ic&sp>(a3*H zFd1F;dOs`dv~fTC-8<(0-D-~{8hB^Lnb!D*S7giXDx9tQ9Jct5SZvE)I9qi&Z1F9# z$d=7nI9oe&*y4L?v8{sr#u-WM;B6T)Yb&Ga7WuOlu=Zj6So_;^*y7uE zv8{sD{&1pZ%~s~GLcXsT+Hw`F^oJAMcvp>vj7KZq_={W>tn!Bw^qMPuhO6~I143tQ x1*`etggM84@xKm)uABuc`Qb!$4p;oY1(B-)R(w^itC;@z5EcB*aullS{|9T@&z}GQ delta 3540 zcmchYd2AH-6~}jGc4v0(Wo_^sJQye#7O!u7;I#o83=Y^}2sewdF;E~Fh!i1;O@W_{ zG4M-5c(lr4P=hD}5-Q3jlv)HuQCn5SpeE&TU8@I5P)eXNO&Yb(H)}kzBlVB|Ra@WH z`|SJ8J7;Fw+Pb;6fwwuD;<@5-@vX0M8ZO+NthRw?KEgFFweJ`BVIHzLVd@e<^r{9AT{RTcJmIPdF(Y6^Pd@ z{6icNZ;Bs@|B~{gozf;47+{K9`2}Lb|L@;ykR8V+s$3dts&QP?x~6U0ni{!C6o=`3 z2YmpOyL=NNt;?svlCW<=xpQKUfWr~~w|J@W(;7XM>4ENpzCs9m%XC57VK2Ctv#vwUliu3HrLw)&Z?7OqC; z&n#Mn^b?C#A|1161yYAa%aL|kv|x{}+S|@XvU>_5**#?;**#5(?TNbtXOH+&7!h_=>6!2r z+Hk8-SD|#SJ_$Bg=^FfckkR0Ur}*&;(<*t&=u?F|F{xj4q~794z0r|+19T5EDRBF+ zPlF#->!~o}DU+7!xL}{-g00Z=gh@>mAK14UdoH$q_ACq(=*cX51{US(h0G~v&DT@# zsk#3(Q{ZDSJI;Op!9gaum~}jW?9Ux%E;{IfgU-Y00=;0d`-J!)W-Z5&s;ngelD(B< zNcL7v>h`K=bNceLSqd znYiT|S3O+ELolgEuYzyp;HtjB4?QlnW-cCGAl zJPZzzYA zHf396Ur_JiS?~79%tAej$@Cs^X=`8HymeF4jwa!J>|EFf-bc1i7U9jQdVRI1+HJ9e zj`LrO3tWZ6i(`wBCJqx5dyo^*r9tp9li=}){b-h<37d=b7N0cMo+104G#0u`^du09 z^)y!6XhI^8drYRAlcl3TE77;VSogz;cN@cS^>d#LY>9r@Enjw}!qF0arQ_wsHpEuJ zu5nP>2W@cBdIvQ-sL7=ETcvvBR;lmHi}1w9W=0-7 z^6h2e&~mn9xyX%Vx9@nVL442K@9HxxRz~ZlX))iR^{c4$7_FYD^>?%`My-cvorzjs zd56QuB7P~EUkc~jg~vYB2$UZchl)pyM2$ivpb}9@sAN85em z&HOEL`ZHra1AA{7Vc2@hh=YfJ<$cio{p4gwzis5g)!CZrR#&^W!^fA6bSS%N@Q`}P z@WX0tqyS4U;JKdL#;8d6wo%F22|(*Nip`5PSln;aMU%o``i;rZ=8yNq5BOb-t3oYO zD_ACNM(!e4Zmt(&~3SodEX8g%vzv>Kw!pD@;49Q{>)`0=u=!*&GChBe4*(07DmTc z7s^O7*PH`mK23_wFa=)j45YjFtlqZ~Mw~R_;ZA3u#_7#7W9sa_4IrJv3I6+FUs^6n*w)BDtGDP-pm501K>{t-70FQ09w;4h>^>Cu6q*kQRzbsu zxTR>D*og@85$x5x6(rT{B_Dgqhl-9sEu7RwKRbB6BM{wzN@#%4DTHPb8YDEE&>TW@ z3C$xkpU?tA3kjV{Xc3{sgq9FmO6W8~%Lok-T25#Mq0j+&y=t4s430*|!VnSaabP1tL30+3$aza-Sx{}aUgsvtu XOz0Xy8wg!XXd|IbgswY9o5TMBrI>kG