feat(mqtt): implement QoS 1 PUBACK and QoS 2 PUBREC/PUBREL/PUBCOMP handlers
QoS 1: deliver message then send PUBACK. QoS 2: store in-memory pending PUBREL, send PUBREC; on PUBREL deliver and send PUBCOMP. Wire all four PI-packet dispatches (PUBACK/PUBREC/PUBREL/PUBCOMP) in parser. Add QoS2Pending dictionary to MqttHandler for in-memory QoS 2 tracking. 6 new tests for QoS 1/2 flows including full QoS 2 handshake.
This commit is contained in:
@@ -174,14 +174,14 @@ public sealed class MqttPubSubTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Parser_PublishQoS1_ShouldReturnNotImplemented()
|
||||
public void Parser_PublishQoS1_ShouldSendPubAck()
|
||||
{
|
||||
var c = CreateConnectedMqttClient();
|
||||
|
||||
// PUBLISH QoS 1: type=0x32, topic="t", PI=1, payload="x"
|
||||
// PUBLISH QoS 1: type=0x32, topic="t", PI=5, payload="x"
|
||||
var data = new List<byte>();
|
||||
data.Add(0x00); data.Add(0x01); data.Add((byte)'t'); // topic
|
||||
data.Add(0x00); data.Add(0x01); // PI = 1
|
||||
data.Add(0x00); data.Add(0x05); // PI = 5
|
||||
data.Add((byte)'x'); // payload
|
||||
|
||||
var buf = new List<byte>();
|
||||
@@ -190,8 +190,146 @@ public sealed class MqttPubSubTests
|
||||
buf.AddRange(data);
|
||||
|
||||
var err = MqttParser.Parse(c, buf.ToArray(), buf.Count);
|
||||
err.ShouldNotBeNull();
|
||||
err.ShouldBeOfType<NotImplementedException>();
|
||||
err.ShouldBeNull();
|
||||
|
||||
// Verify PUBACK: [0x40] [0x02] [PI high] [PI low]
|
||||
var ms = GetStream(c);
|
||||
var written = ms.ToArray();
|
||||
written.Length.ShouldBe(4);
|
||||
written[0].ShouldBe(MqttPacket.PubAck); // 0x40
|
||||
written[1].ShouldBe((byte)0x02);
|
||||
written[2].ShouldBe((byte)0x00); // PI high
|
||||
written[3].ShouldBe((byte)0x05); // PI low
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Parser_PublishQoS2_ShouldSendPubRec()
|
||||
{
|
||||
var c = CreateConnectedMqttClient();
|
||||
|
||||
// PUBLISH QoS 2: type=0x34, topic="t", PI=10, payload="y"
|
||||
var data = new List<byte>();
|
||||
data.Add(0x00); data.Add(0x01); data.Add((byte)'t'); // topic
|
||||
data.Add(0x00); data.Add(0x0A); // PI = 10
|
||||
data.Add((byte)'y'); // payload
|
||||
|
||||
var buf = new List<byte>();
|
||||
buf.Add((byte)(MqttPacket.Pub | MqttPubFlag.QoS2)); // 0x34
|
||||
buf.Add((byte)data.Count);
|
||||
buf.AddRange(data);
|
||||
|
||||
var err = MqttParser.Parse(c, buf.ToArray(), buf.Count);
|
||||
err.ShouldBeNull();
|
||||
|
||||
// Verify PUBREC: [0x50] [0x02] [PI high] [PI low]
|
||||
var ms = GetStream(c);
|
||||
var written = ms.ToArray();
|
||||
written.Length.ShouldBe(4);
|
||||
written[0].ShouldBe(MqttPacket.PubRec); // 0x50
|
||||
written[1].ShouldBe((byte)0x02);
|
||||
written[2].ShouldBe((byte)0x00); // PI high
|
||||
written[3].ShouldBe((byte)0x0A); // PI low
|
||||
|
||||
// Message should be stored in QoS2Pending.
|
||||
c.Mqtt!.QoS2Pending.ShouldContainKey((ushort)10);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Parser_QoS2_FullHandshake_PubRecPubRelPubComp()
|
||||
{
|
||||
var c = CreateConnectedMqttClient();
|
||||
|
||||
// Step 1: PUBLISH QoS 2 → PUBREC
|
||||
var pubData = new List<byte>();
|
||||
pubData.Add(0x00); pubData.Add(0x01); pubData.Add((byte)'t');
|
||||
pubData.Add(0x00); pubData.Add(0x07); // PI = 7
|
||||
pubData.AddRange(Encoding.UTF8.GetBytes("qos2msg"));
|
||||
|
||||
var pubBuf = new List<byte>();
|
||||
pubBuf.Add((byte)(MqttPacket.Pub | MqttPubFlag.QoS2));
|
||||
pubBuf.Add((byte)pubData.Count);
|
||||
pubBuf.AddRange(pubData);
|
||||
|
||||
var err = MqttParser.Parse(c, pubBuf.ToArray(), pubBuf.Count);
|
||||
err.ShouldBeNull();
|
||||
c.Mqtt!.QoS2Pending.ShouldContainKey((ushort)7);
|
||||
|
||||
// Reset stream to capture only PUBCOMP.
|
||||
var ms = GetStream(c);
|
||||
ms.SetLength(0);
|
||||
|
||||
// Step 2: PUBREL from client → PUBCOMP
|
||||
// PUBREL: [0x62] [0x02] [PI high] [PI low]
|
||||
var pubrelBuf = new byte[] { 0x62, 0x02, 0x00, 0x07 };
|
||||
err = MqttParser.Parse(c, pubrelBuf, pubrelBuf.Length);
|
||||
err.ShouldBeNull();
|
||||
|
||||
// Verify PUBCOMP was sent.
|
||||
var written = ms.ToArray();
|
||||
written.Length.ShouldBe(4);
|
||||
written[0].ShouldBe(MqttPacket.PubComp); // 0x70
|
||||
written[1].ShouldBe((byte)0x02);
|
||||
written[2].ShouldBe((byte)0x00);
|
||||
written[3].ShouldBe((byte)0x07);
|
||||
|
||||
// Message should be removed from QoS2Pending.
|
||||
c.Mqtt.QoS2Pending.ShouldNotContainKey((ushort)7);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Parser_PubAck_ShouldRemoveFromPending()
|
||||
{
|
||||
var c = CreateConnectedMqttClient();
|
||||
|
||||
// Pre-populate pending.
|
||||
c.Mqtt!.Pending[(ushort)3] = null;
|
||||
c.Mqtt.Pending.ShouldContainKey((ushort)3);
|
||||
|
||||
// PUBACK: [0x40] [0x02] [0x00] [0x03]
|
||||
var buf = new byte[] { 0x40, 0x02, 0x00, 0x03 };
|
||||
var err = MqttParser.Parse(c, buf, buf.Length);
|
||||
err.ShouldBeNull();
|
||||
|
||||
c.Mqtt.Pending.ShouldNotContainKey((ushort)3);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Parser_PubRec_ShouldSendPubRel()
|
||||
{
|
||||
var c = CreateConnectedMqttClient();
|
||||
|
||||
// Pre-populate pending.
|
||||
c.Mqtt!.Pending[(ushort)9] = null;
|
||||
|
||||
// PUBREC: [0x50] [0x02] [0x00] [0x09]
|
||||
var buf = new byte[] { 0x50, 0x02, 0x00, 0x09 };
|
||||
var err = MqttParser.Parse(c, buf, buf.Length);
|
||||
err.ShouldBeNull();
|
||||
|
||||
// Should have sent PUBREL: [0x62] [0x02] [0x00] [0x09]
|
||||
var ms = GetStream(c);
|
||||
var written = ms.ToArray();
|
||||
written.Length.ShouldBe(4);
|
||||
written[0].ShouldBe((byte)0x62); // PUBREL with bit 1 set
|
||||
written[3].ShouldBe((byte)0x09);
|
||||
|
||||
// Pending should be cleared.
|
||||
c.Mqtt.Pending.ShouldNotContainKey((ushort)9);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Parser_PubComp_ShouldRemoveFromPending()
|
||||
{
|
||||
var c = CreateConnectedMqttClient();
|
||||
|
||||
c.Mqtt!.Pending[(ushort)15] = null;
|
||||
|
||||
// PUBCOMP: [0x70] [0x02] [0x00] [0x0F]
|
||||
var buf = new byte[] { 0x70, 0x02, 0x00, 0x0F };
|
||||
var err = MqttParser.Parse(c, buf, buf.Length);
|
||||
err.ShouldBeNull();
|
||||
|
||||
c.Mqtt.Pending.ShouldNotContainKey((ushort)15);
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
|
||||
Reference in New Issue
Block a user