Beispiel #1
0
    def test_no_procedure(self):
        """
        Test that calls with no procedure in the request body are rejected.
        """
        resource = CallerResource({}, None)

        with LogCapturer() as l:
            request = yield renderResource(
                resource,
                b"/",
                method=b"POST",
                headers={b"Content-Type": [b"application/json"]},
                body=b"{}")

        self.assertEqual(request.code, 400)

        errors = l.get_category("AR455")
        self.assertEqual(len(errors), 1)
        self.assertEqual(errors[0]["code"], 400)
Beispiel #2
0
    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)
Beispiel #3
0
    def test_unknown_key(self):
        """
        An unknown key in a request should 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="spamapp", signSecret="foobar")

        self.assertEqual(request.code, 401)

        errors = l.get_category("AR460")
        self.assertEqual(len(errors), 1)
        self.assertEqual(errors[0]["code"], 401)
Beispiel #4
0
    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)
Beispiel #5
0
    def test_multiple_content_length(self):
        """
        Requests with multiple Content-Length headers will be rejected.
        """
        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"],
                         b"Content-Length": ["1", "10"]},
                body=publishBody))

        self.assertEqual(request.code, 400)

        errors = l.get_category("AR463")
        self.assertEqual(len(errors), 1)
        self.assertEqual(errors[0]["code"], 400)
Beispiel #6
0
    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()
    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, 202)
        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)
Beispiel #8
0
    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 test_bad_method(self):
        """
        An incorrect method will mean the request is rejected.
        """
        session = MockPublisherSession(self)
        resource = PublisherResource({}, session)

        with LogCapturer("debug") as l:
            request = self.successResultOf(
                renderResource(
                    resource,
                    b"/",
                    method=b"BLBLBLB",
                    headers={b"Content-Type": [b"application/json"]},
                    body=publishBody))

        self.assertEqual(request.code, 405)
        errors = l.get_category("AR405")
        self.assertEqual(len(errors), 1)
        self.assertEqual(errors[0]["code"], 405)
    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)
Beispiel #11
0
    def test_publish_cberror(self):
        """
        A publish that errors with a Crossbar failure will return a generic
        error to the client and log the exception.
        """
        class RejectingPublisherSession(object):
            """
            A mock WAMP session.
            """
            def publish(self, topic, *args, **kwargs):
                return maybeDeferred(self._publish, topic, *args, **kwargs)

            def _publish(self, topic, *args, **kwargs):
                raise ValueError("ono")

        session = RejectingPublisherSession()
        resource = PublisherResource({}, session)

        with LogCapturer() as l:
            request = yield renderResource(
                resource, b"/",
                method=b"POST",
                headers={b"Content-Type": [b"application/json"]},
                body=b'{"topic": "com.test.messages", "args": [1]}')

        self.assertEqual(request.code, 500)

        logs = l.get_category("AR456")
        self.assertEqual(len(logs), 1)
        self.assertEqual(logs[0]["code"], 500)

        logs = l.get_category("AR500")
        self.assertEqual(len(logs), 1)

        self.assertEqual(json.loads(native_string(request.get_written_data())),
                         {"error": "wamp.error.runtime_error",
                          "args": ["Sorry, Crossbar.io has encountered a problem."],
                          "kwargs": {}})

        # We manually logged it, so this one is OK
        self.flushLoggedErrors(ValueError)
    def test_too_large_body(self):
        """
        A too large body 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"]},
                    body=publishBody))

        self.assertEqual(request.code, 413)

        errors = l.get_category("AR413")
        self.assertEqual(len(errors), 1)
        self.assertEqual(errors[0]["code"], 413)
    def test_JSON_list_body(self):
        """
        A body that is not a JSON dict will be rejected by the server.
        """
        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"]},
                    body=b"[{},{}]"))

        self.assertEqual(request.code, 400)

        errors = l.get_category("AR454")
        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)
Beispiel #15
0
    def test_recv_packet(self):
        """
        On receiving a packet, a trace log message is emitted with details of
        the received packet.
        """
        h = BasicHandler()
        r, t, p, cp = make_test_items(h)

        data = (
            # CONNECT
            b"101300044d51545404020002000774657374313233")

        with LogCapturer("trace") as logs:
            for x in iterbytes(unhexlify(data)):
                p.dataReceived(x)

        sent_logs = logs.get_category("MQ100")
        self.assertEqual(len(sent_logs), 1)
        self.assertEqual(sent_logs[0]["log_level"], LogLevel.debug)
        self.assertEqual(sent_logs[0]["txaio_trace"], True)
        self.assertIn("Connect", logs.log_text.getvalue())
    def test_wrong_seq(self):
        """
        A missing sequence in a request should mean the request is rejected.
        """
        session = MockPublisherSession(self)
        resource = PublisherResource(resourceOptions, session)

        signedParams = makeSignedArguments({}, "bazapp", "foobar", publishBody)
        signedParams[b'seq'] = [b"notaseq"]

        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)
