feat: add MQTT will message delivery on abnormal disconnect (Gap 6.2)
Adds WillMessage class, SetWill/ClearWill/GetWill methods to MqttSessionStore, PublishWillMessage that dispatches via OnPublish delegate (or tracks as delayed when DelayIntervalSeconds > 0), and 10 unit tests covering all will message behaviors.
This commit is contained in:
146
tests/NATS.Server.Tests/Mqtt/MqttFlowControllerTests.cs
Normal file
146
tests/NATS.Server.Tests/Mqtt/MqttFlowControllerTests.cs
Normal file
@@ -0,0 +1,146 @@
|
||||
// Go reference: server/mqtt.go — mqttMaxAckPending, flow control logic.
|
||||
|
||||
using NATS.Server.Mqtt;
|
||||
using Shouldly;
|
||||
|
||||
namespace NATS.Server.Tests.Mqtt;
|
||||
|
||||
public sealed class MqttFlowControllerTests
|
||||
{
|
||||
// 1. TryAcquire succeeds when under limit
|
||||
[Fact]
|
||||
public async Task TryAcquire_succeeds_when_under_limit()
|
||||
{
|
||||
using var fc = new MqttFlowController(defaultMaxAckPending: 1024);
|
||||
|
||||
var result = await fc.TryAcquireAsync("sub-1");
|
||||
|
||||
result.ShouldBeTrue();
|
||||
}
|
||||
|
||||
// 2. TryAcquire fails when at limit
|
||||
[Fact]
|
||||
public async Task TryAcquire_fails_when_at_limit()
|
||||
{
|
||||
using var fc = new MqttFlowController(defaultMaxAckPending: 1);
|
||||
|
||||
var first = await fc.TryAcquireAsync("sub-1");
|
||||
var second = await fc.TryAcquireAsync("sub-1");
|
||||
|
||||
first.ShouldBeTrue();
|
||||
second.ShouldBeFalse();
|
||||
}
|
||||
|
||||
// 3. Release allows next acquire
|
||||
[Fact]
|
||||
public async Task Release_allows_next_acquire()
|
||||
{
|
||||
using var fc = new MqttFlowController(defaultMaxAckPending: 1);
|
||||
|
||||
var first = await fc.TryAcquireAsync("sub-1");
|
||||
first.ShouldBeTrue();
|
||||
|
||||
// At limit — second should fail
|
||||
var atLimit = await fc.TryAcquireAsync("sub-1");
|
||||
atLimit.ShouldBeFalse();
|
||||
|
||||
fc.Release("sub-1");
|
||||
|
||||
// After release a slot is available again
|
||||
var afterRelease = await fc.TryAcquireAsync("sub-1");
|
||||
afterRelease.ShouldBeTrue();
|
||||
}
|
||||
|
||||
// 4. GetPendingCount tracks pending
|
||||
[Fact]
|
||||
public async Task GetPendingCount_tracks_pending()
|
||||
{
|
||||
using var fc = new MqttFlowController(defaultMaxAckPending: 10);
|
||||
|
||||
await fc.AcquireAsync("sub-1");
|
||||
await fc.AcquireAsync("sub-1");
|
||||
await fc.AcquireAsync("sub-1");
|
||||
|
||||
fc.GetPendingCount("sub-1").ShouldBe(3);
|
||||
}
|
||||
|
||||
// 5. GetPendingCount decrements on release
|
||||
[Fact]
|
||||
public async Task GetPendingCount_decrements_on_release()
|
||||
{
|
||||
using var fc = new MqttFlowController(defaultMaxAckPending: 10);
|
||||
|
||||
await fc.AcquireAsync("sub-1");
|
||||
await fc.AcquireAsync("sub-1");
|
||||
await fc.AcquireAsync("sub-1");
|
||||
|
||||
fc.Release("sub-1");
|
||||
|
||||
fc.GetPendingCount("sub-1").ShouldBe(2);
|
||||
}
|
||||
|
||||
// 6. GetPendingCount returns zero for unknown subscription
|
||||
[Fact]
|
||||
public void GetPendingCount_zero_for_unknown()
|
||||
{
|
||||
using var fc = new MqttFlowController();
|
||||
|
||||
fc.GetPendingCount("does-not-exist").ShouldBe(0);
|
||||
}
|
||||
|
||||
// 7. RemoveSubscription cleans up
|
||||
[Fact]
|
||||
public async Task RemoveSubscription_cleans_up()
|
||||
{
|
||||
using var fc = new MqttFlowController(defaultMaxAckPending: 10);
|
||||
|
||||
await fc.AcquireAsync("sub-1");
|
||||
fc.SubscriptionCount.ShouldBe(1);
|
||||
|
||||
fc.RemoveSubscription("sub-1");
|
||||
|
||||
fc.SubscriptionCount.ShouldBe(0);
|
||||
}
|
||||
|
||||
// 8. SubscriptionCount tracks independent subscriptions
|
||||
[Fact]
|
||||
public async Task SubscriptionCount_tracks_subscriptions()
|
||||
{
|
||||
using var fc = new MqttFlowController(defaultMaxAckPending: 10);
|
||||
|
||||
await fc.AcquireAsync("sub-a");
|
||||
await fc.AcquireAsync("sub-b");
|
||||
await fc.AcquireAsync("sub-c");
|
||||
|
||||
fc.SubscriptionCount.ShouldBe(3);
|
||||
}
|
||||
|
||||
// 9. DefaultMaxAckPending can be updated via UpdateLimit
|
||||
[Fact]
|
||||
public void DefaultMaxAckPending_can_be_updated()
|
||||
{
|
||||
using var fc = new MqttFlowController(defaultMaxAckPending: 1024);
|
||||
fc.DefaultMaxAckPending.ShouldBe(1024);
|
||||
|
||||
fc.UpdateLimit(512);
|
||||
|
||||
fc.DefaultMaxAckPending.ShouldBe(512);
|
||||
}
|
||||
|
||||
// 10. Dispose cleans up all subscriptions
|
||||
[Fact]
|
||||
public async Task Dispose_cleans_up_all()
|
||||
{
|
||||
var fc = new MqttFlowController(defaultMaxAckPending: 10);
|
||||
|
||||
await fc.AcquireAsync("sub-x");
|
||||
await fc.AcquireAsync("sub-y");
|
||||
await fc.AcquireAsync("sub-z");
|
||||
|
||||
fc.SubscriptionCount.ShouldBe(3);
|
||||
|
||||
fc.Dispose();
|
||||
|
||||
fc.SubscriptionCount.ShouldBe(0);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user