using System.Text.Json; using Shouldly; using ZB.MOM.NatsNet.Server; namespace ZB.MOM.NatsNet.Server.Tests.JetStream; public sealed class ConsumerPoliciesTests { [Fact] public void ConsumerAction_StringAndJsonParity_ShouldMatchGo() { ConsumerAction.CreateOrUpdate.String().ShouldBe("\"create_or_update\""); ConsumerAction.Create.String().ShouldBe("\"create\""); ConsumerAction.Update.String().ShouldBe("\"update\""); JsonSerializer.Serialize(ConsumerAction.Create).ShouldBe("\"create\""); JsonSerializer.Deserialize("\"update\"").ShouldBe(ConsumerAction.Update); Should.Throw(() => JsonSerializer.Deserialize("\"bogus\"")); } [Fact] public void PriorityPolicy_StringAndJsonParity_ShouldMatchGo() { PriorityPolicy.PriorityNone.String().ShouldBe("\"none\""); PriorityPolicy.PriorityOverflow.String().ShouldBe("\"overflow\""); PriorityPolicy.PriorityPinnedClient.String().ShouldBe("\"pinned_client\""); PriorityPolicy.PriorityPrioritized.String().ShouldBe("\"prioritized\""); JsonSerializer.Serialize(PriorityPolicy.PriorityPinnedClient).ShouldBe("\"pinned_client\""); JsonSerializer.Deserialize("\"prioritized\"").ShouldBe(PriorityPolicy.PriorityPrioritized); Should.Throw(() => JsonSerializer.Deserialize("\"none-ish\"")); } [Fact] public void ConsumerPolicies_StringParity_ShouldMatchGo() { DeliverPolicy.DeliverByStartSequence.String().ShouldBe("by_start_sequence"); AckPolicy.AckExplicit.String().ShouldBe("explicit"); ReplayPolicy.ReplayInstant.String().ShouldBe("instant"); } [Fact] public void SubjectTokens_Subjects_RemovesEmptyValues() { var subjects = SubjectTokens.Subjects(new[] { "foo.*", string.Empty, " ", "bar.>" }); subjects.ShouldBe(["foo.*", "bar.>"]); } [Fact] public void SetConsumerConfigDefaults_InvalidNegativesInPedanticMode_ReturnsError() { var cfg = new ConsumerConfig { MaxDeliver = -2 }; var streamCfg = new StreamConfig { Name = "ORDERS", Replicas = 3 }; var err = NatsConsumer.SetConsumerConfigDefaults(cfg, streamCfg, null, pedantic: true); err.ShouldNotBeNull(); cfg.MaxDeliver.ShouldBe(-2); } [Fact] public void SetConsumerConfigDefaults_AppliesGoDefaults_ShouldPopulateExpectedValues() { var cfg = new ConsumerConfig { Durable = "D", MaxDeliver = 0, AckPolicy = AckPolicy.AckExplicit, Replicas = 0, }; var streamCfg = new StreamConfig { Name = "ORDERS", Replicas = 3 }; var limits = new JetStreamAccountLimits { MaxAckPending = 2500 }; var err = NatsConsumer.SetConsumerConfigDefaults(cfg, streamCfg, limits, pedantic: false); err.ShouldBeNull(); cfg.MaxDeliver.ShouldBe(-1); cfg.AckWait.ShouldBe(TimeSpan.FromSeconds(30)); cfg.MaxAckPending.ShouldBe(2500); cfg.Replicas.ShouldBe(3); } [Fact] public void CheckConsumerCfg_DurableNameMismatch_ReturnsError() { var cfg = new ConsumerConfig { Name = "A", Durable = "B", AckPolicy = AckPolicy.AckExplicit }; var streamCfg = new StreamConfig { Name = "ORDERS", Replicas = 1 }; var err = NatsConsumer.CheckConsumerCfg(cfg, streamCfg, null, isRecovering: false); err.ShouldNotBeNull(); err.ErrCode.ShouldBe(JsApiErrors.ConsumerCreateDurableAndNameMismatch.ErrCode); } [Fact] public void CheckConsumerCfg_OverlappingFilterSubjects_ReturnsError() { var cfg = new ConsumerConfig { Durable = "D", AckPolicy = AckPolicy.AckExplicit, FilterSubjects = ["orders.*", "orders.created"], }; var streamCfg = new StreamConfig { Name = "ORDERS", Replicas = 1 }; var err = NatsConsumer.CheckConsumerCfg(cfg, streamCfg, null, isRecovering: false); err.ShouldNotBeNull(); err.ErrCode.ShouldBe(JsApiErrors.ConsumerOverlappingSubjectFilters.ErrCode); } [Fact] public void CheckConsumerCfg_WithValidPullConfig_ReturnsNull() { var cfg = new ConsumerConfig { Durable = "D", AckPolicy = AckPolicy.AckExplicit, FilterSubject = "orders.created", }; var streamCfg = new StreamConfig { Name = "ORDERS", Replicas = 1, Retention = RetentionPolicy.LimitsPolicy, Subjects = ["orders.>"], }; var err = NatsConsumer.CheckConsumerCfg(cfg, streamCfg, null, isRecovering: false); err.ShouldBeNull(); } }