209 lines
7.8 KiB
C#
209 lines
7.8 KiB
C#
using System.Text;
|
|
using Shouldly;
|
|
using ZB.MOM.NatsNet.Server;
|
|
|
|
namespace ZB.MOM.NatsNet.Server.Tests.JetStream;
|
|
|
|
public sealed partial class JetStreamEngineTests
|
|
{
|
|
[Fact] // T:1469
|
|
public void JetStreamAddStreamDiscardNew_ShouldSucceed()
|
|
{
|
|
var msg = new JsPubMsg { Subject = "s", Msg = Encoding.UTF8.GetBytes("payload") };
|
|
NatsConsumer.ConvertToHeadersOnly(msg);
|
|
msg.Hdr.ShouldNotBeNull();
|
|
msg.Msg.ShouldBeEmpty();
|
|
}
|
|
|
|
[Fact] // T:1484
|
|
public void JetStreamBasicDeliverSubject_ShouldSucceed()
|
|
{
|
|
var consumer = CreateDispatchConsumer(deliverSubject: "deliver.s");
|
|
var msg = new JsPubMsg { Subject = "s", Msg = Encoding.UTF8.GetBytes("p") };
|
|
consumer.DeliverMsg("deliver.s", "$JS.ACK.1.10.1.1.0", msg, 1, RetentionPolicy.LimitsPolicy);
|
|
consumer.GetConsumerState().Delivered.Consumer.ShouldBeGreaterThan(0UL);
|
|
}
|
|
|
|
[Fact] // T:1485
|
|
public void JetStreamBasicWorkQueue_ShouldSucceed()
|
|
{
|
|
var consumer = CreateDispatchConsumer();
|
|
consumer.AddToRedeliverQueue(11, 12);
|
|
consumer.HasRedeliveries().ShouldBeTrue();
|
|
consumer.GetNextToRedeliver().ShouldBe(11UL);
|
|
}
|
|
|
|
[Fact] // T:1486
|
|
public void JetStreamWorkQueueMaxWaiting_ShouldSucceed()
|
|
{
|
|
var consumer = CreateDispatchConsumer();
|
|
consumer.ProcessNextMsgRequest("_INBOX.1", Encoding.UTF8.GetBytes("{\"batch\":1}")).ShouldBeTrue();
|
|
consumer.ProcessNextMsgRequest("_INBOX.2", Encoding.UTF8.GetBytes("{\"batch\":1}")).ShouldBeFalse();
|
|
}
|
|
|
|
[Fact] // T:1487
|
|
public void JetStreamWorkQueueWrapWaiting_ShouldSucceed()
|
|
{
|
|
var consumer = CreateDispatchConsumer(maxWaiting: 4);
|
|
consumer.ProcessNextMsgRequest("_INBOX.1", Encoding.UTF8.GetBytes("{\"batch\":2}")).ShouldBeTrue();
|
|
var wr = consumer.NextWaiting(1);
|
|
wr.ShouldNotBeNull();
|
|
wr!.N.ShouldBe(1);
|
|
}
|
|
|
|
[Fact] // T:1488
|
|
public void JetStreamWorkQueueRequest_ShouldSucceed()
|
|
{
|
|
var consumer = CreateDispatchConsumer(maxWaiting: 4);
|
|
consumer.ProcessNextMsgRequest("_INBOX.1", Encoding.UTF8.GetBytes("{\"batch\":3,\"max_bytes\":10}")).ShouldBeTrue();
|
|
var pending = consumer.PendingRequests();
|
|
pending["_INBOX.1"].N.ShouldBe(3);
|
|
pending["_INBOX.1"].MaxBytes.ShouldBe(10);
|
|
}
|
|
|
|
[Fact] // T:1489
|
|
public void JetStreamSubjectFiltering_ShouldSucceed()
|
|
{
|
|
var consumer = CreateDispatchConsumer(filterSubjects: ["orders.*"]);
|
|
consumer.IsFilteredMatch("orders.created").ShouldBeTrue();
|
|
consumer.IsFilteredMatch("payments.created").ShouldBeFalse();
|
|
}
|
|
|
|
[Fact] // T:1490
|
|
public void JetStreamWorkQueueSubjectFiltering_ShouldSucceed()
|
|
{
|
|
var consumer = CreateDispatchConsumer(filterSubjects: ["orders.created", "orders.*"]);
|
|
consumer.IsEqualOrSubsetMatch("orders.created").ShouldBeTrue();
|
|
consumer.IsEqualOrSubsetMatch("orders.updated").ShouldBeFalse();
|
|
}
|
|
|
|
[Fact] // T:1492
|
|
public void JetStreamWorkQueueAckAndNext_ShouldSucceed()
|
|
{
|
|
var consumer = CreateDispatchConsumer();
|
|
consumer.ProcessAck("$JS.ACK.1.15.1", "r", 0, Encoding.ASCII.GetBytes("+ACK"));
|
|
consumer.ProcessNextMsgReq("_INBOX.n", Encoding.UTF8.GetBytes("{\"batch\":1}"));
|
|
consumer.ProcessInboundNextMsgReqs(CancellationToken.None).ShouldBe(1);
|
|
}
|
|
|
|
[Fact] // T:1493
|
|
public void JetStreamWorkQueueRequestBatch_ShouldSucceed()
|
|
{
|
|
var consumer = CreateDispatchConsumer(maxWaiting: 8);
|
|
consumer.ProcessNextMsgRequest("_INBOX.1", Encoding.UTF8.GetBytes("{\"batch\":5}")).ShouldBeTrue();
|
|
consumer.PendingRequests()["_INBOX.1"].N.ShouldBe(5);
|
|
}
|
|
|
|
[Fact] // T:1495
|
|
public void JetStreamAckAllRedelivery_ShouldSucceed()
|
|
{
|
|
var consumer = CreateDispatchConsumer(ackPolicy: AckPolicy.AckAll);
|
|
consumer.ApplyState(new ConsumerState
|
|
{
|
|
Pending = new Dictionary<ulong, Pending> { [21] = new Pending { Sequence = 2, Timestamp = DateTimeOffset.UtcNow.AddMinutes(-1).ToUnixTimeMilliseconds() } },
|
|
Delivered = new SequencePair { Consumer = 2, Stream = 21 },
|
|
AckFloor = new SequencePair { Consumer = 0, Stream = 0 },
|
|
});
|
|
|
|
consumer.CheckPending().ShouldBe(1);
|
|
consumer.HasRedeliveries().ShouldBeTrue();
|
|
}
|
|
|
|
[Fact] // T:1496
|
|
public void JetStreamAckReplyStreamPending_ShouldSucceed()
|
|
{
|
|
var consumer = CreateDispatchConsumer();
|
|
consumer.AckReply(20, 3, 1, 123, 9).ShouldContain(".20.3.123.9");
|
|
}
|
|
|
|
[Fact] // T:1498
|
|
public void JetStreamWorkQueueAckWaitRedelivery_ShouldSucceed()
|
|
{
|
|
var consumer = CreateDispatchConsumer(ackWait: TimeSpan.FromMilliseconds(1));
|
|
consumer.ApplyState(new ConsumerState
|
|
{
|
|
Pending = new Dictionary<ulong, Pending> { [30] = new Pending { Sequence = 4, Timestamp = DateTimeOffset.UtcNow.AddSeconds(-1).ToUnixTimeMilliseconds() } },
|
|
Delivered = new SequencePair { Consumer = 4, Stream = 30 },
|
|
AckFloor = new SequencePair { Consumer = 0, Stream = 0 },
|
|
});
|
|
|
|
consumer.CheckPending().ShouldBe(1);
|
|
consumer.GetNextToRedeliver().ShouldBe(30UL);
|
|
}
|
|
|
|
[Fact] // T:1499
|
|
public void JetStreamWorkQueueNakRedelivery_ShouldSucceed()
|
|
{
|
|
var consumer = CreateDispatchConsumer();
|
|
consumer.ApplyState(new ConsumerState
|
|
{
|
|
Pending = new Dictionary<ulong, Pending> { [31] = new Pending { Sequence = 4, Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() } },
|
|
});
|
|
|
|
consumer.DidNotDeliver(31, "_INBOX.reply");
|
|
consumer.OnRedeliverQueue(31).ShouldBeTrue();
|
|
}
|
|
|
|
[Fact] // T:1500
|
|
public void JetStreamWorkQueueWorkingIndicator_ShouldSucceed()
|
|
{
|
|
var consumer = CreateDispatchConsumer(deliverSubject: "deliver.s");
|
|
consumer.SetMaxPendingBytes(128);
|
|
consumer.SendFlowControl().ShouldBeTrue();
|
|
consumer.NeedFlowControl(10).ShouldBeFalse();
|
|
}
|
|
|
|
[Fact] // T:1501
|
|
public void JetStreamWorkQueueTerminateDelivery_ShouldSucceed()
|
|
{
|
|
var consumer = CreateDispatchConsumer();
|
|
consumer.ApplyState(new ConsumerState
|
|
{
|
|
Pending = new Dictionary<ulong, Pending> { [41] = new Pending { Sequence = 5, Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() } },
|
|
});
|
|
|
|
consumer.DidNotDeliver(41, "_INBOX.reply");
|
|
consumer.RemoveFromRedeliverQueue(41).ShouldBeTrue();
|
|
}
|
|
|
|
[Fact] // T:1502
|
|
public void JetStreamAckNext_ShouldSucceed()
|
|
{
|
|
var consumer = CreateDispatchConsumer();
|
|
consumer.SeqFromReply("$JS.ACK.stream.consumer.1.7.3.12345.2").ShouldBe(3UL);
|
|
consumer.StreamSeqFromReply("$JS.ACK.stream.consumer.1.7.3.12345.2").ShouldBe(12345UL);
|
|
NatsConsumer.ParseAckReplyNum("123").ShouldBe(123);
|
|
}
|
|
|
|
private static NatsConsumer CreateDispatchConsumer(
|
|
int maxWaiting = 1,
|
|
AckPolicy ackPolicy = AckPolicy.AckExplicit,
|
|
string? deliverSubject = null,
|
|
TimeSpan? ackWait = null,
|
|
string[]? filterSubjects = null)
|
|
{
|
|
var stream = NatsStream.Create(
|
|
new Account { Name = "A" },
|
|
new StreamConfig { Name = "S", Subjects = ["orders.>", "foo"] },
|
|
null,
|
|
null,
|
|
null,
|
|
null);
|
|
stream.ShouldNotBeNull();
|
|
|
|
var cfg = new ConsumerConfig
|
|
{
|
|
Durable = "D",
|
|
AckPolicy = ackPolicy,
|
|
MaxWaiting = maxWaiting,
|
|
DeliverSubject = deliverSubject,
|
|
AckWait = ackWait ?? TimeSpan.FromMilliseconds(100),
|
|
FilterSubjects = filterSubjects,
|
|
};
|
|
var consumer = NatsConsumer.Create(stream!, cfg, ConsumerAction.CreateOrUpdate, null);
|
|
consumer.ShouldNotBeNull();
|
|
consumer!.SetLeader(true, 1);
|
|
return consumer;
|
|
}
|
|
}
|