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, }, }, }, }, }