using NATS.Server.Auth; using NATS.Server.Mqtt; using NATS.Server.Subscriptions; namespace NATS.Server.Mqtt.Tests; /// /// Tests for the MqttNatsClientAdapter and cross-protocol bridging concepts. /// Verifies that MQTT connections can participate in the NATS SubList and /// that topic/subject translation works end-to-end. /// public class MqttCrossProtocolTests { [Fact] public void Adapter_implements_INatsClient() { using var stream = new MemoryStream(); var listener = CreateTestListener(); var connection = new MqttConnection(stream, listener); var adapter = new MqttNatsClientAdapter(connection, 42); adapter.Id.ShouldBe((ulong)42); adapter.Kind.ShouldBe(ClientKind.Client); adapter.ClientOpts.ShouldBeNull(); } [Fact] public void Adapter_add_and_remove_subscription() { using var stream = new MemoryStream(); var listener = CreateTestListener(); var connection = new MqttConnection(stream, listener); var adapter = new MqttNatsClientAdapter(connection, 1); var account = new Account("test"); adapter.Account = account; // Add subscription var sub = adapter.AddSubscription("sensor.temp", "sid1"); sub.Subject.ShouldBe("sensor.temp"); sub.Client.ShouldBe(adapter); adapter.Subscriptions.Count.ShouldBe(1); // Verify it's in the SubList var result = account.SubList.Match("sensor.temp"); result.PlainSubs.ShouldContain(s => s.Sid == "sid1"); // Remove subscription adapter.RemoveSubscription("sid1"); adapter.Subscriptions.Count.ShouldBe(0); // Verify removed from SubList result = account.SubList.Match("sensor.temp"); result.PlainSubs.ShouldNotContain(s => s.Sid == "sid1"); } [Fact] public void Adapter_remove_all_subscriptions() { using var stream = new MemoryStream(); var listener = CreateTestListener(); var connection = new MqttConnection(stream, listener); var adapter = new MqttNatsClientAdapter(connection, 1); var account = new Account("test"); adapter.Account = account; adapter.AddSubscription("a.b", "s1"); adapter.AddSubscription("c.d", "s2"); adapter.AddSubscription("e.f", "s3"); adapter.Subscriptions.Count.ShouldBe(3); adapter.RemoveAllSubscriptions(); adapter.Subscriptions.Count.ShouldBe(0); } [Fact] public void Adapter_queue_outbound_is_noop() { using var stream = new MemoryStream(); var listener = CreateTestListener(); var connection = new MqttConnection(stream, listener); var adapter = new MqttNatsClientAdapter(connection, 1); adapter.QueueOutbound(new byte[] { 1, 2, 3 }).ShouldBeTrue(); } [Fact] public void Adapter_signal_flush_is_noop() { using var stream = new MemoryStream(); var listener = CreateTestListener(); var connection = new MqttConnection(stream, listener); var adapter = new MqttNatsClientAdapter(connection, 1); // Should not throw adapter.SignalFlush(); } [Fact] public void Topic_mapper_integration_with_sublist() { var account = new Account("test"); // Simulate an MQTT client subscribing to "sensor/+" var natsSubject = MqttTopicMapper.MqttToNats("sensor/+"); natsSubject.ShouldBe("sensor.*"); var sub = new Subscription { Subject = natsSubject, Sid = "mqtt-sub-1", }; account.SubList.Insert(sub); // Simulate a NATS publish to "sensor.temp" — should match var result = account.SubList.Match("sensor.temp"); result.PlainSubs.ShouldContain(s => s.Sid == "mqtt-sub-1"); // "sensor.humidity" should also match result = account.SubList.Match("sensor.humidity"); result.PlainSubs.ShouldContain(s => s.Sid == "mqtt-sub-1"); // "other.temp" should NOT match result = account.SubList.Match("other.temp"); result.PlainSubs.ShouldNotContain(s => s.Sid == "mqtt-sub-1"); } [Fact] public void Topic_mapper_multilevel_wildcard_with_sublist() { var account = new Account("test"); // MQTT subscribe to "home/#" var natsSubject = MqttTopicMapper.MqttToNats("home/#"); natsSubject.ShouldBe("home.>"); var sub = new Subscription { Subject = natsSubject, Sid = "mqtt-sub-2", }; account.SubList.Insert(sub); // Should match multi-level subjects account.SubList.Match("home.living.light").PlainSubs .ShouldContain(s => s.Sid == "mqtt-sub-2"); account.SubList.Match("home.kitchen").PlainSubs .ShouldContain(s => s.Sid == "mqtt-sub-2"); } [Fact] public void Adapter_mqtt_client_id_exposed() { using var stream = new MemoryStream(); var listener = CreateTestListener(); var connection = new MqttConnection(stream, listener); var adapter = new MqttNatsClientAdapter(connection, 1); // ClientId comes from the underlying connection adapter.MqttClientId.ShouldBe(string.Empty); // not yet connected } private static MqttListener CreateTestListener() => new("127.0.0.1", 0); }