spec/mqtt_client_spec.rb in mqtt-0.3.1 vs spec/mqtt_client_spec.rb in mqtt-0.4.0
- old
+ new
@@ -166,18 +166,54 @@
client.cert_file = fixture_path('client.pem')
expect(client.ssl_context.cert).to be_a(OpenSSL::X509::Certificate)
end
end
+ describe "setting a client certificate directly" do
+ it "should add a certificate to the SSL context" do
+ expect(client.ssl_context.cert).to be_nil
+ client.cert = File.read(fixture_path('client.pem'))
+ expect(client.ssl_context.cert).to be_a(OpenSSL::X509::Certificate)
+ end
+ end
+
describe "setting a client private key file path" do
it "should add a certificate to the SSL context" do
expect(client.ssl_context.key).to be_nil
client.key_file = fixture_path('client.key')
expect(client.ssl_context.key).to be_a(OpenSSL::PKey::RSA)
end
end
+ describe "setting a client private key directly" do
+ it "should add a certificate to the SSL context" do
+ expect(client.ssl_context.key).to be_nil
+ client.key = File.read(fixture_path('client.key'))
+ expect(client.ssl_context.key).to be_a(OpenSSL::PKey::RSA)
+ end
+ end
+
+ describe "setting an encrypted client private key, w/the correct passphrase" do
+ let(:key_pass) { 'mqtt' }
+
+ it "should add the decrypted certificate to the SSL context" do
+ expect(client.ssl_context.key).to be_nil
+ client.key_file = [fixture_path('client.pass.key'), key_pass]
+ expect(client.ssl_context.key).to be_a(OpenSSL::PKey::RSA)
+ end
+ end
+
+ describe "setting an encrypted client private key, w/an incorrect passphrase" do
+ let(:key_pass) { 'ttqm' }
+
+ it "should raise an OpenSSL::PKey::RSAError exception" do
+ expect(client.ssl_context.key).to be_nil
+ expect { client.key_file = [fixture_path('client.pass.key'), key_pass] }.to(
+ raise_error(OpenSSL::PKey::RSAError, /Neither PUB key nor PRIV key/))
+ end
+ end
+
describe "setting a Certificate Authority file path" do
it "should add a CA file path to the SSL context" do
expect(client.ssl_context.ca_file).to be_nil
client.ca_file = fixture_path('root-ca.pem')
expect(client.ssl_context.ca_file).to eq(fixture_path('root-ca.pem'))
@@ -251,22 +287,32 @@
it "should try and read an acknowledgement packet to the socket if not connected" do
expect(client).to receive(:receive_connack).once
client.connect('myclient')
end
- it "should throw an exception if no host is configured" do
+ it "should raise an exception if no host is configured" do
expect {
client = MQTT::Client.new
client.connect
}.to raise_error(
'No MQTT server host set when attempting to connect'
)
end
- it "should disconnect after connecting, if a block is given" do
- expect(client).to receive(:disconnect).once
- client.connect('myclient') { nil }
+ context "if a block is given" do
+ it "should disconnect after connecting" do
+ expect(client).to receive(:disconnect).once
+ client.connect('myclient') { nil }
+ end
+
+ it "should disconnect even if the block raises an exception" do
+ expect(client).to receive(:disconnect).once
+ begin
+ client.connect('myclient') { raise StandardError }
+ rescue StandardError
+ end
+ end
end
it "should not disconnect after connecting, if no block is given" do
expect(client).to receive(:disconnect).never
client.connect('myclient')
@@ -285,11 +331,11 @@
"\x00\x08password"
)
end
context "no client id is given" do
- it "should throw an exception if the clean session flag is false" do
+ it "should raise an exception if the clean session flag is false" do
expect {
client.client_id = nil
client.clean_session = false
client.connect
}.to raise_error(
@@ -374,11 +420,11 @@
it "should have set the Will's retain flag to true" do
expect(client.will_retain).to be_falsey
end
- it "should have set the Will's retain QOS value to 1" do
+ it "should have set the Will's retain QoS value to 1" do
expect(client.will_qos).to eq(1)
end
it "should include the will in the CONNECT message" do
client.connect('myclient')
@@ -421,41 +467,41 @@
before(:each) do
client.instance_variable_set('@socket', socket)
allow(IO).to receive(:select).and_return([[socket], [], []])
end
- it "should not throw an exception for a successful CONNACK packet" do
+ it "should not raise an exception for a successful CONNACK packet" do
socket.write("\x20\x02\x00\x00")
socket.rewind
expect { client.send(:receive_connack) }.not_to raise_error
end
- it "should throw an exception if the packet type isn't CONNACK" do
+ it "should raise an exception if the packet type isn't CONNACK" do
socket.write("\xD0\x00")
socket.rewind
expect { client.send(:receive_connack) }.to raise_error(MQTT::ProtocolException)
end
- it "should throw an exception if the CONNACK packet return code is 'unacceptable protocol version'" do
+ it "should raise an exception if the CONNACK packet return code is 'unacceptable protocol version'" do
socket.write("\x20\x02\x00\x01")
socket.rewind
expect { client.send(:receive_connack) }.to raise_error(MQTT::ProtocolException, /unacceptable protocol version/i)
end
- it "should throw an exception if the CONNACK packet return code is 'client identifier rejected'" do
+ it "should raise an exception if the CONNACK packet return code is 'client identifier rejected'" do
socket.write("\x20\x02\x00\x02")
socket.rewind
expect { client.send(:receive_connack) }.to raise_error(MQTT::ProtocolException, /client identifier rejected/i)
end
- it "should throw an exception if the CONNACK packet return code is 'server unavailable'" do
+ it "should raise an exception if the CONNACK packet return code is 'server unavailable'" do
socket.write("\x20\x02\x00\x03")
socket.rewind
expect { client.send(:receive_connack) }.to raise_error(MQTT::ProtocolException, /server unavailable/i)
end
- it "should throw an exception if the CONNACK packet return code is an unknown" do
+ it "should raise an exception if the CONNACK packet return code is an unknown" do
socket.write("\x20\x02\x00\xAA")
socket.rewind
expect { client.send(:receive_connack) }.to raise_error(MQTT::ProtocolException, /connection refused/i)
end
end
@@ -489,27 +535,10 @@
expect(socket).to receive(:close)
client.disconnect
end
end
- describe "when calling the 'ping' method" do
- before(:each) do
- client.instance_variable_set('@socket', socket)
- end
-
- it "should write a valid PINGREQ packet to the socket" do
- client.ping
- expect(socket.string).to eq("\xC0\x00")
- end
-
- it "should update the time a ping was last sent" do
- client.instance_variable_set('@last_pingreq', 0)
- client.ping
- expect(client.instance_variable_get('@last_pingreq')).not_to eq(0)
- end
- end
-
describe "when calling the 'publish' method" do
before(:each) do
client.instance_variable_set('@socket', socket)
end
@@ -521,42 +550,59 @@
it "should write a valid PUBLISH packet to the socket with the retain flag set" do
client.publish('topic','payload', true, 0)
expect(socket.string).to eq("\x31\x0e\x00\x05topicpayload")
end
- it "should write a valid PUBLISH packet to the socket with the QOS set to 1" do
+ it "should write a valid PUBLISH packet to the socket with the QoS set to 1" do
+ inject_puback(1)
client.publish('topic','payload', false, 1)
expect(socket.string).to eq("\x32\x10\x00\x05topic\x00\x01payload")
end
- it "should write a valid PUBLISH packet to the socket with the QOS set to 2" do
+ it "should write a valid PUBLISH packet to the socket with the QoS set to 2" do
+ inject_puback(1)
client.publish('topic','payload', false, 2)
expect(socket.string).to eq("\x34\x10\x00\x05topic\x00\x01payload")
end
it "should write a valid PUBLISH packet with no payload" do
client.publish('test')
expect(socket.string).to eq("\x30\x06\x00\x04test")
end
- it "should throw an ArgumentError exception, if the topic is nil" do
+ it "should write a valid PUBLISH packet with frozen payload" do
+ client.publish('topic', 'payload'.freeze, false, 0)
+ expect(socket.string).to eq("\x30\x0e\x00\x05topicpayload")
+ end
+
+ it "should raise an ArgumentError exception, if the topic is nil" do
expect {
client.publish(nil)
}.to raise_error(
ArgumentError,
'Topic name cannot be nil'
)
end
- it "should throw an ArgumentError exception, if the topic is empty" do
+ it "should raise an ArgumentError exception, if the topic is empty" do
expect {
client.publish("")
}.to raise_error(
ArgumentError,
'Topic name cannot be empty'
)
end
+
+ it "correctly assigns consecutive ids to packets with QoS 1" do
+ inject_puback(1)
+ inject_puback(2)
+
+ expect(client).to receive(:send_packet) { |packet| expect(packet.id).to eq(1) }
+ client.publish "topic", "message", false, 1
+ expect(client).to receive(:send_packet) { |packet| expect(packet.id).to eq(2) }
+ client.publish "topic", "message", false, 1
+ end
end
describe "when calling the 'subscribe' method" do
before(:each) do
client.instance_variable_set('@socket', socket)
@@ -614,66 +660,89 @@
describe "when calling the 'get' method" do
before(:each) do
client.instance_variable_set('@socket', socket)
end
- it "should successfull receive a valid PUBLISH packet with a QoS 0" do
+ it "should successfully receive a valid PUBLISH packet with a QoS 0" do
inject_packet(:topic => 'topic0', :payload => 'payload0', :qos => 0)
topic,payload = client.get
expect(topic).to eq('topic0')
expect(payload).to eq('payload0')
end
- it "should successfull receive a valid PUBLISH packet with a QoS 1" do
+ it "should successfully receive a valid PUBLISH packet with a QoS 1" do
inject_packet(:topic => 'topic1', :payload => 'payload1', :qos => 1)
topic,payload = client.get
expect(topic).to eq('topic1')
expect(payload).to eq('payload1')
expect(client.queue_empty?).to be_truthy
end
+ it "acks calling #get_packet and qos=1" do
+ inject_packet(:topic => 'topic1', :payload => 'payload1', :qos => 1)
+ expect(client).to receive(:send_packet).with(an_instance_of(MQTT::Packet::Puback))
+ client.get_packet
+ end
+
+ it "acks calling #get and qos=1" do
+ inject_packet(:topic => 'topic1', :payload => 'payload1', :qos => 1)
+ expect(client).to receive(:send_packet).with(an_instance_of(MQTT::Packet::Puback))
+ client.get
+ end
+
context "with a block" do
- it "should successfull receive a more than 1 message" do
+ it "should successfully receive more than 1 message" do
inject_packet(:topic => 'topic0', :payload => 'payload0')
inject_packet(:topic => 'topic1', :payload => 'payload1')
payloads = []
client.get do |topic,payload|
payloads << payload
break if payloads.size > 1
end
expect(payloads.size).to eq(2)
expect(payloads).to eq(['payload0', 'payload1'])
end
+
+ it "acks when qos > 1 after running the block" do
+ inject_packet(:topic => 'topic1', :payload => 'payload1', :qos => 1)
+ inject_packet(:topic => 'topic2', :payload => 'payload1')
+ expect(client).to receive(:send_packet).with(an_instance_of(MQTT::Packet::Puback))
+ payloads = []
+ client.get do |topic,payload|
+ payloads << payload
+ break if payloads.size > 1
+ end
+ end
end
end
describe "when calling the 'get_packet' method" do
before(:each) do
client.instance_variable_set('@socket', socket)
end
- it "should successfull receive a valid PUBLISH packet with a QoS 0" do
+ it "should successfully receive a valid PUBLISH packet with a QoS 0" do
inject_packet(:topic => 'topic0', :payload => 'payload0', :qos => 0)
packet = client.get_packet
expect(packet.class).to eq(MQTT::Packet::Publish)
expect(packet.qos).to eq(0)
expect(packet.topic).to eq('topic0')
expect(packet.payload).to eq('payload0')
end
- it "should successfull receive a valid PUBLISH packet with a QoS 1" do
+ it "should successfully receive a valid PUBLISH packet with a QoS 1" do
inject_packet(:topic => 'topic1', :payload => 'payload1', :qos => 1)
packet = client.get_packet
expect(packet.class).to eq(MQTT::Packet::Publish)
expect(packet.qos).to eq(1)
expect(packet.topic).to eq('topic1')
expect(packet.payload).to eq('payload1')
expect(client.queue_empty?).to be_truthy
end
context "with a block" do
- it "should successfull receive a more than 1 packet" do
+ it "should successfully receive more than 1 packet" do
inject_packet(:topic => 'topic0', :payload => 'payload0')
inject_packet(:topic => 'topic1', :payload => 'payload1')
packets = []
client.get_packet do |packet|
packets << packet
@@ -727,35 +796,57 @@
socket.rewind
client.send(:receive_packet)
expect(@read_queue.size).to eq(0)
end
- it "should send a ping packet if one is due" do
- expect(IO).to receive(:select).and_return(nil)
- client.instance_variable_set('@last_pingreq', Time.at(0))
- expect(client).to receive(:ping).once
+ it "should close the socket if there is an exception" do
+ expect(socket).to receive(:close).once
+ allow(MQTT::Packet).to receive(:read).and_raise(MQTT::Exception)
client.send(:receive_packet)
end
+ it "should pass exceptions up to parent thread" do
+ expect(@parent_thread).to receive(:raise).once
+ allow(MQTT::Packet).to receive(:read).and_raise(MQTT::Exception)
+ client.send(:receive_packet)
+ end
+
it "should update last_ping_response when receiving a Pingresp" do
allow(MQTT::Packet).to receive(:read).and_return MQTT::Packet::Pingresp.new
client.instance_variable_set '@last_ping_response', Time.at(0)
client.send :receive_packet
expect(client.last_ping_response).to be_within(1).of Time.now
end
+ end
- it "should close the socket if there is an exception" do
- expect(socket).to receive(:close).once
- allow(MQTT::Packet).to receive(:read).and_raise(MQTT::Exception)
- client.send(:receive_packet)
+ describe "when calling the 'keep_alive!' method" do
+ before(:each) do
+ client.instance_variable_set('@socket', socket)
end
- it "should pass exceptions up to parent thread" do
- expect(@parent_thread).to receive(:raise).once
- allow(MQTT::Packet).to receive(:read).and_raise(MQTT::Exception)
- client.send(:receive_packet)
+ it "should send a ping packet if one is due" do
+ client.instance_variable_set('@last_ping_request', Time.at(0))
+ client.send('keep_alive!')
+ expect(socket.string).to eq("\xC0\x00")
end
+
+ it "should update the time a ping was last sent" do
+ client.instance_variable_set('@last_ping_request', Time.at(0))
+ client.send('keep_alive!')
+ expect(client.instance_variable_get('@last_ping_request')).not_to eq(0)
+ end
+
+ it "should raise an exception if no ping response has been received" do
+ client.instance_variable_set('@last_ping_request', Time.now)
+ client.instance_variable_set('@last_ping_response', Time.at(0))
+ expect {
+ client.send('keep_alive!')
+ }.to raise_error(
+ MQTT::ProtocolException,
+ /No Ping Response received for \d+ seconds/
+ )
+ end
end
describe "generating a client identifier" do
context "with default parameters" do
let(:client_id) { MQTT::Client.generate_client_id }
@@ -793,8 +884,13 @@
private
def inject_packet(opts={})
packet = MQTT::Packet::Publish.new(opts)
client.instance_variable_get('@read_queue').push(packet)
+ end
+
+ def inject_puback(packet_id)
+ packet = MQTT::Packet::Puback.new(:id => packet_id)
+ client.instance_variable_get('@pubacks')[packet_id] = packet
end
end