Beispiel #17
0
    def test_qos_2_failure_drops_connection(self):
        """
        Transient failures (like an exception from
        handler.process_publish_qos_2) will cause the connection it happened on
        to be dropped.

        Compliance statement MQTT-4.8.0-2
        """
        class PubHandler(BasicHandler):
            def process_publish_qos_2(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=2,
                        retain=False,
                        topic_name=u"foo",
                        packet_identifier=1,
                        payload=b"bar").serialise())

        with LogCapturer("trace") as logs:
            for x in iterbytes(data):
                p.dataReceived(x)

        sent_logs = logs.get_category("MQ505")
        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()
Beispiel #18
0
    def test_cb_failure(self):
        """
        Test that calls with no procedure in the request body are rejected.
        """
        resource = CallerResource({}, None)

        with LogCapturer() as l:
            request = yield renderResource(
                resource, b"/",
                method=b"POST",
                headers={b"Content-Type": [b"application/json"]},
                body=b'{"procedure": "foo"}')

        self.assertEqual(request.code, 500)
        self.assertEqual(json.loads(native_string(request.get_written_data())),
                         {"error": "wamp.error.runtime_error", "args": ["Sorry, Crossbar.io has encountered a problem."], "kwargs": {}})

        errors = l.get_category("AR500")
        self.assertEqual(len(errors), 1)

        # We manually logged the errors; we can flush them from the log
        self.flushLoggedErrors()
Beispiel #19
0
    def test_publish_error(self):
        """
        A publish that errors will return the error to the client.
        """
        class RejectingPublisherSession(object):
            """
            A mock WAMP session.
            """
            def publish(self, topic, *args, **kwargs):
                return maybeDeferred(self._publish, topic, *args, **kwargs)

            def _publish(self, topic, *args, **kwargs):
                raise ApplicationError(u'wamp.error.not_authorized', foo="bar")

        session = RejectingPublisherSession()
        resource = PublisherResource({}, session)

        with LogCapturer() as l:
            request = yield renderResource(
                resource,
                b"/",
                method=b"POST",
                headers={b"Content-Type": [b"application/json"]},
                body=b'{"topic": "com.test.messages", "args": [1]}')

        self.assertEqual(request.code, 200)

        logs = l.get_category("AR456")
        self.assertEqual(len(logs), 1)
        self.assertEqual(logs[0]["code"], 200)

        self.assertEqual(
            json.loads(native_string(request.get_written_data())), {
                "error": "wamp.error.not_authorized",
                "args": [],
                "kwargs": {
                    "foo": "bar"
                }
            })
Beispiel #20
0
    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="test123", flags=ConnectFlags(clean_session=False))
        pub = Publish(duplicate=False, qos_level=3, retain=False,
                      topic_name="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_ASCII_denied(self):
        """
        A body with an ASCII charset is denied, it must be UTF-8.
        """
        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=ascii"]
                               },
                               body=b''))

        self.assertEqual(request.code, 400)

        errors = l.get_category("AR450")
        self.assertEqual(len(errors), 1)
        self.assertEqual(errors[0]["code"], 400)
Beispiel #22
0
    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
        """
        class SubHandler(BasicHandler):
            @inlineCallbacks
            def process_subscribe(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() +
                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("MQ501")
        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()
Beispiel #23
0
    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
        """
        sessions = {}

        # 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 = Clock()
        t = StringTransport()
        p = MQTTServerTwistedProtocol(h, r, sessions)

        p.makeConnection(t)

        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_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)
