// 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"); } }