From c466127d09537435bd4965fffa9e3812a4a344cd Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Sun, 1 Mar 2026 00:08:35 -0500 Subject: [PATCH] batch37 task7 port wave T1 mapped tests and verify statuses --- .../Auth/JwtProcessor.cs | 15 +- .../Batch37StreamMessagesMappedTests.cs | 181 ++++++++++++++++++ porting.db | Bin 6758400 -> 6758400 bytes 3 files changed, 192 insertions(+), 4 deletions(-) create mode 100644 dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/Batch37StreamMessagesMappedTests.cs diff --git a/dotnet/src/ZB.MOM.NatsNet.Server/Auth/JwtProcessor.cs b/dotnet/src/ZB.MOM.NatsNet.Server/Auth/JwtProcessor.cs index de4ef4e..b7e0b3a 100644 --- a/dotnet/src/ZB.MOM.NatsNet.Server/Auth/JwtProcessor.cs +++ b/dotnet/src/ZB.MOM.NatsNet.Server/Auth/JwtProcessor.cs @@ -125,10 +125,17 @@ public static class JwtProcessor var start = today + startTime; var end = today + endTime; - // If start > end, end is on the next day (overnight range). + // If start > end, this range crosses midnight. if (startTime > endTime) { - end = end.AddDays(1); + if (now.TimeOfDay < endTime) + { + start = start.AddDays(-1); + } + else + { + end = end.AddDays(1); + } } if (start <= now && now < end) @@ -225,12 +232,12 @@ public static class JwtProcessor public static Exception? ValidateTrustedOperators(ServerOptions opts) { if (opts.TrustedOperators == null || opts.TrustedOperators.Count == 0) - return null; + return (Exception?)null; // Full trusted operator JWT validation requires a NATS JWT library. // Each operator JWT should be decoded and its signing key chain verified. // For now, we accept any non-empty operator list and validate at connect time. - return null; + return (Exception?)null; } } diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/Batch37StreamMessagesMappedTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/Batch37StreamMessagesMappedTests.cs new file mode 100644 index 0000000..4da976b --- /dev/null +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/Batch37StreamMessagesMappedTests.cs @@ -0,0 +1,181 @@ +using NSubstitute; +using Shouldly; +using ZB.MOM.NatsNet.Server; +using ZB.MOM.NatsNet.Server.Internal; + +namespace ZB.MOM.NatsNet.Server.Tests.JetStream; + +public sealed class Batch37StreamMessagesMappedTests +{ + [Fact] // T:828 + public void JetStreamClusterStreamDirectGetMsg_ShouldSucceed() + { + var stream = CreateStream(); + stream.SetupStore(null).ShouldBeNull(); + stream.Store!.StoreMsg("orders.created", null, [1], 0); + var request = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(new JsApiMsgGetRequest { Seq = 1 }); + + var response = stream.ProcessDirectGetRequest("reply", null, request); + + response.Error.ShouldBeNull(); + response.Message.ShouldNotBeNull(); + response.Message!.Sequence.ShouldBe(1UL); + } + + [Fact] // T:954 + public void JetStreamClusterRollupSubjectAndWatchers_ShouldSucceed() + { + var hdr = NatsMessageHeaders.GenHeader(null, NatsHeaderConstants.JsMsgRollup, "SUB"); + + NatsStream.GetRollup(hdr).ShouldBe("sub"); + } + + [Fact] // T:987 + public void JetStreamClusterMirrorDeDupWindow_ShouldSucceed() + { + var stream = CreateStream(duplicates: TimeSpan.FromSeconds(5)); + var now = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() * 1_000_000L; + stream.StoreMsgId(new NatsStream.DedupeEntry { Id = "dup-1", Seq = 10, TimestampNanos = now }); + + stream.CheckMsgId("dup-1").ShouldNotBeNull(); + stream.NumMsgIds().ShouldBe(1); + } + + [Fact] // T:1642 + public void JetStreamDirectMsgGet_ShouldSucceed() + { + var stream = CreateStream(); + stream.SetupStore(null).ShouldBeNull(); + stream.ProcessJetStreamMsg("events", string.Empty, null, [7], 0, 0, null, false, true).ShouldBeNull(); + + var req = System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(new JsApiMsgGetRequest { Seq = 1 }); + var response = stream.ProcessDirectGetRequest("reply", null, req); + response.Message.ShouldNotBeNull(); + response.Message!.Data.ShouldBe([7]); + } + + [Fact] // T:1643 + public void JetStreamDirectMsgGetNext_ShouldSucceed() + { + var stream = CreateStream(); + stream.SetupStore(null).ShouldBeNull(); + stream.ProcessJetStreamMsg("events.a", string.Empty, null, [1], 0, 0, null, false, true).ShouldBeNull(); + stream.ProcessJetStreamMsg("events.a", string.Empty, null, [2], 0, 0, null, false, true).ShouldBeNull(); + + var response = stream.GetDirectRequest(new JsApiMsgGetRequest { NextFor = "events.*", Seq = 1 }, "reply"); + response.Message.ShouldNotBeNull(); + response.Message!.Sequence.ShouldBeGreaterThanOrEqualTo(1UL); + } + + [Fact] // T:383 + public void FileStoreSnapshot_ShouldSucceed() + { + var root = Path.Combine(Path.GetTempPath(), $"batch37-snap-{Guid.NewGuid():N}"); + Directory.CreateDirectory(root); + try + { + var stream = CreateStream(storage: StorageType.FileStorage, storeDir: root); + stream.SetupStore(new FileStoreConfig { StoreDir = root }).ShouldBeNull(); + stream.ProcessJetStreamMsg("snap.a", string.Empty, null, [1], 0, 0, null, false, true).ShouldBeNull(); + + var (result, error) = stream.Snapshot(TimeSpan.FromSeconds(2), checkMsgs: false, includeConsumers: false); + error.ShouldBeNull(); + result.ShouldNotBeNull(); + } + finally + { + if (Directory.Exists(root)) + Directory.Delete(root, recursive: true); + } + } + + [Fact] // T:2617 + public void NRGSnapshotAndRestart_ShouldSucceed() + { + var raft = new Raft { GroupName = "RG" }; + raft.InstallSnapshot([1, 2, 3], force: true); + + raft.Wps.ShouldBe([1, 2, 3]); + } + + [Fact] // T:2672 + public void NRGSnapshotCatchup_ShouldSucceed() + { + var raft = new Raft { GroupName = "RG", StateValue = (int)RaftState.Leader }; + raft.SendSnapshot([4, 5, 6]); + + raft.Wps.ShouldBe([4, 5, 6]); + } + + [Fact] // T:2695 + public void NRGNoLogResetOnCorruptedSendToFollower_ShouldSucceed() + { + var raft = new Raft { GroupName = "RG", StateValue = (int)RaftState.Leader }; + Should.NotThrow(() => raft.SendSnapshot([0, 0, 0])); + } + + [Fact] // T:635 + public void GatewayQueueSub_ShouldSucceed() + { + var outq = new JsOutQ(); + outq.SendMsg("inbox.gateway", [1, 2]).Error.ShouldBeNull(); + outq.Pop().ShouldNotBeNull(); + } + + [Fact] // T:1892 + public void JWTImportsOnServerRestartAndClientsReconnect_ShouldSucceed() + { + var account = new Account { Name = "A" }; + var cfg = new StreamConfig { Name = "RESTORE", Storage = StorageType.MemoryStorage }; + using var snapshot = new MemoryStream([1, 2, 3, 4]); + + var (stream, error) = account.RestoreStream(cfg, snapshot); + + error.ShouldBeNull(); + stream.ShouldNotBeNull(); + } + + [Fact] // T:1961 + public void LeafNodeQueueGroupDistributionWithDaisyChainAndGateway_ShouldSucceed() + { + var account = Account.NewAccount("L"); + var leaf1 = new ClientConnection(ClientKind.Leaf); + var leaf2 = new ClientConnection(ClientKind.Leaf); + + ((INatsAccount)account).AddClient(leaf1); + ((INatsAccount)account).AddClient(leaf2); + + account.NumLocalLeafNodes().ShouldBe(2); + } + + [Fact] // T:2426 + public void NoRaceJetStreamConsumerFilterPerfDegradation_ShouldSucceed() + { + var stream = CreateStream(subjects: ["perf.>"]); + stream.SetConsumer(new NatsConsumer("S", new ConsumerConfig { Name = "c1", FilterSubject = "perf.*" }, DateTime.UtcNow)); + + stream.PotentialFilteredConsumers().ShouldBeTrue(); + stream.PartitionUnique("c2", ["perf.x"]).ShouldBeFalse(); + } + + private static NatsStream CreateStream( + TimeSpan? duplicates = null, + StorageType storage = StorageType.MemoryStorage, + string[]? subjects = null, + string? storeDir = null) + { + var account = new Account { Name = "A" }; + if (!string.IsNullOrWhiteSpace(storeDir)) + account.JetStream = new JsAccount { StoreDir = storeDir }; + + var config = new StreamConfig + { + Name = "S", + Storage = storage, + Subjects = subjects ?? ["events.>"], + Retention = RetentionPolicy.InterestPolicy, + Duplicates = duplicates ?? TimeSpan.FromSeconds(1), + }; + return new NatsStream(account, config, DateTime.UtcNow); + } +} diff --git a/porting.db b/porting.db index 2a047c69ed43a71e281e4f0645284d5e268f809f..a6a6f7cfef9fe783232e0b171aa3b7065220c5ed 100644 GIT binary patch delta 2768 zcmd7UTTD}D902fh>*?v~<(#&1DXee+xkGPwL0}V{w>c3J?^NJOPq9#HZ3~OWxWXJY z#%ykL{FAM5lI_97J>W7u8#4=g^BTm0mUZ`24rcK z9P8d!v%D=9QRMz+B^s4`lxVXY4lBN`sYy~;iB$J1v3>h}k{t3$-9b4Tb@~T_L8(W^ zu=P?bB6s&G5vd<11wuVicz+;-^J4p@pwc6EAAB}N3d#LS)Z_9j6RKS`)h@T>T3Y9- zt@Et7I8HGVH?E&f{n<*@aE0n1Dg87YSeoroPhX|h@htWtoa{>6ouUwp7^Fcg(jpG& z5Rdf8fQ;(hDU;2|k29ADevX!53O&Z*iI|CYBqy;8xC&=k)gz+0 zfnOhjER;tNnpJr}>`wnod$s;45002+=~2M4PeI*hZ1|VN0SNI*r8@Fg|0`z?p0LdhlJ# z-vHG6i;F&EQ^;`9h2Y#y}D(K!vCXEk?zt1eKyPRE{c8 zC8|Qz$bp>5h1|%4YS0q26fHx`Q7u}5>QFsuK(C`W&`Q*Zn$RlrCR&ZwptWcnT94jB z8_?TmBWgyQPz&0OTG1BNhPI+@Xgk`0cA{NqH)=;-v3vpm#?b5kz9@`|ay2wf`E9{K=)c?%LcY|||NJWO^pYS&6uc6oPW*YDs;P6=4mpzT zY18(?0;k=bE-fzq1x~vwZQ6-{()KM#lXs>~n_7;Jr>}PV0%R>n?g007e}?+g1OLjh FzX2tF$jbl# delta 1426 zcmY+@ZA=?=9Ki9r>$Sbd-nDg%mi2*ll&6kT9tUGW*#i%br@7IM@od{+W+KDkoH5$r z(wYTziwnQR5QJnea4$BtIR1%QA~U{LmN*hNV<16ZNTRYAiZelfE$+o#@_F(7-~ayq zyUSgCVNw@I*RteW6lEBpD5?E;Og!Cc( zL4;|>p?nY>8Hq;Z{j$!%M53{ss?=14rph%{rm0d*m1rucsp1#amEJz^BGXl*sePLA zYpPIF1)9pApJP}zcSqd72Q~~7X_4ur;Za3q8JX%(T7=}rQwHe}i}c8VjEF-#O3e|G zgk-Yu)MSeY_t*!NuprzMZoV zRXe%Tphz^Ex;=8EQR!i63C@3}W+sIeg$OO`^Ah{0uJ-?Bg$dy!;a%Ykp;u@Z$^;** z^hbSAA6M^_zvF5%Pk*1j7P_yh>dunGSJfLi^cd*p)T6o~eHUq-Q(2lEzpJk5U^8DW zg#NP%4|G(?B4hVeQyn9ENLP$Grh}iiR8A5N${HoSbzyW+_>DZKp?!lLq9ukdT znQ4_ZwNihAUNsMyOXPlLo|!dvb4ASO%sBHtxuTkDX!y=-t`?+-d{~)EePLwVY;Knr zWeVzUs!mwQQh9RGX)d5)=1rw9;Wj7SmI=3&bBk~D&v}L0YpgP~D!WZZ@)4<9f0}L5 zg_&l$nySAZva^>jj9-FBn-&kW#jHH^d96J8$zzT4;@CUm=d2XXG>o|*G-TbAnqYu< zwpD;jZtFuB`Q77%Ym*iS%>8Rgp9sQqp~VE>WLu*!IPda9yT{s)9!vK4t>`7R3uU0) zD3kQ~Z5e}b{s&hRI5)EmpnhVrz>oiAloS3_TXiQ4IK6FPde0)j*3U}+#!b<#&M|X993G%ogmwfU3ofo%#u6< zeR56#%x&8AFum*&!S~qJoV>m4LT;3eJjjdoq8#KyxhN0iqXJZj{AeF4LIG5af~W+Q zqB2yDDo`cbkE&2LI)G}>LG&`JMRh2I>d_%|7#%@JQ3Gm3O(=E0ub^Y71+}6!)Q&pP zar7!Wfli`M6h^1eY1Dy!8v!r9?RKmz!&YlcI@JVbJdH7eXHt#=BX9(y3