Beispiel #25
0
class TestamentTests(unittest.TestCase):

    def setUp(self):

        self.logs = LogCapturer()
        self.logs.__enter__()
        self.addCleanup(lambda: self.logs.__exit__(None, None, None))

    def test_destroy_testament_sent_on_destroy(self):
        """
        If one session calls wamp.session.add_testament and then the session is
        destroyed, the message it filed as a testament will be sent to
        subscribers of the chosen topic.
        """
        router, server_factory = make_router()

        class ObservingSession(ApplicationSession):

            @inlineCallbacks
            def onJoin(self, details):
                self.events = []
                self.s = yield self.subscribe(
                    lambda *a, **kw: self.events.append({'args': a, 'kwargs': kw}),
                    u'com.test.destroyed')

        session, pump = connect_application_session(server_factory,
                                                    ApplicationSession)

        ob_session, ob_pump = connect_application_session(server_factory,
                                                          ObservingSession)

        d = session.call(u"wamp.session.add_testament", u"com.test.destroyed",
                         [u'hello'], {})
        pump.flush()

        # Make sure it returns None
        self.assertEqual(self.successResultOf(d), None)

        # No testament sent yet
        pump.flush()
        ob_pump.flush()
        self.assertEqual(ob_session.events, [])

        # Then leave...
        session.leave()
        pump.flush()
        ob_pump.flush()

        # Testament is sent
        self.assertEqual(ob_session.events,
                         [{'args': (u"hello",), 'kwargs': {}}])

    def test_destroy_testament_not_sent_when_cleared(self):
        """
        If one session calls wamp.session.add_testament, then the same session
        calls wamp.session.flush_testaments, and then the session is destroyed,
        the message it filed as a testament will not be sent, as it was
        deleted.
        """
        router, server_factory = make_router()

        class ObservingSession(ApplicationSession):

            @inlineCallbacks
            def onJoin(self, details):
                self.events = []
                self.s = yield self.subscribe(
                    lambda *a, **kw: self.events.append({'args': a, 'kwargs': kw}),
                    u'com.test.destroyed')

        session, pump = connect_application_session(server_factory,
                                                    ApplicationSession)

        ob_session, ob_pump = connect_application_session(server_factory,
                                                          ObservingSession)

        d = session.call(u"wamp.session.add_testament", u"com.test.destroyed",
                         [u'hello'], {})
        pump.flush()

        # Make sure it returns None
        self.assertEqual(self.successResultOf(d), None)

        # No testament sent yet
        pump.flush()
        ob_pump.flush()
        self.assertEqual(ob_session.events, [])

        # Flush the testament
        d = session.call(u"wamp.session.flush_testaments")
        pump.flush()

        # Make sure it returns None
        self.assertEqual(self.successResultOf(d), None)

        # Then leave...
        session.leave()
        pump.flush()
        ob_pump.flush()

        # No testaments were sent
        self.assertEqual(ob_session.events, [])

    def test_add_testament_needs_valid_scope(self):
        """
        Only 'detatched' and 'destroyed' are valid scopes for add_testament.
        """
        router, server_factory = make_router()

        session, pump = connect_application_session(server_factory,
                                                    ApplicationSession)

        d = session.call(u"wamp.session.add_testament", u"com.test.destroyed",
                         [u'hello'], {}, scope=u"bar")
        pump.flush()

        # Make sure it returns a failure
        failure = self.failureResultOf(d)
        self.assertEqual(failure.value.args,
                         (u"scope must be destroyed or detatched",))

    def test_flush_testament_needs_valid_scope(self):
        """
        Only 'detatched' and 'destroyed' are valid scopes for flush_testament.
        """
        router, server_factory = make_router()

        session, pump = connect_application_session(server_factory,
                                                    ApplicationSession)

        d = session.call(u"wamp.session.flush_testaments", scope=u"bar")
        pump.flush()

        # Make sure it returns a failure
        failure = self.failureResultOf(d)
        self.assertEqual(failure.value.args,
                         (u"scope must be destroyed or detatched",))

    def test_one_scope_does_not_affect_other(self):
        """
        Adding a testament to one scope and flushing the other maintains the
        added testament.
        """
        router, server_factory = make_router()

        class ObservingSession(ApplicationSession):

            @inlineCallbacks
            def onJoin(self, details):
                self.events = []
                self.s = yield self.subscribe(
                    lambda *a, **kw: self.events.append({'args': a, 'kwargs': kw}),
                    u'com.test.dc')

        session, pump = connect_application_session(server_factory,
                                                    ApplicationSession)

        ob_session, ob_pump = connect_application_session(server_factory,
                                                          ObservingSession)

        # Add a destroyed testament
        d = session.call(u"wamp.session.add_testament", u"com.test.dc",
                         [u'destroyed'], {}, scope=u"destroyed")
        pump.flush()
        self.assertEqual(self.successResultOf(d), None)

        # Add a detatched testament
        d = session.call(u"wamp.session.add_testament", u"com.test.dc",
                         [u'detatched'], {}, scope=u"detatched")
        pump.flush()
        self.assertEqual(self.successResultOf(d), None)

        # No testament sent yet
        pump.flush()
        ob_pump.flush()
        self.assertEqual(ob_session.events, [])

        # Flush the destroyed testament
        d = session.call(u"wamp.session.flush_testaments", scope=u"destroyed")
        pump.flush()

        # Make sure it returns None
        self.assertEqual(self.successResultOf(d), None)

        # Then leave...
        session.leave()
        pump.flush()
        ob_pump.flush()

        # Just the detatched testament is sent
        self.assertEqual(ob_session.events, [{"args": (u'detatched',), "kwargs": {}}])
