def test_unknown_connect_code_must_lose_connection(self): """ A non-zero, and non-1-to-5 connect code from the handler must result in a lost connection, and no CONNACK. Compliance statements MQTT-3.2.2-4, MQTT-3.2.2-5 """ sessions = {} d = Deferred() # noqa h = BasicHandler(6) r = Clock() t = StringTransport() p = MQTTServerTwistedProtocol(h, r, sessions) cp = MQTTClientParser() # noqa p.makeConnection(t) data = (Connect(client_id=u"test123", flags=ConnectFlags(clean_session=False)).serialise()) for x in iterbytes(data): p.dataReceived(x) self.assertTrue(t.disconnecting) self.assertEqual(t.value(), b'')
def test_non_allowed_qos_not_queued(self): """ A non-QoS 0, 1, or 2 message will be rejected by the publish layer. """ h = BasicHandler() r, t, p, cp = make_test_items(h) data = (Connect(client_id=u"test123", flags=ConnectFlags(clean_session=True)).serialise()) for x in iterbytes(data): p.dataReceived(x) # Connect has happened events = cp.data_received(t.value()) t.clear() self.assertFalse(t.disconnecting) self.assertIsInstance(events[0], ConnACK) # WAMP layer calls send_publish w/ invalid QoS with self.assertRaises(ValueError): p.send_publish(u"hello", 5, b'some bytes', False) # Nothing will be sent self.assertEqual(t.value(), b'') # Advance the clock r.advance(0.1) # Still nothing self.assertEqual(t.value(), b'')
def test_transport_paused_while_processing(self): """ The transport is paused whilst the MQTT protocol is parsing/handling existing items. """ sessions = {} d = Deferred() h = BasicHandler() h.process_connect = lambda x: d r = Clock() t = StringTransport() p = MQTTServerTwistedProtocol(h, r, sessions) t.connected = True p.makeConnection(t) data = (Connect(client_id=u"test123", flags=ConnectFlags(clean_session=False)).serialise()) self.assertEqual(t.producerState, 'producing') for x in iterbytes(data): p.dataReceived(x) self.assertEqual(t.producerState, 'paused') d.callback(0) self.assertEqual(t.producerState, 'producing')
def test_exception_in_connect_drops_connection(self): """ Transient failures (like an exception from handler.process_connect) will cause the connection it happened on to be dropped. Compliance statement MQTT-4.8.0-2 """ class SubHandler(BasicHandler): def process_unsubscribe(self, event): raise Exception("boom!") h = SubHandler() r, t, p, cp = make_test_items(h) data = ( Connect(client_id=u"test123", flags=ConnectFlags(clean_session=True)).serialise() + Unsubscribe(packet_identifier=1234, topics=[u"foo"]).serialise()) with LogCapturer("trace") as logs: for x in iterbytes(data): p.dataReceived(x) sent_logs = logs.get_category("MQ502") self.assertEqual(len(sent_logs), 1) self.assertEqual(sent_logs[0]["log_level"], LogLevel.critical) self.assertEqual(sent_logs[0]["log_failure"].value.args[0], "boom!") events = cp.data_received(t.value()) self.assertEqual(len(events), 1) self.assertTrue(t.disconnecting) # We got the error, we need to flush it so it doesn't make the test # error self.flushLoggedErrors()
def test_non_zero_connect_code_must_have_no_present_session(self): """ A non-zero connect code in a CONNACK must be paired with no session present. Compliance statement MQTT-3.2.2-4 """ sessions = {} d = Deferred() # noqa h = BasicHandler(self.connect_code) r = Clock() t = StringTransport() p = MQTTServerTwistedProtocol(h, r, sessions) cp = MQTTClientParser() p.makeConnection(t) data = (Connect(client_id=u"test123", flags=ConnectFlags(clean_session=False)).serialise()) for x in iterbytes(data): p.dataReceived(x) events = cp.data_received(t.value()) self.assertEqual(len(events), 1) self.assertEqual(attr.asdict(events[0]), { 'return_code': self.connect_code, 'session_present': False, })
def test_lose_conn_on_reserved_qos3(self): """ If we get, somehow, a QoS "3" Publish (one with both QoS bits set to 3), we will drop the connection. Compliance statement: MQTT-3.3.1-4 """ h = BasicHandler() r, t, p, cp = make_test_items(h) conn = Connect(client_id=u"test123", flags=ConnectFlags(clean_session=False)) pub = Publish(duplicate=False, qos_level=3, retain=False, topic_name=u"foo", packet_identifier=1, payload=b"bar") with LogCapturer("trace") as logs: p._handle_events([conn, pub]) sent_logs = logs.get_category("MQ403") self.assertEqual(len(sent_logs), 1) self.assertEqual(sent_logs[0]["log_level"], LogLevel.error) self.assertTrue(t.disconnecting)
def test_subscribe_always_gets_packet(self): """ Subscriptions always get a ConnACK, even if none of the subscriptions were successful. Compliance statements MQTT-3.8.4-1 """ sessions = {} class SubHandler(BasicHandler): def process_subscribe(self, event): return succeed([128]) h = SubHandler() r = Clock() t = StringTransport() p = MQTTServerTwistedProtocol(h, r, sessions) cp = MQTTClientParser() p.makeConnection(t) data = (Connect(client_id=u"test123", flags=ConnectFlags(clean_session=True)).serialise() + Subscribe(packet_identifier=1234, topic_requests=[SubscriptionTopicRequest(u"a", 0) ]).serialise()) for x in iterbytes(data): p.dataReceived(x) events = cp.data_received(t.value()) self.assertEqual(len(events), 2) self.assertEqual(events[1].return_codes, [128])
def test_lose_conn_on_unimplemented_packet(self): """ If we get a valid, but unimplemented for that role packet (e.g. SubACK, which we will only ever send, and getting it is a protocol violation), we will drop the connection. Compliance statement: MQTT-4.8.0-1 """ # This shouldn't normally happen, but just in case. from crossbar.adapter.mqtt import protocol protocol.server_packet_handlers[protocol.P_SUBACK] = SubACK self.addCleanup( lambda: protocol.server_packet_handlers.pop(protocol.P_SUBACK)) h = BasicHandler() r, t, p, cp = make_test_items(h) data = (Connect(client_id=u"test123", flags=ConnectFlags(clean_session=False)).serialise() + SubACK(1, [1]).serialise()) with LogCapturer("trace") as logs: for x in iterbytes(data): p.dataReceived(x) sent_logs = logs.get_category("MQ402") self.assertEqual(len(sent_logs), 1) self.assertEqual(sent_logs[0]["log_level"], LogLevel.error) self.assertEqual(sent_logs[0]["packet_id"], "SubACK") self.assertTrue(t.disconnecting)
def test_subscribe_same_id(self): """ SubACKs have the same packet IDs as the Subscription that it is replying to. Compliance statements MQTT-3.8.4-2 """ sessions = {} class SubHandler(BasicHandler): def process_subscribe(self, event): return succeed([0]) h = SubHandler() r = Clock() t = StringTransport() p = MQTTServerTwistedProtocol(h, r, sessions) cp = MQTTClientParser() p.makeConnection(t) data = (Connect(client_id=u"test123", flags=ConnectFlags(clean_session=True)).serialise() + Subscribe(packet_identifier=1234, topic_requests=[SubscriptionTopicRequest(u"a", 0) ]).serialise()) for x in iterbytes(data): p.dataReceived(x) events = cp.data_received(t.value()) self.assertEqual(len(events), 2) self.assertEqual(events[1].return_codes, [0]) self.assertEqual(events[1].packet_identifier, 1234)
def test_basic_publish(self): reactor, router, server_factory, session_factory = build_mqtt_server() session, pump = connect_application_session( server_factory, ObservingSession, component_config=ComponentConfig(realm=u"mqtt")) client_transport, client_protocol, mqtt_pump = connect_mqtt_server(server_factory) client_transport.write( Connect(client_id=u"testclient", username=u"test123", password=u"password", flags=ConnectFlags(clean_session=False, username=True, password=True)).serialise()) mqtt_pump.flush() # We get a CONNECT self.assertEqual(client_protocol.data, ConnACK(session_present=False, return_code=0).serialise()) client_protocol.data = b"" client_transport.write( Publish(duplicate=False, qos_level=0, retain=False, topic_name=u"test", payload=b'{"kwargs": {"bar": "baz"}}').serialise()) mqtt_pump.flush() pump.flush() # This needs to be replaced with the real deal, see https://github.com/crossbario/crossbar/issues/885 self.assertEqual(len(session.events), 1) self.assertEqual( session.events, [{"args": tuple(), "kwargs": {u'bar': u'baz'}}])
def test_self_subscribe(host, port): record = [ Frame(send=True, data=Connect(client_id=u"test_selfsub", flags=ConnectFlags(clean_session=True))), Frame(send=False, data=ConnACK(session_present=False, return_code=0)), Frame(send=True, data=Subscribe( packet_identifier=1234, topic_requests=[SubscriptionTopicRequest(u"foo", 2)])), Frame(send=False, data=SubACK(packet_identifier=1234, return_codes=[2])), Frame(send=True, data=Publish(duplicate=False, qos_level=0, topic_name=u"foo", payload=b"abc", retain=False)), Frame(send=False, data=Publish(duplicate=False, qos_level=0, topic_name=u"foo", payload=b"abc", retain=False)), Frame(send=True, data=Disconnect()), ConnectionLoss(), ] r = SelectReactor() f = ReplayClientFactory(r, record) e = TCP4ClientEndpoint(r, host, port) e.connect(f) r.run() return Result("self_subscribe", f.success, f.reason, f.client_transcript)
def test_qos_2_queues_message(self): """ The WAMP layer calling send_publish will queue a message up for sending, and send it next time it has a chance. """ h = BasicHandler() r, t, p, cp = make_test_items(h) data = (Connect(client_id=u"test123", flags=ConnectFlags(clean_session=True)).serialise()) for x in iterbytes(data): p.dataReceived(x) # Connect has happened events = cp.data_received(t.value()) t.clear() self.assertFalse(t.disconnecting) self.assertIsInstance(events[0], ConnACK) # WAMP layer calls send_publish, with QoS 2 p.send_publish(u"hello", 2, b'some bytes', False) # Nothing should have been sent yet, it is queued self.assertEqual(t.value(), b'') # Advance the clock r.advance(0.1) # We should now get the sent Publish expected_publish = Publish(duplicate=False, qos_level=2, retain=False, packet_identifier=1, topic_name=u"hello", payload=b"some bytes") events = cp.data_received(t.value()) t.clear() self.assertEqual(len(events), 1) self.assertEqual(events[0], expected_publish) # We send the PubREC, which we should get a PubREL back with pubrec = PubREC(packet_identifier=1) for x in iterbytes(pubrec.serialise()): p.dataReceived(x) events = cp.data_received(t.value()) t.clear() self.assertEqual(len(events), 1) self.assertEqual(events[0], PubREL(packet_identifier=1)) # We send the PubCOMP, which has no response pubcomp = PubCOMP(packet_identifier=1) for x in iterbytes(pubcomp.serialise()): p.dataReceived(x) self.assertFalse(t.disconnecting)
def _test_retained(self): """ The MQTT client can set and receive retained messages. """ reactor, router, server_factory, session_factory = build_mqtt_server() client_transport, client_protocol, mqtt_pump = connect_mqtt_server( server_factory) client_transport.write( Connect(client_id=u"testclient", username=u"test123", password=u"password", flags=ConnectFlags(clean_session=False, username=True, password=True)).serialise()) client_transport.write( Publish(duplicate=False, qos_level=1, retain=True, topic_name=u"com/test/wamp", packet_identifier=123, payload=b'{}').serialise()) mqtt_pump.flush() self.assertEqual( client_protocol.data, (ConnACK(session_present=False, return_code=0).serialise() + PubACK(packet_identifier=123).serialise())) client_protocol.data = b"" client_transport.write( Subscribe(packet_identifier=1, topic_requests=[ SubscriptionTopicRequest( topic_filter=u"com/test/wamp", max_qos=0) ]).serialise()) mqtt_pump.flush() self.assertEqual( client_protocol.data, SubACK(packet_identifier=1, return_codes=[0]).serialise()) client_protocol.data = b"" reactor.advance(0.1) mqtt_pump.flush() # This needs to be replaced with the real deal, see https://github.com/crossbario/crossbar/issues/885 self.assertEqual( client_protocol.data, Publish(duplicate=False, qos_level=0, retain=True, topic_name=u"com/test/wamp", payload=json.dumps( {}, sort_keys=True).encode('utf8')).serialise())
def _(proto): p.append(proto) proto.transport.write( Connect(client_id=u"test123", flags=ConnectFlags(clean_session=False)).serialise()) proto.transport.write( Publish(duplicate=False, qos_level=1, retain=False, topic_name=u"test", payload=b"{}", packet_identifier=1).serialise())
def test_allow_connects_with_same_id_if_disconnected(self): """ If a client connects and there is an existing session which is disconnected, it may connect. """ sessions = {} h = BasicHandler() r = Clock() t = StringTransport() p = MQTTServerTwistedProtocol(h, r, sessions) cp = MQTTClientParser() p.makeConnection(t) data = (Connect(client_id=u"test123", flags=ConnectFlags(clean_session=False)).serialise()) for x in iterbytes(data): p.dataReceived(x) self.assertFalse(t.disconnecting) events = cp.data_received(t.value()) self.assertEqual(len(events), 1) self.assertEqual(attr.asdict(events[0]), { 'return_code': 0, 'session_present': False, }) p.connectionLost(None) # New session r2 = Clock() t2 = StringTransport() p2 = MQTTServerTwistedProtocol(h, r2, sessions) cp2 = MQTTClientParser() p2.makeConnection(t2) # Send the same connect, with the same client ID for x in iterbytes(data): p2.dataReceived(x) # Connection allowed events = cp2.data_received(t2.value()) self.assertEqual(len(events), 1) self.assertEqual(attr.asdict(events[0]), { 'return_code': 0, 'session_present': True, }) # Same session self.assertEqual(p.session, p2.session)
def _test_basic_subscribe(self): """ The MQTT client can subscribe to a WAMP topic and get messages. """ reactor, router, server_factory, session_factory = build_mqtt_server() client_transport, client_protocol, mqtt_pump = connect_mqtt_server( server_factory) session, pump = connect_application_session( server_factory, ApplicationSession, component_config=ComponentConfig(realm=u"mqtt")) client_transport.write( Connect(client_id=u"testclient", username=u"test123", password=u"password", flags=ConnectFlags(clean_session=False, username=True, password=True)).serialise()) client_transport.write( Subscribe(packet_identifier=1, topic_requests=[ SubscriptionTopicRequest( topic_filter=u"com/test/wamp", max_qos=0) ]).serialise()) mqtt_pump.flush() self.assertEqual( client_protocol.data, (ConnACK(session_present=False, return_code=0).serialise() + SubACK(packet_identifier=1, return_codes=[0]).serialise())) client_protocol.data = b"" session.publish(u"com.test.wamp", u"bar") pump.flush() reactor.advance(0.1) mqtt_pump.flush() self.assertEqual( client_protocol.data, Publish(duplicate=False, qos_level=0, retain=False, topic_name=u"com/test/wamp", payload=b'{"args":["bar"]}').serialise())
def test_qos_1_sends_ack(self): """ When a QoS 1 Publish packet is recieved, we send a PubACK with the same packet identifier as the original Publish. Compliance statement MQTT-3.3.4-1 Spec part 3.4 """ got_packets = [] class PubHandler(BasicHandler): def process_publish_qos_1(self, event): got_packets.append(event) return succeed(None) h = PubHandler() r, t, p, cp = make_test_items(h) pub = Publish(duplicate=False, qos_level=1, retain=False, topic_name=u"foo", packet_identifier=1, payload=b"bar").serialise() data = (Connect(client_id=u"test123", flags=ConnectFlags(clean_session=True)).serialise() + pub) with LogCapturer("trace") as logs: for x in iterbytes(data): p.dataReceived(x) events = cp.data_received(t.value()) self.assertFalse(t.disconnecting) # ConnACK + PubACK with the same packet ID self.assertEqual(len(events), 2) self.assertEqual(events[1], PubACK(packet_identifier=1)) # The publish handler should have been called self.assertEqual(len(got_packets), 1) self.assertEqual(got_packets[0].serialise(), pub) # We should get a debug message saying we got the publish messages = logs.get_category("MQ202") self.assertEqual(len(messages), 1) self.assertEqual(messages[0]["publish"].serialise(), pub)
def test_connect(host, port): record = [ Frame(send=True, data=Connect(client_id=u"test_cleanconnect", flags=ConnectFlags(clean_session=True))), Frame(send=False, data=ConnACK(session_present=False, return_code=0)), Frame(send=True, data=Disconnect()), ConnectionLoss(), ] r = SelectReactor() f = ReplayClientFactory(r, record) e = TCP4ClientEndpoint(r, host, port) e.connect(f) r.run() return Result("connect", f.success, f.reason, f.client_transcript)
def test_qos1_send_wrong_confirm(host, port): record = [ Frame(send=True, data=Connect(client_id=u"test_wrong_confirm_qos1", flags=ConnectFlags(clean_session=True))), Frame(send=False, data=ConnACK(session_present=False, return_code=0)), Frame(send=True, data=Subscribe( packet_identifier=1234, topic_requests=[SubscriptionTopicRequest(u"foo", 2)])), Frame(send=False, data=SubACK(packet_identifier=1234, return_codes=[2])), Frame(send=True, data=Publish(duplicate=False, qos_level=1, topic_name=u"foo", payload=b"abc", retain=False, packet_identifier=12)), Frame(send=False, data=[ PubACK(packet_identifier=12), Publish(duplicate=False, qos_level=1, topic_name=u"foo", payload=b"abc", retain=False, packet_identifier=1) ]), # We send a pubrel to the packet_id expecting a puback Frame(send=True, data=PubREL(packet_identifier=1)), # ..aaaaand we get a pubcomp back (even though mosquitto warns). Frame(send=False, data=PubCOMP(packet_identifier=1)), Frame(send=True, data=Disconnect()), ConnectionLoss(), ] r = SelectReactor() f = ReplayClientFactory(r, record) e = TCP4ClientEndpoint(r, host, port) e.connect(f) r.run() return Result("qos1_wrong_confirm", f.success, f.reason, f.client_transcript)
def test_lastwill(self): """ The MQTT client can set a last will message which will be published when it disconnects. """ reactor, router, server_factory, session_factory = build_mqtt_server() session, pump = connect_application_session( server_factory, ObservingSession, component_config=ComponentConfig(realm=u"mqtt")) client_transport, client_protocol, mqtt_pump = connect_mqtt_server( server_factory) client_transport.write( Connect(client_id=u"testclient", username=u"test123", password=u"password", will_topic=u"test", will_message=b'{"args": ["foobar"]}', flags=ConnectFlags(clean_session=False, username=True, password=True, will=True)).serialise()) mqtt_pump.flush() # We get a CONNECT self.assertEqual( client_protocol.data, ConnACK(session_present=False, return_code=0).serialise()) client_protocol.data = b"" client_transport.write(Disconnect().serialise()) mqtt_pump.flush() pump.flush() self.assertEqual(client_transport.disconnected, True) # This needs to be replaced with the real deal, see https://github.com/crossbario/crossbar/issues/885 self.assertEqual(len(session.events), 1) self.assertEqual(session.events, [{ "args": (u"foobar", ), "kwargs": {} }])
def test_unknown_connect_code_must_lose_connection(self): """ A non-zero, and non-1-to-5 connect code from the handler must result in a lost connection, and no CONNACK. Compliance statements MQTT-3.2.2-4, MQTT-3.2.2-5 """ h = BasicHandler(6) r, t, p, cp = make_test_items(h) data = (Connect(client_id=u"test123", flags=ConnectFlags(clean_session=False)).serialise()) for x in iterbytes(data): p.dataReceived(x) self.assertTrue(t.disconnecting) self.assertEqual(t.value(), b'')
def test_exception_in_subscribe_drops_connection(self): """ Transient failures (like an exception from handler.process_subscribe) will cause the connection it happened on to be dropped. Compliance statement MQTT-4.8.0-2 """ sessions = {} class SubHandler(BasicHandler): @inlineCallbacks def process_subscribe(self, event): raise Exception("boom!") h = SubHandler() r = Clock() t = StringTransport() p = MQTTServerTwistedProtocol(h, r, sessions) cp = MQTTClientParser() p.makeConnection(t) data = (Connect(client_id=u"test123", flags=ConnectFlags(clean_session=True)).serialise() + Subscribe(packet_identifier=1234, topic_requests=[SubscriptionTopicRequest(u"a", 0) ]).serialise()) with LogCapturer("trace") as logs: for x in iterbytes(data): p.dataReceived(x) sent_logs = logs.get_category("MQ500") self.assertEqual(len(sent_logs), 1) self.assertEqual(sent_logs[0]["log_level"], LogLevel.critical) self.assertEqual(sent_logs[0]["log_failure"].value.args[0], "boom!") events = cp.data_received(t.value()) self.assertEqual(len(events), 1) self.assertTrue(t.disconnecting) # We got the error, we need to flush it so it doesn't make the test # error self.flushLoggedErrors()
def test_qos_0_sends_no_ack(self): """ When a QoS 0 Publish packet is recieved, we don't send back a PubACK. """ got_packets = [] class PubHandler(BasicHandler): def process_publish_qos_0(self, event): got_packets.append(event) return succeed(None) h = PubHandler() r, t, p, cp = make_test_items(h) pub = Publish(duplicate=False, qos_level=0, retain=False, topic_name=u"foo", packet_identifier=None, payload=b"bar").serialise() data = (Connect(client_id=u"test123", flags=ConnectFlags(clean_session=True)).serialise() + pub) with LogCapturer("trace") as logs: for x in iterbytes(data): p.dataReceived(x) events = cp.data_received(t.value()) self.assertFalse(t.disconnecting) # Just the connack, no puback. self.assertEqual(len(events), 1) # The publish handler should have been called self.assertEqual(len(got_packets), 1) self.assertEqual(got_packets[0].serialise(), pub) # We should get a debug message saying we got the publish messages = logs.get_category("MQ201") self.assertEqual(len(messages), 1) self.assertEqual(messages[0]["publish"].serialise(), pub)
def test_uninvited_pubrel(host, port): record = [ Frame(send=True, data=Connect(client_id=u"test_pubrel", flags=ConnectFlags(clean_session=True))), Frame(send=False, data=ConnACK(session_present=False, return_code=0)), Frame(send=True, data=PubREL(packet_identifier=1234)), Frame(send=False, data=PubCOMP(packet_identifier=1234)), Frame(send=True, data=Disconnect()), ConnectionLoss(), ] r = SelectReactor() f = ReplayClientFactory(r, record) e = TCP4ClientEndpoint(r, host, port) e.connect(f) r.run() return Result("uninvited_pubrel", f.success, f.reason, f.client_transcript)
def test_reserved_packet_15(host, port): record = [ Frame(send=True, data=Connect(client_id=u"test_reserved15", flags=ConnectFlags(clean_session=True))), Frame(send=False, data=ConnACK(session_present=False, return_code=0)), Frame( send=True, # v pkt 15 right here data=b"\xf0\x13\x00\x04MQTT\x04\x02\x00\x02\x00\x07test123"), ConnectionLoss() ] r = SelectReactor() f = ReplayClientFactory(r, record) e = TCP4ClientEndpoint(r, host, port) e.connect(f) r.run() return Result("reserved_pkt15", f.success, f.reason, f.client_transcript)
def test_transport_paused_while_processing(self): """ The transport is paused whilst the MQTT protocol is parsing/handling existing items. """ d = Deferred() h = BasicHandler() h.process_connect = lambda x: d r, t, p, cp = make_test_items(h) data = (Connect(client_id=u"test123", flags=ConnectFlags(clean_session=False)).serialise()) self.assertEqual(t.producerState, 'producing') for x in iterbytes(data): p.dataReceived(x) self.assertEqual(t.producerState, 'paused') d.callback((0, False)) self.assertEqual(t.producerState, 'producing')
def test_qos_0_failure_drops_connection(self): """ Transient failures (like an exception from handler.process_publish_qos_0) will cause the connection it happened on to be dropped. Compliance statement MQTT-4.8.0-2 """ class PubHandler(BasicHandler): def process_publish_qos_0(self, event): raise Exception("boom!") h = PubHandler() r, t, p, cp = make_test_items(h) data = (Connect(client_id=u"test123", flags=ConnectFlags(clean_session=True)).serialise() + Publish(duplicate=False, qos_level=0, retain=False, topic_name=u"foo", packet_identifier=None, payload=b"bar").serialise()) with LogCapturer("trace") as logs: for x in iterbytes(data): p.dataReceived(x) sent_logs = logs.get_category("MQ503") self.assertEqual(len(sent_logs), 1) self.assertEqual(sent_logs[0]["log_level"], LogLevel.critical) self.assertEqual(sent_logs[0]["log_failure"].value.args[0], "boom!") events = cp.data_received(t.value()) self.assertEqual(len(events), 1) self.assertTrue(t.disconnecting) # We got the error, we need to flush it so it doesn't make the test # error self.flushLoggedErrors()
def test_qos_0_queues_message(self): """ The WAMP layer calling send_publish will queue a message up for sending, and send it next time it has a chance. """ h = BasicHandler() r, t, p, cp = make_test_items(h) data = (Connect(client_id=u"test123", flags=ConnectFlags(clean_session=True)).serialise()) for x in iterbytes(data): p.dataReceived(x) # Connect has happened events = cp.data_received(t.value()) t.clear() self.assertFalse(t.disconnecting) self.assertIsInstance(events[0], ConnACK) # WAMP layer calls send_publish p.send_publish(u"hello", 0, b'some bytes', False) # Nothing should have been sent yet, it is queued self.assertEqual(t.value(), b'') # Advance the clock r.advance(0.1) # We should now get the sent Publish events = cp.data_received(t.value()) self.assertEqual(len(events), 1) self.assertEqual( events[0], Publish(duplicate=False, qos_level=0, retain=False, packet_identifier=None, topic_name=u"hello", payload=b"some bytes"))
def test_non_zero_connect_code_must_have_no_present_session(self): """ A non-zero connect code in a CONNACK must be paired with no session present. Compliance statement MQTT-3.2.2-4 """ h = BasicHandler(self.connect_code) r, t, p, cp = make_test_items(h) data = (Connect(client_id=u"test123", flags=ConnectFlags(clean_session=False)).serialise()) for x in iterbytes(data): p.dataReceived(x) events = cp.data_received(t.value()) self.assertEqual(len(events), 1) self.assertEqual(attr.asdict(events[0]), { 'return_code': self.connect_code, 'session_present': False, })
def test_unsubscription_gets_unsuback_with_same_id(self): """ When an unsubscription is processed, the UnsubACK has the same ID. Unsubscriptions are always processed. Compliance statements MQTT-3.10.4-4, MQTT-3.10.4-5, MQTT-3.12.4-1 """ got_packets = [] class SubHandler(BasicHandler): def process_unsubscribe(self, event): got_packets.append(event) return succeed(None) h = SubHandler() r, t, p, cp = make_test_items(h) unsub = Unsubscribe(packet_identifier=1234, topics=[u"foo"]).serialise() data = (Connect(client_id=u"test123", flags=ConnectFlags(clean_session=True)).serialise() + unsub) for x in iterbytes(data): p.dataReceived(x) events = cp.data_received(t.value()) self.assertEqual(len(events), 2) self.assertFalse(t.disconnecting) # UnsubACK that has the same ID self.assertIsInstance(events[1], UnsubACK) self.assertEqual(events[1].packet_identifier, 1234) # The unsubscribe handler should have been called self.assertEqual(len(got_packets), 1) self.assertEqual(got_packets[0].serialise(), unsub)