Move 29 MQTT test files from NATS.Server.Tests into a dedicated NATS.Server.Mqtt.Tests project. Update namespaces, add InternalsVisibleTo, and replace Task.Delay calls with PollHelper.WaitUntilAsync for proper synchronization.
93 lines
3.1 KiB
C#
93 lines
3.1 KiB
C#
using NSubstitute;
|
|
using NATS.Server.JetStream.Storage;
|
|
using NATS.Server.Mqtt;
|
|
|
|
namespace NATS.Server.Mqtt.Tests;
|
|
|
|
// Go reference: server/mqtt.go ($MQTT_msgs, $MQTT_sess, $MQTT_rmsgs JetStream streams)
|
|
|
|
public class MqttPersistenceTests
|
|
{
|
|
[Fact]
|
|
public async Task Session_persists_across_restart()
|
|
{
|
|
// Go reference: server/mqtt.go mqttStoreSession — session survives restart
|
|
var store = MqttSessionStoreTestHelper.CreateWithJetStream();
|
|
|
|
await store.ConnectAsync("client-1", cleanSession: false);
|
|
store.AddSubscription("client-1", "topic/test", qos: 1);
|
|
await store.SaveSessionAsync("client-1");
|
|
|
|
// Simulate restart — new store backed by the same IStreamStore
|
|
var recovered = MqttSessionStoreTestHelper.CreateWithJetStream(store.BackingStore!);
|
|
await recovered.ConnectAsync("client-1", cleanSession: false);
|
|
|
|
var subs = recovered.GetSubscriptions("client-1");
|
|
subs.ShouldContainKey("topic/test");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Clean_session_deletes_existing()
|
|
{
|
|
// Go reference: server/mqtt.go cleanSession=true deletes saved state
|
|
var store = MqttSessionStoreTestHelper.CreateWithJetStream();
|
|
|
|
await store.ConnectAsync("client-2", cleanSession: false);
|
|
store.AddSubscription("client-2", "persist/me", qos: 1);
|
|
await store.SaveSessionAsync("client-2");
|
|
|
|
// Reconnect with clean session
|
|
await store.ConnectAsync("client-2", cleanSession: true);
|
|
|
|
var subs = store.GetSubscriptions("client-2");
|
|
subs.ShouldBeEmpty();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Retained_message_survives_restart()
|
|
{
|
|
// Go reference: server/mqtt.go retained message persistence via JetStream
|
|
var retained = MqttRetainedStoreTestHelper.CreateWithJetStream();
|
|
|
|
await retained.SetRetainedAsync("sensors/temp", "72.5"u8.ToArray());
|
|
|
|
// Simulate restart
|
|
var recovered = MqttRetainedStoreTestHelper.CreateWithJetStream(retained.BackingStore!);
|
|
var msg = await recovered.GetRetainedAsync("sensors/temp");
|
|
|
|
msg.ShouldNotBeNull();
|
|
System.Text.Encoding.UTF8.GetString(msg).ShouldBe("72.5");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Retained_message_cleared_with_empty_payload()
|
|
{
|
|
// Go reference: server/mqtt.go empty payload clears retained
|
|
var retained = MqttRetainedStoreTestHelper.CreateWithJetStream();
|
|
|
|
await retained.SetRetainedAsync("sensors/temp", "72.5"u8.ToArray());
|
|
await retained.SetRetainedAsync("sensors/temp", ReadOnlyMemory<byte>.Empty); // clear
|
|
|
|
var msg = await retained.GetRetainedAsync("sensors/temp");
|
|
msg.ShouldBeNull();
|
|
}
|
|
}
|
|
|
|
public static class MqttSessionStoreTestHelper
|
|
{
|
|
public static MqttSessionStore CreateWithJetStream(IStreamStore? backingStore = null)
|
|
{
|
|
var store = backingStore ?? new MemStore();
|
|
return new MqttSessionStore(store);
|
|
}
|
|
}
|
|
|
|
public static class MqttRetainedStoreTestHelper
|
|
{
|
|
public static MqttRetainedStore CreateWithJetStream(IStreamStore? backingStore = null)
|
|
{
|
|
var store = backingStore ?? new MemStore();
|
|
return new MqttRetainedStore(store);
|
|
}
|
|
}
|