Beispiel #26
0
class MQTTAdapterTests(TestCase):

    def setUp(self):

        self.logs = LogCapturer()
        self.logs.__enter__()
        self.addCleanup(lambda: self.logs.__exit__(None, None, None))

    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_tls_auth(self):
        """
        A MQTT client can connect using mutually authenticated TLS
        authentication.
        """
        reactor, router, server_factory, session_factory = build_mqtt_server()
        real_reactor = selectreactor.SelectReactor()
        logger = make_logger()

        session, pump = connect_application_session(
            server_factory, ObservingSession, component_config=ComponentConfig(realm=u"mqtt"))

        endpoint = create_listening_endpoint_from_config({
            "type": "tcp",
            "port": 1099,
            "interface": "0.0.0.0",
            "tls": {
                "certificate": "server.crt",
                "key": "server.key",
                "dhparam": "dhparam",
                "ca_certificates": [
                    "ca.cert.pem",
                    "intermediate.cert.pem"
                ]},
        }, FilePath(__file__).sibling('certs').path, real_reactor, logger)

        client_endpoint = create_connecting_endpoint_from_config({
            "type": "tcp",
            "host": "127.0.0.1",
            "port": 1099,
            "tls": {
                "certificate": "client.crt",
                "hostname": u"localhost",
                "key": "client.key",
                "ca_certificates": [
                    "ca.cert.pem",
                    "intermediate.cert.pem"
                ]},
        }, FilePath(__file__).sibling('certs').path, real_reactor, logger)

        p = []
        l = endpoint.listen(server_factory)

        class TestProtocol(Protocol):
            data = b""
            expected = (ConnACK(session_present=False, return_code=0).serialise() + PubACK(packet_identifier=1).serialise())

            def dataReceived(self_, data):
                self_.data = self_.data + data

                if len(self_.data) == len(self_.expected):
                    self.assertEqual(self_.data, self_.expected)
                    real_reactor.stop()

        @l.addCallback
        def _listening(factory):
            d = client_endpoint.connect(Factory.forProtocol(TestProtocol))

            @d.addCallback
            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())

        lc = LoopingCall(pump.flush)
        lc.clock = real_reactor
        lc.start(0.01)

        def timeout():
            print("Timing out :(")
            real_reactor.stop()
            print(self.logs.log_text.getvalue())

        # Timeout, just in case
        real_reactor.callLater(10, timeout)
        real_reactor.run()

        client_protocol = p[0]

        # We get a CONNECT
        self.assertEqual(client_protocol.data,
                         ConnACK(session_present=False, return_code=0).serialise() + PubACK(packet_identifier=1).serialise())
        client_protocol.data = b""

        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": {}}])

    def test_tls_auth_denied(self):
        """
        A MQTT client offering the wrong certificate won't be authenticated.
        """
        reactor, router, server_factory, session_factory = build_mqtt_server()
        real_reactor = selectreactor.SelectReactor()
        logger = make_logger()

        session, pump = connect_application_session(
            server_factory, ObservingSession, component_config=ComponentConfig(realm=u"mqtt"))

        endpoint = create_listening_endpoint_from_config({
            "type": "tcp",
            "port": 1099,
            "interface": "0.0.0.0",
            "tls": {
                "certificate": "server.crt",
                "key": "server.key",
                "dhparam": "dhparam",
                "ca_certificates": [
                    "ca.cert.pem",
                    "intermediate.cert.pem"
                ]},
        }, FilePath(__file__).sibling('certs').path, real_reactor, logger)

        client_endpoint = create_connecting_endpoint_from_config({
            "type": "tcp",
            "host": "127.0.0.1",
            "port": 1099,
            "tls": {
                # BAD key: trusted by the CA, but wrong ID
                "certificate": "client_1.crt",
                "hostname": u"localhost",
                "key": "client_1.key",
                "ca_certificates": [
                    "ca.cert.pem",
                    "intermediate.cert.pem"
                ]},
        }, FilePath(__file__).sibling('certs').path, real_reactor, logger)

        p = []
        l = endpoint.listen(server_factory)

        class TestProtocol(Protocol):
            data = b""
            expected = (
                ConnACK(session_present=False, return_code=1).serialise())

            def dataReceived(self_, data):
                self_.data = self_.data + data

                if len(self_.data) == len(self_.expected):
                    self.assertEqual(self_.data, self_.expected)
                    real_reactor.stop()

        @l.addCallback
        def _listening(factory):
            d = client_endpoint.connect(Factory.forProtocol(TestProtocol))

            @d.addCallback
            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())

        lc = LoopingCall(pump.flush)
        lc.clock = real_reactor
        lc.start(0.01)

        def timeout():
            print("Timing out :(")
            real_reactor.stop()
            print(self.logs.log_text.getvalue())

        # Timeout, just in case
        real_reactor.callLater(10, timeout)
        real_reactor.run()

        client_protocol = p[0]

        # We get a CONNECT
        self.assertEqual(client_protocol.data,
                         ConnACK(session_present=False, return_code=1).serialise())
        client_protocol.data = b""

        pump.flush()

        # No events!
        self.assertEqual(len(session.events), 0)

    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_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 _test_lastwill(self):
        """
        FIXME: reactivate this test.

        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"]}])
Beispiel #27
0
    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))
Beispiel #28
0
class TestamentTests(unittest.TestCase):

    # FIXME:
    # [ERROR] Traceback (most recent call last):
    # File "/home/oberstet/scm/crossbario/crossbar/crossbar/router/test/test_testament.py", line 203, in test_one_scope_does_not_affect_other
    # d = session.call(u"wamp.session.add_testament", u"com.test.dc",
    # builtins.AttributeError: 'NoneType' object has no attribute 'call'
    skip = True

    def setUp(self):

        self.logs = LogCapturer()
        self.logs.__enter__()
        self.addCleanup(lambda: self.logs.__exit__(None, None, None))

    def test_destroy_testament_sent_on_destroy(self):
        """
        If one session calls wamp.session.add_testament and then the session is
        destroyed, the message it filed as a testament will be sent to
        subscribers of the chosen topic.
        """
        router, server_factory, router_factory = make_router_and_realm()

        class ObservingSession(ApplicationSession):

            @inlineCallbacks
            def onJoin(self, details):
                self.events = []
                self.s = yield self.subscribe(
                    lambda *a, **kw: self.events.append({'args': a, 'kwargs': kw}),
                    u'com.test.destroyed')

        session, pump = connect_application_session(server_factory,
                                                    ApplicationSession)

        ob_session, ob_pump = connect_application_session(server_factory,
                                                          ObservingSession)

        d = session.call(u"wamp.session.add_testament", u"com.test.destroyed",
                         [u'hello'], {})
        pump.flush()

        # Make sure it returns a publication ID
        self.assertIsInstance(self.successResultOf(d), (int, ))

        # No testament sent yet
        pump.flush()
        ob_pump.flush()
        self.assertEqual(ob_session.events, [])

        # Then leave...
        session.leave()
        pump.flush()
        ob_pump.flush()

        # Testament is sent
        self.assertEqual(ob_session.events,
                         [{'args': (u"hello",), 'kwargs': {}}])

    def test_destroy_testament_not_sent_when_cleared(self):
        """
        If one session calls wamp.session.add_testament, then the same session
        calls wamp.session.flush_testaments, and then the session is destroyed,
        the message it filed as a testament will not be sent, as it was
        deleted.
        """
        router, server_factory, router_factory = make_router_and_realm()

        class ObservingSession(ApplicationSession):

            @inlineCallbacks
            def onJoin(self, details):
                self.events = []
                self.s = yield self.subscribe(
                    lambda *a, **kw: self.events.append({'args': a, 'kwargs': kw}),
                    u'com.test.destroyed')

        session, pump = connect_application_session(server_factory,
                                                    ApplicationSession)

        ob_session, ob_pump = connect_application_session(server_factory,
                                                          ObservingSession)

        d = session.call(u"wamp.session.add_testament", u"com.test.destroyed",
                         [u'hello'], {})
        pump.flush()

        # Make sure it returns an integer (the testament event publication ID)
        self.assertIsInstance(self.successResultOf(d), (int, ))

        # No testament sent yet
        pump.flush()
        ob_pump.flush()
        self.assertEqual(ob_session.events, [])

        # Flush the testament
        d = session.call(u"wamp.session.flush_testaments")
        pump.flush()

        # Make sure it returns flushed count 1
        self.assertEqual(self.successResultOf(d), 1)

        # Then leave...
        session.leave()
        pump.flush()
        ob_pump.flush()

        # No testaments were sent
        self.assertEqual(ob_session.events, [])

    def test_add_testament_needs_valid_scope(self):
        """
        Only 'detached' and 'destroyed' are valid scopes for add_testament.
        """
        router, server_factory, router_factory = make_router_and_realm()

        session, pump = connect_application_session(server_factory,
                                                    ApplicationSession)

        d = session.call(u"wamp.session.add_testament", u"com.test.destroyed",
                         [u'hello'], {}, scope=u"bar")
        pump.flush()

        # Make sure it returns a failure
        failure = self.failureResultOf(d)
        self.assertEqual(failure.value.args,
                         (u"scope must be destroyed or detached",))

    def test_flush_testament_needs_valid_scope(self):
        """
        Only 'detached' and 'destroyed' are valid scopes for flush_testament.
        """
        router, server_factory, router_factory = make_router_and_realm()

        session, pump = connect_application_session(server_factory,
                                                    ApplicationSession)

        d = session.call(u"wamp.session.flush_testaments", scope=u"bar")
        pump.flush()

        # Make sure it returns a failure
        failure = self.failureResultOf(d)
        self.assertEqual(failure.value.args,
                         (u"scope must be destroyed or detached",))

    def test_one_scope_does_not_affect_other(self):
        """
        Adding a testament to one scope and flushing the other maintains the
        added testament.
        """
        router, server_factory, router_factory = make_router_and_realm()

        class ObservingSession(ApplicationSession):

            @inlineCallbacks
            def onJoin(self, details):
                self.events = []
                self.s = yield self.subscribe(
                    lambda *a, **kw: self.events.append({'args': a, 'kwargs': kw}),
                    u'com.test.dc')

        session, pump = connect_application_session(server_factory,
                                                    ApplicationSession)

        ob_session, ob_pump = connect_application_session(server_factory,
                                                          ObservingSession)

        # Add a destroyed testament
        d = session.call(u"wamp.session.add_testament", u"com.test.dc",
                         [u'destroyed'], {}, scope=u"destroyed")
        pump.flush()
        self.assertIsInstance(self.successResultOf(d), (int, ))

        # Add a detached testament
        d = session.call(u"wamp.session.add_testament", u"com.test.dc",
                         [u'detached'], {}, scope=u"detached")
        pump.flush()
        self.assertIsInstance(self.successResultOf(d), (int, ))

        # No testament sent yet
        pump.flush()
        ob_pump.flush()
        self.assertEqual(ob_session.events, [])

        # Flush the destroyed testament
        d = session.call(u"wamp.session.flush_testaments", scope=u"destroyed")
        pump.flush()

        # Make sure it returns number of flushed testaments
        self.assertEqual(self.successResultOf(d), 1)

        # Then leave...
        session.leave()
        pump.flush()
        ob_pump.flush()

        # Just the detached testament is sent
        self.assertEqual(ob_session.events, [{"args": (u'detached',), "kwargs": {}}])
Beispiel #29
0
class TestamentTests(unittest.TestCase):

    # FIXME:
    # [ERROR] Traceback (most recent call last):
    # File "/home/oberstet/scm/crossbario/crossbar/crossbar/router/test/test_testament.py", line 203, in test_one_scope_does_not_affect_other
    # d = session.call("wamp.session.add_testament", "com.test.dc",
    # builtins.AttributeError: 'NoneType' object has no attribute 'call'
    skip = True

    def setUp(self):

        self.logs = LogCapturer()
        self.logs.__enter__()
        self.addCleanup(lambda: self.logs.__exit__(None, None, None))

    def test_destroy_testament_sent_on_destroy(self):
        """
        If one session calls wamp.session.add_testament and then the session is
        destroyed, the message it filed as a testament will be sent to
        subscribers of the chosen topic.
        """
        router, server_factory, router_factory = make_router_and_realm()

        class ObservingSession(ApplicationSession):
            @inlineCallbacks
            def onJoin(self, details):
                self.events = []
                self.s = yield self.subscribe(
                    lambda *a, **kw: self.events.append({
                        'args': a,
                        'kwargs': kw
                    }), 'com.test.destroyed')

        session, pump = connect_application_session(server_factory,
                                                    ApplicationSession)

        ob_session, ob_pump = connect_application_session(
            server_factory, ObservingSession)

        d = session.call("wamp.session.add_testament", "com.test.destroyed",
                         ['hello'], {})
        pump.flush()

        # Make sure it returns a publication ID
        self.assertIsInstance(self.successResultOf(d), (int, ))

        # No testament sent yet
        pump.flush()
        ob_pump.flush()
        self.assertEqual(ob_session.events, [])

        # Then leave...
        session.leave()
        pump.flush()
        ob_pump.flush()

        # Testament is sent
        self.assertEqual(ob_session.events, [{
            'args': ("hello", ),
            'kwargs': {}
        }])

    def test_destroy_testament_not_sent_when_cleared(self):
        """
        If one session calls wamp.session.add_testament, then the same session
        calls wamp.session.flush_testaments, and then the session is destroyed,
        the message it filed as a testament will not be sent, as it was
        deleted.
        """
        router, server_factory, router_factory = make_router_and_realm()

        class ObservingSession(ApplicationSession):
            @inlineCallbacks
            def onJoin(self, details):
                self.events = []
                self.s = yield self.subscribe(
                    lambda *a, **kw: self.events.append({
                        'args': a,
                        'kwargs': kw
                    }), 'com.test.destroyed')

        session, pump = connect_application_session(server_factory,
                                                    ApplicationSession)

        ob_session, ob_pump = connect_application_session(
            server_factory, ObservingSession)

        d = session.call("wamp.session.add_testament", "com.test.destroyed",
                         ['hello'], {})
        pump.flush()

        # Make sure it returns an integer (the testament event publication ID)
        self.assertIsInstance(self.successResultOf(d), (int, ))

        # No testament sent yet
        pump.flush()
        ob_pump.flush()
        self.assertEqual(ob_session.events, [])

        # Flush the testament
        d = session.call("wamp.session.flush_testaments")
        pump.flush()

        # Make sure it returns flushed count 1
        self.assertEqual(self.successResultOf(d), 1)

        # Then leave...
        session.leave()
        pump.flush()
        ob_pump.flush()

        # No testaments were sent
        self.assertEqual(ob_session.events, [])

    def test_add_testament_needs_valid_scope(self):
        """
        Only 'detached' and 'destroyed' are valid scopes for add_testament.
        """
        router, server_factory, router_factory = make_router_and_realm()

        session, pump = connect_application_session(server_factory,
                                                    ApplicationSession)

        d = session.call("wamp.session.add_testament",
                         "com.test.destroyed", ['hello'], {},
                         scope="bar")
        pump.flush()

        # Make sure it returns a failure
        failure = self.failureResultOf(d)
        self.assertEqual(failure.value.args,
                         ("scope must be destroyed or detached", ))

    def test_flush_testament_needs_valid_scope(self):
        """
        Only 'detached' and 'destroyed' are valid scopes for flush_testament.
        """
        router, server_factory, router_factory = make_router_and_realm()

        session, pump = connect_application_session(server_factory,
                                                    ApplicationSession)

        d = session.call("wamp.session.flush_testaments", scope="bar")
        pump.flush()

        # Make sure it returns a failure
        failure = self.failureResultOf(d)
        self.assertEqual(failure.value.args,
                         ("scope must be destroyed or detached", ))

    def test_one_scope_does_not_affect_other(self):
        """
        Adding a testament to one scope and flushing the other maintains the
        added testament.
        """
        router, server_factory, router_factory = make_router_and_realm()

        class ObservingSession(ApplicationSession):
            @inlineCallbacks
            def onJoin(self, details):
                self.events = []
                self.s = yield self.subscribe(
                    lambda *a, **kw: self.events.append({
                        'args': a,
                        'kwargs': kw
                    }), 'com.test.dc')

        session, pump = connect_application_session(server_factory,
                                                    ApplicationSession)

        ob_session, ob_pump = connect_application_session(
            server_factory, ObservingSession)

        # Add a destroyed testament
        d = session.call("wamp.session.add_testament",
                         "com.test.dc", ['destroyed'], {},
                         scope="destroyed")
        pump.flush()
        self.assertIsInstance(self.successResultOf(d), (int, ))

        # Add a detached testament
        d = session.call("wamp.session.add_testament",
                         "com.test.dc", ['detached'], {},
                         scope="detached")
        pump.flush()
        self.assertIsInstance(self.successResultOf(d), (int, ))

        # No testament sent yet
        pump.flush()
        ob_pump.flush()
        self.assertEqual(ob_session.events, [])

        # Flush the destroyed testament
        d = session.call("wamp.session.flush_testaments", scope="destroyed")
        pump.flush()

        # Make sure it returns number of flushed testaments
        self.assertEqual(self.successResultOf(d), 1)

        # Then leave...
        session.leave()
        pump.flush()
        ob_pump.flush()

        # Just the detached testament is sent
        self.assertEqual(ob_session.events, [{
            "args": ('detached', ),
            "kwargs": {}
        }])
Beispiel #30
0
    def setUp(self):

        self.logs = LogCapturer()
        self.logs.__enter__()
        self.addCleanup(lambda: self.logs.__exit__(None, None, None))
Beispiel #31
0
class TestamentTests(unittest.TestCase):
    def setUp(self):

        self.logs = LogCapturer()
        self.logs.__enter__()
        self.addCleanup(lambda: self.logs.__exit__(None, None, None))

    def test_destroy_testament_sent_on_destroy(self):
        """
        If one session calls wamp.session.add_testament and then the session is
        destroyed, the message it filed as a testament will be sent to
        subscribers of the chosen topic.
        """
        router, server_factory, router_factory = make_router_and_realm()

        class ObservingSession(ApplicationSession):
            @inlineCallbacks
            def onJoin(self, details):
                self.events = []
                self.s = yield self.subscribe(
                    lambda *a, **kw: self.events.append({
                        'args': a,
                        'kwargs': kw
                    }), u'com.test.destroyed')

        session, pump = connect_application_session(server_factory,
                                                    ApplicationSession)

        ob_session, ob_pump = connect_application_session(
            server_factory, ObservingSession)

        d = session.call(u"wamp.session.add_testament", u"com.test.destroyed",
                         [u'hello'], {})
        pump.flush()

        # Make sure it returns None
        self.assertEqual(self.successResultOf(d), None)

        # No testament sent yet
        pump.flush()
        ob_pump.flush()
        self.assertEqual(ob_session.events, [])

        # Then leave...
        session.leave()
        pump.flush()
        ob_pump.flush()

        # Testament is sent
        self.assertEqual(ob_session.events, [{
            'args': (u"hello", ),
            'kwargs': {}
        }])

    def test_destroy_testament_not_sent_when_cleared(self):
        """
        If one session calls wamp.session.add_testament, then the same session
        calls wamp.session.flush_testaments, and then the session is destroyed,
        the message it filed as a testament will not be sent, as it was
        deleted.
        """
        router, server_factory, router_factory = make_router_and_realm()

        class ObservingSession(ApplicationSession):
            @inlineCallbacks
            def onJoin(self, details):
                self.events = []
                self.s = yield self.subscribe(
                    lambda *a, **kw: self.events.append({
                        'args': a,
                        'kwargs': kw
                    }), u'com.test.destroyed')

        session, pump = connect_application_session(server_factory,
                                                    ApplicationSession)

        ob_session, ob_pump = connect_application_session(
            server_factory, ObservingSession)

        d = session.call(u"wamp.session.add_testament", u"com.test.destroyed",
                         [u'hello'], {})
        pump.flush()

        # Make sure it returns None
        self.assertEqual(self.successResultOf(d), None)

        # No testament sent yet
        pump.flush()
        ob_pump.flush()
        self.assertEqual(ob_session.events, [])

        # Flush the testament
        d = session.call(u"wamp.session.flush_testaments")
        pump.flush()

        # Make sure it returns None
        self.assertEqual(self.successResultOf(d), None)

        # Then leave...
        session.leave()
        pump.flush()
        ob_pump.flush()

        # No testaments were sent
        self.assertEqual(ob_session.events, [])

    def test_add_testament_needs_valid_scope(self):
        """
        Only 'detatched' and 'destroyed' are valid scopes for add_testament.
        """
        router, server_factory, router_factory = make_router_and_realm()

        session, pump = connect_application_session(server_factory,
                                                    ApplicationSession)

        d = session.call(u"wamp.session.add_testament",
                         u"com.test.destroyed", [u'hello'], {},
                         scope=u"bar")
        pump.flush()

        # Make sure it returns a failure
        failure = self.failureResultOf(d)
        self.assertEqual(failure.value.args,
                         (u"scope must be destroyed or detatched", ))

    def test_flush_testament_needs_valid_scope(self):
        """
        Only 'detatched' and 'destroyed' are valid scopes for flush_testament.
        """
        router, server_factory, router_factory = make_router_and_realm()

        session, pump = connect_application_session(server_factory,
                                                    ApplicationSession)

        d = session.call(u"wamp.session.flush_testaments", scope=u"bar")
        pump.flush()

        # Make sure it returns a failure
        failure = self.failureResultOf(d)
        self.assertEqual(failure.value.args,
                         (u"scope must be destroyed or detatched", ))

    def test_one_scope_does_not_affect_other(self):
        """
        Adding a testament to one scope and flushing the other maintains the
        added testament.
        """
        router, server_factory, router_factory = make_router_and_realm()

        class ObservingSession(ApplicationSession):
            @inlineCallbacks
            def onJoin(self, details):
                self.events = []
                self.s = yield self.subscribe(
                    lambda *a, **kw: self.events.append({
                        'args': a,
                        'kwargs': kw
                    }), u'com.test.dc')

        session, pump = connect_application_session(server_factory,
                                                    ApplicationSession)

        ob_session, ob_pump = connect_application_session(
            server_factory, ObservingSession)

        # Add a destroyed testament
        d = session.call(u"wamp.session.add_testament",
                         u"com.test.dc", [u'destroyed'], {},
                         scope=u"destroyed")
        pump.flush()
        self.assertEqual(self.successResultOf(d), None)

        # Add a detatched testament
        d = session.call(u"wamp.session.add_testament",
                         u"com.test.dc", [u'detatched'], {},
                         scope=u"detatched")
        pump.flush()
        self.assertEqual(self.successResultOf(d), None)

        # No testament sent yet
        pump.flush()
        ob_pump.flush()
        self.assertEqual(ob_session.events, [])

        # Flush the destroyed testament
        d = session.call(u"wamp.session.flush_testaments", scope=u"destroyed")
        pump.flush()

        # Make sure it returns None
        self.assertEqual(self.successResultOf(d), None)

        # Then leave...
        session.leave()
        pump.flush()
        ob_pump.flush()

        # Just the detatched testament is sent
        self.assertEqual(ob_session.events, [{
            "args": (u'detatched', ),
            "kwargs": {}
        }])