# encoding: BINARY # Encoding is set to binary, so that the binary packets aren't validated as UTF-8 $:.unshift(File.dirname(__FILE__)) require 'spec_helper' describe MQTT::Packet do describe "when creating a new packet" do it "should allow you to set the packet flags as a hash parameter" do packet = MQTT::Packet.new( :flags => [true, false, true, false] ) expect(packet.flags).to eq([true, false, true, false]) end it "should have a custom inspect method" do packet = MQTT::Packet.new expect(packet.inspect).to eq('#') end it "should have a type_id method to get the integer ID of the packet type" do packet = MQTT::Packet::Pingreq.new expect(packet.type_id).to eq(12) end end it "should let you change attributes using the update_attributes method" do packet = MQTT::Packet.new(:flags => [false, false, false, true]) packet.update_attributes(:flags => [false, false, true, true]) expect(packet.flags).to eq([false, false, true, true]) end describe "protected methods" do let(:packet) { MQTT::Packet.new } it "should provide a encode_bytes method to get some bytes as Integers" do data = packet.send(:encode_bytes, 0x48, 0x65, 0x6c, 0x6c, 'o'.unpack('C1')[0]) expect(data).to eq('Hello') end it "should provide a encode_bits method to encode an array of bits to a string" do data = packet.send(:encode_bits, [false, true, true, false, true, false, true, false]) expect(data).to eq('V') end it "should provide a add_short method to get a big-endian unsigned 16-bit integer" do data = packet.send(:encode_short, 1024) expect(data).to eq("\x04\x00") expect(data.encoding.to_s).to eq("ASCII-8BIT") end it "should raise an error if too big argument for encode_short" do expect { data = packet.send(:encode_short, 0x10000) }.to raise_error( 'Value too big for short' ) end it "should provide a add_string method to get a string preceeded by its length" do data = packet.send(:encode_string, 'quack') expect(data).to eq("\x00\x05quack") expect(data.encoding.to_s).to eq("ASCII-8BIT") end it "should provide a shift_short method to get a 16-bit unsigned integer" do buffer = "\x22\x8Bblahblah" expect(packet.send(:shift_short,buffer)).to eq(8843) expect(buffer).to eq('blahblah') end it "should provide a shift_byte method to get one byte as integers" do buffer = "\x01blahblah" expect(packet.send(:shift_byte,buffer)).to eq(1) expect(buffer).to eq('blahblah') end it "should provide a shift_byte method to get one byte as integers" do buffer = "Yblahblah" expect(packet.send(:shift_bits, buffer)).to eq([true, false, false, true, true, false, true, false]) expect(buffer).to eq('blahblah') end it "should provide a shift_string method to get a string preceeded by its length" do buffer = "\x00\x05Hello World" expect(packet.send(:shift_string,buffer)).to eq("Hello") expect(buffer).to eq(' World') end end describe "deprecated attributes" do it "should still have a message_id method that is that same as id" do packet = MQTT::Packet.new packet.message_id = 1234 expect(packet.message_id).to eq(1234) expect(packet.id).to eq(1234) packet.id = 4321 expect(packet.message_id).to eq(4321) expect(packet.id).to eq(4321) end end end describe MQTT::Packet::Publish do describe "when creating a packet" do it "should allow you to set the packet QoS level as a hash parameter" do packet = MQTT::Packet::Publish.new( :qos => 2 ) expect(packet.qos).to eq(2) end it "should allow you to set the packet retain flag as a hash parameter" do packet = MQTT::Packet::Publish.new( :retain => true ) expect(packet.retain).to be_truthy end it "should raise an exception the QoS is greater than 2" do expect { packet = MQTT::Packet::Publish.new( :qos => 3 ) }.to raise_error( 'Invalid QoS value: 3' ) end it "should raise an exception the QoS is less than 0" do expect { packet = MQTT::Packet::Publish.new( :qos => -1 ) }.to raise_error( 'Invalid QoS value: -1' ) end end describe "when setting attributes on a packet" do let(:packet) { MQTT::Packet::Publish.new( :duplicate => false, :qos => 0, :retain => false ) } it "should let you change the dup flag of a packet" do packet.duplicate = true expect(packet.duplicate).to be_truthy end it "should let you change the dup flag of a packet using an integer" do packet.duplicate = 1 expect(packet.duplicate).to be_truthy end it "should let you change the QoS value of a packet" do packet.qos = 1 expect(packet.qos).to eq(1) end it "should let you change the retain flag of a packet" do packet.retain = true expect(packet.retain).to be_truthy end it "should let you change the retain flag of a packet using an integer" do packet.retain = 1 expect(packet.retain).to be_truthy end end describe "when serialising a packet" do it "should output the correct bytes for a packet with default QoS and no flags" do packet = MQTT::Packet::Publish.new( :topic => 'test', :payload => 'hello world' ) expect(packet.to_s).to eq("\x30\x11\x00\x04testhello world") end it "should output the correct bytes for a packet with QoS 1 and no flags" do packet = MQTT::Packet::Publish.new( :id => 5, :qos => 1, :topic => 'a/b', :payload => 'hello world' ) expect(packet.to_s).to eq("\x32\x12\x00\x03a/b\x00\x05hello world") end it "should output the correct bytes for a packet with QoS 2 and retain flag set" do packet = MQTT::Packet::Publish.new( :id => 5, :qos => 2, :retain => true, :topic => 'c/d', :payload => 'hello world' ) expect(packet.to_s).to eq("\x35\x12\x00\x03c/d\x00\x05hello world") end it "should output the correct bytes for a packet with QoS 2 and dup flag set" do packet = MQTT::Packet::Publish.new( :id => 5, :qos => 2, :duplicate => true, :topic => 'c/d', :payload => 'hello world' ) expect(packet.to_s).to eq("\x3C\x12\x00\x03c/d\x00\x05hello world") end it "should output the correct bytes for a packet with an empty payload" do packet = MQTT::Packet::Publish.new( :topic => 'test' ) expect(packet.to_s).to eq("\x30\x06\x00\x04test") end it "should output a string as binary / 8-bit ASCII" do packet = MQTT::Packet::Publish.new( :topic => 'test', :payload => 'hello world' ) expect(packet.to_s.encoding.to_s).to eq("ASCII-8BIT") end it "should support passing in non-strings to the topic and payload" do packet = MQTT::Packet::Publish.new( :topic => :symbol, :payload => 1234 ) expect(packet.to_s).to eq("\x30\x0c\x00\x06symbol1234") end it "should raise an exception when there is no topic name" do expect { MQTT::Packet::Publish.new.to_s }.to raise_error( 'Invalid topic name when serialising packet' ) end it "should raise an exception when there is an empty topic name" do expect { MQTT::Packet::Publish.new( :topic => '' ).to_s }.to raise_error( 'Invalid topic name when serialising packet' ) end end describe "when serialising an oversized packet" do it "should raise an exception when body is bigger than 256MB" do expect { packet = MQTT::Packet::Publish.new( :topic => 'test', :payload => 'x'*268435455 ) packet.to_s }.to raise_error( 'Error serialising packet: body is more than 256MB' ) end end describe "when parsing a packet with QoS 0" do let(:packet) { MQTT::Packet.parse( "\x30\x11\x00\x04testhello world" ) } it "should correctly create the right type of packet object" do expect(packet.class).to eq(MQTT::Packet::Publish) end it "should set the QoS level correctly" do expect(packet.qos).to eq(0) end it "should set the RETAIN flag correctly" do expect(packet.retain).to be_falsey end it "should set the DUP flag correctly" do expect(packet.duplicate).to be_falsey end it "should set the topic name correctly" do expect(packet.topic).to eq('test') expect(packet.topic.encoding.to_s).to eq('UTF-8') end it "should set the payload correctly" do expect(packet.payload).to eq('hello world') expect(packet.payload.encoding.to_s).to eq('ASCII-8BIT') end end describe "when parsing a packet with QoS 2 and retain and dup flags set" do let(:packet) { MQTT::Packet.parse( "\x3D\x12\x00\x03c/d\x00\x05hello world" ) } it "should correctly create the right type of packet object" do expect(packet.class).to eq(MQTT::Packet::Publish) end it "should set the QoS level correctly" do expect(packet.qos).to eq(2) end it "should set the RETAIN flag correctly" do expect(packet.retain).to be_truthy end it "should set the DUP flag correctly" do expect(packet.duplicate).to be_truthy end it "should set the topic name correctly" do expect(packet.topic).to eq('c/d') expect(packet.topic.encoding.to_s).to eq('UTF-8') end it "should set the payload correctly" do expect(packet.payload).to eq('hello world') expect(packet.payload.encoding.to_s).to eq('ASCII-8BIT') end end describe "when parsing a packet with an empty payload" do let(:packet) { MQTT::Packet.parse( "\x30\x06\x00\x04test" ) } it "should correctly create the right type of packet object" do expect(packet.class).to eq(MQTT::Packet::Publish) end it "should set the topic name correctly" do expect(packet.topic).to eq('test') end it "should set the payload correctly" do expect(packet.payload).to be_empty end end describe "when parsing a packet with a QoS value of 3" do it "should raise an exception" do expect { packet = MQTT::Packet.parse( "\x36\x12\x00\x03a/b\x00\x05hello world" ) }.to raise_error( MQTT::ProtocolException, 'Invalid packet: QoS value of 3 is not allowed' ) end end describe "when parsing a packet with QoS value of 0 and DUP set" do it "should raise an exception" do expect { packet = MQTT::Packet.parse( "\x38\x10\x00\x03a/bhello world" ) }.to raise_error( MQTT::ProtocolException, 'Invalid packet: DUP cannot be set for QoS 0' ) end end describe "when parsing a packet with a body of 314 bytes" do let(:packet) { # 0x30 = publish # 0xC1 = (65 * 1) # 0x02 = (2 * 128) MQTT::Packet.parse( "\x30\xC1\x02\x00\x05topic" + ('x' * 314) ) } it "should parse the packet type correctly" do expect(packet.class).to eq(MQTT::Packet::Publish) end it "should get the topic name correctly" do expect(packet.topic).to eq('topic') end it "should get the body length correctly" do expect(packet.payload.bytesize).to eq(314) end end describe "when parsing a packet with a body of 16kbytes" do let(:packet) do # 0x30 = publish # 0x87 = (7 * 1) # 0x80 = (0 * 128) # 0x01 = (1 * 16384) MQTT::Packet.parse( "\x30\x87\x80\x01\x00\x05topic" + ('x'*16384) ) end it "should parse the packet type correctly" do expect(packet.class).to eq(MQTT::Packet::Publish) end it "should get the topic name correctly" do expect(packet.topic).to eq('topic') end it "should get the body length correctly" do expect(packet.payload.bytesize).to eq(16384) end end describe "processing a packet containing UTF-8 character" do let(:packet) do MQTT::Packet::Publish.new( :topic => "Test ①".force_encoding("UTF-8"), :payload => "Snowman: ☃".force_encoding("UTF-8") ) end it "should have the correct topic byte length" do expect(packet.topic.bytesize).to eq(8) end it "should have the correct topic string length", :unless => RUBY_VERSION =~ /^1\.8/ do # Ruby 1.8 doesn't support UTF-8 properly expect(packet.topic.length).to eq(6) end it "should have the correct payload byte length" do expect(packet.payload.bytesize).to eq(12) end it "should have the correct payload string length", :unless => RUBY_VERSION =~ /^1\.8/ do # Ruby 1.8 doesn't support UTF-8 properly expect(packet.payload.length).to eq(10) end it "should encode to MQTT packet correctly" do expect(packet.to_s).to eq("\x30\x16\x00\x08Test \xE2\x91\xA0Snowman: \xE2\x98\x83".force_encoding('BINARY')) end it "should parse the serialised packet" do packet2 = MQTT::Packet.parse( packet.to_s ) expect(packet2.topic).to eq("Test ①".force_encoding('UTF-8')) expect(packet2.payload).to eq("Snowman: ☃".force_encoding('BINARY')) end end describe "reading a packet from a socket" do let(:socket) { StringIO.new("\x30\x11\x00\x04testhello world") } let(:packet) { MQTT::Packet.read(socket) } it "should correctly create the right type of packet object" do expect(packet.class).to eq(MQTT::Packet::Publish) end it "should set the body length is read correctly" do expect(packet.body_length).to eq(17) end it "should set the QoS level correctly" do expect(packet.qos).to eq(0) end it "should set the RETAIN flag correctly" do expect(packet.retain).to be_falsey end it "should set the DUP flag correctly" do expect(packet.duplicate).to be_falsey end it "should set the topic name correctly" do expect(packet.topic).to eq('test') expect(packet.topic.encoding.to_s).to eq('UTF-8') end it "should set the payload correctly" do expect(packet.payload).to eq('hello world') expect(packet.payload.encoding.to_s).to eq('ASCII-8BIT') end end describe "when calling the inspect method" do it "should output the payload, if it is less than 16 bytes" do packet = MQTT::Packet::Publish.new( :topic => "topic", :payload => "payload" ) expect(packet.inspect).to eq("#") end it "should output the length of the payload, if it is more than 16 bytes" do packet = MQTT::Packet::Publish.new( :topic => "topic", :payload => 'x'*32 ) expect(packet.inspect).to eq("#") end it "should only output the length of a binary payload" do packet = MQTT::Packet.parse("\x31\x12\x00\x04test\x8D\xF8\x09\x40\xC4\xE7\x4f\xF0\xFF\x30\xE0\xE7") expect(packet.inspect).to eq("#") end end end describe MQTT::Packet::Connect do describe "when serialising a packet" do it "should output the correct bytes for a packet with no flags" do packet = MQTT::Packet::Connect.new( :client_id => 'myclient' ) expect(packet.to_s).to eq("\020\026\x00\x06MQIsdp\x03\x02\x00\x0f\x00\x08myclient") end it "should output the correct bytes for a packet with clean session turned off" do packet = MQTT::Packet::Connect.new( :client_id => 'myclient', :clean_session => false ) expect(packet.to_s).to eq("\020\026\x00\x06MQIsdp\x03\x00\x00\x0f\x00\x08myclient") end context "protocol version 3.1.0" do it "should raise an exception when there is no client identifier" do expect { MQTT::Packet::Connect.new(:version => '3.1.0', :client_id => '').to_s }.to raise_error( 'Client identifier too short while serialising packet' ) end it "should raise an exception when the client identifier is too long" do expect { client_id = '0EB8D2FE7C254715B4467C5B2ECAD100' MQTT::Packet::Connect.new(:version => '3.1.0', :client_id => client_id).to_s }.to raise_error( 'Client identifier too long when serialising packet' ) end end context "protocol version 3.1.1" do it "should allow no client identifier" do packet = MQTT::Packet::Connect.new( :version => '3.1.1', :client_id => '', :clean_session => true ) expect(packet.to_s).to eq("\020\014\x00\x04MQTT\x04\x02\x00\x0f\x00\x00") end it "should allow a 32 character client identifier" do client_id = '0EB8D2FE7C254715B4467C5B2ECAD100' packet = MQTT::Packet::Connect.new( :version => '3.1.1', :client_id => client_id, :clean_session => true ) expect(packet.to_s).to eq("\x10,\x00\x04MQTT\x04\x02\x00\x0F\x00\x200EB8D2FE7C254715B4467C5B2ECAD100") end end it "should raise an exception if the keep alive value is less than 0" do expect { MQTT::Packet::Connect.new(:client_id => 'test', :keep_alive => -2).to_s }.to raise_error( 'Invalid keep-alive value: cannot be less than 0' ) end it "should output the correct bytes for a packet with a Will" do packet = MQTT::Packet::Connect.new( :client_id => 'myclient', :clean_session => true, :will_qos => 1, :will_retain => false, :will_topic => 'topic', :will_payload => 'hello' ) expect(packet.to_s).to eq( "\x10\x24"+ "\x00\x06MQIsdp"+ "\x03\x0e\x00\x0f"+ "\x00\x08myclient"+ "\x00\x05topic\x00\x05hello" ) end it "should output the correct bytes for a packet with a username and password" do packet = MQTT::Packet::Connect.new( :client_id => 'myclient', :username => 'username', :password => 'password' ) expect(packet.to_s).to eq( "\x10\x2A"+ "\x00\x06MQIsdp"+ "\x03\xC2\x00\x0f"+ "\x00\x08myclient"+ "\x00\x08username"+ "\x00\x08password" ) end it "should output the correct bytes for a packet with everything" do packet = MQTT::Packet::Connect.new( :client_id => '12345678901234567890123', :clean_session => true, :keep_alive => 65535, :will_qos => 2, :will_retain => true, :will_topic => 'will_topic', :will_payload => 'will_message', :username => 'user0123456789', :password => 'pass0123456789' ) expect(packet.to_s).to eq( "\x10\x5F"+ # fixed header (2) "\x00\x06MQIsdp"+ # protocol name (8) "\x03\xf6"+ # protocol level + flags (2) "\xff\xff"+ # keep alive (2) "\x00\x1712345678901234567890123"+ # client identifier (25) "\x00\x0Awill_topic"+ # will topic (12) "\x00\x0Cwill_message"+ # will message (14) "\x00\x0Euser0123456789"+ # username (16) "\x00\x0Epass0123456789" ) # password (16) end context 'protocol version 3.1.1' do it "should output the correct bytes for a packet with no flags" do packet = MQTT::Packet::Connect.new( :version => '3.1.1', :client_id => 'myclient' ) expect(packet.to_s).to eq("\020\024\x00\x04MQTT\x04\x02\x00\x0f\x00\x08myclient") end end context 'an invalid protocol version number' do it "should raise a protocol exception" do expect { packet = MQTT::Packet::Connect.new( :version => 'x.x.x', :client_id => 'myclient' ) }.to raise_error( ArgumentError, "Unsupported protocol version: x.x.x" ) end end end describe "when parsing a simple 3.1.0 Connect packet" do let(:packet) do MQTT::Packet.parse( "\x10\x16\x00\x06MQIsdp\x03\x00\x00\x0a\x00\x08myclient" ) end it "should correctly create the right type of packet object" do expect(packet.class).to eq(MQTT::Packet::Connect) end it "should set the fixed header flags of the packet correctly" do expect(packet.flags).to eq([false, false, false, false]) end it "should set the Protocol Name of the packet correctly" do expect(packet.protocol_name).to eq('MQIsdp') expect(packet.protocol_name.encoding.to_s).to eq('UTF-8') end it "should set the Protocol Level of the packet correctly" do expect(packet.protocol_level).to eq(3) end it "should set the Protocol version of the packet correctly" do expect(packet.version).to eq('3.1.0') end it "should set the Client Identifier of the packet correctly" do expect(packet.client_id).to eq('myclient') expect(packet.client_id.encoding.to_s).to eq('UTF-8') end it "should set the Keep Alive timer of the packet correctly" do expect(packet.keep_alive).to eq(10) end it "should set not have the clean session flag set" do expect(packet.clean_session).to be_falsey end it "should set the the username field of the packet to nil" do expect(packet.username).to be_nil end it "should set the the password field of the packet to nil" do expect(packet.password).to be_nil end end describe "when parsing a simple 3.1.1 Connect packet" do let(:packet) do MQTT::Packet.parse( "\x10\x14\x00\x04MQTT\x04\x00\x00\x0a\x00\x08myclient" ) end it "should correctly create the right type of packet object" do expect(packet.class).to eq(MQTT::Packet::Connect) end it "should set the fixed header flags of the packet correctly" do expect(packet.flags).to eq([false, false, false, false]) end it "should set the Protocol Name of the packet correctly" do expect(packet.protocol_name).to eq('MQTT') expect(packet.protocol_name.encoding.to_s).to eq('UTF-8') end it "should set the Protocol Level of the packet correctly" do expect(packet.protocol_level).to eq(4) end it "should set the Protocol version of the packet correctly" do expect(packet.version).to eq('3.1.1') end it "should set the Client Identifier of the packet correctly" do expect(packet.client_id).to eq('myclient') expect(packet.client_id.encoding.to_s).to eq('UTF-8') end it "should set the Keep Alive timer of the packet correctly" do expect(packet.keep_alive).to eq(10) end it "should set not have the clean session flag set" do expect(packet.clean_session).to be_falsey end it "should set the the username field of the packet to nil" do expect(packet.username).to be_nil end it "should set the the password field of the packet to nil" do expect(packet.password).to be_nil end end describe "when parsing a Connect packet with the clean session flag set" do let(:packet) do MQTT::Packet.parse( "\x10\x16\x00\x06MQIsdp\x03\x02\x00\x0a\x00\x08myclient" ) end it "should set the clean session flag" do expect(packet.clean_session).to be_truthy end end describe "when parsing a Connect packet with a Will and Testament" do let(:packet) do MQTT::Packet.parse( "\x10\x24\x00\x06MQIsdp\x03\x0e\x00\x0a\x00\x08myclient\x00\x05topic\x00\x05hello" ) end it "should correctly create the right type of packet object" do expect(packet.class).to eq(MQTT::Packet::Connect) end it "should set the fixed header flags of the packet correctly" do expect(packet.flags).to eq([false, false, false, false]) end it "should set the Protocol Name of the packet correctly" do expect(packet.protocol_name).to eq('MQIsdp') expect(packet.protocol_name.encoding.to_s).to eq('UTF-8') end it "should set the Protocol Level of the packet correctly" do expect(packet.protocol_level).to eq(3) end it "should set the Protocol version of the packet correctly" do expect(packet.version).to eq('3.1.0') end it "should set the Client Identifier of the packet correctly" do expect(packet.client_id).to eq('myclient') expect(packet.client_id.encoding.to_s).to eq('UTF-8') end it "should set the clean session flag should be set" do expect(packet.clean_session).to be_truthy end it "should set the QoS of the Will should be 1" do expect(packet.will_qos).to eq(1) end it "should set the Will retain flag should be false" do expect(packet.will_retain).to be_falsey end it "should set the Will topic of the packet correctly" do expect(packet.will_topic).to eq('topic') expect(packet.will_topic.encoding.to_s).to eq('UTF-8') end it "should set the Will payload of the packet correctly" do expect(packet.will_payload).to eq('hello') expect(packet.will_payload.encoding.to_s).to eq('UTF-8') end end describe "when parsing a Connect packet with a username and password" do let(:packet) do MQTT::Packet.parse( "\x10\x2A"+ "\x00\x06MQIsdp"+ "\x03\xC0\x00\x0a"+ "\x00\x08myclient"+ "\x00\x08username"+ "\x00\x08password" ) end it "should correctly create the right type of packet object" do expect(packet.class).to eq(MQTT::Packet::Connect) end it "should set the fixed header flags of the packet correctly" do expect(packet.flags).to eq([false, false, false, false]) end it "should set the Protocol Name of the packet correctly" do expect(packet.protocol_name).to eq('MQIsdp') expect(packet.protocol_name.encoding.to_s).to eq('UTF-8') end it "should set the Protocol Level of the packet correctly" do expect(packet.protocol_level).to eq(3) end it "should set the Protocol version of the packet correctly" do expect(packet.version).to eq('3.1.0') end it "should set the Client Identifier of the packet correctly" do expect(packet.client_id).to eq('myclient') expect(packet.client_id.encoding.to_s).to eq('UTF-8') end it "should set the Keep Alive Timer of the packet correctly" do expect(packet.keep_alive).to eq(10) end it "should set the Username of the packet correctly" do expect(packet.username).to eq('username') expect(packet.username.encoding.to_s).to eq('UTF-8') end it "should set the Username of the packet correctly" do expect(packet.password).to eq('password') expect(packet.password.encoding.to_s).to eq('UTF-8') end end describe "when parsing a Connect that has a username but no password" do let(:packet) do MQTT::Packet.parse( "\x10\x20\x00\x06MQIsdp\x03\x80\x00\x0a\x00\x08myclient\x00\x08username" ) end it "should set the Username of the packet correctly" do expect(packet.username).to eq('username') expect(packet.username.encoding.to_s).to eq('UTF-8') end it "should set the Username of the packet correctly" do expect(packet.password).to be_nil end end describe "when parsing a Connect that has a password but no username" do let(:packet) do MQTT::Packet.parse( "\x10\x20\x00\x06MQIsdp\x03\x40\x00\x0a\x00\x08myclient\x00\x08password" ) end it "should set the Username of the packet correctly" do expect(packet.username).to be_nil end it "should set the Username of the packet correctly" do expect(packet.password).to eq('password') expect(packet.password.encoding.to_s).to eq('UTF-8') end end describe "when parsing a Connect packet has the username and password flags set but doesn't have the fields" do let(:packet) do MQTT::Packet.parse( "\x10\x16\x00\x06MQIsdp\x03\xC0\x00\x0a\x00\x08myclient" ) end it "should set the Username of the packet correctly" do expect(packet.username).to be_nil end it "should set the Username of the packet correctly" do expect(packet.password).to be_nil end end describe "when parsing a Connect packet with every option set" do let(:packet) do MQTT::Packet.parse( "\x10\x5F"+ # fixed header (2) "\x00\x06MQIsdp"+ # protocol name (8) "\x03\xf6"+ # protocol level + flags (2) "\xff\xff"+ # keep alive (2) "\x00\x1712345678901234567890123"+ # client identifier (25) "\x00\x0Awill_topic"+ # will topic (12) "\x00\x0Cwill_message"+ # will message (14) "\x00\x0Euser0123456789"+ # username (16) "\x00\x0Epass0123456789" # password (16) ) end it "should correctly create the right type of packet object" do expect(packet.class).to eq(MQTT::Packet::Connect) end it "should set the fixed header flags of the packet correctly" do expect(packet.flags).to eq([false, false, false, false]) end it "should set the Protocol Name of the packet correctly" do expect(packet.protocol_name).to eq('MQIsdp') expect(packet.protocol_name.encoding.to_s).to eq('UTF-8') end it "should set the Protocol Level of the packet correctly" do expect(packet.protocol_level).to eq(3) end it "should set the Protocol version of the packet correctly" do expect(packet.version).to eq('3.1.0') end it "should set the Keep Alive Timer of the packet correctly" do expect(packet.keep_alive).to eq(65535) end it "should set the Client Identifier of the packet correctly" do expect(packet.client_id).to eq('12345678901234567890123') expect(packet.client_id.encoding.to_s).to eq('UTF-8') end it "should set the Will QoS of the packet correctly" do expect(packet.will_qos).to eq(2) end it "should set the Will retain flag of the packet correctly" do expect(packet.will_retain).to be_truthy end it "should set the Will topic of the packet correctly" do expect(packet.will_topic).to eq('will_topic') expect(packet.will_topic.encoding.to_s).to eq('UTF-8') end it "should set the Will payload of the packet correctly" do expect(packet.will_payload).to eq('will_message') expect(packet.will_payload.encoding.to_s).to eq('UTF-8') end it "should set the Username of the packet correctly" do expect(packet.username).to eq('user0123456789') expect(packet.username.encoding.to_s).to eq('UTF-8') end it "should set the Username of the packet correctly" do expect(packet.password).to eq('pass0123456789') expect(packet.password.encoding.to_s).to eq('UTF-8') end end describe "when parsing packet with an unknown protocol name" do it "should raise a protocol exception" do expect { packet = MQTT::Packet.parse( "\x10\x16\x00\x06FooBar\x03\x00\x00\x0a\x00\x08myclient" ) }.to raise_error( MQTT::ProtocolException, "Unsupported protocol: FooBar/3" ) end end describe "when parsing packet with an unknown protocol level" do it "should raise a protocol exception" do expect { packet = MQTT::Packet.parse( "\x10\x16\x00\x06MQIsdp\x02\x00\x00\x0a\x00\x08myclient" ) }.to raise_error( MQTT::ProtocolException, "Unsupported protocol: MQIsdp/2" ) end end describe "when parsing packet with invalid fixed header flags" do it "should raise a protocol exception" do expect { MQTT::Packet.parse( "\x13\x16\x00\x06MQIsdp\x03\x00\x00\x0a\x00\x08myclient" ) }.to raise_error( MQTT::ProtocolException, "Invalid flags in CONNECT packet header" ) end end describe "when calling the inspect method" do it "should output correct string for the default options" do packet = MQTT::Packet::Connect.new expect(packet.inspect).to eq("#") end it "should output correct string when parameters are given" do packet = MQTT::Packet::Connect.new( :keep_alive => 10, :client_id => 'c123', :clean_session => false, :username => 'foo' ) expect(packet.inspect).to eq("#") end end describe "deprecated attributes" do it "should still have a protocol_version method that is that same as protocol_level" do packet = MQTT::Packet::Connect.new packet.protocol_version = 5 expect(packet.protocol_version).to eq(5) expect(packet.protocol_level).to eq(5) packet.protocol_version = 4 expect(packet.protocol_version).to eq(4) expect(packet.protocol_level).to eq(4) end end end describe MQTT::Packet::Connack do describe "when setting attributes on a packet" do let(:packet) { MQTT::Packet::Connack.new } it "should let you change the session present flag of a packet" do packet.session_present = true expect(packet.session_present).to be_truthy end it "should let you change the session present flag of a packet using an integer" do packet.session_present = 1 expect(packet.session_present).to be_truthy end it "should let you change the return code of a packet" do packet.return_code = 3 expect(packet.return_code).to eq(3) end end describe "when serialising a packet" do it "should output the correct bytes for a sucessful connection acknowledgement packet without Session Present set" do packet = MQTT::Packet::Connack.new( :return_code => 0x00, :session_present => false ) expect(packet.to_s).to eq("\x20\x02\x00\x00") end it "should output the correct bytes for a sucessful connection acknowledgement packet with Session Present set" do packet = MQTT::Packet::Connack.new( :return_code => 0x00, :session_present => true ) expect(packet.to_s).to eq("\x20\x02\x01\x00") end end describe "when parsing a successful Connection Accepted packet" do let(:packet) do MQTT::Packet.parse( "\x20\x02\x00\x00" ) end it "should correctly create the right type of packet object" do expect(packet.class).to eq(MQTT::Packet::Connack) end it "should set the fixed header flags of the packet correctly" do expect(packet.flags).to eq([false, false, false, false]) end it "should set the Session Pression flag of the packet correctly" do expect(packet.session_present).to eq(false) end it "should set the return code of the packet correctly" do expect(packet.return_code).to eq(0x00) end it "should set the return message of the packet correctly" do expect(packet.return_msg).to match(/Connection Accepted/i) end end describe "when parsing a successful Connection Accepted packet with Session Present set" do let(:packet) do MQTT::Packet.parse( "\x20\x02\x01\x00" ) end it "should correctly create the right type of packet object" do expect(packet.class).to eq(MQTT::Packet::Connack) end it "should set the fixed header flags of the packet correctly" do expect(packet.flags).to eq([false, false, false, false]) end it "should set the Session Pression flag of the packet correctly" do expect(packet.session_present).to eq(true) end it "should set the return code of the packet correctly" do expect(packet.return_code).to eq(0x00) end it "should set the return message of the packet correctly" do expect(packet.return_msg).to match(/Connection Accepted/i) end end describe "when parsing a unacceptable protocol version packet" do let(:packet) do MQTT::Packet.parse( "\x20\x02\x00\x01" ) end it "should correctly create the right type of packet object" do expect(packet.class).to eq(MQTT::Packet::Connack) end it "should set the return code of the packet correctly" do expect(packet.return_code).to eq(0x01) end it "should set the return message of the packet correctly" do expect(packet.return_msg).to match(/unacceptable protocol version/i) end end describe "when parsing a client identifier rejected packet" do let(:packet) { MQTT::Packet.parse( "\x20\x02\x00\x02" ) } it "should correctly create the right type of packet object" do expect(packet.class).to eq(MQTT::Packet::Connack) end it "should set the return code of the packet correctly" do expect(packet.return_code).to eq(0x02) end it "should set the return message of the packet correctly" do expect(packet.return_msg).to match(/client identifier rejected/i) end end describe "when parsing a server unavailable packet" do let(:packet) do MQTT::Packet.parse( "\x20\x02\x00\x03" ) end it "should correctly create the right type of packet object" do expect(packet.class).to eq(MQTT::Packet::Connack) end it "should set the return code of the packet correctly" do expect(packet.return_code).to eq(0x03) end it "should set the return message of the packet correctly" do expect(packet.return_msg).to match(/server unavailable/i) end end describe "when parsing a server unavailable packet" do let(:packet) do MQTT::Packet.parse( "\x20\x02\x00\x04" ) end it "should correctly create the right type of packet object" do expect(packet.class).to eq(MQTT::Packet::Connack) end it "should set the return code of the packet correctly" do expect(packet.return_code).to eq(0x04) end it "should set the return message of the packet correctly" do expect(packet.return_msg).to match(/bad user name or password/i) end end describe "when parsing a server unavailable packet" do let(:packet) do MQTT::Packet.parse( "\x20\x02\x00\x05" ) end it "should correctly create the right type of packet object" do expect(packet.class).to eq(MQTT::Packet::Connack) end it "should set the return code of the packet correctly" do expect(packet.return_code).to eq(0x05) end it "should set the return message of the packet correctly" do expect(packet.return_msg).to match(/not authorised/i) end end describe "when parsing an unknown connection refused packet" do let(:packet) { MQTT::Packet.parse( "\x20\x02\x00\x10" ) } it "should correctly create the right type of packet object" do expect(packet.class).to eq(MQTT::Packet::Connack) end it "should set the return code of the packet correctly" do expect(packet.return_code).to eq(0x10) end it "should set the return message of the packet correctly" do expect(packet.return_msg).to match(/Connection refused: error code 16/i) end end describe "when parsing packet with invalid Connack flags set" do it "should raise an exception" do expect { packet = MQTT::Packet.parse( "\x20\x02\xff\x05" ) }.to raise_error( MQTT::ProtocolException, "Invalid flags in Connack variable header" ) end end describe "when parsing packet with extra bytes on the end" do it "should raise an exception" do expect { packet = MQTT::Packet.parse( "\x20\x03\x00\x00\x00" ) }.to raise_error( MQTT::ProtocolException, "Extra bytes at end of Connect Acknowledgment packet" ) end end describe "when parsing packet with invalid fixed header flags" do it "should raise a protocol exception" do expect { MQTT::Packet.parse( "\x23\x02\x00\x00" ) }.to raise_error( MQTT::ProtocolException, "Invalid flags in CONNACK packet header" ) end end describe "when calling the inspect method" do it "should output the right string when the return code is 0" do packet = MQTT::Packet::Connack.new( :return_code => 0x00 ) expect(packet.inspect).to eq("#") end it "should output the right string when the return code is 0x0F" do packet = MQTT::Packet::Connack.new( :return_code => 0x0F ) expect(packet.inspect).to eq("#") end end end describe MQTT::Packet::Puback do describe "when serialising a packet" do it "should output the correct bytes for a packet with no flags" do packet = MQTT::Packet::Puback.new( :id => 0x1234 ) expect(packet.to_s).to eq("\x40\x02\x12\x34") end end describe "when parsing a packet" do let(:packet) { MQTT::Packet.parse( "\x40\x02\x12\x34" ) } it "should correctly create the right type of packet object" do expect(packet.class).to eq(MQTT::Packet::Puback) end it "should set the message id of the packet correctly" do expect(packet.id).to eq(0x1234) end end describe "when parsing packet with extra bytes on the end" do it "should raise an exception" do expect { packet = MQTT::Packet.parse( "\x40\x03\x12\x34\x00" ) }.to raise_error( MQTT::ProtocolException, "Extra bytes at end of Publish Acknowledgment packet" ) end end describe "when parsing packet with invalid fixed header flags" do it "should raise a protocol exception" do expect { MQTT::Packet.parse( "\x43\x02\x12\x34" ) }.to raise_error( MQTT::ProtocolException, "Invalid flags in PUBACK packet header" ) end end it "should output the right string when calling inspect" do packet = MQTT::Packet::Puback.new( :id => 0x1234 ) expect(packet.inspect).to eq("#") end end describe MQTT::Packet::Pubrec do describe "when serialising a packet" do it "should output the correct bytes for a packet with no flags" do packet = MQTT::Packet::Pubrec.new( :id => 0x1234 ) expect(packet.to_s).to eq("\x50\x02\x12\x34") end end describe "when parsing a packet" do let(:packet) { MQTT::Packet.parse( "\x50\x02\x12\x34" ) } it "should correctly create the right type of packet object" do expect(packet.class).to eq(MQTT::Packet::Pubrec) end it "should set the message id of the packet correctly" do expect(packet.id).to eq(0x1234) end end describe "when parsing packet with extra bytes on the end" do it "should raise an exception" do expect { packet = MQTT::Packet.parse( "\x50\x03\x12\x34\x00" ) }.to raise_error( MQTT::ProtocolException, "Extra bytes at end of Publish Received packet" ) end end describe "when parsing packet with invalid fixed header flags" do it "should raise a protocol exception" do expect { MQTT::Packet.parse( "\x53\x02\x12\x34" ) }.to raise_error( MQTT::ProtocolException, "Invalid flags in PUBREC packet header" ) end end it "should output the right string when calling inspect" do packet = MQTT::Packet::Pubrec.new( :id => 0x1234 ) expect(packet.inspect).to eq("#") end end describe MQTT::Packet::Pubrel do describe "when serialising a packet" do it "should output the correct bytes for a packet with no flags" do packet = MQTT::Packet::Pubrel.new( :id => 0x1234 ) expect(packet.to_s).to eq("\x62\x02\x12\x34") end end describe "when parsing a packet" do let(:packet) { MQTT::Packet.parse( "\x62\x02\x12\x34" ) } it "should correctly create the right type of packet object" do expect(packet.class).to eq(MQTT::Packet::Pubrel) end it "should set the message id of the packet correctly" do expect(packet.id).to eq(0x1234) end end describe "when parsing packet with extra bytes on the end" do it "should raise an exception" do expect { packet = MQTT::Packet.parse( "\x62\x03\x12\x34\x00" ) }.to raise_error( MQTT::ProtocolException, "Extra bytes at end of Publish Release packet" ) end end describe "when parsing packet with invalid fixed header flags" do it "should raise a protocol exception" do expect { MQTT::Packet.parse( "\x60\x02\x12\x34" ) }.to raise_error( MQTT::ProtocolException, "Invalid flags in PUBREL packet header" ) end end it "should output the right string when calling inspect" do packet = MQTT::Packet::Pubrel.new( :id => 0x1234 ) expect(packet.inspect).to eq("#") end end describe MQTT::Packet::Pubcomp do describe "when serialising a packet" do it "should output the correct bytes for a packet with no flags" do packet = MQTT::Packet::Pubcomp.new( :id => 0x1234 ) expect(packet.to_s).to eq("\x70\x02\x12\x34") end end describe "when parsing a packet" do let(:packet) { MQTT::Packet.parse( "\x70\x02\x12\x34" ) } it "should correctly create the right type of packet object" do expect(packet.class).to eq(MQTT::Packet::Pubcomp) end it "should set the message id of the packet correctly" do expect(packet.id).to eq(0x1234) end end describe "when parsing packet with extra bytes on the end" do it "should raise an exception" do expect { MQTT::Packet.parse( "\x70\x03\x12\x34\x00" ) }.to raise_error( MQTT::ProtocolException, "Extra bytes at end of Publish Complete packet" ) end end describe "when parsing packet with invalid fixed header flags" do it "should raise a protocol exception" do expect { MQTT::Packet.parse( "\x72\x02\x12\x34" ) }.to raise_error( MQTT::ProtocolException, "Invalid flags in PUBCOMP packet header" ) end end it "should output the right string when calling inspect" do packet = MQTT::Packet::Pubcomp.new( :id => 0x1234 ) expect(packet.inspect).to eq("#") end end describe MQTT::Packet::Subscribe do describe "setting the packet's topics" do let(:packet) { MQTT::Packet::Subscribe.new } it "should be able to set the topics from a String 'a/b'" do packet.topics = 'a/b' expect(packet.topics).to eq([["a/b", 0]]) end it "should be able to set the multiple topics from an array ['a/b', 'b/c']" do packet.topics = ['a/b', 'b/c'] expect(packet.topics).to eq([["a/b", 0], ['b/c', 0]]) end it "should be able to set the topics from a Hash {'a/b' => 0, 'b/c' => 1}" do packet.topics = {'a/b' => 0, 'b/c' => 1} expect(packet.topics).to eq([["a/b", 0], ["b/c", 1]]) end it "should be able to set the topics from a single level array ['a/b', 0]" do packet.topics = ['a/b', 0] expect(packet.topics).to eq([["a/b", 0]]) end it "should be able to set the topics from a two level array [['a/b' => 0], ['b/c' => 1]]" do packet.topics = [['a/b', 0], ['b/c', 1]] expect(packet.topics).to eq([['a/b', 0], ['b/c', 1]]) end it "should raise an exception when setting topic with a non-string" do expect { packet.topics = 56 }.to raise_error( 'Invalid topics input: 56' ) end end describe "when serialising a packet" do it "should output the correct bytes for a packet with a single topic" do packet = MQTT::Packet::Subscribe.new( :id => 1, :topics => 'a/b' ) expect(packet.to_s).to eq("\x82\x08\x00\x01\x00\x03a/b\x00") end it "should output the correct bytes for a packet with multiple topics" do packet = MQTT::Packet::Subscribe.new( :id => 6, :topics => [['a/b', 0], ['c/d', 1]] ) expect(packet.to_s).to eq("\x82\x0e\000\x06\x00\x03a/b\x00\x00\x03c/d\x01") end it "should raise an exception when no topics are given" do expect { MQTT::Packet::Subscribe.new.to_s }.to raise_error( 'no topics given when serialising packet' ) end end describe "when parsing a packet with a single topic" do let(:packet) { MQTT::Packet.parse( "\x82\x08\x00\x01\x00\x03a/b\x00" ) } it "should correctly create the right type of packet object" do expect(packet.class).to eq(MQTT::Packet::Subscribe) end it "should set the fixed header flags of the packet correctly" do expect(packet.flags).to eq([false, true, false, false]) end it "should set the Message ID correctly" do expect(packet.id).to eq(1) end it "should set the topic name correctly" do expect(packet.topics).to eq([['a/b',0]]) end end describe "when parsing a packet with a two topics" do let(:packet) { MQTT::Packet.parse( "\x82\x0e\000\x06\x00\x03a/b\x00\x00\x03c/d\x01" ) } it "should correctly create the right type of packet object" do expect(packet.class).to eq(MQTT::Packet::Subscribe) end it "should set the fixed header flags of the packet correctly" do expect(packet.flags).to eq([false, true, false, false]) end it "should set the Message ID correctly" do expect(packet.id).to eq(6) end it "should set the topic name correctly" do expect(packet.topics).to eq([['a/b',0],['c/d',1]]) end end describe "when parsing packet with invalid fixed header flags" do it "should raise a protocol exception" do expect { MQTT::Packet.parse( "\x80\x08\x00\x01\x00\x03a/b\x00" ) }.to raise_error( MQTT::ProtocolException, "Invalid flags in SUBSCRIBE packet header" ) end end describe "when calling the inspect method" do it "should output correct string for a single topic" do packet = MQTT::Packet::Subscribe.new(:topics => 'test') expect(packet.inspect).to eq("#") end it "should output correct string for multiple topics" do packet = MQTT::Packet::Subscribe.new(:topics => {'a' => 1, 'b' => 0, 'c' => 2}) expect(packet.inspect).to eq("#") end end end describe MQTT::Packet::Suback do describe "when serialising a packet" do it "should output the correct bytes for an acknowledgement to a single topic" do packet = MQTT::Packet::Suback.new( :id => 5, :return_codes => 0 ) expect(packet.to_s).to eq("\x90\x03\x00\x05\x00") end it "should output the correct bytes for an acknowledgement to a two topics" do packet = MQTT::Packet::Suback.new( :id => 6 , :return_codes => [0,1] ) expect(packet.to_s).to eq("\x90\x04\x00\x06\x00\x01") end it "should raise an exception when no granted QoSs are given" do expect { MQTT::Packet::Suback.new( :id => 7 ).to_s }.to raise_error( 'no granted QoS given when serialising packet' ) end it "should raise an exception if the granted QoS is not an integer" do expect { MQTT::Packet::Suback.new( :id => 8, :return_codes => :foo ).to_s }.to raise_error( 'return_codes should be an integer or an array of return codes' ) end end describe "when parsing a packet with a single QoS value of 0" do let(:packet) { MQTT::Packet.parse( "\x90\x03\x12\x34\x00" ) } it "should correctly create the right type of packet object" do expect(packet.class).to eq(MQTT::Packet::Suback) end it "should set the message id of the packet correctly" do expect(packet.id).to eq(0x1234) end it "should set the Granted QoS of the packet correctly" do expect(packet.return_codes).to eq([0]) end end describe "when parsing a packet with two QoS values" do let(:packet) { MQTT::Packet.parse( "\x90\x04\x12\x34\x01\x01" ) } it "should correctly create the right type of packet object" do expect(packet.class).to eq(MQTT::Packet::Suback) end it "should set the message id of the packet correctly" do expect(packet.id).to eq(0x1234) end it "should set the Granted QoS of the packet correctly" do expect(packet.return_codes).to eq([1,1]) end end describe "when parsing packet with invalid fixed header flags" do it "should raise a protocol exception" do expect { MQTT::Packet.parse( "\x92\x03\x12\x34\x00" ) }.to raise_error( MQTT::ProtocolException, "Invalid flags in SUBACK packet header" ) end end describe "when calling the inspect method" do it "should output correct string for a single granted qos" do packet = MQTT::Packet::Suback.new(:id => 0x1234, :return_codes => 0) expect(packet.inspect).to eq("#") end it "should output correct string for multiple topics" do packet = MQTT::Packet::Suback.new(:id => 0x1235, :return_codes => [0,1,2]) expect(packet.inspect).to eq("#") end end describe "deprecated attributes" do it "should still have a granted_qos method that is that same as return_codes" do packet = MQTT::Packet::Suback.new packet.granted_qos = [0,1,2] expect(packet.granted_qos).to eq([0,1,2]) expect(packet.return_codes).to eq([0,1,2]) packet.return_codes = [0,1,0] expect(packet.granted_qos).to eq([0,1,0]) expect(packet.return_codes).to eq([0,1,0]) end end end describe MQTT::Packet::Unsubscribe do describe "when serialising a packet" do it "should output the correct bytes for a packet with single topic" do packet = MQTT::Packet::Unsubscribe.new( :id => 5, :topics => 'a/b' ) expect(packet.to_s).to eq("\xa2\x07\x00\x05\x00\x03a/b") end it "should output the correct bytes for a packet with multiple topics" do packet = MQTT::Packet::Unsubscribe.new( :id => 6, :topics => ['a/b','c/d'] ) expect(packet.to_s).to eq("\xa2\x0c\000\006\000\003a/b\000\003c/d") end it "should raise an exception when no topics are given" do expect { MQTT::Packet::Unsubscribe.new.to_s }.to raise_error( 'no topics given when serialising packet' ) end end describe "when parsing a packet" do let(:packet) { MQTT::Packet.parse( "\xa2\f\000\005\000\003a/b\000\003c/d" ) } it "should correctly create the right type of packet object" do expect(packet.class).to eq(MQTT::Packet::Unsubscribe) end it "should set the fixed header flags of the packet correctly" do expect(packet.flags).to eq([false, true, false, false]) end it "should set the topic name correctly" do expect(packet.topics).to eq(['a/b','c/d']) end end describe "when parsing packet with invalid fixed header flags" do it "should raise a protocol exception" do expect { MQTT::Packet.parse( "\xa0\x07\x00\x05\x00\x03a/b" ) }.to raise_error( MQTT::ProtocolException, "Invalid flags in UNSUBSCRIBE packet header" ) end end describe "when calling the inspect method" do it "should output correct string for a single topic" do packet = MQTT::Packet::Unsubscribe.new(:topics => 'test') expect(packet.inspect).to eq("#") end it "should output correct string for multiple topics" do packet = MQTT::Packet::Unsubscribe.new( :id => 42, :topics => ['a', 'b', 'c'] ) expect(packet.inspect).to eq("#") end end end describe MQTT::Packet::Unsuback do describe "when serialising a packet" do it "should output the correct bytes for a packet with no flags" do packet = MQTT::Packet::Unsuback.new( :id => 0x1234 ) expect(packet.to_s).to eq("\xB0\x02\x12\x34") end end describe "when parsing a packet" do let(:packet) do MQTT::Packet.parse( "\xB0\x02\x12\x34" ) end it "should correctly create the right type of packet object" do expect(packet.class).to eq(MQTT::Packet::Unsuback) end it "should set the message id of the packet correctly" do expect(packet.id).to eq(0x1234) end end describe "when parsing packet with extra bytes on the end" do it "should raise an exception" do expect { packet = MQTT::Packet.parse( "\xB0\x03\x12\x34\x00" ) }.to raise_error( MQTT::ProtocolException, "Extra bytes at end of Unsubscribe Acknowledgment packet" ) end end describe "when parsing packet with invalid fixed header flags" do it "should raise a protocol exception" do expect { MQTT::Packet.parse( "\xB2\x02\x12\x34" ) }.to raise_error( MQTT::ProtocolException, "Invalid flags in UNSUBACK packet header" ) end end it "should output the right string when calling inspect" do packet = MQTT::Packet::Unsuback.new( :id => 0x1234 ) expect(packet.inspect).to eq("#") end end describe MQTT::Packet::Pingreq do describe "when serialising a packet" do it "should output the correct bytes for a packet with no flags" do packet = MQTT::Packet::Pingreq.new expect(packet.to_s).to eq("\xC0\x00") end end describe "when parsing a packet" do it "should correctly create the right type of packet object" do packet = MQTT::Packet.parse( "\xC0\x00" ) expect(packet.class).to eq(MQTT::Packet::Pingreq) end it "should raise an exception if the packet has a payload" do expect { MQTT::Packet.parse( "\xC0\x05hello" ) }.to raise_error( 'Extra bytes at end of Ping Request packet' ) end end describe "when parsing packet with invalid fixed header flags" do it "should raise a protocol exception" do expect { MQTT::Packet.parse( "\xC2\x00" ) }.to raise_error( MQTT::ProtocolException, "Invalid flags in PINGREQ packet header" ) end end it "should output the right string when calling inspect" do packet = MQTT::Packet::Pingreq.new expect(packet.inspect).to eq("#") end end describe MQTT::Packet::Pingresp do describe "when serialising a packet" do it "should output the correct bytes for a packet with no flags" do packet = MQTT::Packet::Pingresp.new expect(packet.to_s).to eq("\xD0\x00") end end describe "when parsing a packet" do it "should correctly create the right type of packet object" do packet = MQTT::Packet.parse( "\xD0\x00" ) expect(packet.class).to eq(MQTT::Packet::Pingresp) end it "should raise an exception if the packet has a payload" do expect { MQTT::Packet.parse( "\xD0\x05hello" ) }.to raise_error( 'Extra bytes at end of Ping Response packet' ) end end describe "when parsing packet with invalid fixed header flags" do it "should raise a protocol exception" do expect { MQTT::Packet.parse( "\xD2\x00" ) }.to raise_error( MQTT::ProtocolException, "Invalid flags in PINGRESP packet header" ) end end it "should output the right string when calling inspect" do packet = MQTT::Packet::Pingresp.new expect(packet.inspect).to eq("#") end end describe MQTT::Packet::Disconnect do describe "when serialising a packet" do it "should output the correct bytes for a packet with no flags" do packet = MQTT::Packet::Disconnect.new expect(packet.to_s).to eq("\xE0\x00") end end describe "when parsing a packet" do it "should correctly create the right type of packet object" do packet = MQTT::Packet.parse( "\xE0\x00" ) expect(packet.class).to eq(MQTT::Packet::Disconnect) end it "should raise an exception if the packet has a payload" do expect { MQTT::Packet.parse( "\xE0\x05hello" ) }.to raise_error( 'Extra bytes at end of Disconnect packet' ) end end describe "when parsing packet with invalid fixed header flags" do it "should raise a protocol exception" do expect { MQTT::Packet.parse( "\xE2\x00" ) }.to raise_error( MQTT::ProtocolException, "Invalid flags in DISCONNECT packet header" ) end end it "should output the right string when calling inspect" do packet = MQTT::Packet::Disconnect.new expect(packet.inspect).to eq("#") end end describe "Serialising an invalid packet" do context "that has a no type" do it "should raise an exception" do expect { MQTT::Packet.new.to_s }.to raise_error( RuntimeError, "Invalid packet type: MQTT::Packet" ) end end end describe "Reading in an invalid packet from a socket" do context "that has 0 length" do it "should raise an exception" do expect { socket = StringIO.new MQTT::Packet.read(socket) }.to raise_error( MQTT::ProtocolException, "Failed to read byte from socket" ) end end context "that has an incomplete packet length header" do it "should raise an exception" do expect { socket = StringIO.new("\x30\xFF") MQTT::Packet.read(socket) }.to raise_error( MQTT::ProtocolException, "Failed to read byte from socket" ) end end context "that has the maximum number of bytes in the length header" do it "should raise an exception" do expect { socket = StringIO.new("\x30\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF") MQTT::Packet.read(socket) }.to raise_error( MQTT::ProtocolException, "Failed to parse packet - input buffer (4) is not the same as the body length header (268435455)" ) end end end describe "Parsing an invalid packet" do context "that has no length" do it "should raise an exception" do expect { MQTT::Packet.parse( "" ) }.to raise_error( MQTT::ProtocolException, "Invalid packet: less than 2 bytes long" ) end end context "that has an invalid type identifier" do it "should raise an exception" do expect { MQTT::Packet.parse( "\x00\x00" ) }.to raise_error( MQTT::ProtocolException, "Invalid packet type identifier: 0" ) end end context "that has an incomplete packet length header" do it "should raise an exception" do expect { MQTT::Packet.parse( "\x30\xFF" ) }.to raise_error( MQTT::ProtocolException, "The packet length header is incomplete" ) end end context "that has too many bytes in the length field" do it "should raise an exception" do expect { MQTT::Packet.parse( "\x30\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" ) }.to raise_error( MQTT::ProtocolException, 'Failed to parse packet - input buffer (4) is not the same as the body length header (268435455)' ) end end context "that has a bigger buffer than expected" do it "should raise an exception" do expect { MQTT::Packet.parse( "\x30\x11\x00\x04testhello big world" ) }.to raise_error( MQTT::ProtocolException, "Failed to parse packet - input buffer (21) is not the same as the body length header (17)" ) end end context "that has a smaller buffer than expected" do it "should raise an exception" do expect { MQTT::Packet.parse( "\x30\x11\x00\x04testhello" ) }.to raise_error( MQTT::ProtocolException, "Failed to parse packet - input buffer (11) is not the same as the body length header (17)" ) end end end