Files
MQTTnet/Source/MQTTnet.Tests/Formatter/MqttPacketSerialization_V5_Tests.cs
T
Alexander Köpke 07409e3a35 Allow user properties as pre-encoded UTF-8 binary buffer (#2228)
* Add support for binary user properties in MQTT 5.0

* Optimize byte array reversal in SubscriptionTopicHashTests

* Use InvariantCulture for topic string conversion in Load_Tests

* switched to `Microsoft.Testing.Platform`

* Refactor CI workflow to separate signed and unsigned package builds

* Add condition to sign job to prevent execution on external pull requests

* Remove fallback to value as string in MqttUserProperty

* Refactor MqttUserProperty handling to use byte arrays for improved performance and add deprecation warnings for legacy methods
2025-12-18 17:46:35 +01:00

409 lines
20 KiB
C#

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Buffers;
using System.Text;
using MQTTnet.Formatter;
using MQTTnet.Packets;
using MQTTnet.Protocol;
namespace MQTTnet.Tests.Formatter;
// ReSharper disable InconsistentNaming
[TestClass]
public sealed class MqttPacketSerialization_V5_Tests
{
[TestMethod]
public void Empty_Auth_Packet_Is_Success()
{
var buffer = MqttPacketSerializationHelper.EncodePacket(new MqttAuthPacket(), MqttProtocolVersion.V500);
var packet = MqttPacketSerializationHelper.DecodePacket(buffer, MqttProtocolVersion.V500);
Assert.IsNotNull(packet);
Assert.IsInstanceOfType<MqttAuthPacket>(packet);
}
[TestMethod]
public void Serialize_Full_MqttAuthPacket_V500()
{
var authPacket = new MqttAuthPacket
{
AuthenticationData = "AuthenticationData"u8.ToArray(),
AuthenticationMethod = "AuthenticationMethod",
ReasonCode = MqttAuthenticateReasonCode.ContinueAuthentication,
ReasonString = "ReasonString",
UserProperties = [new MqttUserProperty("Foo", Encoding.UTF8.GetBytes("Bar"))]
};
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(authPacket, MqttProtocolVersion.V500);
CollectionAssert.AreEqual(authPacket.AuthenticationData, deserialized.AuthenticationData);
Assert.AreEqual(authPacket.AuthenticationMethod, deserialized.AuthenticationMethod);
Assert.AreEqual(authPacket.ReasonCode, deserialized.ReasonCode);
Assert.AreEqual(authPacket.ReasonString, deserialized.ReasonString);
CollectionAssert.AreEqual(authPacket.UserProperties, deserialized.UserProperties);
}
[TestMethod]
public void Serialize_Full_MqttConnAckPacket_V500()
{
var connAckPacket = new MqttConnAckPacket
{
AuthenticationData = "AuthenticationData"u8.ToArray(),
AuthenticationMethod = "AuthenticationMethod",
ReasonCode = MqttConnectReasonCode.ServerUnavailable,
ReasonString = "ReasonString",
ReceiveMaximum = 123,
ResponseInformation = "ResponseInformation",
RetainAvailable = true,
ReturnCode = MqttConnectReturnCode.ConnectionRefusedNotAuthorized,
ServerReference = "ServerReference",
AssignedClientIdentifier = "AssignedClientIdentifier",
IsSessionPresent = true,
MaximumPacketSize = 456,
MaximumQoS = MqttQualityOfServiceLevel.ExactlyOnce,
ServerKeepAlive = 789,
SessionExpiryInterval = 852,
SharedSubscriptionAvailable = true,
SubscriptionIdentifiersAvailable = true,
TopicAliasMaximum = 963,
WildcardSubscriptionAvailable = true,
UserProperties = [new MqttUserProperty("Foo", Encoding.UTF8.GetBytes("Bar"))]
};
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(connAckPacket, MqttProtocolVersion.V500);
CollectionAssert.AreEqual(connAckPacket.AuthenticationData, deserialized.AuthenticationData);
Assert.AreEqual(connAckPacket.AuthenticationMethod, deserialized.AuthenticationMethod);
Assert.AreEqual(connAckPacket.ReasonCode, deserialized.ReasonCode);
Assert.AreEqual(connAckPacket.ReasonString, deserialized.ReasonString);
Assert.AreEqual(connAckPacket.ReceiveMaximum, deserialized.ReceiveMaximum);
Assert.AreEqual(connAckPacket.ResponseInformation, deserialized.ResponseInformation);
Assert.AreEqual(connAckPacket.RetainAvailable, deserialized.RetainAvailable);
// Return Code only used in MQTTv3
Assert.AreEqual(connAckPacket.ServerReference, deserialized.ServerReference);
Assert.AreEqual(connAckPacket.AssignedClientIdentifier, deserialized.AssignedClientIdentifier);
Assert.AreEqual(connAckPacket.IsSessionPresent, deserialized.IsSessionPresent);
Assert.AreEqual(connAckPacket.MaximumPacketSize, deserialized.MaximumPacketSize);
Assert.AreEqual(connAckPacket.MaximumQoS, deserialized.MaximumQoS);
Assert.AreEqual(connAckPacket.ServerKeepAlive, deserialized.ServerKeepAlive);
Assert.AreEqual(connAckPacket.SessionExpiryInterval, deserialized.SessionExpiryInterval);
Assert.AreEqual(connAckPacket.SharedSubscriptionAvailable, deserialized.SharedSubscriptionAvailable);
Assert.AreEqual(connAckPacket.SubscriptionIdentifiersAvailable, deserialized.SubscriptionIdentifiersAvailable);
Assert.AreEqual(connAckPacket.TopicAliasMaximum, deserialized.TopicAliasMaximum);
Assert.AreEqual(connAckPacket.WildcardSubscriptionAvailable, deserialized.WildcardSubscriptionAvailable);
CollectionAssert.AreEqual(connAckPacket.UserProperties, deserialized.UserProperties);
}
[TestMethod]
public void Serialize_Full_MqttConnectPacket_V500()
{
var connectPacket = new MqttConnectPacket
{
Username = "Username",
Password = "Password"u8.ToArray(),
ClientId = "ClientId",
AuthenticationData = "AuthenticationData"u8.ToArray(),
AuthenticationMethod = "AuthenticationMethod",
CleanSession = true,
ReceiveMaximum = 123,
WillFlag = true,
WillTopic = "WillTopic",
WillMessage = "WillMessage"u8.ToArray(),
WillRetain = true,
KeepAlivePeriod = 456,
MaximumPacketSize = 789,
RequestProblemInformation = true,
RequestResponseInformation = true,
SessionExpiryInterval = 27,
TopicAliasMaximum = 67,
WillContentType = "WillContentType",
WillCorrelationData = "WillCorrelationData"u8.ToArray(),
WillDelayInterval = 782,
WillQoS = MqttQualityOfServiceLevel.ExactlyOnce,
WillResponseTopic = "WillResponseTopic",
WillMessageExpiryInterval = 542,
WillPayloadFormatIndicator = MqttPayloadFormatIndicator.CharacterData,
UserProperties = [new MqttUserProperty("Foo", Encoding.UTF8.GetBytes("Bar"))],
WillUserProperties = [new MqttUserProperty("WillFoo", Encoding.UTF8.GetBytes("WillBar"))]
};
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(connectPacket, MqttProtocolVersion.V500);
Assert.AreEqual(connectPacket.Username, deserialized.Username);
CollectionAssert.AreEqual(connectPacket.Password, deserialized.Password);
Assert.AreEqual(connectPacket.ClientId, deserialized.ClientId);
CollectionAssert.AreEqual(connectPacket.AuthenticationData, deserialized.AuthenticationData);
Assert.AreEqual(connectPacket.AuthenticationMethod, deserialized.AuthenticationMethod);
Assert.AreEqual(connectPacket.CleanSession, deserialized.CleanSession);
Assert.AreEqual(connectPacket.ReceiveMaximum, deserialized.ReceiveMaximum);
Assert.AreEqual(connectPacket.WillFlag, deserialized.WillFlag);
Assert.AreEqual(connectPacket.WillTopic, deserialized.WillTopic);
CollectionAssert.AreEqual(connectPacket.WillMessage, deserialized.WillMessage);
Assert.AreEqual(connectPacket.WillRetain, deserialized.WillRetain);
Assert.AreEqual(connectPacket.KeepAlivePeriod, deserialized.KeepAlivePeriod);
Assert.AreEqual(connectPacket.MaximumPacketSize, deserialized.MaximumPacketSize);
Assert.AreEqual(connectPacket.RequestProblemInformation, deserialized.RequestProblemInformation);
Assert.AreEqual(connectPacket.RequestResponseInformation, deserialized.RequestResponseInformation);
Assert.AreEqual(connectPacket.SessionExpiryInterval, deserialized.SessionExpiryInterval);
Assert.AreEqual(connectPacket.TopicAliasMaximum, deserialized.TopicAliasMaximum);
Assert.AreEqual(connectPacket.WillContentType, deserialized.WillContentType);
CollectionAssert.AreEqual(connectPacket.WillCorrelationData, deserialized.WillCorrelationData);
Assert.AreEqual(connectPacket.WillDelayInterval, deserialized.WillDelayInterval);
Assert.AreEqual(connectPacket.WillQoS, deserialized.WillQoS);
Assert.AreEqual(connectPacket.WillResponseTopic, deserialized.WillResponseTopic);
Assert.AreEqual(connectPacket.WillMessageExpiryInterval, deserialized.WillMessageExpiryInterval);
Assert.AreEqual(connectPacket.WillPayloadFormatIndicator, deserialized.WillPayloadFormatIndicator);
CollectionAssert.AreEqual(connectPacket.UserProperties, deserialized.UserProperties);
CollectionAssert.AreEqual(connectPacket.WillUserProperties, deserialized.WillUserProperties);
}
[TestMethod]
public void Serialize_Full_MqttDisconnectPacket_V500()
{
var disconnectPacket = new MqttDisconnectPacket
{
ReasonCode = MqttDisconnectReasonCode.QuotaExceeded,
ReasonString = "ReasonString",
ServerReference = "ServerReference",
SessionExpiryInterval = 234,
UserProperties = [new MqttUserProperty("Foo", Encoding.UTF8.GetBytes("Bar"))]
};
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(disconnectPacket, MqttProtocolVersion.V500);
Assert.AreEqual(disconnectPacket.ReasonCode, deserialized.ReasonCode);
Assert.AreEqual(disconnectPacket.ReasonString, deserialized.ReasonString);
Assert.AreEqual(disconnectPacket.ServerReference, deserialized.ServerReference);
Assert.AreEqual(disconnectPacket.SessionExpiryInterval, deserialized.SessionExpiryInterval);
CollectionAssert.AreEqual(disconnectPacket.UserProperties, deserialized.UserProperties);
}
[TestMethod]
public void Serialize_Full_MqttPingReqPacket_V500()
{
var pingReqPacket = new MqttPingReqPacket();
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(pingReqPacket, MqttProtocolVersion.V500);
Assert.IsNotNull(deserialized);
}
[TestMethod]
public void Serialize_Full_MqttPingRespPacket_V500()
{
var pingRespPacket = new MqttPingRespPacket();
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(pingRespPacket, MqttProtocolVersion.V500);
Assert.IsNotNull(deserialized);
}
[TestMethod]
public void Serialize_Full_MqttPubAckPacket_V500()
{
var pubAckPacket = new MqttPubAckPacket
{
PacketIdentifier = 123,
ReasonCode = MqttPubAckReasonCode.NoMatchingSubscribers,
ReasonString = "ReasonString",
UserProperties = [new MqttUserProperty("Foo", Encoding.UTF8.GetBytes("Bar"))]
};
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(pubAckPacket, MqttProtocolVersion.V500);
Assert.AreEqual(pubAckPacket.PacketIdentifier, deserialized.PacketIdentifier);
Assert.AreEqual(pubAckPacket.ReasonCode, deserialized.ReasonCode);
Assert.AreEqual(pubAckPacket.ReasonString, deserialized.ReasonString);
CollectionAssert.AreEqual(pubAckPacket.UserProperties, deserialized.UserProperties);
}
[TestMethod]
public void Serialize_Full_MqttPubCompPacket_V500()
{
var pubCompPacket = new MqttPubCompPacket
{
PacketIdentifier = 123,
ReasonCode = MqttPubCompReasonCode.PacketIdentifierNotFound,
ReasonString = "ReasonString",
UserProperties = [new MqttUserProperty("Foo", Encoding.UTF8.GetBytes("Bar"))]
};
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(pubCompPacket, MqttProtocolVersion.V500);
Assert.AreEqual(pubCompPacket.PacketIdentifier, deserialized.PacketIdentifier);
Assert.AreEqual(pubCompPacket.ReasonCode, deserialized.ReasonCode);
Assert.AreEqual(pubCompPacket.ReasonString, deserialized.ReasonString);
CollectionAssert.AreEqual(pubCompPacket.UserProperties, deserialized.UserProperties);
}
[TestMethod]
public void Serialize_Full_MqttPublishPacket_V500()
{
var publishPacket = new MqttPublishPacket
{
PacketIdentifier = 123,
Dup = true,
Retain = true,
PayloadSegment = new ArraySegment<byte>("Payload"u8.ToArray()),
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtLeastOnce,
Topic = "Topic",
ResponseTopic = "/Response",
ContentType = "Content-Type",
CorrelationData = "CorrelationData"u8.ToArray(),
TopicAlias = 27,
SubscriptionIdentifiers = [123],
MessageExpiryInterval = 38,
PayloadFormatIndicator = MqttPayloadFormatIndicator.CharacterData,
UserProperties = [new MqttUserProperty("Foo", Encoding.UTF8.GetBytes("Bar"))]
};
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(publishPacket, MqttProtocolVersion.V500);
Assert.AreEqual(publishPacket.PacketIdentifier, deserialized.PacketIdentifier);
Assert.AreEqual(publishPacket.Dup, deserialized.Dup);
Assert.AreEqual(publishPacket.Retain, deserialized.Retain);
CollectionAssert.AreEqual(publishPacket.Payload.ToArray(), deserialized.Payload.ToArray());
Assert.AreEqual(publishPacket.QualityOfServiceLevel, deserialized.QualityOfServiceLevel);
Assert.AreEqual(publishPacket.Topic, deserialized.Topic);
Assert.AreEqual(publishPacket.ResponseTopic, deserialized.ResponseTopic);
Assert.AreEqual(publishPacket.ContentType, deserialized.ContentType);
CollectionAssert.AreEqual(publishPacket.CorrelationData, deserialized.CorrelationData);
Assert.AreEqual(publishPacket.TopicAlias, deserialized.TopicAlias);
CollectionAssert.AreEqual(publishPacket.SubscriptionIdentifiers, deserialized.SubscriptionIdentifiers);
Assert.AreEqual(publishPacket.MessageExpiryInterval, deserialized.MessageExpiryInterval);
Assert.AreEqual(publishPacket.PayloadFormatIndicator, deserialized.PayloadFormatIndicator);
CollectionAssert.AreEqual(publishPacket.UserProperties, deserialized.UserProperties);
}
[TestMethod]
public void Serialize_Full_MqttPubRecPacket_V500()
{
var pubRecPacket = new MqttPubRecPacket
{
PacketIdentifier = 123,
ReasonCode = MqttPubRecReasonCode.UnspecifiedError,
ReasonString = "ReasonString",
UserProperties = [new MqttUserProperty("Foo", Encoding.UTF8.GetBytes("Bar"))]
};
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(pubRecPacket, MqttProtocolVersion.V500);
Assert.AreEqual(pubRecPacket.PacketIdentifier, deserialized.PacketIdentifier);
Assert.AreEqual(pubRecPacket.ReasonCode, deserialized.ReasonCode);
Assert.AreEqual(pubRecPacket.ReasonString, deserialized.ReasonString);
CollectionAssert.AreEqual(pubRecPacket.UserProperties, deserialized.UserProperties);
}
[TestMethod]
public void Serialize_Full_MqttPubRelPacket_V500()
{
var pubRelPacket = new MqttPubRelPacket
{
PacketIdentifier = 123,
ReasonCode = MqttPubRelReasonCode.PacketIdentifierNotFound,
ReasonString = "ReasonString",
UserProperties = [new MqttUserProperty("Foo", Encoding.UTF8.GetBytes("Bar"))]
};
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(pubRelPacket, MqttProtocolVersion.V500);
Assert.AreEqual(pubRelPacket.PacketIdentifier, deserialized.PacketIdentifier);
Assert.AreEqual(pubRelPacket.ReasonCode, deserialized.ReasonCode);
Assert.AreEqual(pubRelPacket.ReasonString, deserialized.ReasonString);
CollectionAssert.AreEqual(pubRelPacket.UserProperties, deserialized.UserProperties);
}
[TestMethod]
public void Serialize_Full_MqttSubAckPacket_V500()
{
var subAckPacket = new MqttSubAckPacket
{
PacketIdentifier = 123,
ReasonString = "ReasonString",
ReasonCodes = [MqttSubscribeReasonCode.GrantedQoS1],
UserProperties = [new MqttUserProperty("Foo", Encoding.UTF8.GetBytes("Bar"))]
};
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(subAckPacket, MqttProtocolVersion.V500);
Assert.AreEqual(subAckPacket.PacketIdentifier, deserialized.PacketIdentifier);
Assert.AreEqual(subAckPacket.ReasonString, deserialized.ReasonString);
Assert.HasCount(subAckPacket.ReasonCodes.Count, deserialized.ReasonCodes);
Assert.AreEqual(subAckPacket.ReasonCodes[0], deserialized.ReasonCodes[0]);
CollectionAssert.AreEqual(subAckPacket.UserProperties, deserialized.UserProperties);
}
[TestMethod]
public void Serialize_Full_MqttSubscribePacket_V500()
{
var subscribePacket = new MqttSubscribePacket
{
PacketIdentifier = 123,
SubscriptionIdentifier = 456,
TopicFilters =
[
new MqttTopicFilter
{
Topic = "Topic",
NoLocal = true,
RetainHandling = MqttRetainHandling.SendAtSubscribeIfNewSubscriptionOnly,
RetainAsPublished = true,
QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce
}
],
UserProperties = [new MqttUserProperty("Foo", Encoding.UTF8.GetBytes("Bar"))]
};
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(subscribePacket, MqttProtocolVersion.V500);
Assert.AreEqual(subscribePacket.PacketIdentifier, deserialized.PacketIdentifier);
Assert.AreEqual(subscribePacket.SubscriptionIdentifier, deserialized.SubscriptionIdentifier);
Assert.HasCount(1, deserialized.TopicFilters);
Assert.AreEqual(subscribePacket.TopicFilters[0].Topic, deserialized.TopicFilters[0].Topic);
Assert.AreEqual(subscribePacket.TopicFilters[0].NoLocal, deserialized.TopicFilters[0].NoLocal);
Assert.AreEqual(subscribePacket.TopicFilters[0].RetainHandling, deserialized.TopicFilters[0].RetainHandling);
Assert.AreEqual(subscribePacket.TopicFilters[0].RetainAsPublished, deserialized.TopicFilters[0].RetainAsPublished);
Assert.AreEqual(subscribePacket.TopicFilters[0].QualityOfServiceLevel, deserialized.TopicFilters[0].QualityOfServiceLevel);
CollectionAssert.AreEqual(subscribePacket.UserProperties, deserialized.UserProperties);
}
[TestMethod]
public void Serialize_Full_MqttUnsubAckPacket_V500()
{
var unsubAckPacket = new MqttUnsubAckPacket
{
PacketIdentifier = 123,
ReasonCodes = [MqttUnsubscribeReasonCode.ImplementationSpecificError],
ReasonString = "ReasonString",
UserProperties = [new MqttUserProperty("Foo", Encoding.UTF8.GetBytes("Bar"))]
};
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(unsubAckPacket, MqttProtocolVersion.V500);
Assert.AreEqual(unsubAckPacket.PacketIdentifier, deserialized.PacketIdentifier);
Assert.AreEqual(unsubAckPacket.ReasonString, deserialized.ReasonString);
Assert.HasCount(unsubAckPacket.ReasonCodes.Count, deserialized.ReasonCodes);
Assert.AreEqual(unsubAckPacket.ReasonCodes[0], deserialized.ReasonCodes[0]);
CollectionAssert.AreEqual(unsubAckPacket.UserProperties, deserialized.UserProperties);
}
[TestMethod]
public void Serialize_Full_MqttUnsubscribePacket_V500()
{
var unsubscribePacket = new MqttUnsubscribePacket
{
PacketIdentifier = 123,
TopicFilters = ["TopicFilter1"],
UserProperties = [new MqttUserProperty("Foo", Encoding.UTF8.GetBytes("Bar"))]
};
var deserialized = MqttPacketSerializationHelper.EncodeAndDecodePacket(unsubscribePacket, MqttProtocolVersion.V500);
Assert.AreEqual(unsubscribePacket.PacketIdentifier, deserialized.PacketIdentifier);
Assert.HasCount(unsubscribePacket.TopicFilters.Count, deserialized.TopicFilters);
Assert.AreEqual(unsubscribePacket.TopicFilters[0], deserialized.TopicFilters[0]);
CollectionAssert.AreEqual(unsubscribePacket.UserProperties, deserialized.UserProperties);
}
}