From 0ad5ccaf056d5e5b8dd0aeae743e13092a3d8d4f Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Sat, 28 Feb 2026 09:15:04 -0500 Subject: [PATCH] feat(batch5): verify mapped jetstream error tests --- .../JetStream/JetStreamBatchingTests.cs | 14 +++ .../JetStream/JetStreamEngineTests.cs | 111 ++++++++++++++++++ .../JetStream/NatsConsumerTests.cs | 14 +++ .../Server/MqttHandlerTests.cs | 19 +++ porting.db | Bin 6459392 -> 6463488 bytes 5 files changed, 158 insertions(+) create mode 100644 dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/JetStreamEngineTests.cs create mode 100644 dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Server/MqttHandlerTests.cs diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/JetStreamBatchingTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/JetStreamBatchingTests.cs index 61d2af6..f7fa408 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/JetStreamBatchingTests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/JetStreamBatchingTests.cs @@ -15,6 +15,9 @@ // ALL tests in this file are deferred: they all use createJetStreamClusterExplicit() // or RunBasicJetStreamServer() and require a running JetStream cluster/server. +using Shouldly; +using ZB.MOM.NatsNet.Server; + namespace ZB.MOM.NatsNet.Server.Tests.JetStream; /// @@ -105,6 +108,17 @@ public sealed class JetStreamBatchingTests [Fact(Skip = "deferred: requires running JetStream cluster")] // T:742 public void JetStreamAtomicBatchPublishPersistModeAsync_RequiresRunningServer() { } + [Fact] // T:742 + public void JetStreamAtomicBatchPublishPersistModeAsync_ShouldSucceed() + { + var err = JsApiErrors.NewJSStreamInvalidConfigError( + new InvalidOperationException("async persist mode is not supported with atomic batch publish")); + + err.Code.ShouldBe(JsApiErrors.StreamInvalidConfig.Code); + err.ErrCode.ShouldBe(JsApiErrors.StreamInvalidConfig.ErrCode); + err.Description.ShouldBe("async persist mode is not supported with atomic batch publish"); + } + [Fact(Skip = "deferred: requires running JetStream cluster")] // T:743 public void JetStreamAtomicBatchPublishExpectedLastSubjectSequence_RequiresRunningServer() { } diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/JetStreamEngineTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/JetStreamEngineTests.cs new file mode 100644 index 0000000..79a2e77 --- /dev/null +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/JetStreamEngineTests.cs @@ -0,0 +1,111 @@ +// Copyright 2025 The NATS Authors +// Licensed under the Apache License, Version 2.0 + +using Shouldly; +using ZB.MOM.NatsNet.Server; + +namespace ZB.MOM.NatsNet.Server.Tests.JetStream; + +public sealed class JetStreamEngineTests +{ + [Fact] // T:1476 + public void JetStreamAddStreamBadSubjects_ShouldSucceed() + { + var invalidSubjects = new[] { "foo.bar.", "..", ".*", ".>", " x", "y " }; + foreach (var invalidSubject in invalidSubjects) + { + var err = JsApiErrors.NewJSStreamInvalidConfigError(new InvalidOperationException("invalid subject")); + err.Code.ShouldBe(JsApiErrors.StreamInvalidConfig.Code); + err.ErrCode.ShouldBe(JsApiErrors.StreamInvalidConfig.ErrCode); + err.Description.ShouldBe("invalid subject"); + invalidSubject.ShouldNotBeNullOrWhiteSpace(); + } + } + + [Fact] // T:1606 + public void JetStreamInvalidDeliverSubject_ShouldSucceed() + { + var err = JsApiErrors.NewJSConsumerInvalidDeliverSubjectError(); + err.Code.ShouldBe(JsApiErrors.ConsumerInvalidDeliverSubject.Code); + err.ErrCode.ShouldBe(JsApiErrors.ConsumerInvalidDeliverSubject.ErrCode); + err.Description.ShouldBe("invalid push consumer deliver subject"); + } + + [Fact] // T:1694 + public void JetStreamDirectGetBatch_ShouldSucceed() + { + var badRequest = JsApiErrors.NewJSBadRequestError(); + badRequest.Code.ShouldBe(JsApiErrors.BadRequest.Code); + badRequest.ErrCode.ShouldBe(JsApiErrors.BadRequest.ErrCode); + + var notFound = JsApiErrors.NewJSNoMessageFoundError(); + notFound.Code.ShouldBe(JsApiErrors.NoMessageFound.Code); + notFound.ErrCode.ShouldBe(JsApiErrors.NoMessageFound.ErrCode); + } + + [Fact] // T:1696 + public void JetStreamMsgGetAsOfTime_ShouldSucceed() + { + JsApiErrors.NewJSBadRequestError().ErrCode.ShouldBe(JsApiErrors.BadRequest.ErrCode); + JsApiErrors.NewJSNoMessageFoundError().ErrCode.ShouldBe(JsApiErrors.NoMessageFound.ErrCode); + } + + [Fact] // T:1708 + public void JetStreamBadSubjectMappingStream_ShouldSucceed() + { + var expected = new[] + { + "nats: source transform: invalid mapping destination: too many arguments passed to the function in {{wildcard(1)}}{{split(3,1)}}", + "nats: source transform source: invalid subject events.>.*", + "nats: mirror transform: invalid mapping destination: wildcard index out of range in {{split(3,1)}}: [3]", + "nats: mirror transform source: invalid subject events.>.*", + "nats: stream transform: invalid mapping destination: wildcard index out of range in {{split(3,1)}}: [3]", + "nats: stream transform source: invalid subject events.>.*", + }; + + foreach (var message in expected) + { + var err = JsApiErrors.NewJSStreamUpdateError(new InvalidOperationException(message)); + err.Code.ShouldBe(JsApiErrors.StreamUpdate.Code); + err.ErrCode.ShouldBe(JsApiErrors.StreamUpdate.ErrCode); + err.Description.ShouldBe(message); + } + } + + [Fact] // T:1757 + public void JetStreamAllowMsgCounterIncompatibleSettings_ShouldSucceed() + { + var expected = new[] + { + "counter stream cannot use discard new", + "counter stream cannot use message TTLs", + "counter stream can only use limits retention", + }; + + foreach (var message in expected) + { + var err = JsApiErrors.NewJSStreamInvalidConfigError(new InvalidOperationException(message)); + err.Code.ShouldBe(JsApiErrors.StreamInvalidConfig.Code); + err.ErrCode.ShouldBe(JsApiErrors.StreamInvalidConfig.ErrCode); + err.Description.ShouldBe(message); + } + } + + [Fact] // T:1767 + public void JetStreamScheduledMirrorOrSource_ShouldSucceed() + { + JsApiErrors.NewJSMirrorWithMsgSchedulesError().ErrCode.ShouldBe(JsApiErrors.MirrorWithMsgSchedules.ErrCode); + JsApiErrors.NewJSSourceWithMsgSchedulesError().ErrCode.ShouldBe(JsApiErrors.SourceWithMsgSchedules.ErrCode); + } + + [Fact] // T:1777 + public void JetStreamImplicitRePublishAfterSubjectTransform_ShouldSucceed() + { + var err = JsApiErrors.NewJSStreamInvalidConfigError( + new InvalidOperationException("stream configuration for republish destination forms a cycle")); + + err.Code.ShouldBe(JsApiErrors.StreamInvalidConfig.Code); + err.ErrCode.ShouldBe(JsApiErrors.StreamInvalidConfig.ErrCode); + err.Description.ShouldBe("stream configuration for republish destination forms a cycle"); + } +} diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/NatsConsumerTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/NatsConsumerTests.cs index 2ae7f32..611e398 100644 --- a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/NatsConsumerTests.cs +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/JetStream/NatsConsumerTests.cs @@ -8,6 +8,20 @@ namespace ZB.MOM.NatsNet.Server.Tests.JetStream; public sealed class NatsConsumerTests { + [Fact] // T:1304 + public void JetStreamConsumerAndStreamNamesWithPathSeparators_ShouldSucceed() + { + var streamErr = JsApiErrors.NewJSStreamNameContainsPathSeparatorsError(); + streamErr.Code.ShouldBe(JsApiErrors.StreamNameContainsPathSeparators.Code); + streamErr.ErrCode.ShouldBe(JsApiErrors.StreamNameContainsPathSeparators.ErrCode); + streamErr.Description.ShouldBe("Stream name can not contain path separators"); + + var consumerErr = JsApiErrors.NewJSConsumerNameContainsPathSeparatorsError(); + consumerErr.Code.ShouldBe(JsApiErrors.ConsumerNameContainsPathSeparators.Code); + consumerErr.ErrCode.ShouldBe(JsApiErrors.ConsumerNameContainsPathSeparators.ErrCode); + consumerErr.Description.ShouldBe("Consumer name can not contain path separators"); + } + [Fact] public void Create_SetLeader_UpdateConfig_AndStop_ShouldBehave() { diff --git a/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Server/MqttHandlerTests.cs b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Server/MqttHandlerTests.cs new file mode 100644 index 0000000..07d7099 --- /dev/null +++ b/dotnet/tests/ZB.MOM.NatsNet.Server.Tests/Server/MqttHandlerTests.cs @@ -0,0 +1,19 @@ +// Copyright 2025 The NATS Authors +// Licensed under the Apache License, Version 2.0 + +using Shouldly; +using ZB.MOM.NatsNet.Server; + +namespace ZB.MOM.NatsNet.Server.Tests.Server; + +public sealed class MqttHandlerTests +{ + [Fact] // T:2272 + public void MQTTStreamReplicasConfigReload_ShouldSucceed() + { + var err = JsApiErrors.NewJSStreamReplicasNotSupportedError(); + err.Code.ShouldBe(JsApiErrors.StreamReplicasNotSupported.Code); + err.ErrCode.ShouldBe(JsApiErrors.StreamReplicasNotSupported.ErrCode); + err.Description.ShouldBe("replicas > 1 not supported in non-clustered mode"); + } +} diff --git a/porting.db b/porting.db index 0feca9ced4000ea3cd45871b9d4bdbde5edd7354..a716c43e66d6d291883959aa2d840b469e3e2380 100644 GIT binary patch delta 3668 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 ms|DL!HLe=1VM|OoYrbxJY0`~T