using NATS.Server.JetStream; using NATS.Server.JetStream.Models; using NATS.Server.JetStream.Validation; namespace NATS.Server.JetStream.Tests; public class JetStreamRetentionRuntimeParityTests { [Fact] public async Task Workqueue_and_interest_retention_apply_correct_eviction_rules_under_ack_and_interest_changes() { var invariantViolations = new List(); var invalidWorkQueue = JetStreamConfigValidator.Validate(new StreamConfig { Name = "WQ_INVALID", Subjects = ["wq.invalid"], Retention = RetentionPolicy.WorkQueue, MaxConsumers = 0, }); if (invalidWorkQueue.IsValid) invariantViolations.Add("WorkQueue retention accepted MaxConsumers=0."); var manager = new StreamManager(); manager.CreateOrUpdate(new StreamConfig { Name = "WQ", Subjects = ["wq.*"], Retention = RetentionPolicy.WorkQueue, MaxConsumers = 1, MaxMsgs = 1, }).Error.ShouldBeNull(); manager.CreateOrUpdate(new StreamConfig { Name = "INT", Subjects = ["int.*"], Retention = RetentionPolicy.Interest, MaxMsgsPer = 1, }).Error.ShouldBeNull(); manager.Capture("wq.a", "first"u8.ToArray()); manager.Capture("wq.a", "second"u8.ToArray()); manager.TryGet("WQ", out var wq).ShouldBeTrue(); var wqState = await wq.Store.GetStateAsync(default); if (wqState.Messages != 1) invariantViolations.Add($"WorkQueue stream expected 1 message, found {wqState.Messages}."); manager.Capture("int.a", "one"u8.ToArray()); manager.Capture("int.a", "two"u8.ToArray()); manager.TryGet("INT", out var interest).ShouldBeTrue(); var interestState = await interest.Store.GetStateAsync(default); if (interestState.Messages != 1) invariantViolations.Add($"Interest stream expected 1 message after per-subject pruning, found {interestState.Messages}."); invariantViolations.ShouldBeEmpty(); } }