def test_no_timestamp(self): """ No timestamp in a request should mean the request is rejected. """ session = MockPublisherSession(self) resource = PublisherResource(resourceOptions, session) signedParams = makeSignedArguments({}, "bazapp", "foobar", publishBody) del signedParams[b'timestamp'] with LogCapturer() as l: request = yield renderResource( resource, b"/", method=b"POST", headers={b"Content-Type": [b"application/json"]}, body=publishBody, params=signedParams) self.assertEqual(request.code, 400) errors = l.get_category("AR461") self.assertEqual(len(errors), 1) self.assertEqual(errors[0]["code"], 400)
def test_incorrect_secret(self): """ An incorrect secret (but an otherwise well-formed signature) will mean the request is rejected. """ session = MockPublisherSession(self) resource = PublisherResource(resourceOptions, session) with LogCapturer() as l: request = yield renderResource( resource, b"/", method=b"POST", headers={b"Content-Type": [b"application/json"]}, body=publishBody, sign=True, signKey="bazapp", signSecret="foobar2") self.assertEqual(request.code, 401) errors = l.get_category("AR459") self.assertEqual(len(errors), 1) self.assertEqual(errors[0]["code"], 401)
def test_publish_needs_topic(self): """ Test that attempted publishes without a topic will be rejected. """ session = MockPublisherSession(self) resource = PublisherResource({}, session) with LogCapturer() as l: request = yield renderResource( resource, b"/", method=b"POST", headers={b"Content-Type": [b"application/json"]}, body=b'{}') self.assertEqual(len(session._published_messages), 0) self.assertEqual(request.code, 400) errors = l.get_category("AR455") self.assertEqual(len(errors), 1) self.assertEqual(errors[0]["code"], 400) self.assertEqual(json.loads(native_string(request.get_written_data())), {"error": log_categories["AR455"], "args": [], "kwargs": {}})
def test_good_signature(self): """ A valid, correct signature will mean the request is processed. """ session = MockPublisherSession(self) resource = PublisherResource(resourceOptions, session) with LogCapturer() as l: request = yield renderResource( resource, b"/", method=b"POST", headers={b"Content-Type": [b"application/json"]}, body=publishBody, sign=True, signKey="bazapp", signSecret="foobar") self.assertEqual(request.code, 200) self.assertEqual(json.loads(native_string(request.get_written_data())), {"id": session._published_messages[0]["id"]}) logs = l.get_category("AR203") self.assertEqual(len(logs), 1)
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): @inlineCallbacks def process_connect(self, event): raise Exception("boom!") h = SubHandler() r, t, p, cp = make_test_items(h) data = ( Connect(client_id="test123", flags=ConnectFlags(clean_session=True)).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), 0) 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_outdated_delta(self): """ If the delta between now and the timestamp in the request is larger than C{timestamp_delta_limit}, the request is rejected. """ custOpts = {"timestamp_delta_limit": 1} custOpts.update(resourceOptions) session = MockPublisherSession(self) resource = PublisherResource(custOpts, session) signedParams = makeSignedArguments({}, "bazapp", "foobar", publishBody) signedParams[b'timestamp'] = [b"2011-10-14T16:59:51.123Z"] with LogCapturer() as l: request = yield renderResource( resource, b"/", method=b"POST", headers={b"Content-Type": [b"application/json"]}, body=publishBody, params=signedParams) self.assertEqual(request.code, 400) errors = l.get_category("AR462") self.assertEqual(len(errors), 1) self.assertEqual(errors[0]["code"], 400)
def test_unknown_encoding(self): """ A body, when the Content-Type has been set to something other than charset=utf-8, will error out. """ session = MockPublisherSession(self) resource = PublisherResource({}, session) with LogCapturer("debug") as l: request = self.successResultOf( renderResource(resource, b"/", method=b"POST", headers={ b"Content-Type": [b"application/json;charset=blarg"] }, body=b'{"args": ["\x61\x62\x63\xe9"]}')) self.assertEqual(request.code, 400) errors = l.get_category("AR450") self.assertEqual(len(errors), 1) self.assertEqual(errors[0]["code"], 400)
def test_not_matching_bodylength(self): """ A body length that is different than the Content-Length header will mean the request is rejected. """ session = MockPublisherSession(self) resource = PublisherResource({"post_body_limit": 1}, session) with LogCapturer("debug") as l: request = self.successResultOf( renderResource(resource, b"/", method=b"POST", headers={ b"Content-Type": [b"application/json"], b"Content-Length": [1] }, body=publishBody)) self.assertEqual(request.code, 400) errors = l.get_category("AR465") self.assertEqual(len(errors), 1) self.assertEqual(errors[0]["code"], 400)
def setUp(self): self.logs = LogCapturer() self.logs.__enter__() self.addCleanup(lambda: self.logs.__exit__(None, None, None))
def test_qos_2_sends_ack(self): """ When a QoS 2 Publish packet is recieved, we send a PubREC with the same packet identifier as the original Publish, wait for a PubREL, and then send a PubCOMP. Compliance statement MQTT-4.3.3-2 Spec part 3.4, 4.3.3 """ got_packets = [] class PubHandler(BasicHandler): def process_publish_qos_2(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=2, 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 + PubREC with the same packet ID self.assertEqual(len(events), 2) self.assertEqual(events[1], PubREC(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("MQ203") self.assertEqual(len(messages), 1) self.assertEqual(messages[0]["publish"].serialise(), pub) # Clear the client transport t.clear() # Now we send the PubREL pubrel = PubREL(packet_identifier=1) for x in iterbytes(pubrel.serialise()): p.dataReceived(x) events = cp.data_received(t.value()) self.assertFalse(t.disconnecting) # We should get a PubCOMP in response self.assertEqual(len(events), 1) self.assertEqual(events[0], PubCOMP(packet_identifier=1))
def test_failure(self): """ A failed call returns the error to the client. """ session = TestSession(types.ComponentConfig(u'realm1')) self.session_factory.add(session, authrole=u"test_role") session2 = ApplicationSession(types.ComponentConfig(u'realm1')) self.session_factory.add(session2, authrole=u"test_role") resource = CallerResource({}, session2) tests = [ (u"com.myapp.sqrt", (0, ), { u"error": u"wamp.error.runtime_error", u"args": [u"don't ask foolish questions ;)"], u"kwargs": {} }), (u"com.myapp.checkname", ("foo", ), { u"error": u"com.myapp.error.reserved", u"args": [], u"kwargs": {} }), (u"com.myapp.checkname", ("*", ), { u"error": u"com.myapp.error.invalid_length", u"args": [], u"kwargs": { "min": 3, "max": 10 } }), (u"com.myapp.checkname", ("hello", ), { u"error": u"com.myapp.error.mixed_case", u"args": ["hello", "HELLO"], u"kwargs": {} }), (u"com.myapp.compare", (1, 10), { u"error": u"com.myapp.error1", u"args": [9], u"kwargs": {} }), ] for procedure, args, err in tests: with LogCapturer() as l: request = yield renderResource( resource, b"/", method=b"POST", headers={b"Content-Type": [b"application/json"]}, body=dump_json({ "procedure": procedure, "args": args }).encode('utf8')) self.assertEqual(request.code, 200) self.assertEqual( json.loads(native_string(request.get_written_data())), err) logs = l.get_category("AR458") self.assertEqual(len(logs), 1) self.assertEqual(logs[0]["code"], 200) # We manually logged the errors; we can flush them from the log self.flushLoggedErrors()