mqtt/server/internal/packets/packets_tables_test.go

1417 lines
33 KiB
Go
Raw Normal View History

2022-08-15 23:06:20 +03:00
package packets
type packetTestData struct {
group string // group specifies a group that should run the test, blank for all
rawBytes []byte // the bytes that make the packet
actualBytes []byte // the actual byte array that is created in the event of a byte mutation (eg. MQTT-2.3.1-1 qos/packet id)
packet *Packet // the packet that is expected
desc string // a description of the test
failFirst error // expected fail result to be run immediately after the method is called
expect interface{} // generic expected fail result to be checked
isolate bool // isolate can be used to isolate a test
primary bool // primary is a test that should be run using readPackets
meta interface{} // meta conains a metadata value used in testing on a case-by-case basis.
code byte // code is an expected validation return code
}
func encodeTestOK(wanted packetTestData) bool {
if wanted.rawBytes == nil {
return false
}
if wanted.group != "" && wanted.group != "encode" {
return false
}
return true
}
func decodeTestOK(wanted packetTestData) bool {
if wanted.group != "" && wanted.group != "decode" {
return false
}
return true
}
var expectedPackets = map[byte][]packetTestData{
Connect: {
{
desc: "MQTT 3.1",
primary: true,
rawBytes: []byte{
byte(Connect << 4), 17, // Fixed header
0, 6, // Protocol Name - MSB+LSB
'M', 'Q', 'I', 's', 'd', 'p', // Protocol Name
3, // Protocol Version
0, // Packet Flags
0, 30, // Keepalive
0, 3, // Client ID - MSB+LSB
'z', 'e', 'n', // Client ID "zen"
},
packet: &Packet{
FixedHeader: FixedHeader{
Type: Connect,
Remaining: 17,
},
ProtocolName: []byte("MQIsdp"),
ProtocolVersion: 3,
CleanSession: false,
Keepalive: 30,
ClientIdentifier: "zen",
},
},
{
desc: "MQTT 3.1.1",
primary: true,
rawBytes: []byte{
byte(Connect << 4), 16, // Fixed header
0, 4, // Protocol Name - MSB+LSB
'M', 'Q', 'T', 'T', // Protocol Name
4, // Protocol Version
0, // Packet Flags
0, 60, // Keepalive
0, 4, // Client ID - MSB+LSB
'z', 'e', 'n', '3', // Client ID "zen"
},
packet: &Packet{
FixedHeader: FixedHeader{
Type: Connect,
Remaining: 16,
},
ProtocolName: []byte("MQTT"),
ProtocolVersion: 4,
CleanSession: false,
Keepalive: 60,
ClientIdentifier: "zen3",
},
},
{
desc: "MQTT 3.1.1, Clean Session",
rawBytes: []byte{
byte(Connect << 4), 15, // Fixed header
0, 4, // Protocol Name - MSB+LSB
'M', 'Q', 'T', 'T', // Protocol Name
4, // Protocol Version
2, // Packet Flags
0, 45, // Keepalive
0, 3, // Client ID - MSB+LSB
'z', 'e', 'n', // Client ID "zen"
},
packet: &Packet{
FixedHeader: FixedHeader{
Type: Connect,
Remaining: 15,
},
ProtocolName: []byte("MQTT"),
ProtocolVersion: 4,
CleanSession: true,
Keepalive: 45,
ClientIdentifier: "zen",
},
},
{
desc: "MQTT 3.1.1, Clean Session, LWT",
rawBytes: []byte{
byte(Connect << 4), 31, // Fixed header
0, 4, // Protocol Name - MSB+LSB
'M', 'Q', 'T', 'T', // Protocol Name
4, // Protocol Version
14, // Packet Flags
0, 27, // Keepalive
0, 3, // Client ID - MSB+LSB
'z', 'e', 'n', // Client ID "zen"
0, 3, // Will Topic - MSB+LSB
'l', 'w', 't',
0, 9, // Will Message MSB+LSB
'n', 'o', 't', ' ', 'a', 'g', 'a', 'i', 'n',
},
packet: &Packet{
FixedHeader: FixedHeader{
Type: Connect,
Remaining: 31,
},
ProtocolName: []byte("MQTT"),
ProtocolVersion: 4,
CleanSession: true,
Keepalive: 27,
ClientIdentifier: "zen",
WillFlag: true,
WillTopic: "lwt",
WillMessage: []byte("not again"),
WillQos: 1,
},
},
{
desc: "MQTT 3.1.1, Username, Password",
rawBytes: []byte{
byte(Connect << 4), 28, // Fixed header
0, 4, // Protocol Name - MSB+LSB
'M', 'Q', 'T', 'T', // Protocol Name
4, // Protocol Version
194, // Packet Flags
0, 20, // Keepalive
0, 3, // Client ID - MSB+LSB
'z', 'e', 'n', // Client ID "zen"
0, 5, // Username MSB+LSB
'm', 'o', 'c', 'h', 'i',
0, 4, // Password MSB+LSB
',', '.', '/', ';',
},
packet: &Packet{
FixedHeader: FixedHeader{
Type: Connect,
Remaining: 28,
},
ProtocolName: []byte("MQTT"),
ProtocolVersion: 4,
CleanSession: true,
Keepalive: 20,
ClientIdentifier: "zen",
UsernameFlag: true,
PasswordFlag: true,
Username: []byte("mochi"),
Password: []byte(",./;"),
},
},
{
desc: "MQTT 3.1.1, Username, Password, LWT",
primary: true,
rawBytes: []byte{
byte(Connect << 4), 44, // Fixed header
0, 4, // Protocol Name - MSB+LSB
'M', 'Q', 'T', 'T', // Protocol Name
4, // Protocol Version
206, // Packet Flags
0, 120, // Keepalive
0, 3, // Client ID - MSB+LSB
'z', 'e', 'n', // Client ID "zen"
0, 3, // Will Topic - MSB+LSB
'l', 'w', 't',
0, 9, // Will Message MSB+LSB
'n', 'o', 't', ' ', 'a', 'g', 'a', 'i', 'n',
0, 5, // Username MSB+LSB
'm', 'o', 'c', 'h', 'i',
0, 4, // Password MSB+LSB
',', '.', '/', ';',
},
packet: &Packet{
FixedHeader: FixedHeader{
Type: Connect,
Remaining: 44,
},
ProtocolName: []byte("MQTT"),
ProtocolVersion: 4,
CleanSession: true,
Keepalive: 120,
ClientIdentifier: "zen",
UsernameFlag: true,
PasswordFlag: true,
Username: []byte("mochi"),
Password: []byte(",./;"),
WillFlag: true,
WillTopic: "lwt",
WillMessage: []byte("not again"),
WillQos: 1,
},
},
// Fail States
{
desc: "Malformed Connect - protocol name",
group: "decode",
failFirst: ErrMalformedProtocolName,
rawBytes: []byte{
byte(Connect << 4), 0, // Fixed header
0, 7, // Protocol Name - MSB+LSB
'M', 'Q', 'I', 's', 'd', // Protocol Name
},
},
{
desc: "Malformed Connect - protocol version",
group: "decode",
failFirst: ErrMalformedProtocolVersion,
rawBytes: []byte{
byte(Connect << 4), 0, // Fixed header
0, 4, // Protocol Name - MSB+LSB
'M', 'Q', 'T', 'T', // Protocol Name
},
},
{
desc: "Malformed Connect - flags",
group: "decode",
failFirst: ErrMalformedFlags,
rawBytes: []byte{
byte(Connect << 4), 0, // Fixed header
0, 4, // Protocol Name - MSB+LSB
'M', 'Q', 'T', 'T', // Protocol Name
4, // Protocol Version
},
},
{
desc: "Malformed Connect - keepalive",
group: "decode",
failFirst: ErrMalformedKeepalive,
rawBytes: []byte{
byte(Connect << 4), 0, // Fixed header
0, 4, // Protocol Name - MSB+LSB
'M', 'Q', 'T', 'T', // Protocol Name
4, // Protocol Version
0, // Flags
},
},
{
desc: "Malformed Connect - client id",
group: "decode",
failFirst: ErrMalformedClientID,
rawBytes: []byte{
byte(Connect << 4), 0, // Fixed header
0, 4, // Protocol Name - MSB+LSB
'M', 'Q', 'T', 'T', // Protocol Name
4, // Protocol Version
0, // Flags
0, 20, // Keepalive
0, 3, // Client ID - MSB+LSB
'z', 'e', // Client ID "zen"
},
},
{
desc: "Malformed Connect - will topic",
group: "decode",
failFirst: ErrMalformedWillTopic,
rawBytes: []byte{
byte(Connect << 4), 0, // Fixed header
0, 4, // Protocol Name - MSB+LSB
'M', 'Q', 'T', 'T', // Protocol Name
4, // Protocol Version
14, // Flags
0, 20, // Keepalive
0, 3, // Client ID - MSB+LSB
'z', 'e', 'n', // Client ID "zen"
0, 6, // Will Topic - MSB+LSB
'l',
},
},
{
desc: "Malformed Connect - will flag",
group: "decode",
failFirst: ErrMalformedWillMessage,
rawBytes: []byte{
byte(Connect << 4), 0, // Fixed header
0, 4, // Protocol Name - MSB+LSB
'M', 'Q', 'T', 'T', // Protocol Name
4, // Protocol Version
14, // Flags
0, 20, // Keepalive
0, 3, // Client ID - MSB+LSB
'z', 'e', 'n', // Client ID "zen"
0, 3, // Will Topic - MSB+LSB
'l', 'w', 't',
0, 9, // Will Message MSB+LSB
'n', 'o', 't', ' ', 'a',
},
},
{
desc: "Malformed Connect - username",
group: "decode",
failFirst: ErrMalformedUsername,
rawBytes: []byte{
byte(Connect << 4), 0, // Fixed header
0, 4, // Protocol Name - MSB+LSB
'M', 'Q', 'T', 'T', // Protocol Name
4, // Protocol Version
206, // Flags
0, 20, // Keepalive
0, 3, // Client ID - MSB+LSB
'z', 'e', 'n', // Client ID "zen"
0, 3, // Will Topic - MSB+LSB
'l', 'w', 't',
0, 9, // Will Message MSB+LSB
'n', 'o', 't', ' ', 'a', 'g', 'a', 'i', 'n',
0, 5, // Username MSB+LSB
'm', 'o', 'c',
},
},
{
desc: "Malformed Connect - password",
group: "decode",
failFirst: ErrMalformedPassword,
rawBytes: []byte{
byte(Connect << 4), 0, // Fixed header
0, 4, // Protocol Name - MSB+LSB
'M', 'Q', 'T', 'T', // Protocol Name
4, // Protocol Version
206, // Flags
0, 20, // Keepalive
0, 3, // Client ID - MSB+LSB
'z', 'e', 'n', // Client ID "zen"
0, 3, // Will Topic - MSB+LSB
'l', 'w', 't',
0, 9, // Will Message MSB+LSB
'n', 'o', 't', ' ', 'a', 'g', 'a', 'i', 'n',
0, 5, // Username MSB+LSB
'm', 'o', 'c', 'h', 'i',
0, 4, // Password MSB+LSB
',', '.',
},
},
// Validation Tests
{
desc: "Invalid Protocol Name",
group: "validate",
code: CodeConnectProtocolViolation,
packet: &Packet{
ProtocolName: []byte("stuff"),
},
},
{
desc: "Invalid Protocol Version",
group: "validate",
code: CodeConnectBadProtocolVersion,
packet: &Packet{
ProtocolName: []byte("MQTT"),
ProtocolVersion: 2,
},
},
{
desc: "Invalid Protocol Version",
group: "validate",
code: CodeConnectBadProtocolVersion,
packet: &Packet{
ProtocolName: []byte("MQIsdp"),
ProtocolVersion: 2,
},
},
{
desc: "Reserved bit not 0",
group: "validate",
code: CodeConnectProtocolViolation,
packet: &Packet{
ProtocolName: []byte("MQTT"),
ProtocolVersion: 4,
ReservedBit: 1,
},
},
{
desc: "Client ID too long",
group: "validate",
code: CodeConnectProtocolViolation,
packet: &Packet{
ProtocolName: []byte("MQTT"),
ProtocolVersion: 4,
ClientIdentifier: func() string {
return string(make([]byte, 65536))
}(),
},
},
{
desc: "Has Password Flag but no Username flag",
group: "validate",
code: CodeConnectProtocolViolation,
packet: &Packet{
ProtocolName: []byte("MQTT"),
ProtocolVersion: 4,
PasswordFlag: true,
},
},
{
desc: "Username too long",
group: "validate",
code: CodeConnectProtocolViolation,
packet: &Packet{
ProtocolName: []byte("MQTT"),
ProtocolVersion: 4,
UsernameFlag: true,
Username: func() []byte {
return make([]byte, 65536)
}(),
},
},
{
desc: "Password too long",
group: "validate",
code: CodeConnectProtocolViolation,
packet: &Packet{
ProtocolName: []byte("MQTT"),
ProtocolVersion: 4,
UsernameFlag: true,
Username: []byte{},
PasswordFlag: true,
Password: func() []byte {
return make([]byte, 65536)
}(),
},
},
{
desc: "Clean session false and client id not set",
group: "validate",
code: CodeConnectBadClientID,
packet: &Packet{
ProtocolName: []byte("MQTT"),
ProtocolVersion: 4,
CleanSession: false,
},
},
// Spec Tests
{
// @SPEC [MQTT-1.4.0-1]
// The character data in a UTF-8 encoded string MUST be well-formed UTF-8
// as defined by the Unicode specification [Unicode] and restated in RFC 3629 [RFC 3629].
// In particular this data MUST NOT include encodings of code points between U+D800 and U+DFFF.
desc: "Invalid UTF8 string (a) - Code point U+D800.",
group: "decode",
failFirst: ErrMalformedClientID,
rawBytes: []byte{
byte(Connect << 4), 0, // Fixed header
0, 4, // Protocol Name - MSB+LSB
'M', 'Q', 'T', 'T', // Protocol Name
4, // Protocol Version
0, // Flags
0, 20, // Keepalive
0, 4, // Client ID - MSB+LSB
'e', 0xed, 0xa0, 0x80, // Client id bearing U+D800
},
},
{
desc: "Invalid UTF8 string (b) - Code point U+DFFF.",
group: "decode",
failFirst: ErrMalformedClientID,
rawBytes: []byte{
byte(Connect << 4), 0, // Fixed header
0, 4, // Protocol Name - MSB+LSB
'M', 'Q', 'T', 'T', // Protocol Name
4, // Protocol Version
0, // Flags
0, 20, // Keepalive
0, 4, // Client ID - MSB+LSB
'e', 0xed, 0xa3, 0xbf, // Client id bearing U+D8FF
},
},
// @SPEC [MQTT-1.4.0-2]
// A UTF-8 encoded string MUST NOT include an encoding of the null character U+0000.
{
desc: "Invalid UTF8 string (c) - Code point U+0000.",
group: "decode",
failFirst: ErrMalformedClientID,
rawBytes: []byte{
byte(Connect << 4), 0, // Fixed header
0, 4, // Protocol Name - MSB+LSB
'M', 'Q', 'T', 'T', // Protocol Name
4, // Protocol Version
0, // Flags
0, 20, // Keepalive
0, 3, // Client ID - MSB+LSB
'e', 0xc0, 0x80, // Client id bearing U+0000
},
},
// @ SPEC [MQTT-1.4.0-3]
// A UTF-8 encoded sequence 0xEF 0xBB 0xBF is always to be interpreted to mean U+FEFF ("ZERO WIDTH NO-BREAK SPACE")
// wherever it appears in a string and MUST NOT be skipped over or stripped off by a packet receiver.
{
desc: "UTF8 string must not skip or strip code point U+FEFF.",
//group: "decode",
//failFirst: ErrMalformedClientID,
rawBytes: []byte{
byte(Connect << 4), 18, // Fixed header
0, 4, // Protocol Name - MSB+LSB
'M', 'Q', 'T', 'T', // Protocol Name
4, // Protocol Version
0, // Flags
0, 20, // Keepalive
0, 6, // Client ID - MSB+LSB
'e', 'b', 0xEF, 0xBB, 0xBF, 'd', // Client id bearing U+FEFF
},
packet: &Packet{
FixedHeader: FixedHeader{
Type: Connect,
Remaining: 16,
},
ProtocolName: []byte("MQTT"),
ProtocolVersion: 4,
Keepalive: 20,
ClientIdentifier: string([]byte{'e', 'b', 0xEF, 0xBB, 0xBF, 'd'}),
},
},
},
Connack: {
{
desc: "Accepted, No Session",
primary: true,
rawBytes: []byte{
byte(Connack << 4), 2, // fixed header
0, // No existing session
Accepted,
},
packet: &Packet{
FixedHeader: FixedHeader{
Type: Connack,
Remaining: 2,
},
SessionPresent: false,
ReturnCode: Accepted,
},
},
{
desc: "Accepted, Session Exists",
primary: true,
rawBytes: []byte{
byte(Connack << 4), 2, // fixed header
1, // Session present
Accepted,
},
packet: &Packet{
FixedHeader: FixedHeader{
Type: Connack,
Remaining: 2,
},
SessionPresent: true,
ReturnCode: Accepted,
},
},
{
desc: "Bad Protocol Version",
rawBytes: []byte{
byte(Connack << 4), 2, // fixed header
1, // Session present
CodeConnectBadProtocolVersion,
},
packet: &Packet{
FixedHeader: FixedHeader{
Type: Connack,
Remaining: 2,
},
SessionPresent: true,
ReturnCode: CodeConnectBadProtocolVersion,
},
},
{
desc: "Bad Client ID",
rawBytes: []byte{
byte(Connack << 4), 2, // fixed header
1, // Session present
CodeConnectBadClientID,
},
packet: &Packet{
FixedHeader: FixedHeader{
Type: Connack,
Remaining: 2,
},
SessionPresent: true,
ReturnCode: CodeConnectBadClientID,
},
},
{
desc: "Server Unavailable",
rawBytes: []byte{
byte(Connack << 4), 2, // fixed header
1, // Session present
CodeConnectServerUnavailable,
},
packet: &Packet{
FixedHeader: FixedHeader{
Type: Connack,
Remaining: 2,
},
SessionPresent: true,
ReturnCode: CodeConnectServerUnavailable,
},
},
{
desc: "Bad Username or Password",
rawBytes: []byte{
byte(Connack << 4), 2, // fixed header
1, // Session present
CodeConnectBadAuthValues,
},
packet: &Packet{
FixedHeader: FixedHeader{
Type: Connack,
Remaining: 2,
},
SessionPresent: true,
ReturnCode: CodeConnectBadAuthValues,
},
},
{
desc: "Not Authorised",
rawBytes: []byte{
byte(Connack << 4), 2, // fixed header
1, // Session present
CodeConnectNotAuthorised,
},
packet: &Packet{
FixedHeader: FixedHeader{
Type: Connack,
Remaining: 2,
},
SessionPresent: true,
ReturnCode: CodeConnectNotAuthorised,
},
},
// Fail States
{
desc: "Malformed Connack - session present",
group: "decode",
failFirst: ErrMalformedSessionPresent,
rawBytes: []byte{
byte(Connect << 4), 2, // Fixed header
},
},
{
desc: "Malformed Connack - bad return code",
group: "decode",
//primary: true,
failFirst: ErrMalformedReturnCode,
rawBytes: []byte{
byte(Connect << 4), 2, // Fixed header
0,
},
},
},
Publish: {
{
desc: "Publish - No payload",
primary: true,
rawBytes: []byte{
byte(Publish << 4), 7, // Fixed header
0, 5, // Topic Name - LSB+MSB
'a', '/', 'b', '/', 'c', // Topic Name
},
packet: &Packet{
FixedHeader: FixedHeader{
Type: Publish,
Remaining: 7,
},
TopicName: "a/b/c",
Payload: []byte{},
},
},
{
desc: "Publish - basic",
primary: true,
rawBytes: []byte{
byte(Publish << 4), 18, // Fixed header
0, 5, // Topic Name - LSB+MSB
'a', '/', 'b', '/', 'c', // Topic Name
'h', 'e', 'l', 'l', 'o', ' ', 'm', 'o', 'c', 'h', 'i', // Payload
},
packet: &Packet{
FixedHeader: FixedHeader{
Type: Publish,
Remaining: 18,
},
TopicName: "a/b/c",
Payload: []byte("hello mochi"),
},
},
{
desc: "Publish - QoS:1, Packet ID",
primary: true,
rawBytes: []byte{
byte(Publish<<4) | 2, 14, // Fixed header
0, 5, // Topic Name - LSB+MSB
'a', '/', 'b', '/', 'c', // Topic Name
0, 7, // Packet ID - LSB+MSB
'h', 'e', 'l', 'l', 'o', // Payload
},
packet: &Packet{
FixedHeader: FixedHeader{
Type: Publish,
Qos: 1,
Remaining: 14,
},
TopicName: "a/b/c",
Payload: []byte("hello"),
PacketID: 7,
},
meta: byte(2),
},
{
desc: "Publish - QoS:1, Packet ID, No payload",
primary: true,
rawBytes: []byte{
byte(Publish<<4) | 2, 9, // Fixed header
0, 5, // Topic Name - LSB+MSB
'y', '/', 'u', '/', 'i', // Topic Name
0, 8, // Packet ID - LSB+MSB
},
packet: &Packet{
FixedHeader: FixedHeader{
Type: Publish,
Qos: 1,
Remaining: 9,
},
TopicName: "y/u/i",
PacketID: 8,
Payload: []byte{},
},
meta: byte(2),
},
{
desc: "Publish - Retain",
rawBytes: []byte{
byte(Publish<<4) | 1, 10, // Fixed header
0, 3, // Topic Name - LSB+MSB
'a', '/', 'b', // Topic Name
'h', 'e', 'l', 'l', 'o', // Payload
},
packet: &Packet{
FixedHeader: FixedHeader{
Type: Publish,
Retain: true,
},
TopicName: "a/b",
Payload: []byte("hello"),
},
meta: byte(1),
},
{
desc: "Publish - Dup",
rawBytes: []byte{
byte(Publish<<4) | 8, 10, // Fixed header
0, 3, // Topic Name - LSB+MSB
'a', '/', 'b', // Topic Name
'h', 'e', 'l', 'l', 'o', // Payload
},
packet: &Packet{
FixedHeader: FixedHeader{
Type: Publish,
Dup: true,
},
TopicName: "a/b",
Payload: []byte("hello"),
},
meta: byte(8),
},
// Fail States
{
desc: "Malformed Publish - topic name",
group: "decode",
failFirst: ErrMalformedTopic,
rawBytes: []byte{
byte(Publish << 4), 7, // Fixed header
0, 5, // Topic Name - LSB+MSB
'a', '/',
0, 11, // Packet ID - LSB+MSB
},
},
{
desc: "Malformed Publish - Packet ID",
group: "decode",
failFirst: ErrMalformedPacketID,
rawBytes: []byte{
byte(Publish<<4) | 2, 7, // Fixed header
0, 5, // Topic Name - LSB+MSB
'x', '/', 'y', '/', 'z', // Topic Name
0, // Packet ID - LSB+MSB
},
},
// Copy tests
{
desc: "Publish - basic copyable",
group: "copy",
rawBytes: []byte{
byte(Publish << 4), 18, // Fixed header
0, 5, // Topic Name - LSB+MSB
'z', '/', 'e', '/', 'n', // Topic Name
'm', 'o', 'c', 'h', 'i', ' ', 'm', 'o', 'c', 'h', 'i', // Payload
},
packet: &Packet{
FixedHeader: FixedHeader{
Type: Publish,
Dup: true,
Retain: true,
Qos: 1,
},
TopicName: "z/e/n",
Payload: []byte("mochi mochi"),
},
},
// Spec tests
{
// @SPEC [MQTT-2.3.1-5]
// A PUBLISH Packet MUST NOT contain a Packet Identifier if its QoS value is set to 0.
desc: "[MQTT-2.3.1-5] Packet ID must be 0 if QoS is 0 (a)",
group: "encode",
// this version tests for correct byte array mutuation.
// this does not check if -incoming- packets are parsed as correct,
// it is impossible for the parser to determine if the payload start is incorrect.
rawBytes: []byte{
byte(Publish << 4), 12, // Fixed header
0, 5, // Topic Name - LSB+MSB
'a', '/', 'b', '/', 'c', // Topic Name
0, 3, // Packet ID - LSB+MSB
'h', 'e', 'l', 'l', 'o', // Payload
},
actualBytes: []byte{
byte(Publish << 4), 12, // Fixed header
0, 5, // Topic Name - LSB+MSB
'a', '/', 'b', '/', 'c', // Topic Name
// Packet ID is removed.
'h', 'e', 'l', 'l', 'o', // Payload
},
packet: &Packet{
FixedHeader: FixedHeader{
Type: Publish,
Remaining: 12,
},
TopicName: "a/b/c",
Payload: []byte("hello"),
},
},
{
// @SPEC [MQTT-2.3.1-1].
// SUBSCRIBE, UNSUBSCRIBE, and PUBLISH (in cases where QoS > 0) Control Packets MUST contain a non-zero 16-bit Packet Identifier.
desc: "[MQTT-2.3.1-1] No Packet ID with QOS > 0",
group: "encode",
expect: ErrMissingPacketID,
code: Failed,
rawBytes: []byte{
byte(Publish<<4) | 2, 14, // Fixed header
0, 5, // Topic Name - LSB+MSB
'a', '/', 'b', '/', 'c', // Topic Name
0, 0, // Packet ID - LSB+MSB
'h', 'e', 'l', 'l', 'o', // Payload
},
packet: &Packet{
FixedHeader: FixedHeader{
Type: Publish,
Qos: 1,
},
TopicName: "a/b/c",
Payload: []byte("hello"),
PacketID: 0,
},
meta: byte(2),
},
/*
{
// @SPEC [MQTT-2.3.1-1].
// SUBSCRIBE, UNSUBSCRIBE, and PUBLISH (in cases where QoS > 0) Control Packets MUST contain a non-zero 16-bit Packet Identifier.
desc: "[MQTT-2.3.1-1] No Packet ID with QOS > 0",
group: "validate",
//primary: true,
expect: ErrMissingPacketID,
code: Failed,
rawBytes: []byte{
byte(Publish<<4) | 2, 14, // Fixed header
0, 5, // Topic Name - LSB+MSB
'a', '/', 'b', '/', 'c', // Topic Name
0, 0, // Packet ID - LSB+MSB
'h', 'e', 'l', 'l', 'o', // Payload
},
packet: &Packet{
FixedHeader: FixedHeader{
Type: Publish,
Qos: 1,
},
TopicName: "a/b/c",
Payload: []byte("hello"),
PacketID: 0,
},
meta: byte(2),
},
*/
// Validation Tests
{
// @SPEC [MQTT-2.3.1-5]
desc: "[MQTT-2.3.1-5] Packet ID must be 0 if QoS is 0 (b)",
group: "validate",
expect: ErrSurplusPacketID,
code: Failed,
packet: &Packet{
FixedHeader: FixedHeader{
Type: Publish,
Remaining: 12,
Qos: 0,
},
TopicName: "a/b/c",
Payload: []byte("hello"),
PacketID: 3,
},
},
{
// @SPEC [MQTT-2.3.1-1].
// SUBSCRIBE, UNSUBSCRIBE, and PUBLISH (in cases where QoS > 0) Control Packets MUST contain a non-zero 16-bit Packet Identifier.
desc: "[MQTT-2.3.1-1] No Packet ID with QOS > 0",
group: "validate",
expect: ErrMissingPacketID,
code: Failed,
packet: &Packet{
FixedHeader: FixedHeader{
Type: Publish,
Qos: 1,
},
PacketID: 0,
},
},
},
Puback: {
{
desc: "Puback",
primary: true,
rawBytes: []byte{
byte(Puback << 4), 2, // Fixed header
0, 11, // Packet ID - LSB+MSB
},
packet: &Packet{
FixedHeader: FixedHeader{
Type: Puback,
Remaining: 2,
},
PacketID: 11,
},
},
// Fail states
{
desc: "Malformed Puback - Packet ID",
group: "decode",
failFirst: ErrMalformedPacketID,
rawBytes: []byte{
byte(Puback << 4), 2, // Fixed header
0, // Packet ID - LSB+MSB
},
},
},
Pubrec: {
{
desc: "Pubrec",
primary: true,
rawBytes: []byte{
byte(Pubrec << 4), 2, // Fixed header
0, 12, // Packet ID - LSB+MSB
},
packet: &Packet{
FixedHeader: FixedHeader{
Type: Pubrec,
Remaining: 2,
},
PacketID: 12,
},
},
// Fail states
{
desc: "Malformed Pubrec - Packet ID",
group: "decode",
failFirst: ErrMalformedPacketID,
rawBytes: []byte{
byte(Pubrec << 4), 2, // Fixed header
0, // Packet ID - LSB+MSB
},
},
},
Pubrel: {
{
desc: "Pubrel",
primary: true,
rawBytes: []byte{
byte(Pubrel<<4) | 2, 2, // Fixed header
0, 12, // Packet ID - LSB+MSB
},
packet: &Packet{
FixedHeader: FixedHeader{
Type: Pubrel,
Remaining: 2,
Qos: 1,
},
PacketID: 12,
},
meta: byte(2),
},
// Fail states
{
desc: "Malformed Pubrel - Packet ID",
group: "decode",
failFirst: ErrMalformedPacketID,
rawBytes: []byte{
byte(Pubrel << 4), 2, // Fixed header
0, // Packet ID - LSB+MSB
},
},
},
Pubcomp: {
{
desc: "Pubcomp",
primary: true,
rawBytes: []byte{
byte(Pubcomp << 4), 2, // Fixed header
0, 14, // Packet ID - LSB+MSB
},
packet: &Packet{
FixedHeader: FixedHeader{
Type: Pubcomp,
Remaining: 2,
},
PacketID: 14,
},
},
// Fail states
{
desc: "Malformed Pubcomp - Packet ID",
group: "decode",
failFirst: ErrMalformedPacketID,
rawBytes: []byte{
byte(Pubcomp << 4), 2, // Fixed header
0, // Packet ID - LSB+MSB
},
},
},
Subscribe: {
{
desc: "Subscribe",
primary: true,
rawBytes: []byte{
byte(Subscribe << 4), 30, // Fixed header
0, 15, // Packet ID - LSB+MSB
0, 3, // Topic Name - LSB+MSB
'a', '/', 'b', // Topic Name
0, // QoS
0, 11, // Topic Name - LSB+MSB
'd', '/', 'e', '/', 'f', '/', 'g', '/', 'h', '/', 'i', // Topic Name
1, // QoS
0, 5, // Topic Name - LSB+MSB
'x', '/', 'y', '/', 'z', // Topic Name
2, // QoS
},
packet: &Packet{
FixedHeader: FixedHeader{
Type: Subscribe,
Remaining: 30,
},
PacketID: 15,
Topics: []string{
"a/b",
"d/e/f/g/h/i",
"x/y/z",
},
Qoss: []byte{0, 1, 2},
},
},
// Fail states
{
desc: "Malformed Subscribe - Packet ID",
group: "decode",
failFirst: ErrMalformedPacketID,
rawBytes: []byte{
byte(Subscribe << 4), 2, // Fixed header
0, // Packet ID - LSB+MSB
},
},
{
desc: "Malformed Subscribe - topic",
group: "decode",
failFirst: ErrMalformedTopic,
rawBytes: []byte{
byte(Subscribe << 4), 2, // Fixed header
0, 21, // Packet ID - LSB+MSB
0, 3, // Topic Name - LSB+MSB
'a', '/',
},
},
{
desc: "Malformed Subscribe - qos",
group: "decode",
failFirst: ErrMalformedQoS,
rawBytes: []byte{
byte(Subscribe << 4), 2, // Fixed header
0, 22, // Packet ID - LSB+MSB
0, 3, // Topic Name - LSB+MSB
'j', '/', 'b', // Topic Name
},
},
{
desc: "Malformed Subscribe - qos out of range",
group: "decode",
failFirst: ErrMalformedQoS,
rawBytes: []byte{
byte(Subscribe << 4), 2, // Fixed header
0, 22, // Packet ID - LSB+MSB
0, 3, // Topic Name - LSB+MSB
'c', '/', 'd', // Topic Name
5, // QoS
},
},
// Validation
{
// @SPEC [MQTT-2.3.1-1].
// SUBSCRIBE, UNSUBSCRIBE, and PUBLISH (in cases where QoS > 0) Control Packets MUST contain a non-zero 16-bit Packet Identifier.
desc: "[MQTT-2.3.1-1] Subscribe No Packet ID with QOS > 0",
group: "validate",
expect: ErrMissingPacketID,
code: Failed,
packet: &Packet{
FixedHeader: FixedHeader{
Type: Subscribe,
Qos: 1,
},
PacketID: 0,
},
},
// Spec tests
{
// @SPEC [MQTT-2.3.1-1].
// SUBSCRIBE, UNSUBSCRIBE, and PUBLISH (in cases where QoS > 0) Control Packets MUST contain a non-zero 16-bit Packet Identifier.
desc: "[MQTT-2.3.1-1] Subscribe No Packet ID with QOS > 0",
group: "encode",
code: Failed,
expect: ErrMissingPacketID,
rawBytes: []byte{
byte(Subscribe<<4) | 1<<1, 10, // Fixed header
0, 0, // Packet ID - LSB+MSB
0, 5, // Topic Name - LSB+MSB
'a', '/', 'b', '/', 'c', // Topic Name
1, // QoS
},
packet: &Packet{
FixedHeader: FixedHeader{
Type: Subscribe,
Qos: 1,
Remaining: 10,
},
Topics: []string{
"a/b/c",
},
Qoss: []byte{1},
PacketID: 0,
},
meta: byte(2),
},
},
Suback: {
{
desc: "Suback",
primary: true,
rawBytes: []byte{
byte(Suback << 4), 6, // Fixed header
0, 17, // Packet ID - LSB+MSB
0, // Return Code QoS 0
1, // Return Code QoS 1
2, // Return Code QoS 2
0x80, // Return Code fail
},
packet: &Packet{
FixedHeader: FixedHeader{
Type: Suback,
Remaining: 6,
},
PacketID: 17,
ReturnCodes: []byte{0, 1, 2, 0x80},
},
},
// Fail states
{
desc: "Malformed Suback - Packet ID",
group: "decode",
failFirst: ErrMalformedPacketID,
rawBytes: []byte{
byte(Subscribe << 4), 2, // Fixed header
0, // Packet ID - LSB+MSB
},
},
},
Unsubscribe: {
{
desc: "Unsubscribe",
primary: true,
rawBytes: []byte{
byte(Unsubscribe << 4), 27, // Fixed header
0, 35, // Packet ID - LSB+MSB
0, 3, // Topic Name - LSB+MSB
'a', '/', 'b', // Topic Name
0, 11, // Topic Name - LSB+MSB
'd', '/', 'e', '/', 'f', '/', 'g', '/', 'h', '/', 'i', // Topic Name
0, 5, // Topic Name - LSB+MSB
'x', '/', 'y', '/', 'z', // Topic Name
},
packet: &Packet{
FixedHeader: FixedHeader{
Type: Unsubscribe,
Remaining: 27,
},
PacketID: 35,
Topics: []string{
"a/b",
"d/e/f/g/h/i",
"x/y/z",
},
},
},
// Fail states
{
desc: "Malformed Unsubscribe - Packet ID",
group: "decode",
failFirst: ErrMalformedPacketID,
rawBytes: []byte{
byte(Unsubscribe << 4), 2, // Fixed header
0, // Packet ID - LSB+MSB
},
},
{
desc: "Malformed Unsubscribe - topic",
group: "decode",
failFirst: ErrMalformedTopic,
rawBytes: []byte{
byte(Unsubscribe << 4), 2, // Fixed header
0, 21, // Packet ID - LSB+MSB
0, 3, // Topic Name - LSB+MSB
'a', '/',
},
},
// Validation
{
// @SPEC [MQTT-2.3.1-1].
// SUBSCRIBE, UNSUBSCRIBE, and PUBLISH (in cases where QoS > 0) Control Packets MUST contain a non-zero 16-bit Packet Identifier.
desc: "[MQTT-2.3.1-1] Subscribe No Packet ID with QOS > 0",
group: "validate",
expect: ErrMissingPacketID,
code: Failed,
packet: &Packet{
FixedHeader: FixedHeader{
Type: Unsubscribe,
Qos: 1,
},
PacketID: 0,
},
},
// Spec tests
{
// @SPEC [MQTT-2.3.1-1].
// SUBSCRIBE, UNSUBSCRIBE, and PUBLISH (in cases where QoS > 0) Control Packets MUST contain a non-zero 16-bit Packet Identifier.
desc: "[MQTT-2.3.1-1] Unsubscribe No Packet ID with QOS > 0",
group: "encode",
code: Failed,
expect: ErrMissingPacketID,
rawBytes: []byte{
byte(Unsubscribe<<4) | 1<<1, 9, // Fixed header
0, 0, // Packet ID - LSB+MSB
0, 5, // Topic Name - LSB+MSB
'a', '/', 'b', '/', 'c', // Topic Name
},
packet: &Packet{
FixedHeader: FixedHeader{
Type: Unsubscribe,
Qos: 1,
Remaining: 9,
},
Topics: []string{
"a/b/c",
},
PacketID: 0,
},
meta: byte(2),
},
},
Unsuback: {
{
desc: "Unsuback",
primary: true,
rawBytes: []byte{
byte(Unsuback << 4), 2, // Fixed header
0, 37, // Packet ID - LSB+MSB
},
packet: &Packet{
FixedHeader: FixedHeader{
Type: Unsuback,
Remaining: 2,
},
PacketID: 37,
},
},
// Fail states
{
desc: "Malformed Unsuback - Packet ID",
group: "decode",
failFirst: ErrMalformedPacketID,
rawBytes: []byte{
byte(Unsuback << 4), 2, // Fixed header
0, // Packet ID - LSB+MSB
},
},
},
Pingreq: {
{
desc: "Ping request",
primary: true,
rawBytes: []byte{
byte(Pingreq << 4), 0, // fixed header
},
packet: &Packet{
FixedHeader: FixedHeader{
Type: Pingreq,
Remaining: 0,
},
},
},
},
Pingresp: {
{
desc: "Ping response",
primary: true,
rawBytes: []byte{
byte(Pingresp << 4), 0, // fixed header
},
packet: &Packet{
FixedHeader: FixedHeader{
Type: Pingresp,
Remaining: 0,
},
},
},
},
Disconnect: {
{
desc: "Disconnect",
primary: true,
rawBytes: []byte{
byte(Disconnect << 4), 0, // fixed header
},
packet: &Packet{
FixedHeader: FixedHeader{
Type: Disconnect,
Remaining: 0,
},
},
},
},
}