Phase 1: Binary MQTT 3.1.1 wire protocol with PipeReader-based parsing, full packet type dispatch, and MQTT 3.1.1 compliance checks. Phase 2: Auth pipeline routing MQTT CONNECT through AuthService, TLS transport with SslStream wrapping, pinned cert validation. Phase 3: IMessageRouter refactor (NatsClient → INatsClient), MqttNatsClientAdapter for cross-protocol bridging, MqttTopicMapper with full Go-parity topic/subject translation. Phase 4: /connz mqtt_client field population, /varz actual MQTT port. Phase 5: JetStream persistence — MqttStreamInitializer creates 5 internal streams, MqttConsumerManager for QoS 1/2 consumers, subject-keyed session/retained lookups replacing linear scans. All 503 MQTT tests and 1589 Core tests pass.
136 lines
4.1 KiB
C#
136 lines
4.1 KiB
C#
// Tests for MqttQoS1Tracker (Gap 6.3 — JetStream-backed QoS 1/2 tracking).
|
|
// Go reference: golang/nats-server/server/mqtt.go mqttProcessPub (~line 1200).
|
|
|
|
using NATS.Server.Mqtt;
|
|
using Shouldly;
|
|
|
|
namespace NATS.Server.Mqtt.Tests.Mqtt;
|
|
|
|
public sealed class MqttQoSTrackingTests
|
|
{
|
|
// ── QoS 1 Tracker ────────────────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public void Register_assigns_packet_id()
|
|
{
|
|
// Go reference: server/mqtt.go mqttProcessPub — assigns non-zero packet ID for QoS 1
|
|
var tracker = new MqttQoS1Tracker();
|
|
|
|
var id = tracker.Register("sensors/temp", [0x01, 0x02]);
|
|
|
|
id.ShouldNotBe((ushort)0);
|
|
}
|
|
|
|
[Fact]
|
|
public void Register_increments_packet_id()
|
|
{
|
|
// Go reference: server/mqtt.go — each outgoing QoS 1 message gets a unique packet ID
|
|
var tracker = new MqttQoS1Tracker();
|
|
|
|
var id1 = tracker.Register("sensors/temp", [0x01]);
|
|
var id2 = tracker.Register("sensors/humidity", [0x02]);
|
|
|
|
id1.ShouldNotBe(id2);
|
|
}
|
|
|
|
[Fact]
|
|
public void Acknowledge_removes_pending()
|
|
{
|
|
// Go reference: server/mqtt.go mqttProcessPubAck — removes message from pending set
|
|
var tracker = new MqttQoS1Tracker();
|
|
var id = tracker.Register("sensors/temp", [0xAB]);
|
|
|
|
tracker.PendingCount.ShouldBe(1);
|
|
var removed = tracker.Acknowledge(id);
|
|
|
|
removed.ShouldNotBeNull();
|
|
tracker.PendingCount.ShouldBe(0);
|
|
}
|
|
|
|
[Fact]
|
|
public void Acknowledge_returns_null_for_unknown()
|
|
{
|
|
// Go reference: server/mqtt.go — PUBACK for unknown packet ID is silently ignored
|
|
var tracker = new MqttQoS1Tracker();
|
|
|
|
var result = tracker.Acknowledge(9999);
|
|
|
|
result.ShouldBeNull();
|
|
}
|
|
|
|
[Fact]
|
|
public void PendingCount_reflects_current_state()
|
|
{
|
|
// Register 3 messages, acknowledge 1, expect count of 2
|
|
var tracker = new MqttQoS1Tracker();
|
|
var id1 = tracker.Register("a/b", [1]);
|
|
tracker.Register("c/d", [2]);
|
|
tracker.Register("e/f", [3]);
|
|
|
|
tracker.Acknowledge(id1);
|
|
|
|
tracker.PendingCount.ShouldBe(2);
|
|
}
|
|
|
|
[Fact]
|
|
public void IsPending_true_for_registered()
|
|
{
|
|
// Go reference: server/mqtt.go — registered QoS 1 message is in the pending set
|
|
var tracker = new MqttQoS1Tracker();
|
|
var id = tracker.Register("topic/x", [0xFF]);
|
|
|
|
tracker.IsPending(id).ShouldBeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void IsPending_false_after_acknowledge()
|
|
{
|
|
// Go reference: server/mqtt.go — message is removed from pending after PUBACK
|
|
var tracker = new MqttQoS1Tracker();
|
|
var id = tracker.Register("topic/x", [0xFF]);
|
|
tracker.Acknowledge(id);
|
|
|
|
tracker.IsPending(id).ShouldBeFalse();
|
|
}
|
|
|
|
[Fact]
|
|
public void GetPendingForRedelivery_returns_all_pending()
|
|
{
|
|
// Go reference: server/mqtt.go reconnect path — all unacked messages are redelivered
|
|
var tracker = new MqttQoS1Tracker();
|
|
tracker.Register("a", [1]);
|
|
tracker.Register("b", [2]);
|
|
tracker.Register("c", [3]);
|
|
|
|
var pending = tracker.GetPendingForRedelivery();
|
|
|
|
pending.Count.ShouldBe(3);
|
|
}
|
|
|
|
[Fact]
|
|
public void GetPendingForRedelivery_increments_delivery_count()
|
|
{
|
|
// Go reference: server/mqtt.go reconnect redelivery — DUP flag set, delivery count increments
|
|
var tracker = new MqttQoS1Tracker();
|
|
tracker.Register("test/topic", [0xDE, 0xAD]);
|
|
|
|
var pending = tracker.GetPendingForRedelivery();
|
|
|
|
pending[0].DeliveryCount.ShouldBe(2);
|
|
}
|
|
|
|
[Fact]
|
|
public void Clear_removes_all_pending()
|
|
{
|
|
// Go reference: server/mqtt.go session cleanup — all pending messages discarded on clean session
|
|
var tracker = new MqttQoS1Tracker();
|
|
tracker.Register("x", [1]);
|
|
tracker.Register("y", [2]);
|
|
tracker.Register("z", [3]);
|
|
|
|
tracker.Clear();
|
|
|
|
tracker.PendingCount.ShouldBe(0);
|
|
}
|
|
}
|