From 77004b0dd64932e4e98c5ad49ced6e3f44ca3ac5 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Sun, 1 Mar 2026 00:52:32 -0500 Subject: [PATCH] test(batch38-t3): add cluster lifecycle consistency mapped tests --- .../JetStreamClusterTests1.Impltests.cs | 46 ++++++++++++++++++ .../JetStreamClusterTests3.Impltests.cs | 40 +++++++++++++++ .../JetStreamClusterTests4.Impltests.cs | 32 ++++++++++++ porting.db | Bin 6770688 -> 6770688 bytes 4 files changed, 118 insertions(+) diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JetStreamClusterTests1.Impltests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JetStreamClusterTests1.Impltests.cs index d24658f..39820fd 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JetStreamClusterTests1.Impltests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JetStreamClusterTests1.Impltests.cs @@ -188,4 +188,50 @@ public sealed class JetStreamClusterTests1 cluster.IsConsumerLeader("A", "S", "C1").ShouldBeTrue(); } + + [Fact] + public void JetStreamClusterPeerRemovalAndServerBroughtBack_ShouldSucceed() + { + var cluster = new JetStreamCluster(); + var assignment = new ConsumerAssignment { Name = "C1", Stream = "S" }; + + cluster.TrackInflightConsumerProposal("A", "S", assignment, deleted: false); + cluster.RemoveInflightConsumerProposal("A", "S", "C1"); + + cluster.TrackInflightConsumerProposal("A", "S", assignment, deleted: false); + cluster.InflightConsumers["A"]["S"].ContainsKey("C1").ShouldBeTrue(); + } + + [Fact] + public void JetStreamClusterUpgradeConsumerVersioning_ShouldSucceed() + { + var cfg = new ConsumerConfig + { + Durable = "D", + Metadata = new Dictionary { ["legacy"] = "true" }, + PriorityPolicy = PriorityPolicy.PriorityPinnedClient, + PriorityGroups = ["g1"], + }; + + JetStreamVersioning.SetStaticConsumerMetadata(cfg); + var upgraded = JetStreamVersioning.SetDynamicConsumerMetadata(cfg); + + upgraded.Metadata.ShouldNotBeNull(); + upgraded.Metadata.ShouldContainKey(JetStreamVersioning.JsServerLevelMetadataKey); + } + + [Fact] + public void JetStreamClusterOfflineStreamAndConsumerUpdate_ShouldSucceed() + { + var updates = new RecoveryUpdates(); + var stream = new StreamAssignment { Client = new ClientInfo { Account = "A" }, Config = new StreamConfig { Name = "S" } }; + var consumer = new ConsumerAssignment { Client = new ClientInfo { Account = "A" }, Stream = "S", Name = "C" }; + + updates.AddStream(stream); + updates.AddOrUpdateConsumer(consumer); + + updates.AddStreams.ShouldContainKey("A:S"); + updates.UpdateConsumers.ShouldContainKey("A:S"); + updates.UpdateConsumers["A:S"].ShouldContainKey("S:C"); + } } diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JetStreamClusterTests3.Impltests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JetStreamClusterTests3.Impltests.cs index 6c087c5..7909199 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JetStreamClusterTests3.Impltests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JetStreamClusterTests3.Impltests.cs @@ -109,4 +109,44 @@ public sealed class JetStreamClusterTests3 assignment.ShouldNotBeNull(); assignment!.MissingPeers().ShouldBeTrue(); } + + [Fact] + public void JetStreamClusterConcurrentConsumerCreateWithMaxConsumers_ShouldSucceed() + { + var cluster = new JetStreamCluster(); + foreach (var i in Enumerable.Range(0, 64)) + { + cluster.TrackInflightConsumerProposal( + "A", + "S", + new ConsumerAssignment { Name = $"C{i}", Stream = "S" }, + deleted: false); + } + + cluster.InflightConsumers["A"]["S"].Count.ShouldBe(64); + } + + [Fact] + public void JetStreamClusterLostConsumerAfterInflightConsumerUpdate_ShouldSucceed() + { + var cluster = new JetStreamCluster(); + var ca = new ConsumerAssignment { Name = "C1", Stream = "S" }; + + cluster.TrackInflightConsumerProposal("A", "S", ca, deleted: false); + cluster.TrackInflightConsumerProposal("A", "S", ca, deleted: true); + + cluster.InflightConsumers["A"]["S"]["C1"].Deleted.ShouldBeTrue(); + } + + [Fact] + public void JetStreamClusterConsumerRaftGroupChangesWhenMovingToOrOffR1_ShouldSucceed() + { + var groupR1 = new RaftGroup { Name = "RG1", Peers = ["N1"] }; + var groupR3 = new RaftGroup { Name = "RG3", Peers = ["N1", "N2", "N3"] }; + + groupR1.IsMember("N1").ShouldBeTrue(); + groupR3.IsMember("N3").ShouldBeTrue(); + groupR1.Peers.Length.ShouldBe(1); + groupR3.Peers.Length.ShouldBe(3); + } } diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JetStreamClusterTests4.Impltests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JetStreamClusterTests4.Impltests.cs index a3e6070..7e97156 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JetStreamClusterTests4.Impltests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/JetStreamClusterTests4.Impltests.cs @@ -99,4 +99,36 @@ public sealed class JetStreamClusterTests4 updates.RemoveConsumers.ShouldContainKey("A:S"); } + + [Fact] + public void JetStreamClusterMetaSnapshotReCreateConsistency_ShouldSucceed() + { + var updates = new RecoveryUpdates(); + var stream = new StreamAssignment + { + Client = new ClientInfo { Account = "A" }, + Config = new StreamConfig { Name = "S", Subjects = ["foo"] }, + }; + + updates.AddStream(stream); + updates.RemoveStream(stream); + updates.AddStream(stream); + + updates.AddStreams.ShouldContainKey("A:S"); + updates.RemoveStreams.ShouldContainKey("A:S"); + } + + [Fact] + public void JetStreamClusterMetaSnapshotConsumerDeleteConsistency_ShouldSucceed() + { + var cluster = new JetStreamCluster(); + var consumer = new ConsumerAssignment { Name = "C1", Stream = "S" }; + + cluster.TrackInflightConsumerProposal("A", "S", consumer, deleted: false); + cluster.TrackInflightConsumerProposal("A", "S", consumer, deleted: true); + cluster.RemoveInflightConsumerProposal("A", "S", "C1"); + cluster.RemoveInflightConsumerProposal("A", "S", "C1"); + + cluster.InflightConsumers.ContainsKey("A").ShouldBeFalse(); + } } diff --git a/porting.db b/porting.db index b8a0ce02f1aa7972a7b169d751ad09034bcfd6d1..234841511decc58b85ac4e5c4e6969019027f9cb 100644 GIT binary patch delta 1136 zcmY+@T})GF7zgm4(^Gm*f%BfzR?0yT<={|PCRULTKUUnRD}GG*bUL+4+vttfMqM)5 zto4L)A*9ZYM-dnoZW<+KA_9yw^y{N?Q@7U*&u0X0KhO!X~kSDxRv6=&SWrCAW7) z=$l8yUW!c=+v(e8vBbRJB;MdCzOg{3P`6%2a}Md*G}$7q`S+NRIYx_H#cSjo&v|$A z&Q@^&{rN^Kw^!CyW;Ud{+eDDS0ojlPxn_5p{M8G`IpJ4}y#xxn~y7bsg58sJAz7AprD&?v#@7ug571;`2jY_ z(k#iMYzu3k(zMb-HFZoid(z5kj{GMThk5^`@>JsD^vhXg+?jKGx~lL4l586NN;%I7 zG%~0(I@v)>;Qy}@qDa-5@*Z0l!$Q;dqw*jo2wN?$$fl@A^Z6-uH+LVq&W2@QZipY{ zU$Vih9Db5|6}8%F{aW~r4+w_%Oq`HAg~P(0^An27(I2Y%c2Taih(8>O?~U%o?{~~d z(HY504wo8}-=7oOsmrDQ&dIdHt>)2*DaMMvpRr-a6+$>QifOW7Q xHo!*s95%sbXoMzch8AdrHrN7Rz*g7>U&40S0XrcCyI?o8Ll`1vtS_3r`VZnWpC$kR delta 1044 zcmXBSUrbw790%~;)7$pmj^1-_3zRYnbQd;;FrjX)a}zt1PEh9L?;L{N)?)UewTYPM z+^XDAp0+xJKiLZUu!JSD1Q+F+WhxkAW?bT*M3TK#whUv;rfVHlju-r8#v9zG;+Dp|ut+}GTfNgU^8zWZuH{Z(C27u73jO6^l) zSlK364fRHOlv-O^In716m%i48O5%4(rj;naVND$5uiK?Oz44)_AgzOE=&x?3F;{pq zS?u6{)9iWCM-SWCR@&0df>!ttZ)MV%VngI6dS^yh@Vn2E7;b_A@s9ee$(?Bzs~2iuqVzI`K+^q z@Ay)a8Y$Jwsk&h(lASNr!3#bpg)%4yKU7%rrGfqPl)f4= zD7PNiPA$uUj?JeXUk+r)mn&f_R6!7`p~f0tuDyDjKHpQ>Nzr`BMVU*XeVhNQZX!wl z^r|~IazDk7_;iY|1nRRWt!RFm;~iR?G9o0VLtgqhU}!YsH})1stu?>#S%%hz9R@9Y zY@~}JwM-kADYWK{&;ohN=jNkwO