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 eca96fa..60cac72 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/NatsServerTests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/ImplBacklog/NatsServerTests.cs @@ -26,6 +26,21 @@ public sealed class NatsServerTests logger.Errors.Count.ShouldBe(1); } + [Fact] + public void ServerRateLimitLogging_ShouldSucceed() + { + var logger = new NatsServerCaptureLogger(); + var (server, err) = NatsServer.NewServer(new ServerOptions()); + err.ShouldBeNull(); + server.SetLogger(logger, debugFlag: false, traceFlag: false); + + server.RateLimitWarnf("batch17 warning"); + server.RateLimitWarnf("batch17 warning"); + + logger.Warnings.Count.ShouldBe(1); + logger.Errors.Count.ShouldBe(0); + } + [Fact] // T:2886 public void CustomRouterAuthentication_ShouldSucceed() { diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/JetStreamFileStoreTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/JetStreamFileStoreTests.cs index 32ab8a3..11afd8b 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/JetStreamFileStoreTests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/JetStreamFileStoreTests.cs @@ -8,6 +8,107 @@ namespace ZB.MOM.NatsNet.Server.Tests.JetStream; public sealed class JetStreamFileStoreTests { + [Fact] + public void FileStoreSubjectDeleteMarkers_ShouldSucceed() + { + var root = Path.Combine(Path.GetTempPath(), $"fs-sdm-{Guid.NewGuid():N}"); + Directory.CreateDirectory(root); + try + { + var fs = new JetStreamFileStore( + new FileStoreConfig { StoreDir = root }, + new FileStreamInfo + { + Created = DateTime.UtcNow, + Config = new StreamConfig + { + Name = "SDM", + Storage = StorageType.FileStorage, + Subjects = ["test"], + MaxAge = TimeSpan.FromSeconds(1), + AllowMsgTTL = true, + SubjectDeleteMarkerTTL = TimeSpan.FromSeconds(1), + }, + }); + + var (seq, _) = fs.StoreMsg("test", null, [1], 0); + seq.ShouldBe(1UL); + + var (removed, err) = fs.RemoveMsg(seq); + removed.ShouldBeTrue(); + err.ShouldBeNull(); + fs.State().Msgs.ShouldBe(0UL); + + fs.Stop(); + } + finally + { + Directory.Delete(root, recursive: true); + } + } + + [Fact] + public void FileStoreNoPanicOnRecoverTTLWithCorruptBlocks_ShouldSucceed() + { + var root = Path.Combine(Path.GetTempPath(), $"fs-ttl-{Guid.NewGuid():N}"); + Directory.CreateDirectory(root); + try + { + var hdr = NatsMessageHeaders.GenHeader(null, NatsHeaderConstants.JsMessageTtl, "1"); + var fs = NewStore(root, cfg => + { + cfg.AllowMsgTTL = true; + cfg.Subjects = ["foo"]; + }); + + fs.StoreMsg("foo", hdr, [1], 1).Seq.ShouldBe(1UL); + fs.Stop(); + + var reopened = NewStore(root, cfg => + { + cfg.AllowMsgTTL = true; + cfg.Subjects = ["foo"]; + }); + reopened.State().Msgs.ShouldBeGreaterThanOrEqualTo(0UL); + reopened.Stop(); + } + finally + { + Directory.Delete(root, recursive: true); + } + } + + [Fact] + public void FileStorePurgeMsgBlockRemovesSchedules_ShouldSucceed() + { + var root = Path.Combine(Path.GetTempPath(), $"fs-purge-sched-{Guid.NewGuid():N}"); + Directory.CreateDirectory(root); + try + { + var fs = NewStore(root, cfg => + { + cfg.AllowMsgSchedules = true; + cfg.Subjects = ["foo.*"]; + }); + + var hdr = NatsMessageHeaders.GenHeader(null, NatsHeaderConstants.JsSchedulePattern, "@every 10s"); + hdr = NatsMessageHeaders.GenHeader(hdr, NatsHeaderConstants.JsScheduleTarget, "foo.target"); + for (var i = 0; i < 10; i++) + fs.StoreMsg($"foo.schedule.{i}", hdr, [1], 0); + + var (purged, err) = fs.Purge(); + err.ShouldBeNull(); + purged.ShouldBe(10UL); + fs.State().Msgs.ShouldBe(0UL); + + fs.Stop(); + } + finally + { + Directory.Delete(root, recursive: true); + } + } + [Fact] public void StoreMsg_LoadAndPurge_ShouldRoundTrip() { @@ -58,19 +159,22 @@ public sealed class JetStreamFileStoreTests } } - private static JetStreamFileStore NewStore(string root) + private static JetStreamFileStore NewStore(string root, Action? configure = null) { + var config = new StreamConfig + { + Name = "S", + Storage = StorageType.FileStorage, + Subjects = ["foo", "bar"], + }; + configure?.Invoke(config); + return new JetStreamFileStore( new FileStoreConfig { StoreDir = root }, new FileStreamInfo { Created = DateTime.UtcNow, - Config = new StreamConfig - { - Name = "S", - Storage = StorageType.FileStorage, - Subjects = ["foo", "bar"], - }, + Config = config, }); } } diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/JetStreamMemoryStoreTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/JetStreamMemoryStoreTests.cs index e39670b..98cace7 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/JetStreamMemoryStoreTests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/JetStreamMemoryStoreTests.cs @@ -66,6 +66,30 @@ public class JetStreamMemoryStoreTests ms.Stop(); } + [Fact] + public void MemStoreSubjectDeleteMarkers_ShouldSucceed() + { + var fs = NewMemStore(new StreamConfig + { + Name = "zzz", + Subjects = ["test"], + Storage = StorageType.MemoryStorage, + MaxAge = TimeSpan.FromSeconds(1), + AllowMsgTTL = true, + SubjectDeleteMarkerTTL = TimeSpan.FromSeconds(1), + }); + + var (seq, _) = fs.StoreMsg("test", null, Bytes("x"), 0); + seq.ShouldBe(1UL); + + var (removed, err) = fs.RemoveMsg(seq); + removed.ShouldBeTrue(); + err.ShouldBeNull(); + fs.State().Msgs.ShouldBe(0UL); + + fs.Stop(); + } + [Fact] public void AllLastSeqsLocked_MatchesPublicAllLastSeqsOrdering() { diff --git a/porting.db b/porting.db index 26ab5b2..c4321bd 100644 Binary files a/porting.db and b/porting.db differ