コード例 #1
0
ファイル: helpers.py プロジェクト: oberstet/crossbar
def make_router(realm_name=u'default'):
    """
    Make a router, and return it and a RawSocket factory.
    """
    # create a router factory
    router_factory = RouterFactory()

    # start a realm
    realm = RouterRealm(None, {u'name': realm_name})
    router = router_factory.start_realm(realm)

    extra = {}
    session_config = ComponentConfig(realm_name, extra)
    realm.session = RouterServiceSession(session_config, router)

    # allow everything
    default_permissions = {
        u'uri': u'',
        u'match': u'prefix',
        u'allow': {
            u'call': True,
            u'register': True,
            u'publish': True,
            u'subscribe': True
        }
    }

    router = router_factory.get(realm_name)
    router.add_role(RouterRoleStaticAuth(router, 'anonymous', default_permissions=default_permissions))

    # create a router session factory
    session_factory = RouterSessionFactory(router_factory)
    session_factory.add(realm.session, authrole=u'trusted')

    # Create a new RawSocket factory
    server_factory = WampRawSocketServerFactory(session_factory, {})

    return router, server_factory
コード例 #2
0
class TestBrokerPublish(unittest.TestCase):
    """
    Tests for crossbar.router.broker.Broker
    """
    def setUp(self):
        """
        Setup router and router session factories.
        """

        # create a router factory
        self.router_factory = RouterFactory('node1', 'router1', None)

        # start a realm
        self.realm = RouterRealm(None, None, {'name': 'realm1'})
        self.router_factory.start_realm(self.realm)

        # allow everything
        self.router = self.router_factory.get('realm1')
        self.router.add_role(
            RouterRoleStaticAuth(self.router,
                                 'test_role',
                                 default_permissions={
                                     'uri': 'com.example.',
                                     'match': 'prefix',
                                     'allow': {
                                         'call': True,
                                         'register': True,
                                         'publish': True,
                                         'subscribe': True,
                                     }
                                 }))

        # create a router session factory
        self.session_factory = RouterSessionFactory(self.router_factory)

    def tearDown(self):
        pass

    def test_add(self):
        """
        Create an application session and add it to a router to
        run embedded.
        """
        d = txaio.create_future()

        class TestSession(ApplicationSession):
            def onJoin(self, details):
                txaio.resolve(d, None)

        session = TestSession(types.ComponentConfig('realm1'))

        self.session_factory.add(session, self.router)

        return d

    def test_application_session_internal_error(self):
        """
        simulate an internal error triggering the 'onJoin' error-case from
        RouterApplicationSession's send() method (from the Hello msg)
        """
        # setup
        the_exception = RuntimeError("sadness")
        errors = []

        class TestSession(ApplicationSession):
            def onJoin(self, *args, **kw):
                raise the_exception

            def onUserError(self, fail, msg):
                errors.append((fail, msg))

        session = TestSession(types.ComponentConfig('realm1'))
        from crossbar.router.session import RouterApplicationSession

        # Note to self: original code was logging directly in
        # RouterApplicationSession -- which *may* actually be better?
        # or not...
        with mock.patch.object(RouterApplicationSession, 'log') as logger:
            # this should call onJoin, triggering our error
            self.session_factory.add(session, self.router)

            if True:
                self.assertEqual(1, len(errors), "Didn't see our error")
                self.assertEqual(the_exception, errors[0][0].value)

            else:
                # check we got the right log.failure() call
                self.assertTrue(len(logger.method_calls) > 0)
                call = logger.method_calls[0]
                # for a MagicMock call-object, 0th thing is the method-name, 1st
                # thing is the arg-tuple, 2nd thing is the kwargs.
                self.assertEqual(call[0], 'failure')
                self.assertEqual(call[1][0].value, the_exception)

    def test_router_session_internal_error_onHello(self):
        """
        similar to above, but during _RouterSession's onMessage handling,
        where it calls self.onHello
        """

        # setup
        transport = mock.MagicMock()
        transport.get_channel_id = mock.MagicMock(return_value=b'deadbeef')
        the_exception = RuntimeError("kerblam")

        def boom(*args, **kw):
            raise the_exception

        session = self.session_factory(
        )  # __call__ on the _RouterSessionFactory
        session.onHello = boom
        session.onOpen(transport)
        msg = message.Hello('realm1', dict(caller=role.RoleCallerFeatures()))

        with mock.patch.object(session, 'log') as logger:
            # do the test; should call onHello which is now "boom", above
            session.onMessage(msg)

            # check we got the right log.failure() call
            self.assertTrue(len(logger.method_calls) > 0)
            call = logger.method_calls[0]
            # for a MagicMock call-object, 0th thing is the method-name, 1st
            # thing is the arg-tuple, 2nd thing is the kwargs.
            self.assertEqual(call[0], 'warn')
            self.assertTrue('err' in call[2])
            self.assertEqual(call[2]['err'].value, the_exception)

    def test_router_session_internal_error_onAuthenticate(self):
        """
        similar to above, but during _RouterSession's onMessage handling,
        where it calls self.onAuthenticate)
        """

        # setup
        transport = mock.MagicMock()
        transport.get_channel_id = mock.MagicMock(return_value=b'deadbeef')
        the_exception = RuntimeError("kerblam")

        def boom(*args, **kw):
            raise the_exception

        session = self.session_factory(
        )  # __call__ on the _RouterSessionFactory
        session.onAuthenticate = boom
        session.onOpen(transport)
        msg = message.Authenticate('bogus signature')

        # do the test; should call onHello which is now "boom", above
        session.onMessage(msg)

        errors = self.flushLoggedErrors()
        self.assertEqual(1, len(errors),
                         "Expected just one error: {}".format(errors))
        self.assertTrue(the_exception in [fail.value for fail in errors])

    def test_router_session_goodbye_custom_message(self):
        """
        Reason should be propagated properly from Goodbye message
        """

        from crossbar.router.session import RouterApplicationSession
        session = ApplicationSession()
        session.onLeave = mock.Mock()
        session._realm = 'realm'
        router = Router(factory=mock.Mock(),
                        realm=RouterRealm(
                            controller=None,
                            id='realm',
                            config=dict(name='realm'),
                        ))
        rap = RouterApplicationSession(session, router)

        rap.send(message.Goodbye('wamp.reason.logout', 'some custom message'))

        leaves = session.onLeave.mock_calls
        self.assertEqual(1, len(leaves))
        details = leaves[0][1][0]
        self.assertEqual('wamp.reason.logout', details.reason)
        self.assertEqual('some custom message', details.message)

    def test_router_session_goodbye_onLeave_error(self):
        """
        Reason should be propagated properly from Goodbye message
        """
        from crossbar.router.session import RouterApplicationSession
        session = ApplicationSession()
        the_exception = RuntimeError("onLeave fails")

        def boom(*args, **kw):
            raise the_exception

        session.onLeave = mock.Mock(side_effect=boom)
        session._realm = 'realm'
        router = Router(factory=mock.Mock(),
                        realm=RouterRealm(
                            controller=None,
                            id='realm',
                            config=dict(name='realm'),
                        ))
        rap = RouterApplicationSession(session, router)

        rap.send(message.Goodbye('wamp.reason.logout', 'some custom message'))

        errors = self.flushLoggedErrors()
        self.assertEqual(1, len(errors))
        self.assertEqual(the_exception, errors[0].value)

    def test_router_session_goodbye_fire_disconnect_error(self):
        """
        Reason should be propagated properly from Goodbye message
        """

        from crossbar.router.session import RouterApplicationSession
        session = ApplicationSession()
        the_exception = RuntimeError("sad times at ridgemont high")

        def boom(*args, **kw):
            if args[0] == 'disconnect':
                return defer.fail(the_exception)
            return defer.succeed(None)

        session.fire = mock.Mock(side_effect=boom)
        session._realm = 'realm'
        router = Router(factory=mock.Mock(),
                        realm=RouterRealm(
                            controller=None,
                            id='realm',
                            config=dict(name='realm'),
                        ))
        rap = RouterApplicationSession(session, router)

        rap.send(message.Goodbye('wamp.reason.logout', 'some custom message'))

        errors = self.flushLoggedErrors()
        self.assertEqual(1, len(errors))
        self.assertEqual(the_exception, errors[0].value)

    def test_router_session_lifecycle(self):
        """
        We see all 'lifecycle' notifications.
        """

        from crossbar.router.session import RouterApplicationSession

        def mock_fire(name, *args, **kw):
            fired.append(name)
            return defer.succeed(None)

        fired = []
        session = ApplicationSession()
        session._realm = 'realm'
        session.fire = mock.Mock(side_effect=mock_fire)
        router = Router(factory=mock.Mock(),
                        realm=RouterRealm(
                            controller=None,
                            id='realm',
                            config=dict(name='realm'),
                        ))
        rap = RouterApplicationSession(session, router)

        # we never fake out the 'Welcome' message, so there will be no
        # 'ready' notification...
        rap.send(message.Goodbye('wamp.reason.logout', 'some custom message'))

        self.assertTrue('connect' in fired)
        self.assertTrue('join' in fired)
        self.assertTrue('ready' in fired)
        self.assertTrue('leave' in fired)
        self.assertTrue('disconnect' in fired)

    def test_add_and_subscribe(self):
        """
        Create an application session that subscribes to some
        topic and add it to a router to run embedded.
        """
        d = txaio.create_future()

        class TestSession(ApplicationSession):
            def onJoin(self, details):
                d2 = self.subscribe(lambda: None, 'com.example.topic1')

                def ok(_):
                    txaio.resolve(d, None)

                def error(err):
                    txaio.reject(d, err)

                txaio.add_callbacks(d2, ok, error)

        session = TestSession(types.ComponentConfig('realm1'))

        self.session_factory.add(session, self.router, authrole='test_role')

        return d

    def test_publish_closed_session(self):
        """
        ensure a session doesn't get Events if it's closed
        (see also issue #431)
        """

        # we want to trigger a deeply-nested condition in
        # processPublish in class Broker -- lets try w/o refactoring
        # anything first...

        class TestSession(ApplicationSession):
            pass

        session0 = TestSession()
        session1 = TestSession()
        router = mock.MagicMock()
        router.new_correlation_id = lambda: 'fake correlation id'
        broker = Broker(router, reactor)

        # let's just "cheat" our way a little to the right state by
        # injecting our subscription "directly" (e.g. instead of
        # faking out an entire Subscribe etc. flow
        # ...so we need _subscriptions_map to have at least one
        # subscription (our test one) for the topic we'll publish to
        broker._subscription_map.add_observer(session0, 'test.topic')

        # simulate the session state we want, which is that a
        # transport is connected (._transport != None) but there
        # _session_id *is* None (not joined yet, or left already)
        self.assertIs(None, session0._session_id)
        session0._transport = mock.MagicMock()
        session0._transport.get_channel_id = mock.MagicMock(
            return_value=b'deadbeef')
        session1._session_id = 1234  # "from" session should look connected + joined
        session1._transport = mock.MagicMock()
        session1._transport.channel_id = b'aaaabeef'

        # here's the main "cheat"; we're faking out the
        # router.authorize because we need it to callback immediately
        router.authorize = mock.MagicMock(
            return_value=txaio.create_future_success(
                dict(allow=True, cache=False, disclose=True)))

        # now we scan call "processPublish" such that we get to the
        # condition we're interested in (this "comes from" session1
        # beacuse by default publishes don't go to the same session)
        pubmsg = message.Publish(123, 'test.topic')
        broker.processPublish(session1, pubmsg)

        # neither session should have sent anything on its transport
        self.assertEquals(session0._transport.method_calls, [])
        self.assertEquals(session1._transport.method_calls, [])

    def test_publish_traced_events(self):
        """
        with two subscribers and message tracing the last event should
        have a magic flag
        """

        # we want to trigger a deeply-nested condition in
        # processPublish in class Broker -- lets try w/o refactoring
        # anything first...

        class TestSession(ApplicationSession):
            pass

        session0 = TestSession()
        session1 = TestSession()
        session2 = TestSession()
        router = mock.MagicMock()
        router.send = mock.Mock()
        router.new_correlation_id = lambda: 'fake correlation id'
        router.is_traced = True
        broker = Broker(router, reactor)

        # let's just "cheat" our way a little to the right state by
        # injecting our subscription "directly" (e.g. instead of
        # faking out an entire Subscribe etc. flow
        # ...so we need _subscriptions_map to have at least one
        # subscription (our test one) for the topic we'll publish to
        broker._subscription_map.add_observer(session0, 'test.topic')
        broker._subscription_map.add_observer(session1, 'test.topic')

        session0._session_id = 1000
        session0._transport = mock.MagicMock()
        session0._transport.get_channel_id = mock.MagicMock(
            return_value=b'deadbeef')

        session1._session_id = 1001
        session1._transport = mock.MagicMock()
        session1._transport.get_channel_id = mock.MagicMock(
            return_value=b'deadbeef')

        session2._session_id = 1002
        session2._transport = mock.MagicMock()
        session2._transport.get_channel_id = mock.MagicMock(
            return_value=b'deadbeef')

        # here's the main "cheat"; we're faking out the
        # router.authorize because we need it to callback immediately
        router.authorize = mock.MagicMock(
            return_value=txaio.create_future_success(
                dict(allow=True, cache=False, disclose=True)))

        # now we scan call "processPublish" such that we get to the
        # condition we're interested in (this "comes from" session1
        # beacuse by default publishes don't go to the same session)
        pubmsg = message.Publish(123, 'test.topic')
        broker.processPublish(session2, pubmsg)

        # extract all the event calls
        events = [
            call[1][1] for call in router.send.mock_calls
            if call[1][0] in [session0, session1, session2]
        ]

        self.assertEqual(2, len(events))
        self.assertFalse(events[0].correlation_is_last)
        self.assertTrue(events[1].correlation_is_last)

    def test_publish_traced_events_batched(self):
        """
        with two subscribers and message tracing the last event should
        have a magic flag
        """

        # we want to trigger a deeply-nested condition in
        # processPublish in class Broker -- lets try w/o refactoring
        # anything first...

        class TestSession(ApplicationSession):
            pass

        session0 = TestSession()
        session1 = TestSession()
        session2 = TestSession()
        session3 = TestSession()
        session4 = TestSession()
        # NOTE! We ensure that "session0" (the publishing session) is
        # *last* in the observation-list to trigger a (now fixed)
        # edge-case)
        sessions = [session1, session2, session3, session4, session0]
        router = mock.MagicMock()
        router.send = mock.Mock()
        router.new_correlation_id = lambda: 'fake correlation id'
        router.is_traced = True
        clock = Clock()
        with replace_loop(clock):
            broker = Broker(router, clock)
            broker._options.event_dispatching_chunk_size = 2

            # to ensure we get "session0" last, we turn on ordering in
            # the observations
            broker._subscription_map._ordered = 1

            # let's just "cheat" our way a little to the right state by
            # injecting our subscription "directly" (e.g. instead of
            # faking out an entire Subscribe etc. flow
            # ...so we need _subscriptions_map to have at least one
            # subscription (our test one) for the topic we'll publish to
            for session in sessions:
                broker._subscription_map.add_observer(session, 'test.topic')

            for i, sess in enumerate(sessions):
                sess._session_id = 1000 + i
                sess._transport = mock.MagicMock()
                sess._transport.get_channel_id = mock.MagicMock(
                    return_value=b'deadbeef')

            # here's the main "cheat"; we're faking out the
            # router.authorize because we need it to callback immediately
            router.authorize = mock.MagicMock(
                return_value=txaio.create_future_success(
                    dict(allow=True, cache=False, disclose=True)))

            # now we scan call "processPublish" such that we get to the
            # condition we're interested in; should go to all sessions
            # except session0
            pubmsg = message.Publish(123, 'test.topic')
            broker.processPublish(session0, pubmsg)
            clock.advance(1)
            clock.advance(1)

            # extract all the event calls
            events = [
                call[1][1] for call in router.send.mock_calls if call[1][0] in
                [session0, session1, session2, session3, session4]
            ]

            # all except session0 should have gotten an event, and
            # session4's should have the "last" flag set
            self.assertEqual(4, len(events))
            self.assertFalse(events[0].correlation_is_last)
            self.assertFalse(events[1].correlation_is_last)
            self.assertFalse(events[2].correlation_is_last)
            self.assertTrue(events[3].correlation_is_last)

    def test_subscribe_unsubscribe(self):
        """
        Make sure the wamp.* event sequence delivered as part of a subscribe/unsubscribe sequence
        is correct and contains the correct session id and subscription id values.
        """
        router = self.router
        test = self

        class Session(mock.MagicMock):

            _private = []
            _events = []
            _authrole = 'trusted'
            _session_id = 0

            def send(self, *args, **argv):
                self._private.append(args[0])

            def publish(self, *args, **argv):
                self._events.append([args, argv])

        class TestSession(ApplicationSession):
            def __init__(self, *args, **kw):
                super().__init__(*args, **kw)
                self._service_session = Session()
                self._router = router

            def onJoin(self, details):
                self._service_session._session_id = details.session
                router.attach(self._service_session)

                router._broker._router._realm.session = self._service_session
                subscription = message.Subscribe(
                    self._service_session._session_id, 'com.example.test1')
                router._broker.processSubscribe(self._service_session,
                                                subscription)
                subscription = message.Subscribe(
                    self._service_session._session_id, 'com.example.test2')
                router._broker.processSubscribe(self._service_session,
                                                subscription)
                subscription = message.Subscribe(
                    self._service_session._session_id, 'com.example.test3')
                router._broker.processSubscribe(self._service_session,
                                                subscription)

                subscriptions = []
                for obj in list(self._service_session._private):
                    subscription = message.Unsubscribe(
                        self._service_session._session_id,
                        subscription=obj.subscription)
                    router._broker.processUnsubscribe(self._service_session,
                                                      subscription)
                    subscriptions.append(obj.subscription)

                def all_done():

                    created = list(subscriptions)
                    subscribes = list(subscriptions)
                    unsubscribes = list(subscriptions)
                    deletes = list(subscriptions)

                    for args, argv in self._service_session._events:
                        if args[0] == 'wamp.subscription.on_create':
                            test.assertEqual(
                                args[1], self._service_session._session_id,
                                'on_create: session id is incorrect!')
                            test.assertTrue(
                                args[2]['id'] in created,
                                'on_create: subscription id is incorrect!')
                            created.remove(args[2]['id'])

                        if args[0] == 'wamp.subscription.on_subscribe':
                            test.assertEqual(
                                args[1], self._service_session._session_id,
                                'on_subscribe: session id is incorrect!')
                            test.assertTrue(
                                args[2] in subscribes,
                                'on_subscribe: subscription id is incorrect!')
                            subscribes.remove(args[2])

                        if args[0] == 'wamp.subscription.on_unsubscribe':
                            test.assertEqual(
                                args[1], self._service_session._session_id,
                                'on_unsubscribe: session id is incorrect!')
                            test.assertTrue(
                                args[2] in unsubscribes,
                                'on_unsubscribe: subscription id is incorrect!'
                            )
                            unsubscribes.remove(args[2])

                        if args[0] == 'wamp.subscription.on_delete':
                            test.assertEqual(
                                args[1], self._service_session._session_id,
                                'on_delete: session id is incorrect!')
                            test.assertTrue(
                                args[2] in deletes,
                                'on_delete: subscription id is incorrect!')
                            deletes.remove(args[2])

                    test.assertEqual(
                        len(created), 0,
                        'incorrect response sequence for on_create')
                    test.assertEqual(
                        len(subscribes), 0,
                        'incorrect response sequence for on_subscribe')
                    test.assertEqual(
                        len(unsubscribes), 0,
                        'incorrect response sequence for on_unsubscribe')
                    test.assertEqual(
                        len(deletes), 0,
                        'incorrect response sequence for on_delete')

                reactor.callLater(0, all_done)

        session = TestSession(types.ComponentConfig('realm1'))
        self.session_factory.add(session, self.router, authrole='trusted')

    def test_subscribe_detach(self):
        """
        Make sure the wamp.* event sequence delivered as part of a subscribe/detach sequence
        is correct and contains the correct session id and subscription id values.
        """
        router = self.router
        test = self

        class Session(mock.MagicMock):
            """
            Mock the session object, this is key to capturing all the replies and publications.
            We get all replies in _private and publications in events, so we can issue the
            request we need to test, then check at the end _events contains the list of
            pub's we are expecting.
            """
            _private = []
            _events = []
            _authrole = 'trusted'
            _session_id = 0

            def send(self, *args, **argv):
                self._private.append(args[0])

            def publish(self, *args, **argv):
                self._events.append([args, argv])

        class TestSession(ApplicationSession):
            def __init__(self, *args, **kw):
                super().__init__(*args, **kw)
                self._service_session = Session()
                self._router = router

            def onJoin(self, details):
                self._service_session._session_id = details.session
                router.attach(self._service_session)

                router._broker._router._realm.session = self._service_session
                subscription = message.Subscribe(
                    self._service_session._session_id, 'com.example.test1')
                router._broker.processSubscribe(self._service_session,
                                                subscription)
                subscription = message.Subscribe(
                    self._service_session._session_id, 'com.example.test2')
                router._broker.processSubscribe(self._service_session,
                                                subscription)
                subscription = message.Subscribe(
                    self._service_session._session_id, 'com.example.test3')
                router._broker.processSubscribe(self._service_session,
                                                subscription)

                subscriptions = []
                for obj in list(self._service_session._private):
                    subscriptions.append(obj.subscription)

                router.detach(self._service_session)

                def all_done():

                    #
                    #   These lists are initialised with the subscription id's we've generated
                    #   with out subscribe sequence, the following routines should decrement each
                    #   list to exactly zero.
                    #
                    created = list(subscriptions)
                    subscribes = list(subscriptions)
                    unsubscribes = list(subscriptions)
                    deletes = list(subscriptions)

                    for args, argv in self._service_session._events:

                        if args[0] == 'wamp.subscription.on_create':
                            test.assertEqual(
                                args[1], self._service_session._session_id,
                                'on_create: session id is incorrect!')
                            test.assertTrue(
                                args[2]['id'] in created,
                                'on_create: subscription id is incorrect!')
                            created.remove(args[2]['id'])

                        if args[0] == 'wamp.subscription.on_subscribe':
                            test.assertEqual(
                                args[1], self._service_session._session_id,
                                'on_subscribe: session id is incorrect!')
                            test.assertTrue(
                                args[2] in subscribes,
                                'on_subscribe: subscription id is incorrect!')
                            subscribes.remove(args[2])

                        if args[0] == 'wamp.subscription.on_unsubscribe':
                            test.assertEqual(
                                args[1], self._service_session._session_id,
                                'on_unsubscribe: session id is incorrect!')
                            test.assertTrue(
                                args[2] in unsubscribes,
                                'on_unsubscribe: subscription id is incorrect!'
                            )
                            unsubscribes.remove(args[2])

                        if args[0] == 'wamp.subscription.on_delete':
                            test.assertEqual(
                                args[1], self._service_session._session_id,
                                'on_delete: session id is incorrect!')
                            test.assertTrue(
                                args[2] in deletes,
                                'on_delete: subscription id is incorrect!')
                            deletes.remove(args[2])

                    test.assertEqual(
                        len(created), 0,
                        'incorrect response sequence for on_create')
                    test.assertEqual(
                        len(subscribes), 0,
                        'incorrect response sequence for on_subscribe')
                    test.assertEqual(
                        len(unsubscribes), 0,
                        'incorrect response sequence for on_unsubscribe')
                    test.assertEqual(
                        len(deletes), 0,
                        'incorrect response sequence for on_delete')

                reactor.callLater(0, all_done)

        session = TestSession(types.ComponentConfig('realm1'))
        self.session_factory.add(session, self.router, authrole='trusted')
コード例 #3
0
ファイル: test_router.py プロジェクト: bb4242/crossbar
class TestEmbeddedSessions(unittest.TestCase):

    """
    Test cases for application session running embedded in router.
    """

    def setUp(self):
        """
        Setup router and router session factories.
        """

        # create a router factory
        self.router_factory = RouterFactory(u'mynode')

        # start a realm
        self.router_factory.start_realm(RouterRealm(None, {u'name': u'realm1'}))

        # allow everything
        default_permissions = {
            u'uri': u'',
            u'match': u'prefix',
            u'call': True,
            u'register': True,
            u'publish': True,
            u'subscribe': True
        }
        router = self.router_factory.get(u'realm1')
        router.add_role(RouterRoleStaticAuth(router, None, default_permissions=default_permissions))

        # create a router session factory
        self.session_factory = RouterSessionFactory(self.router_factory)

    def tearDown(self):
        pass

    def test_add(self):
        """
        Create an application session and add it to a router to
        run embedded.
        """
        d = txaio.create_future()

        class TestSession(ApplicationSession):

            def onJoin(self, details):
                txaio.resolve(d, None)

        session = TestSession(types.ComponentConfig(u'realm1'))

        self.session_factory.add(session)

        return d

    def test_application_session_internal_error(self):
        """
        simulate an internal error triggering the 'onJoin' error-case from
        _RouterApplicationSession's send() method (from the Hello msg)
        """
        # setup
        the_exception = RuntimeError("sadness")
        errors = []

        class TestSession(ApplicationSession):
            def onJoin(self, *args, **kw):
                raise the_exception

            def onUserError(self, *args, **kw):
                errors.append((args, kw))
        session = TestSession(types.ComponentConfig(u'realm1'))

        # in this test, we are just looking for onUserError to get
        # called so we don't need to patch the logger. this should
        # call onJoin, triggering our error
        self.session_factory.add(session)

        # check we got the right log.failure() call
        self.assertTrue(len(errors) > 0, "expected onUserError call")
        fail = errors[0][0][0]
        self.assertTrue(fail.value == the_exception)

    def test_router_session_internal_error_onHello(self):
        """
        similar to above, but during _RouterSession's onMessage handling,
        where it calls self.onHello
        """
        # setup
        transport = mock.MagicMock()
        transport.get_channel_id = mock.MagicMock(return_value=b'deadbeef')
        the_exception = RuntimeError("kerblam")

        def boom(*args, **kw):
            raise the_exception
        session = self.session_factory()  # __call__ on the _RouterSessionFactory
        session.onHello = boom
        session.onOpen(transport)
        msg = message.Hello(u'realm1', dict(caller=role.RoleCallerFeatures()))

        # XXX think: why isn't this using _RouterSession.log?
        from crossbar.router.session import RouterSession
        with mock.patch.object(RouterSession, 'log') as logger:
            # do the test; should call onHello which is now "boom", above
            session.onMessage(msg)

            # check we got the right log.failure() call
            self.assertTrue(len(logger.method_calls) > 0)
            call = logger.method_calls[0]
            # for a MagicMock call-object, 0th thing is the method-name, 1st
            # thing is the arg-tuple, 2nd thing is the kwargs.
            self.assertEqual(call[0], 'failure')
            self.assertTrue('failure' in call[2])
            self.assertEqual(call[2]['failure'].value, the_exception)

    def test_router_session_internal_error_onAuthenticate(self):
        """
        similar to above, but during _RouterSession's onMessage handling,
        where it calls self.onAuthenticate)
        """
        # setup
        transport = mock.MagicMock()
        transport.get_channel_id = mock.MagicMock(return_value=b'deadbeef')
        the_exception = RuntimeError("kerblam")

        def boom(*args, **kw):
            raise the_exception
        session = self.session_factory()  # __call__ on the _RouterSessionFactory
        session.onAuthenticate = boom
        session.onOpen(transport)
        msg = message.Authenticate(u'bogus signature')

        # XXX think: why isn't this using _RouterSession.log?
        from crossbar.router.session import RouterSession
        with mock.patch.object(RouterSession, 'log') as logger:
            # do the test; should call onHello which is now "boom", above
            session.onMessage(msg)

            # check we got the right log.failure() call
            self.assertTrue(len(logger.method_calls) > 0)
            call = logger.method_calls[0]
            # for a MagicMock call-object, 0th thing is the method-name, 1st
            # thing is the arg-tuple, 2nd thing is the kwargs.
            self.assertEqual(call[0], 'failure')
            self.assertTrue('failure' in call[2])
            self.assertEqual(call[2]['failure'].value, the_exception)

    def test_add_and_subscribe(self):
        """
        Create an application session that subscribes to some
        topic and add it to a router to run embedded.
        """
        d = txaio.create_future()

        class TestSession(ApplicationSession):

            def onJoin(self, details):
                # noinspection PyUnusedLocal
                def on_event(*arg, **kwargs):
                    pass

                d2 = self.subscribe(on_event, u'com.example.topic1')

                def ok(_):
                    txaio.resolve(d, None)

                def error(err):
                    txaio.reject(d, err)

                txaio.add_callbacks(d2, ok, error)

        session = TestSession(types.ComponentConfig(u'realm1'))

        self.session_factory.add(session)

        return d
コード例 #4
0
class TestBrokerPublish(unittest.TestCase):
    """
    Tests for crossbar.router.broker.Broker
    """
    def setUp(self):
        """
        Setup router and router session factories.
        """

        # create a router factory
        self.router_factory = RouterFactory(None)

        # start a realm
        self.realm = RouterRealm(None, {u'name': u'realm1'})
        self.router_factory.start_realm(self.realm)

        # allow everything
        self.router = self.router_factory.get(u'realm1')
        self.router.add_role(
            RouterRoleStaticAuth(self.router,
                                 u'test_role',
                                 default_permissions={
                                     u'uri': u'com.example.',
                                     u'match': u'prefix',
                                     u'allow': {
                                         u'call': True,
                                         u'register': True,
                                         u'publish': True,
                                         u'subscribe': True,
                                     }
                                 }))

        # create a router session factory
        self.session_factory = RouterSessionFactory(self.router_factory)

    def tearDown(self):
        pass

    def test_add(self):
        """
        Create an application session and add it to a router to
        run embedded.
        """
        d = txaio.create_future()

        class TestSession(ApplicationSession):
            def onJoin(self, details):
                txaio.resolve(d, None)

        session = TestSession(types.ComponentConfig(u'realm1'))

        self.session_factory.add(session)

        return d

    def test_application_session_internal_error(self):
        """
        simulate an internal error triggering the 'onJoin' error-case from
        RouterApplicationSession's send() method (from the Hello msg)
        """
        # setup
        the_exception = RuntimeError("sadness")
        errors = []

        class TestSession(ApplicationSession):
            def onJoin(self, *args, **kw):
                raise the_exception

            def onUserError(self, fail, msg):
                errors.append((fail, msg))

        session = TestSession(types.ComponentConfig(u'realm1'))
        from crossbar.router.session import RouterApplicationSession

        # Note to self: original code was logging directly in
        # RouterApplicationSession -- which *may* actually be better?
        # or not...
        with mock.patch.object(RouterApplicationSession, 'log') as logger:
            # this should call onJoin, triggering our error
            self.session_factory.add(session)

            if True:
                self.assertEqual(1, len(errors), "Didn't see our error")
                self.assertEqual(the_exception, errors[0][0].value)

            else:
                # check we got the right log.failure() call
                self.assertTrue(len(logger.method_calls) > 0)
                call = logger.method_calls[0]
                # for a MagicMock call-object, 0th thing is the method-name, 1st
                # thing is the arg-tuple, 2nd thing is the kwargs.
                self.assertEqual(call[0], 'failure')
                self.assertEqual(call[1][0].value, the_exception)

    def test_router_session_internal_error_onHello(self):
        """
        similar to above, but during _RouterSession's onMessage handling,
        where it calls self.onHello
        """
        # setup
        transport = mock.MagicMock()
        transport.get_channel_id = mock.MagicMock(return_value=b'deadbeef')
        the_exception = RuntimeError("kerblam")

        def boom(*args, **kw):
            raise the_exception

        session = self.session_factory(
        )  # __call__ on the _RouterSessionFactory
        session.onHello = boom
        session.onOpen(transport)
        msg = message.Hello(u'realm1', dict(caller=role.RoleCallerFeatures()))

        # XXX think: why isn't this using _RouterSession.log?
        from crossbar.router.session import RouterSession
        with mock.patch.object(RouterSession, 'log') as logger:
            # do the test; should call onHello which is now "boom", above
            session.onMessage(msg)

            # check we got the right log.failure() call
            self.assertTrue(len(logger.method_calls) > 0)
            call = logger.method_calls[0]
            # for a MagicMock call-object, 0th thing is the method-name, 1st
            # thing is the arg-tuple, 2nd thing is the kwargs.
            self.assertEqual(call[0], 'failure')
            self.assertTrue('failure' in call[2])
            self.assertEqual(call[2]['failure'].value, the_exception)

    def test_router_session_internal_error_onAuthenticate(self):
        """
        similar to above, but during _RouterSession's onMessage handling,
        where it calls self.onAuthenticate)
        """
        # setup
        transport = mock.MagicMock()
        transport.get_channel_id = mock.MagicMock(return_value=b'deadbeef')
        the_exception = RuntimeError("kerblam")

        def boom(*args, **kw):
            raise the_exception

        session = self.session_factory(
        )  # __call__ on the _RouterSessionFactory
        session.onAuthenticate = boom
        session.onOpen(transport)
        msg = message.Authenticate(u'bogus signature')

        # do the test; should call onHello which is now "boom", above
        session.onMessage(msg)

        errors = self.flushLoggedErrors()
        self.assertEqual(1, len(errors),
                         "Expected just one error: {}".format(errors))
        self.assertTrue(the_exception in [fail.value for fail in errors])

    def test_router_session_goodbye_custom_message(self):
        """
        Reason should be propagated properly from Goodbye message
        """
        from crossbar.router.session import RouterApplicationSession
        session = mock.Mock()
        session._realm = u'realm'
        router_factory = mock.Mock()
        rap = RouterApplicationSession(session, router_factory)

        rap.send(
            message.Hello(u'realm', {u'caller': role.RoleCallerFeatures()}))
        session.reset_mock()
        rap.send(message.Goodbye(u'wamp.reason.logout',
                                 u'some custom message'))

        leaves = [call for call in session.mock_calls if call[0] == 'onLeave']
        self.assertEqual(1, len(leaves))
        details = leaves[0][1][0]
        self.assertEqual(u'wamp.reason.logout', details.reason)
        self.assertEqual(u'some custom message', details.message)

    def test_router_session_goodbye_onLeave_error(self):
        """
        Reason should be propagated properly from Goodbye message
        """
        from crossbar.router.session import RouterApplicationSession
        session = mock.Mock()
        the_exception = RuntimeError("onLeave fails")

        def boom(*args, **kw):
            raise the_exception

        session.onLeave = mock.Mock(side_effect=boom)
        session._realm = u'realm'
        router_factory = mock.Mock()
        rap = RouterApplicationSession(session, router_factory)

        rap.send(
            message.Hello(u'realm', {u'caller': role.RoleCallerFeatures()}))
        session.reset_mock()
        rap.send(message.Goodbye(u'wamp.reason.logout',
                                 u'some custom message'))

        errors = self.flushLoggedErrors()
        self.assertEqual(1, len(errors))
        self.assertEqual(the_exception, errors[0].value)

    def test_router_session_goodbye_fire_disconnect_error(self):
        """
        Reason should be propagated properly from Goodbye message
        """
        from crossbar.router.session import RouterApplicationSession
        session = mock.Mock()
        the_exception = RuntimeError("sad times at ridgemont high")

        def boom(*args, **kw):
            if args[0] == 'disconnect':
                return defer.fail(the_exception)
            return defer.succeed(None)

        session.fire = mock.Mock(side_effect=boom)
        session._realm = u'realm'
        router_factory = mock.Mock()
        rap = RouterApplicationSession(session, router_factory)

        rap.send(
            message.Hello(u'realm', {u'caller': role.RoleCallerFeatures()}))
        session.reset_mock()
        rap.send(message.Goodbye(u'wamp.reason.logout',
                                 u'some custom message'))

        errors = self.flushLoggedErrors()
        self.assertEqual(1, len(errors))
        self.assertEqual(the_exception, errors[0].value)

    def test_router_session_lifecycle(self):
        """
        We see all 'lifecycle' notifications.
        """
        from crossbar.router.session import RouterApplicationSession

        def mock_fire(name, *args, **kw):
            fired.append(name)
            return defer.succeed(None)

        fired = []
        session = mock.Mock()
        session._realm = u'realm'
        session.fire = mock.Mock(side_effect=mock_fire)
        router_factory = mock.Mock()
        rap = RouterApplicationSession(session, router_factory)

        # we never fake out the 'Welcome' message, so there will be no
        # 'ready' notification...
        rap.send(
            message.Hello(u'realm', {u'caller': role.RoleCallerFeatures()}))
        rap.send(message.Goodbye(u'wamp.reason.logout',
                                 u'some custom message'))

        self.assertTrue('connect' in fired)
        self.assertTrue('join' in fired)
        self.assertTrue('ready' in fired)
        self.assertTrue('leave' in fired)
        self.assertTrue('disconnect' in fired)

    def test_add_and_subscribe(self):
        """
        Create an application session that subscribes to some
        topic and add it to a router to run embedded.
        """
        d = txaio.create_future()

        class TestSession(ApplicationSession):
            def onJoin(self, details):
                d2 = self.subscribe(lambda: None, u'com.example.topic1')

                def ok(_):
                    txaio.resolve(d, None)

                def error(err):
                    txaio.reject(d, err)

                txaio.add_callbacks(d2, ok, error)

        session = TestSession(types.ComponentConfig(u'realm1'))

        self.session_factory.add(session, authrole=u'test_role')

        return d

    def test_publish_closed_session(self):
        """
        ensure a session doesn't get Events if it's closed
        (see also issue #431)
        """

        # we want to trigger a deeply-nested condition in
        # processPublish in class Broker -- lets try w/o refactoring
        # anything first...

        class TestSession(ApplicationSession):
            pass

        session0 = TestSession()
        session1 = TestSession()
        router = mock.MagicMock()
        broker = Broker(router, reactor)

        # let's just "cheat" our way a little to the right state by
        # injecting our subscription "directly" (e.g. instead of
        # faking out an entire Subscribe etc. flow
        # ...so we need _subscriptions_map to have at least one
        # subscription (our test one) for the topic we'll publish to
        broker._subscription_map.add_observer(session0, u'test.topic')

        # simulate the session state we want, which is that a
        # transport is connected (._transport != None) but there
        # _session_id *is* None (not joined yet, or left already)
        self.assertIs(None, session0._session_id)
        session0._transport = mock.MagicMock()
        session0._transport.get_channel_id = mock.MagicMock(
            return_value=b'deadbeef')
        session1._session_id = 1234  # "from" session should look connected + joined
        session1._transport = mock.MagicMock()
        session1._transport.channel_id = b'aaaabeef'

        # here's the main "cheat"; we're faking out the
        # router.authorize because we need it to callback immediately
        router.authorize = mock.MagicMock(
            return_value=txaio.create_future_success(
                dict(allow=True, cache=False, disclose=True)))

        # now we scan call "processPublish" such that we get to the
        # condition we're interested in (this "comes from" session1
        # beacuse by default publishes don't go to the same session)
        pubmsg = message.Publish(123, u'test.topic')
        broker.processPublish(session1, pubmsg)

        # neither session should have sent anything on its transport
        self.assertEquals(session0._transport.method_calls, [])
        self.assertEquals(session1._transport.method_calls, [])
コード例 #5
0
ファイル: test_dealer.py プロジェクト: sshyran/crossbar
class TestDealer(unittest.TestCase):
    """
    """
    def setUp(self):
        """
        Setup router and router session factories.
        """

        # create a router factory
        self.router_factory = RouterFactory(None, None)

        # start a realm
        self.realm = RouterRealm(u'realm-001', {u'name': u'realm1'})
        self.router_factory.start_realm(self.realm)

        # allow everything
        self.router = self.router_factory.get(u'realm1')
        self.router.add_role(
            RouterRoleStaticAuth(self.router,
                                 u'test_role',
                                 default_permissions={
                                     u'uri': u'com.example.',
                                     u'match': u'prefix',
                                     u'allow': {
                                         u'call': True,
                                         u'register': True,
                                         u'publish': True,
                                         u'subscribe': True,
                                     }
                                 }))

        # create a router session factory
        self.session_factory = RouterSessionFactory(self.router_factory)

    def tearDown(self):
        pass

    @defer.inlineCallbacks
    def test_outstanding_invoke(self):
        """
        When a call is pending and the callee goes away, it cancels the
        in-flight call
        """

        session = mock.Mock()
        session._realm = u'realm1'
        self.router.authorize = mock.Mock(
            return_value=defer.succeed({
                u'allow': True,
                u'disclose': True
            }))
        rap = RouterApplicationSession(session, self.router_factory)

        rap.send(
            message.Hello(u"realm1", {u'caller': role.RoleCallerFeatures()}))
        rap.send(message.Register(1, u'foo'))

        # we can retrieve the Registration via
        # session.mock_calls[-1][1][0] if req'd

        # re-set the authorize, as the Deferred from above is already
        # used-up and it gets called again to authorize the Call
        self.router.authorize = mock.Mock(
            return_value=defer.succeed({
                u'allow': True,
                u'disclose': True
            }))
        rap.send(message.Call(42, u'foo'))

        orig = rap.send
        d = defer.Deferred()

        rap.send(message.Goodbye())

        def wrapper(*args, **kw):
            d.callback(args[0])
            return orig(*args, **kw)

        rap.send = wrapper

        # we can do this *after* the call to send() the Goodbye
        # (above) because it takes a reactor-turn to actually
        # process the cancel/errors etc -- hence the Deferred and
        # yield in this test...

        msg = yield d

        self.assertEqual(42, msg.request)
        self.assertEqual(u'wamp.error.canceled', msg.error)

    def test_outstanding_invoke_but_caller_gone(self):

        session = mock.Mock()
        outstanding = mock.Mock()
        outstanding.call.request = 1

        dealer = self.router._dealer
        dealer.attach(session)

        dealer._callee_to_invocations[session] = [outstanding]
        # pretend we've disconnected already
        outstanding.caller._transport = None

        dealer.detach(session)

        self.assertEqual([], outstanding.mock_calls)

    def test_call_cancel(self):
        last_message = {'1': []}

        def session_send(msg):
            last_message['1'] = msg

        session = mock.Mock()
        session._transport.send = session_send
        session._session_roles = {
            'callee': role.RoleCalleeFeatures(call_canceling=True)
        }

        dealer = self.router._dealer
        dealer.attach(session)

        def authorize(*args, **kwargs):
            return defer.succeed({u'allow': True, u'disclose': False})

        self.router.authorize = mock.Mock(side_effect=authorize)

        dealer.processRegister(
            session,
            message.Register(1, u'com.example.my.proc', u'exact',
                             message.Register.INVOKE_SINGLE, 1))

        registered_msg = last_message['1']
        self.assertIsInstance(registered_msg, message.Registered)

        dealer.processCall(session, message.Call(2, u'com.example.my.proc',
                                                 []))

        invocation_msg = last_message['1']
        self.assertIsInstance(invocation_msg, message.Invocation)

        dealer.processCancel(session, message.Cancel(2))

        # should receive an INTERRUPT from the dealer now
        interrupt_msg = last_message['1']
        self.assertIsInstance(interrupt_msg, message.Interrupt)
        self.assertEqual(interrupt_msg.request, invocation_msg.request)

        dealer.processInvocationError(
            session,
            message.Error(message.Invocation.MESSAGE_TYPE,
                          invocation_msg.request, u'wamp.error.canceled'))

        call_error_msg = last_message['1']
        self.assertIsInstance(call_error_msg, message.Error)
        self.assertEqual(message.Call.MESSAGE_TYPE,
                         call_error_msg.request_type)
        self.assertEqual(u'wamp.error.canceled', call_error_msg.error)

    def test_call_cancel_two_sessions(self):
        """
        this has 2 different session using the same ID (42) for their Call
        requests to confirm we deal with the fact that these IDs are
        only unique per-session properly
        """
        messages = []

        def session_send(msg):
            messages.append(msg)

        session0 = mock.Mock()
        session0._transport.send = session_send
        session0._session_roles = {
            'callee': role.RoleCalleeFeatures(call_canceling=True)
        }

        session1 = mock.Mock()
        session1._transport.send = session_send
        session1._session_roles = {
            'callee': role.RoleCalleeFeatures(call_canceling=True)
        }

        dealer = self.router._dealer
        dealer.attach(session0)
        dealer.attach(session1)

        def authorize(*args, **kwargs):
            return defer.succeed({u'allow': True, u'disclose': False})

        self.router.authorize = mock.Mock(side_effect=authorize)

        dealer.processRegister(
            session0,
            message.Register(1, u'com.example.my.proc', u'exact',
                             message.Register.INVOKE_SINGLE, 2))

        registered_msg = messages[-1]
        self.assertIsInstance(registered_msg, message.Registered)

        # two calls outstanding to the endpoint, both happen to use
        # the same ID (42) which is legal
        dealer.processCall(session0,
                           message.Call(42, u'com.example.my.proc', []))

        invocation_msg0 = messages[-1]
        self.assertIsInstance(invocation_msg0, message.Invocation)
        dealer.processCall(session1,
                           message.Call(42, u'com.example.my.proc', []))

        invocation_msg1 = messages[-1]
        self.assertIsInstance(invocation_msg1, message.Invocation)

        # now, cancel the first session's call
        dealer.processCancel(session0, message.Cancel(42))

        # should receive an INTERRUPT from the dealer now (for the
        # correct session only)
        interrupt_msg0 = messages[-1]
        self.assertIsInstance(interrupt_msg0, message.Interrupt)
        self.assertEqual(interrupt_msg0.request, invocation_msg0.request)

        dealer.processInvocationError(
            session0,
            message.Error(message.Invocation.MESSAGE_TYPE,
                          invocation_msg0.request, u'wamp.error.canceled'))

    def test_call_cancel_without_callee_support(self):
        last_message = {'1': []}

        def session_send(msg):
            last_message['1'] = msg

        session = mock.Mock()
        session._transport.send = session_send
        session._session_roles = {'callee': role.RoleCalleeFeatures()}

        dealer = self.router._dealer
        dealer.attach(session)

        def authorize(*args, **kwargs):
            return defer.succeed({u'allow': True, u'disclose': False})

        self.router.authorize = mock.Mock(side_effect=authorize)

        dealer.processRegister(
            session,
            message.Register(1, u'com.example.my.proc', u'exact',
                             message.Register.INVOKE_SINGLE, 1))

        registered_msg = last_message['1']
        self.assertIsInstance(registered_msg, message.Registered)

        dealer.processCall(session, message.Call(2, u'com.example.my.proc',
                                                 []))

        invocation_msg = last_message['1']
        self.assertIsInstance(invocation_msg, message.Invocation)

        dealer.processCancel(session, message.Cancel(2))

        # set message to None to make sure that we get nothing back
        last_message['1'] = None

        # should NOT receive an INTERRUPT from the dealer now
        interrupt_msg = last_message['1']
        self.assertIsNone(interrupt_msg)

    def test_force_reregister_kick(self):
        """
        Kick an existing registration with force_reregister=True
        """

        session = mock.Mock()
        session._realm = u'realm1'
        self.router.authorize = mock.Mock(
            return_value=defer.succeed({
                u'allow': True,
                u'disclose': True
            }))
        rap = RouterApplicationSession(session, self.router_factory)

        rap.send(
            message.Hello(u"realm1", {u'caller': role.RoleCallerFeatures()}))
        rap.send(message.Register(1, u'foo'))

        reg_id = session.mock_calls[-1][1][0].registration

        # re-set the authorize, as the Deferred from above is already
        # used-up and it gets called again to authorize the Call
        self.router.authorize = mock.Mock(
            return_value=defer.succeed({
                u'allow': True,
                u'disclose': True
            }))

        # re-register the same procedure
        rap.send(message.Register(2, u'foo', force_reregister=True))

        # the first procedure with 'reg_id' as the Registration ID
        # should have gotten kicked out
        unregs = [
            call[1][0] for call in session.mock_calls if call[0] == 'onMessage'
            and isinstance(call[1][0], message.Unregistered)
        ]
        self.assertEqual(1, len(unregs))
        unreg = unregs[0]
        self.assertEqual(0, unreg.request)
        self.assertEqual(reg_id, unreg.registration)

    def test_yield_on_unowned_invocation(self):
        sessionMessages = {'1': None}

        def session1send(msg):
            sessionMessages['1'] = msg

        def authorize(*args, **kwargs):
            return defer.succeed({u'allow': True, u'disclose': False})

        self.router.authorize = mock.Mock(side_effect=authorize)

        session1 = mock.Mock()
        session1._transport.send = session1send
        session2 = mock.Mock()

        dealer = self.router._dealer
        dealer.attach(session1)
        dealer.attach(session2)

        register = message.Register(1, u'com.example.some.call', u'exact',
                                    message.Register.INVOKE_SINGLE, 1)
        dealer.processRegister(session1, register)
        registered = sessionMessages['1']
        self.assertIsInstance(registered, message.Registered)

        call = message.Call(2, u'com.example.some.call', [], {})
        dealer.processCall(session1, call)
        invocation = sessionMessages['1']
        self.assertIsInstance(invocation, message.Invocation)

        yieldMsg = message.Yield(invocation.request, [u'hello'], {})

        # this yield is happening on a different session than the one that
        # just received the invocation
        def yield_from_wrong_session():
            dealer.processYield(session2, yieldMsg)

        self.failUnlessRaises(ProtocolError, yield_from_wrong_session)

    def test_caller_detach_interrupt_cancel_supported(self):
        last_message = {'1': []}

        def session_send(msg):
            last_message['1'] = msg

        session = mock.Mock()
        session._transport.send = session_send
        session._session_roles = {
            'callee': role.RoleCalleeFeatures(call_canceling=True)
        }

        caller_session = mock.Mock()

        dealer = self.router._dealer
        dealer.attach(session)
        dealer.attach(caller_session)

        def authorize(*args, **kwargs):
            return defer.succeed({u'allow': True, u'disclose': False})

        self.router.authorize = mock.Mock(side_effect=authorize)

        dealer.processRegister(
            session,
            message.Register(1, u'com.example.my.proc', u'exact',
                             message.Register.INVOKE_SINGLE, 1))

        registered_msg = last_message['1']
        self.assertIsInstance(registered_msg, message.Registered)

        dealer.processCall(caller_session,
                           message.Call(2, u'com.example.my.proc', []))

        invocation_msg = last_message['1']
        self.assertIsInstance(invocation_msg, message.Invocation)

        dealer.detach(caller_session)

        # should receive an INTERRUPT from the dealer now
        interrupt_msg = last_message['1']
        self.assertIsInstance(interrupt_msg, message.Interrupt)
        self.assertEqual(interrupt_msg.request, invocation_msg.request)

    def test_caller_detach_interrupt_cancel_not_supported(self):
        last_message = {'1': []}

        def session_send(msg):
            last_message['1'] = msg

        session = mock.Mock()
        session._transport.send = session_send
        session._session_roles = {'callee': role.RoleCalleeFeatures()}

        caller_session = mock.Mock()

        dealer = self.router._dealer
        dealer.attach(session)
        dealer.attach(caller_session)

        def authorize(*args, **kwargs):
            return defer.succeed({u'allow': True, u'disclose': False})

        self.router.authorize = mock.Mock(side_effect=authorize)

        dealer.processRegister(
            session,
            message.Register(1, u'com.example.my.proc', u'exact',
                             message.Register.INVOKE_SINGLE, 1))

        registered_msg = last_message['1']
        self.assertIsInstance(registered_msg, message.Registered)

        dealer.processCall(caller_session,
                           message.Call(2, u'com.example.my.proc', []))

        invocation_msg = last_message['1']
        self.assertIsInstance(invocation_msg, message.Invocation)

        dealer.detach(caller_session)

        # reset recorded message to make sure we don't receive anything
        last_message['1'] = None

        # should NOT receive an INTERRUPT from the dealer now because we don't support cancellation
        self.assertIsNone(last_message['1'])

    def test_concurrency_with_error(self):
        """
        register a concurrency=2 method, called with errors
        """
        callee_messages = []
        caller_messages = []

        def callee_send(msg):
            callee_messages.append(msg)

        session = mock.Mock()
        session._transport.send = callee_send
        session._session_roles = {'callee': role.RoleCalleeFeatures()}

        def caller_send(msg):
            caller_messages.append(msg)

        caller_session = mock.Mock()
        caller_session._transport.send = caller_send

        dealer = self.router._dealer
        dealer.attach(session)
        dealer.attach(caller_session)

        def authorize(*args, **kwargs):
            return defer.succeed({u'allow': True, u'disclose': False})

        self.router.authorize = mock.Mock(side_effect=authorize)

        # we register out procedure, with concurrency=1

        dealer.processRegister(
            session,
            message.Register(request=1,
                             procedure=u'com.example.my.proc',
                             match=u'exact',
                             invoke=message.Register.INVOKE_SINGLE,
                             concurrency=1))

        registered_msg = callee_messages[-1]
        self.assertIsInstance(registered_msg, message.Registered)

        # we have registered our procedure that has concurrency=1
        # and now we call it

        dealer.processCall(caller_session,
                           message.Call(2, u'com.example.my.proc', []))

        # we pretend that the call caused an error of some sort
        invocation_msg = callee_messages[-1]
        self.assertIsInstance(invocation_msg, message.Invocation)
        dealer.processInvocationError(
            session,
            message.Error(
                message.Call.MESSAGE_TYPE,
                invocation_msg.request,
                u"wamp.error.foo",
            ))

        self.assertEqual(1, len(caller_messages))
        self.assertEqual(
            u"wamp.error.foo",
            caller_messages[-1].error,
        )

        # now we call it again, which should work because the
        # previously-outstanding call was resolved with an error
        # (before bug 1105 being fixed this wouldn't work properly)

        dealer.processCall(caller_session,
                           message.Call(3, u'com.example.my.proc', ['foo']))
        invocation_msg = callee_messages[-1]
        self.assertIsInstance(invocation_msg, message.Invocation)

        self.assertEqual(1, len(caller_messages),
                         "got an extra unexpected message")

        dealer.processYield(
            session, message.Yield(
                invocation_msg.request,
                args=['a result'],
            ))

        result_msg = caller_messages[-1]
        self.assertIsInstance(result_msg, message.Result)
        self.assertEqual(result_msg.args, ['a result'])
コード例 #6
0
ファイル: test_dealer.py プロジェクト: vetsin/crossbar
class TestDealer(unittest.TestCase):
    """
    """
    def setUp(self):
        """
        Setup router and router session factories.
        """

        # create a router factory
        self.router_factory = RouterFactory()

        # start a realm
        self.realm = RouterRealm(u'realm-001', {u'name': u'realm1'})
        self.router_factory.start_realm(self.realm)

        # allow everything
        self.router = self.router_factory.get(u'realm1')
        self.router.add_role(
            RouterRoleStaticAuth(self.router,
                                 u'test_role',
                                 default_permissions={
                                     u'uri': u'com.example.',
                                     u'match': u'prefix',
                                     u'allow': {
                                         u'call': True,
                                         u'register': True,
                                         u'publish': True,
                                         u'subscribe': True,
                                     }
                                 }))

        # create a router session factory
        self.session_factory = RouterSessionFactory(self.router_factory)

    def tearDown(self):
        pass

    @defer.inlineCallbacks
    def test_outstanding_invoke(self):
        """
        When a call is pending and the callee goes away, it cancels the
        in-flight call
        """

        session = mock.Mock()
        session._realm = u'realm1'
        self.router.authorize = mock.Mock(
            return_value=defer.succeed({
                u'allow': True,
                u'disclose': True
            }))
        rap = RouterApplicationSession(session, self.router_factory)

        rap.send(
            message.Hello(u"realm1", {u'caller': role.RoleCallerFeatures()}))
        rap.send(message.Register(1, u'foo'))

        # we can retrieve the Registration via
        # session.mock_calls[-1][1][0] if req'd

        # re-set the authorize, as the Deferred from above is already
        # used-up and it gets called again to authorize the Call
        self.router.authorize = mock.Mock(
            return_value=defer.succeed({
                u'allow': True,
                u'disclose': True
            }))
        rap.send(message.Call(42, u'foo'))

        orig = rap.send
        d = defer.Deferred()

        rap.send(message.Goodbye())

        def wrapper(*args, **kw):
            d.callback(args[0])
            return orig(*args, **kw)

        rap.send = wrapper

        # we can do this *after* the call to send() the Goodbye
        # (above) because it takes a reactor-turn to actually
        # process the cancel/errors etc -- hence the Deferred and
        # yield in this test...

        msg = yield d

        self.assertEqual(42, msg.request)
        self.assertEqual(u'wamp.error.canceled', msg.error)
コード例 #7
0
class TestBrokerPublish(unittest.TestCase):
    """
    Tests for crossbar.router.broker.Broker
    """
    def setUp(self):
        """
        Setup router and router session factories.
        """

        # create a router factory
        self.router_factory = RouterFactory(u'mynode')

        # start a realm
        self.realm = RouterRealm(None, {u'name': u'realm1'})
        self.router_factory.start_realm(self.realm)

        # allow everything
        permissions = RouterPermissions('', True, True, True, True, True)
        self.router = self.router_factory.get(u'realm1')
        self.router.add_role(
            RouterRoleStaticAuth(self.router,
                                 None,
                                 default_permissions=permissions))

        # create a router session factory
        self.session_factory = RouterSessionFactory(self.router_factory)

    def tearDown(self):
        pass

    def test_add(self):
        """
        Create an application session and add it to a router to
        run embedded.
        """
        d = txaio.create_future()

        class TestSession(ApplicationSession):
            def onJoin(self, details):
                txaio.resolve(d, None)

        session = TestSession(types.ComponentConfig(u'realm1'))

        self.session_factory.add(session)

        return d

    def test_application_session_internal_error(self):
        """
        simulate an internal error triggering the 'onJoin' error-case from
        _RouterApplicationSession's send() method (from the Hello msg)
        """
        # setup
        the_exception = RuntimeError("sadness")
        errors = []

        class TestSession(ApplicationSession):
            def onJoin(self, *args, **kw):
                raise the_exception

            def onUserError(self, fail, msg):
                errors.append((fail, msg))

        session = TestSession(types.ComponentConfig(u'realm1'))
        from crossbar.router.session import _RouterApplicationSession

        # Note to self: original code was logging directly in
        # _RouterApplicationSession -- which *may* actually be better?
        # or not...
        with mock.patch.object(_RouterApplicationSession, 'log') as logger:
            # this should call onJoin, triggering our error
            self.session_factory.add(session)

            if True:
                self.assertEqual(1, len(errors), "Didn't see our error")
                self.assertEqual(the_exception, errors[0][0].value)

            else:
                # check we got the right log.failure() call
                self.assertTrue(len(logger.method_calls) > 0)
                call = logger.method_calls[0]
                # for a MagicMock call-object, 0th thing is the method-name, 1st
                # thing is the arg-tuple, 2nd thing is the kwargs.
                self.assertEqual(call[0], 'failure')
                self.assertEqual(call[1][0].value, the_exception)

    def test_router_session_internal_error_onHello(self):
        """
        similar to above, but during _RouterSession's onMessage handling,
        where it calls self.onHello
        """
        # setup
        transport = mock.MagicMock()
        the_exception = RuntimeError("kerblam")

        def boom(*args, **kw):
            raise the_exception

        session = self.session_factory(
        )  # __call__ on the _RouterSessionFactory
        session.onHello = boom
        session.onOpen(transport)
        msg = message.Hello(u'realm1', dict(caller=role.RoleCallerFeatures()))

        # XXX think: why isn't this using _RouterSession.log?
        from crossbar.router.session import RouterSession
        with mock.patch.object(RouterSession, 'log') as logger:
            # do the test; should call onHello which is now "boom", above
            session.onMessage(msg)

            # check we got the right log.failure() call
            self.assertTrue(len(logger.method_calls) > 0)
            call = logger.method_calls[0]
            # for a MagicMock call-object, 0th thing is the method-name, 1st
            # thing is the arg-tuple, 2nd thing is the kwargs.
            self.assertEqual(call[0], 'failure')
            self.assertTrue('log_failure' in call[2])
            self.assertEqual(call[2]['log_failure'].value, the_exception)

    def test_router_session_internal_error_onAuthenticate(self):
        """
        similar to above, but during _RouterSession's onMessage handling,
        where it calls self.onAuthenticate)
        """
        # setup
        transport = mock.MagicMock()
        the_exception = RuntimeError("kerblam")

        def boom(*args, **kw):
            raise the_exception

        session = self.session_factory(
        )  # __call__ on the _RouterSessionFactory
        session.onAuthenticate = boom
        session.onOpen(transport)
        msg = message.Authenticate(u'bogus signature')

        # XXX think: why isn't this using _RouterSession.log?
        from crossbar.router.session import RouterSession
        with mock.patch.object(RouterSession, 'log') as logger:
            # do the test; should call onHello which is now "boom", above
            session.onMessage(msg)

            # check we got the right log.failure() call
            self.assertTrue(len(logger.method_calls) > 0)
            call = logger.method_calls[0]
            # for a MagicMock call-object, 0th thing is the method-name, 1st
            # thing is the arg-tuple, 2nd thing is the kwargs.
            self.assertEqual(call[0], 'failure')
            self.assertTrue('log_failure' in call[2])
            self.assertEqual(call[2]['log_failure'].value, the_exception)

    def test_add_and_subscribe(self):
        """
        Create an application session that subscribes to some
        topic and add it to a router to run embedded.
        """
        d = txaio.create_future()

        class TestSession(ApplicationSession):
            def onJoin(self, details):
                d2 = self.subscribe(lambda: None, u'com.example.topic1')

                def ok(_):
                    txaio.resolve(d, None)

                def error(err):
                    txaio.reject(d, err)

                txaio.add_callbacks(d2, ok, error)

        session = TestSession(types.ComponentConfig(u'realm1'))

        self.session_factory.add(session)

        return d

    def test_publish_closed_session(self):
        """
        ensure a session doesn't get Events if it's closed
        (see also issue #431)
        """

        # we want to trigger a deeply-nested condition in
        # processPublish in class Broker -- lets try w/o refactoring
        # anything first...

        class TestSession(ApplicationSession):
            pass

        session0 = TestSession()
        session1 = TestSession()
        router = mock.MagicMock()
        broker = Broker(router)

        # let's just "cheat" our way a little to the right state by
        # injecting our subscription "directly" (e.g. instead of
        # faking out an entire Subscribe etc. flow
        # ...so we need _subscriptions_map to have at least one
        # subscription (our test one) for the topic we'll publish to
        broker._subscription_map.add_observer(session0, u'test.topic')

        # simulate the session state we want, which is that a
        # transport is connected (._transport != None) but there
        # _session_id *is* None (not joined yet, or left already)
        self.assertIs(None, session0._session_id)
        session0._transport = mock.MagicMock()
        session1._session_id = 1234  # "from" session should look connected + joined
        session1._transport = mock.MagicMock()

        # here's the main "cheat"; we're faking out the
        # router.authorize because we need it to callback immediately
        router.authorize = mock.MagicMock(
            return_value=txaio.create_future_success(True))

        # now we scan call "processPublish" such that we get to the
        # condition we're interested in (this "comes from" session1
        # beacuse by default publishes don't go to the same session)
        pubmsg = message.Publish(123, u'test.topic')
        broker.processPublish(session1, pubmsg)

        # neither session should have sent anything on its transport
        self.assertEquals(session0._transport.method_calls, [])
        self.assertEquals(session1._transport.method_calls, [])
コード例 #8
0
ファイル: test_router.py プロジェクト: cosmospham/crossbar
class TestEmbeddedSessions(unittest.TestCase):

    """
    Test cases for application session running embedded in router.
    """

    def setUp(self):
        """
        Setup router and router session factories.
        """

        # create a router factory
        self.router_factory = RouterFactory()

        # start a realm
        self.router_factory.start_realm(RouterRealm(None, {u"name": u"realm1"}))

        # allow everything
        permissions = RouterPermissions("", True, True, True, True, True)
        router = self.router_factory.get(u"realm1")
        router.add_role(RouterRoleStaticAuth(router, None, default_permissions=permissions))

        # create a router session factory
        self.session_factory = RouterSessionFactory(self.router_factory)

    def tearDown(self):
        pass

    def test_add(self):
        """
        Create an application session and add it to a router to
        run embedded.
        """
        d = txaio.create_future()

        class TestSession(ApplicationSession):
            def onJoin(self, details):
                txaio.resolve(d, None)

        session = TestSession(types.ComponentConfig(u"realm1"))

        self.session_factory.add(session)

        return d

    def test_application_session_internal_error(self):
        """
        simulate an internal error triggering the 'onJoin' error-case from
        _RouterApplicationSession's send() method (from the Hello msg)
        """
        # setup
        the_exception = RuntimeError("sadness")

        class TestSession(ApplicationSession):
            def onJoin(self, *args, **kw):
                raise the_exception

        session = TestSession(types.ComponentConfig(u"realm1"))
        from crossbar.router.session import _RouterApplicationSession

        # execute, first patching-out the logger so we can see that
        # log.failure() was called when our exception triggers.
        with mock.patch.object(_RouterApplicationSession, "log") as logger:
            # this should call onJoin, triggering our error
            self.session_factory.add(session)

            # check we got the right log.failure() call
            self.assertTrue(len(logger.method_calls) > 0)
            call = logger.method_calls[0]
            # for a MagicMock call-object, 0th thing is the method-name, 1st
            # thing is the arg-tuple, 2nd thing is the kwargs.
            self.assertEqual(call[0], "failure")
            self.assertTrue("failure" in call[2])
            self.assertEqual(call[2]["failure"].value, the_exception)

    def test_router_session_internal_error_onHello(self):
        """
        similar to above, but during _RouterSession's onMessage handling,
        where it calls self.onHello
        """
        # setup
        transport = mock.MagicMock()
        the_exception = RuntimeError("kerblam")

        def boom(*args, **kw):
            raise the_exception

        session = self.session_factory()  # __call__ on the _RouterSessionFactory
        session.onHello = boom
        session.onOpen(transport)
        msg = message.Hello(u"realm1", dict(caller=role.RoleCallerFeatures()))

        # XXX think: why isn't this using _RouterSession.log?
        from crossbar.router.session import RouterSession

        with mock.patch.object(RouterSession, "log") as logger:
            # do the test; should call onHello which is now "boom", above
            session.onMessage(msg)

            # check we got the right log.failure() call
            self.assertTrue(len(logger.method_calls) > 0)
            call = logger.method_calls[0]
            # for a MagicMock call-object, 0th thing is the method-name, 1st
            # thing is the arg-tuple, 2nd thing is the kwargs.
            self.assertEqual(call[0], "failure")
            self.assertTrue("failure" in call[2])
            self.assertEqual(call[2]["failure"].value, the_exception)

    def test_router_session_internal_error_onAuthenticate(self):
        """
        similar to above, but during _RouterSession's onMessage handling,
        where it calls self.onAuthenticate)
        """
        # setup
        transport = mock.MagicMock()
        the_exception = RuntimeError("kerblam")

        def boom(*args, **kw):
            raise the_exception

        session = self.session_factory()  # __call__ on the _RouterSessionFactory
        session.onAuthenticate = boom
        session.onOpen(transport)
        msg = message.Authenticate(u"bogus signature")

        # XXX think: why isn't this using _RouterSession.log?
        from crossbar.router.session import RouterSession

        with mock.patch.object(RouterSession, "log") as logger:
            # do the test; should call onHello which is now "boom", above
            session.onMessage(msg)

            # check we got the right log.failure() call
            self.assertTrue(len(logger.method_calls) > 0)
            call = logger.method_calls[0]
            # for a MagicMock call-object, 0th thing is the method-name, 1st
            # thing is the arg-tuple, 2nd thing is the kwargs.
            self.assertEqual(call[0], "failure")
            self.assertTrue("failure" in call[2])
            self.assertEqual(call[2]["failure"].value, the_exception)

    def test_add_and_subscribe(self):
        """
        Create an application session that subscribes to some
        topic and add it to a router to run embedded.
        """
        d = txaio.create_future()

        class TestSession(ApplicationSession):
            def onJoin(self, details):
                # noinspection PyUnusedLocal
                def on_event(*arg, **kwargs):
                    pass

                d2 = self.subscribe(on_event, u"com.example.topic1")

                def ok(_):
                    txaio.resolve(d, None)

                def error(err):
                    txaio.reject(d, err)

                txaio.add_callbacks(d2, ok, error)

        session = TestSession(types.ComponentConfig(u"realm1"))

        self.session_factory.add(session)

        return d
コード例 #9
0
ファイル: test_dealer.py プロジェクト: goeddea/crossbar
class TestDealer(unittest.TestCase):
    """
    """

    def setUp(self):
        """
        Setup router and router session factories.
        """

        # create a router factory
        self.router_factory = RouterFactory(None, None)

        # start a realm
        self.realm = RouterRealm(u'realm-001', {u'name': u'realm1'})
        self.router_factory.start_realm(self.realm)

        # allow everything
        self.router = self.router_factory.get(u'realm1')
        self.router.add_role(
            RouterRoleStaticAuth(
                self.router,
                u'test_role',
                default_permissions={
                    u'uri': u'com.example.',
                    u'match': u'prefix',
                    u'allow': {
                        u'call': True,
                        u'register': True,
                        u'publish': True,
                        u'subscribe': True,
                    }
                }
            )
        )

        # create a router session factory
        self.session_factory = RouterSessionFactory(self.router_factory)

    def tearDown(self):
        pass

    @defer.inlineCallbacks
    def test_outstanding_invoke(self):
        """
        When a call is pending and the callee goes away, it cancels the
        in-flight call
        """

        session = mock.Mock()
        session._realm = u'realm1'
        self.router.authorize = mock.Mock(
            return_value=defer.succeed({u'allow': True, u'disclose': True})
        )
        rap = RouterApplicationSession(session, self.router_factory)

        rap.send(message.Hello(u"realm1", {u'caller': role.RoleCallerFeatures()}))
        rap.send(message.Register(1, u'foo'))

        # we can retrieve the Registration via
        # session.mock_calls[-1][1][0] if req'd

        # re-set the authorize, as the Deferred from above is already
        # used-up and it gets called again to authorize the Call
        self.router.authorize = mock.Mock(
            return_value=defer.succeed({u'allow': True, u'disclose': True})
        )
        rap.send(message.Call(42, u'foo'))

        orig = rap.send
        d = defer.Deferred()

        rap.send(message.Goodbye())

        def wrapper(*args, **kw):
            d.callback(args[0])
            return orig(*args, **kw)
        rap.send = wrapper

        # we can do this *after* the call to send() the Goodbye
        # (above) because it takes a reactor-turn to actually
        # process the cancel/errors etc -- hence the Deferred and
        # yield in this test...

        msg = yield d

        self.assertEqual(42, msg.request)
        self.assertEqual(u'wamp.error.canceled', msg.error)

    def test_outstanding_invoke_but_caller_gone(self):

        session = mock.Mock()
        outstanding = mock.Mock()
        outstanding.call.request = 1

        dealer = self.router._dealer
        dealer.attach(session)

        dealer._callee_to_invocations[session] = [outstanding]
        # pretend we've disconnected already
        outstanding.caller._transport = None

        dealer.detach(session)

        self.assertEqual([], outstanding.mock_calls)

    def test_call_cancel(self):
        last_message = {'1': []}

        def session_send(msg):
            last_message['1'] = msg

        session = mock.Mock()
        session._transport.send = session_send
        session._session_roles = {'callee': role.RoleCalleeFeatures(call_canceling=True)}

        dealer = self.router._dealer
        dealer.attach(session)

        def authorize(*args, **kwargs):
            return defer.succeed({u'allow': True, u'disclose': False})

        self.router.authorize = mock.Mock(side_effect=authorize)

        dealer.processRegister(session, message.Register(
            1,
            u'com.example.my.proc',
            u'exact',
            message.Register.INVOKE_SINGLE,
            1
        ))

        registered_msg = last_message['1']
        self.assertIsInstance(registered_msg, message.Registered)

        dealer.processCall(session, message.Call(
            2,
            u'com.example.my.proc',
            []
        ))

        invocation_msg = last_message['1']
        self.assertIsInstance(invocation_msg, message.Invocation)

        dealer.processCancel(session, message.Cancel(
            2
        ))

        # should receive an INTERRUPT from the dealer now
        interrupt_msg = last_message['1']
        self.assertIsInstance(interrupt_msg, message.Interrupt)
        self.assertEqual(interrupt_msg.request, invocation_msg.request)

        dealer.processInvocationError(session, message.Error(
            message.Invocation.MESSAGE_TYPE,
            invocation_msg.request,
            u'wamp.error.canceled'
        ))

        call_error_msg = last_message['1']
        self.assertIsInstance(call_error_msg, message.Error)
        self.assertEqual(message.Call.MESSAGE_TYPE, call_error_msg.request_type)
        self.assertEqual(u'wamp.error.canceled', call_error_msg.error)

    def test_call_cancel_two_sessions(self):
        """
        this has 2 different session using the same ID (42) for their Call
        requests to confirm we deal with the fact that these IDs are
        only unique per-session properly
        """
        messages = []

        def session_send(msg):
            messages.append(msg)

        session0 = mock.Mock()
        session0._transport.send = session_send
        session0._session_roles = {'callee': role.RoleCalleeFeatures(call_canceling=True)}

        session1 = mock.Mock()
        session1._transport.send = session_send
        session1._session_roles = {'callee': role.RoleCalleeFeatures(call_canceling=True)}

        dealer = self.router._dealer
        dealer.attach(session0)
        dealer.attach(session1)

        def authorize(*args, **kwargs):
            return defer.succeed({u'allow': True, u'disclose': False})

        self.router.authorize = mock.Mock(side_effect=authorize)

        dealer.processRegister(session0, message.Register(
            1,
            u'com.example.my.proc',
            u'exact',
            message.Register.INVOKE_SINGLE,
            2
        ))

        registered_msg = messages[-1]
        self.assertIsInstance(registered_msg, message.Registered)

        # two calls outstanding to the endpoint, both happen to use
        # the same ID (42) which is legal
        dealer.processCall(session0, message.Call(
            42,
            u'com.example.my.proc',
            []
        ))

        invocation_msg0 = messages[-1]
        self.assertIsInstance(invocation_msg0, message.Invocation)
        dealer.processCall(session1, message.Call(
            42,
            u'com.example.my.proc',
            []
        ))

        invocation_msg1 = messages[-1]
        self.assertIsInstance(invocation_msg1, message.Invocation)

        # now, cancel the first session's call
        dealer.processCancel(session0, message.Cancel(
            42
        ))

        # should receive an INTERRUPT from the dealer now (for the
        # correct session only)
        interrupt_msg0 = messages[-1]
        self.assertIsInstance(interrupt_msg0, message.Interrupt)
        self.assertEqual(interrupt_msg0.request, invocation_msg0.request)

        dealer.processInvocationError(session0, message.Error(
            message.Invocation.MESSAGE_TYPE,
            invocation_msg0.request,
            u'wamp.error.canceled'
        ))

    def test_call_cancel_without_callee_support(self):
        last_message = {'1': []}

        def session_send(msg):
            last_message['1'] = msg

        session = mock.Mock()
        session._transport.send = session_send
        session._session_roles = {'callee': role.RoleCalleeFeatures()}

        dealer = self.router._dealer
        dealer.attach(session)

        def authorize(*args, **kwargs):
            return defer.succeed({u'allow': True, u'disclose': False})

        self.router.authorize = mock.Mock(side_effect=authorize)

        dealer.processRegister(session, message.Register(
            1,
            u'com.example.my.proc',
            u'exact',
            message.Register.INVOKE_SINGLE,
            1
        ))

        registered_msg = last_message['1']
        self.assertIsInstance(registered_msg, message.Registered)

        dealer.processCall(session, message.Call(
            2,
            u'com.example.my.proc',
            []
        ))

        invocation_msg = last_message['1']
        self.assertIsInstance(invocation_msg, message.Invocation)

        dealer.processCancel(session, message.Cancel(
            2
        ))

        # set message to None to make sure that we get nothing back
        last_message['1'] = None

        # should NOT receive an INTERRUPT from the dealer now
        interrupt_msg = last_message['1']
        self.assertIsNone(interrupt_msg)

    def test_force_reregister_kick(self):
        """
        Kick an existing registration with force_reregister=True
        """

        session = mock.Mock()
        session._realm = u'realm1'
        self.router.authorize = mock.Mock(
            return_value=defer.succeed({u'allow': True, u'disclose': True})
        )
        rap = RouterApplicationSession(session, self.router_factory)

        rap.send(message.Hello(u"realm1", {u'caller': role.RoleCallerFeatures()}))
        rap.send(message.Register(1, u'foo'))

        reg_id = session.mock_calls[-1][1][0].registration

        # re-set the authorize, as the Deferred from above is already
        # used-up and it gets called again to authorize the Call
        self.router.authorize = mock.Mock(
            return_value=defer.succeed({u'allow': True, u'disclose': True})
        )

        # re-register the same procedure
        rap.send(message.Register(2, u'foo', force_reregister=True))

        # the first procedure with 'reg_id' as the Registration ID
        # should have gotten kicked out
        unregs = [
            call[1][0] for call in session.mock_calls
            if call[0] == 'onMessage' and isinstance(call[1][0], message.Unregistered)
        ]
        self.assertEqual(1, len(unregs))
        unreg = unregs[0]
        self.assertEqual(0, unreg.request)
        self.assertEqual(reg_id, unreg.registration)

    def test_yield_on_unowned_invocation(self):
        sessionMessages = {'1': None}

        def session1send(msg):
            sessionMessages['1'] = msg

        def authorize(*args, **kwargs):
            return defer.succeed({u'allow': True, u'disclose': False})

        self.router.authorize = mock.Mock(side_effect=authorize)

        session1 = mock.Mock()
        session1._transport.send = session1send
        session2 = mock.Mock()

        dealer = self.router._dealer
        dealer.attach(session1)
        dealer.attach(session2)

        register = message.Register(1, u'com.example.some.call', u'exact', message.Register.INVOKE_SINGLE, 1)
        dealer.processRegister(session1, register)
        registered = sessionMessages['1']
        self.assertIsInstance(registered, message.Registered)

        call = message.Call(2, u'com.example.some.call', [], {})
        dealer.processCall(session1, call)
        invocation = sessionMessages['1']
        self.assertIsInstance(invocation, message.Invocation)

        yieldMsg = message.Yield(invocation.request, [u'hello'], {})

        # this yield is happening on a different session than the one that
        # just received the invocation
        def yield_from_wrong_session():
            dealer.processYield(session2, yieldMsg)

        self.failUnlessRaises(ProtocolError, yield_from_wrong_session)

    def test_caller_detach_interrupt_cancel_supported(self):
        last_message = {'1': []}

        def session_send(msg):
            last_message['1'] = msg

        session = mock.Mock()
        session._transport.send = session_send
        session._session_roles = {'callee': role.RoleCalleeFeatures(call_canceling=True)}

        caller_session = mock.Mock()

        dealer = self.router._dealer
        dealer.attach(session)
        dealer.attach(caller_session)

        def authorize(*args, **kwargs):
            return defer.succeed({u'allow': True, u'disclose': False})

        self.router.authorize = mock.Mock(side_effect=authorize)

        dealer.processRegister(session, message.Register(
            1,
            u'com.example.my.proc',
            u'exact',
            message.Register.INVOKE_SINGLE,
            1
        ))

        registered_msg = last_message['1']
        self.assertIsInstance(registered_msg, message.Registered)

        dealer.processCall(caller_session, message.Call(
            2,
            u'com.example.my.proc',
            []
        ))

        invocation_msg = last_message['1']
        self.assertIsInstance(invocation_msg, message.Invocation)

        dealer.detach(caller_session)

        # should receive an INTERRUPT from the dealer now
        interrupt_msg = last_message['1']
        self.assertIsInstance(interrupt_msg, message.Interrupt)
        self.assertEqual(interrupt_msg.request, invocation_msg.request)

    def test_caller_detach_interrupt_cancel_not_supported(self):
        last_message = {'1': []}

        def session_send(msg):
            last_message['1'] = msg

        session = mock.Mock()
        session._transport.send = session_send
        session._session_roles = {'callee': role.RoleCalleeFeatures()}

        caller_session = mock.Mock()

        dealer = self.router._dealer
        dealer.attach(session)
        dealer.attach(caller_session)

        def authorize(*args, **kwargs):
            return defer.succeed({u'allow': True, u'disclose': False})

        self.router.authorize = mock.Mock(side_effect=authorize)

        dealer.processRegister(session, message.Register(
            1,
            u'com.example.my.proc',
            u'exact',
            message.Register.INVOKE_SINGLE,
            1
        ))

        registered_msg = last_message['1']
        self.assertIsInstance(registered_msg, message.Registered)

        dealer.processCall(caller_session, message.Call(
            2,
            u'com.example.my.proc',
            []
        ))

        invocation_msg = last_message['1']
        self.assertIsInstance(invocation_msg, message.Invocation)

        dealer.detach(caller_session)

        # reset recorded message to make sure we don't receive anything
        last_message['1'] = None

        # should NOT receive an INTERRUPT from the dealer now because we don't support cancellation
        self.assertIsNone(last_message['1'])

    def test_concurrency_with_error(self):
        """
        register a concurrency=2 method, called with errors
        """
        callee_messages = []
        caller_messages = []

        def callee_send(msg):
            callee_messages.append(msg)

        session = mock.Mock()
        session._transport.send = callee_send
        session._session_roles = {'callee': role.RoleCalleeFeatures()}

        def caller_send(msg):
            caller_messages.append(msg)

        caller_session = mock.Mock()
        caller_session._transport.send = caller_send

        dealer = self.router._dealer
        dealer.attach(session)
        dealer.attach(caller_session)

        def authorize(*args, **kwargs):
            return defer.succeed({u'allow': True, u'disclose': False})

        self.router.authorize = mock.Mock(side_effect=authorize)

        # we register out procedure, with concurrency=1

        dealer.processRegister(session, message.Register(
            request=1,
            procedure=u'com.example.my.proc',
            match=u'exact',
            invoke=message.Register.INVOKE_SINGLE,
            concurrency=1
        ))

        registered_msg = callee_messages[-1]
        self.assertIsInstance(registered_msg, message.Registered)

        # we have registered our procedure that has concurrency=1
        # and now we call it

        dealer.processCall(caller_session, message.Call(
            2,
            u'com.example.my.proc',
            []
        ))

        # we pretend that the call caused an error of some sort
        invocation_msg = callee_messages[-1]
        self.assertIsInstance(invocation_msg, message.Invocation)
        dealer.processInvocationError(
            session, message.Error(
                message.Call.MESSAGE_TYPE,
                invocation_msg.request,
                u"wamp.error.foo",
            )
        )

        self.assertEqual(1, len(caller_messages))
        self.assertEqual(
            u"wamp.error.foo",
            caller_messages[-1].error,
        )

        # now we call it again, which should work because the
        # previously-outstanding call was resolved with an error
        # (before bug 1105 being fixed this wouldn't work properly)

        dealer.processCall(caller_session, message.Call(
            3,
            u'com.example.my.proc',
            ['foo']
        ))
        invocation_msg = callee_messages[-1]
        self.assertIsInstance(invocation_msg, message.Invocation)

        self.assertEqual(1, len(caller_messages), "got an extra unexpected message")

        dealer.processYield(
            session, message.Yield(
                invocation_msg.request,
                args=['a result'],
            )
        )

        result_msg = caller_messages[-1]
        self.assertIsInstance(result_msg, message.Result)
        self.assertEqual(result_msg.args, ['a result'])
コード例 #10
0
ファイル: test_router.py プロジェクト: snowattitudes/crossbar
class TestEmbeddedSessions(unittest.TestCase):
    """
    Test cases for application session running embedded in router.
    """
    def setUp(self):
        """
        Setup router and router session factories.
        """

        # create a router factory
        self.router_factory = RouterFactory()

        # start a realm
        self.router_factory.start_realm(RouterRealm(None,
                                                    {u'name': u'realm1'}))

        # allow everything
        permissions = RouterPermissions('', True, True, True, True, True)
        router = self.router_factory.get(u'realm1')
        router.add_role(
            RouterRoleStaticAuth(router, None,
                                 default_permissions=permissions))

        # create a router session factory
        self.session_factory = RouterSessionFactory(self.router_factory)

    def tearDown(self):
        pass

    def test_add(self):
        """
        Create an application session and add it to a router to
        run embedded.
        """
        d = txaio.create_future()

        class TestSession(ApplicationSession):
            def onJoin(self, details):
                txaio.resolve(d, None)

        session = TestSession(types.ComponentConfig(u'realm1'))

        self.session_factory.add(session)

        return d

    def _test_application_session_internal_error(self):
        """
        simulate an internal error triggering the 'onJoin' error-case from
        _RouterApplicationSession's send() method (from the Hello msg)
        """
        # setup
        the_exception = RuntimeError("sadness")

        class TestSession(ApplicationSession):
            def onJoin(self, *args, **kw):
                raise the_exception

        session = TestSession(types.ComponentConfig(u'realm1'))
        from crossbar.router.session import _RouterApplicationSession

        # execute, first patching-out the logger so we can see that
        # log.failure() was called when our exception triggers.
        with mock.patch.object(_RouterApplicationSession, 'log') as logger:
            # this should call onJoin, triggering our error
            self.session_factory.add(session)

            # check we got the right log.failure() call
            self.assertTrue(len(logger.method_calls) > 0)
            call = logger.method_calls[0]
            # for a MagicMock call-object, 0th thing is the method-name, 1st
            # thing is the arg-tuple, 2nd thing is the kwargs.
            self.assertEqual(call[0], 'failure')
            self.assertTrue('log_failure' in call[2])
            self.assertEqual(call[2]['log_failure'].value, the_exception)

    def test_router_session_internal_error_onHello(self):
        """
        similar to above, but during _RouterSession's onMessage handling,
        where it calls self.onHello
        """
        # setup
        transport = mock.MagicMock()
        the_exception = RuntimeError("kerblam")

        def boom(*args, **kw):
            raise the_exception

        session = self.session_factory(
        )  # __call__ on the _RouterSessionFactory
        session.onHello = boom
        session.onOpen(transport)
        msg = message.Hello(u'realm1', dict(caller=role.RoleCallerFeatures()))

        # XXX think: why isn't this using _RouterSession.log?
        from crossbar.router.session import RouterSession
        with mock.patch.object(RouterSession, 'log') as logger:
            # do the test; should call onHello which is now "boom", above
            session.onMessage(msg)

            # check we got the right log.failure() call
            self.assertTrue(len(logger.method_calls) > 0)
            call = logger.method_calls[0]
            # for a MagicMock call-object, 0th thing is the method-name, 1st
            # thing is the arg-tuple, 2nd thing is the kwargs.
            self.assertEqual(call[0], 'failure')
            self.assertTrue('log_failure' in call[2])
            self.assertEqual(call[2]['log_failure'].value, the_exception)

    def test_router_session_internal_error_onAuthenticate(self):
        """
        similar to above, but during _RouterSession's onMessage handling,
        where it calls self.onAuthenticate)
        """
        # setup
        transport = mock.MagicMock()
        the_exception = RuntimeError("kerblam")

        def boom(*args, **kw):
            raise the_exception

        session = self.session_factory(
        )  # __call__ on the _RouterSessionFactory
        session.onAuthenticate = boom
        session.onOpen(transport)
        msg = message.Authenticate(u'bogus signature')

        # XXX think: why isn't this using _RouterSession.log?
        from crossbar.router.session import RouterSession
        with mock.patch.object(RouterSession, 'log') as logger:
            # do the test; should call onHello which is now "boom", above
            session.onMessage(msg)

            # check we got the right log.failure() call
            self.assertTrue(len(logger.method_calls) > 0)
            call = logger.method_calls[0]
            # for a MagicMock call-object, 0th thing is the method-name, 1st
            # thing is the arg-tuple, 2nd thing is the kwargs.
            self.assertEqual(call[0], 'failure')
            self.assertTrue('log_failure' in call[2])
            self.assertEqual(call[2]['log_failure'].value, the_exception)

    def test_add_and_subscribe(self):
        """
        Create an application session that subscribes to some
        topic and add it to a router to run embedded.
        """
        d = txaio.create_future()

        class TestSession(ApplicationSession):
            def onJoin(self, details):
                # noinspection PyUnusedLocal
                def on_event(*arg, **kwargs):
                    pass

                d2 = self.subscribe(on_event, u'com.example.topic1')

                def ok(_):
                    txaio.resolve(d, None)

                def error(err):
                    txaio.reject(d, err)

                txaio.add_callbacks(d2, ok, error)

        session = TestSession(types.ComponentConfig(u'realm1'))

        self.session_factory.add(session)

        return d
コード例 #11
0
class TestBrokerPublish(unittest.TestCase):
    """
    Tests for crossbar.router.broker.Broker
    """

    def setUp(self):
        """
        Setup router and router session factories.
        """

        # create a router factory
        self.router_factory = RouterFactory(u'mynode')

        # start a realm
        self.realm = RouterRealm(None, {u'name': u'realm1'})
        self.router_factory.start_realm(self.realm)

        # allow everything
        self.router = self.router_factory.get(u'realm1')
        self.router.add_role(
            RouterRoleStaticAuth(
                self.router,
                u'test_role',
                default_permissions={
                    u'uri': u'com.example.',
                    u'match': u'prefix',
                    u'allow': {
                        u'call': True,
                        u'register': True,
                        u'publish': True,
                        u'subscribe': True,
                    }
                }
            )
        )

        # create a router session factory
        self.session_factory = RouterSessionFactory(self.router_factory)

    def tearDown(self):
        pass

    def test_add(self):
        """
        Create an application session and add it to a router to
        run embedded.
        """
        d = txaio.create_future()

        class TestSession(ApplicationSession):

            def onJoin(self, details):
                txaio.resolve(d, None)

        session = TestSession(types.ComponentConfig(u'realm1'))

        self.session_factory.add(session)

        return d

    def test_application_session_internal_error(self):
        """
        simulate an internal error triggering the 'onJoin' error-case from
        RouterApplicationSession's send() method (from the Hello msg)
        """
        # setup
        the_exception = RuntimeError("sadness")
        errors = []

        class TestSession(ApplicationSession):
            def onJoin(self, *args, **kw):
                raise the_exception

            def onUserError(self, fail, msg):
                errors.append((fail, msg))

        session = TestSession(types.ComponentConfig(u'realm1'))
        from crossbar.router.session import RouterApplicationSession

        # Note to self: original code was logging directly in
        # RouterApplicationSession -- which *may* actually be better?
        # or not...
        with mock.patch.object(RouterApplicationSession, 'log') as logger:
            # this should call onJoin, triggering our error
            self.session_factory.add(session)

            if True:
                self.assertEqual(1, len(errors), "Didn't see our error")
                self.assertEqual(the_exception, errors[0][0].value)

            else:
                # check we got the right log.failure() call
                self.assertTrue(len(logger.method_calls) > 0)
                call = logger.method_calls[0]
                # for a MagicMock call-object, 0th thing is the method-name, 1st
                # thing is the arg-tuple, 2nd thing is the kwargs.
                self.assertEqual(call[0], 'failure')
                self.assertEqual(call[1][0].value, the_exception)

    def test_router_session_internal_error_onHello(self):
        """
        similar to above, but during _RouterSession's onMessage handling,
        where it calls self.onHello
        """
        # setup
        transport = mock.MagicMock()
        transport.get_channel_id = mock.MagicMock(return_value=b'deadbeef')
        the_exception = RuntimeError("kerblam")

        def boom(*args, **kw):
            raise the_exception
        session = self.session_factory()  # __call__ on the _RouterSessionFactory
        session.onHello = boom
        session.onOpen(transport)
        msg = message.Hello(u'realm1', dict(caller=role.RoleCallerFeatures()))

        # XXX think: why isn't this using _RouterSession.log?
        from crossbar.router.session import RouterSession
        with mock.patch.object(RouterSession, 'log') as logger:
            # do the test; should call onHello which is now "boom", above
            session.onMessage(msg)

            # check we got the right log.failure() call
            self.assertTrue(len(logger.method_calls) > 0)
            call = logger.method_calls[0]
            # for a MagicMock call-object, 0th thing is the method-name, 1st
            # thing is the arg-tuple, 2nd thing is the kwargs.
            self.assertEqual(call[0], 'failure')
            self.assertTrue('failure' in call[2])
            self.assertEqual(call[2]['failure'].value, the_exception)

    def test_router_session_internal_error_onAuthenticate(self):
        """
        similar to above, but during _RouterSession's onMessage handling,
        where it calls self.onAuthenticate)
        """
        # setup
        transport = mock.MagicMock()
        transport.get_channel_id = mock.MagicMock(return_value=b'deadbeef')
        the_exception = RuntimeError("kerblam")

        def boom(*args, **kw):
            raise the_exception
        session = self.session_factory()  # __call__ on the _RouterSessionFactory
        session.onAuthenticate = boom
        session.onOpen(transport)
        msg = message.Authenticate(u'bogus signature')

        # do the test; should call onHello which is now "boom", above
        session.onMessage(msg)

        errors = self.flushLoggedErrors()
        self.assertEqual(1, len(errors), "Expected just one error: {}".format(errors))
        self.assertTrue(the_exception in [fail.value for fail in errors])

    def test_add_and_subscribe(self):
        """
        Create an application session that subscribes to some
        topic and add it to a router to run embedded.
        """
        d = txaio.create_future()

        class TestSession(ApplicationSession):

            def onJoin(self, details):
                d2 = self.subscribe(lambda: None, u'com.example.topic1')

                def ok(_):
                    txaio.resolve(d, None)

                def error(err):
                    txaio.reject(d, err)

                txaio.add_callbacks(d2, ok, error)

        session = TestSession(types.ComponentConfig(u'realm1'))

        self.session_factory.add(session, authrole=u'test_role')

        return d

    def test_publish_closed_session(self):
        """
        ensure a session doesn't get Events if it's closed
        (see also issue #431)
        """
        # we want to trigger a deeply-nested condition in
        # processPublish in class Broker -- lets try w/o refactoring
        # anything first...

        class TestSession(ApplicationSession):
            pass
        session0 = TestSession()
        session1 = TestSession()
        router = mock.MagicMock()
        broker = Broker(router)

        # let's just "cheat" our way a little to the right state by
        # injecting our subscription "directly" (e.g. instead of
        # faking out an entire Subscribe etc. flow
        # ...so we need _subscriptions_map to have at least one
        # subscription (our test one) for the topic we'll publish to
        broker._subscription_map.add_observer(session0, u'test.topic')

        # simulate the session state we want, which is that a
        # transport is connected (._transport != None) but there
        # _session_id *is* None (not joined yet, or left already)
        self.assertIs(None, session0._session_id)
        session0._transport = mock.MagicMock()
        session0._transport.get_channel_id = mock.MagicMock(return_value=b'deadbeef')
        session1._session_id = 1234  # "from" session should look connected + joined
        session1._transport = mock.MagicMock()
        session1._transport.channel_id = b'aaaabeef'

        # here's the main "cheat"; we're faking out the
        # router.authorize because we need it to callback immediately
        router.authorize = mock.MagicMock(return_value=txaio.create_future_success(dict(allow=True, cache=False, disclose=True)))

        # now we scan call "processPublish" such that we get to the
        # condition we're interested in (this "comes from" session1
        # beacuse by default publishes don't go to the same session)
        pubmsg = message.Publish(123, u'test.topic')
        broker.processPublish(session1, pubmsg)

        # neither session should have sent anything on its transport
        self.assertEquals(session0._transport.method_calls, [])
        self.assertEquals(session1._transport.method_calls, [])
コード例 #12
0
ファイル: test_dealer.py プロジェクト: oberstet/crossbar
class TestDealer(unittest.TestCase):
    """
    """

    def setUp(self):
        """
        Setup router and router session factories.
        """

        # create a router factory
        self.router_factory = RouterFactory()

        # start a realm
        self.realm = RouterRealm(u'realm-001', {u'name': u'realm1'})
        self.router_factory.start_realm(self.realm)

        # allow everything
        self.router = self.router_factory.get(u'realm1')
        self.router.add_role(
            RouterRoleStaticAuth(
                self.router,
                u'test_role',
                default_permissions={
                    u'uri': u'com.example.',
                    u'match': u'prefix',
                    u'allow': {
                        u'call': True,
                        u'register': True,
                        u'publish': True,
                        u'subscribe': True,
                    }
                }
            )
        )

        # create a router session factory
        self.session_factory = RouterSessionFactory(self.router_factory)

    def tearDown(self):
        pass

    @defer.inlineCallbacks
    def test_outstanding_invoke(self):
        """
        When a call is pending and the callee goes away, it cancels the
        in-flight call
        """

        session = mock.Mock()
        session._realm = u'realm1'
        self.router.authorize = mock.Mock(
            return_value=defer.succeed({u'allow': True, u'disclose': True})
        )
        rap = RouterApplicationSession(session, self.router_factory)

        rap.send(message.Hello(u"realm1", {u'caller': role.RoleCallerFeatures()}))
        rap.send(message.Register(1, u'foo'))

        # we can retrieve the Registration via
        # session.mock_calls[-1][1][0] if req'd

        # re-set the authorize, as the Deferred from above is already
        # used-up and it gets called again to authorize the Call
        self.router.authorize = mock.Mock(
            return_value=defer.succeed({u'allow': True, u'disclose': True})
        )
        rap.send(message.Call(42, u'foo'))

        orig = rap.send
        d = defer.Deferred()

        rap.send(message.Goodbye())

        def wrapper(*args, **kw):
            d.callback(args[0])
            return orig(*args, **kw)
        rap.send = wrapper

        # we can do this *after* the call to send() the Goodbye
        # (above) because it takes a reactor-turn to actually
        # process the cancel/errors etc -- hence the Deferred and
        # yield in this test...

        msg = yield d

        self.assertEqual(42, msg.request)
        self.assertEqual(u'wamp.error.canceled', msg.error)
コード例 #13
0
ファイル: test_broker.py プロジェクト: NinjaMSP/crossbar
class TestBrokerPublish(unittest.TestCase):
    """
    Tests for crossbar.router.broker.Broker
    """

    def setUp(self):
        """
        Setup router and router session factories.
        """

        # create a router factory
        self.router_factory = RouterFactory(None, None)

        # start a realm
        self.realm = RouterRealm(None, {u'name': u'realm1'})
        self.router_factory.start_realm(self.realm)

        # allow everything
        self.router = self.router_factory.get(u'realm1')
        self.router.add_role(
            RouterRoleStaticAuth(
                self.router,
                u'test_role',
                default_permissions={
                    u'uri': u'com.example.',
                    u'match': u'prefix',
                    u'allow': {
                        u'call': True,
                        u'register': True,
                        u'publish': True,
                        u'subscribe': True,
                    }
                }
            )
        )

        # create a router session factory
        self.session_factory = RouterSessionFactory(self.router_factory)

    def tearDown(self):
        pass

    def test_add(self):
        """
        Create an application session and add it to a router to
        run embedded.
        """
        d = txaio.create_future()

        class TestSession(ApplicationSession):

            def onJoin(self, details):
                txaio.resolve(d, None)

        session = TestSession(types.ComponentConfig(u'realm1'))

        self.session_factory.add(session)

        return d

    def test_application_session_internal_error(self):
        """
        simulate an internal error triggering the 'onJoin' error-case from
        RouterApplicationSession's send() method (from the Hello msg)
        """
        # setup
        the_exception = RuntimeError("sadness")
        errors = []

        class TestSession(ApplicationSession):
            def onJoin(self, *args, **kw):
                raise the_exception

            def onUserError(self, fail, msg):
                errors.append((fail, msg))

        session = TestSession(types.ComponentConfig(u'realm1'))
        from crossbar.router.session import RouterApplicationSession

        # Note to self: original code was logging directly in
        # RouterApplicationSession -- which *may* actually be better?
        # or not...
        with mock.patch.object(RouterApplicationSession, 'log') as logger:
            # this should call onJoin, triggering our error
            self.session_factory.add(session)

            if True:
                self.assertEqual(1, len(errors), "Didn't see our error")
                self.assertEqual(the_exception, errors[0][0].value)

            else:
                # check we got the right log.failure() call
                self.assertTrue(len(logger.method_calls) > 0)
                call = logger.method_calls[0]
                # for a MagicMock call-object, 0th thing is the method-name, 1st
                # thing is the arg-tuple, 2nd thing is the kwargs.
                self.assertEqual(call[0], 'failure')
                self.assertEqual(call[1][0].value, the_exception)

    def test_router_session_internal_error_onHello(self):
        """
        similar to above, but during _RouterSession's onMessage handling,
        where it calls self.onHello
        """
        # setup
        transport = mock.MagicMock()
        transport.get_channel_id = mock.MagicMock(return_value=b'deadbeef')
        the_exception = RuntimeError("kerblam")

        def boom(*args, **kw):
            raise the_exception
        session = self.session_factory()  # __call__ on the _RouterSessionFactory
        session.onHello = boom
        session.onOpen(transport)
        msg = message.Hello(u'realm1', dict(caller=role.RoleCallerFeatures()))

        # XXX think: why isn't this using _RouterSession.log?
        from crossbar.router.session import RouterSession
        with mock.patch.object(RouterSession, 'log') as logger:
            # do the test; should call onHello which is now "boom", above
            session.onMessage(msg)

            # check we got the right log.failure() call
            self.assertTrue(len(logger.method_calls) > 0)
            call = logger.method_calls[0]
            # for a MagicMock call-object, 0th thing is the method-name, 1st
            # thing is the arg-tuple, 2nd thing is the kwargs.
            self.assertEqual(call[0], 'failure')
            self.assertTrue('failure' in call[2])
            self.assertEqual(call[2]['failure'].value, the_exception)

    def test_router_session_internal_error_onAuthenticate(self):
        """
        similar to above, but during _RouterSession's onMessage handling,
        where it calls self.onAuthenticate)
        """
        # setup
        transport = mock.MagicMock()
        transport.get_channel_id = mock.MagicMock(return_value=b'deadbeef')
        the_exception = RuntimeError("kerblam")

        def boom(*args, **kw):
            raise the_exception
        session = self.session_factory()  # __call__ on the _RouterSessionFactory
        session.onAuthenticate = boom
        session.onOpen(transport)
        msg = message.Authenticate(u'bogus signature')

        # do the test; should call onHello which is now "boom", above
        session.onMessage(msg)

        errors = self.flushLoggedErrors()
        self.assertEqual(1, len(errors), "Expected just one error: {}".format(errors))
        self.assertTrue(the_exception in [fail.value for fail in errors])

    def test_router_session_goodbye_custom_message(self):
        """
        Reason should be propagated properly from Goodbye message
        """
        from crossbar.router.session import RouterApplicationSession
        session = mock.Mock()
        session._realm = u'realm'
        router_factory = mock.Mock()
        rap = RouterApplicationSession(session, router_factory)

        rap.send(message.Hello(u'realm', {u'caller': role.RoleCallerFeatures()}))
        session.reset_mock()
        rap.send(message.Goodbye(u'wamp.reason.logout', u'some custom message'))

        leaves = [call for call in session.mock_calls if call[0] == 'onLeave']
        self.assertEqual(1, len(leaves))
        details = leaves[0][1][0]
        self.assertEqual(u'wamp.reason.logout', details.reason)
        self.assertEqual(u'some custom message', details.message)

    def test_router_session_goodbye_onLeave_error(self):
        """
        Reason should be propagated properly from Goodbye message
        """
        from crossbar.router.session import RouterApplicationSession
        session = mock.Mock()
        the_exception = RuntimeError("onLeave fails")

        def boom(*args, **kw):
            raise the_exception
        session.onLeave = mock.Mock(side_effect=boom)
        session._realm = u'realm'
        router_factory = mock.Mock()
        rap = RouterApplicationSession(session, router_factory)

        rap.send(message.Hello(u'realm', {u'caller': role.RoleCallerFeatures()}))
        session.reset_mock()
        rap.send(message.Goodbye(u'wamp.reason.logout', u'some custom message'))

        errors = self.flushLoggedErrors()
        self.assertEqual(1, len(errors))
        self.assertEqual(the_exception, errors[0].value)

    def test_router_session_goodbye_fire_disconnect_error(self):
        """
        Reason should be propagated properly from Goodbye message
        """
        from crossbar.router.session import RouterApplicationSession
        session = mock.Mock()
        the_exception = RuntimeError("sad times at ridgemont high")

        def boom(*args, **kw):
            if args[0] == 'disconnect':
                return defer.fail(the_exception)
            return defer.succeed(None)
        session.fire = mock.Mock(side_effect=boom)
        session._realm = u'realm'
        router_factory = mock.Mock()
        rap = RouterApplicationSession(session, router_factory)

        rap.send(message.Hello(u'realm', {u'caller': role.RoleCallerFeatures()}))
        session.reset_mock()
        rap.send(message.Goodbye(u'wamp.reason.logout', u'some custom message'))

        errors = self.flushLoggedErrors()
        self.assertEqual(1, len(errors))
        self.assertEqual(the_exception, errors[0].value)

    def test_router_session_lifecycle(self):
        """
        We see all 'lifecycle' notifications.
        """
        from crossbar.router.session import RouterApplicationSession

        def mock_fire(name, *args, **kw):
            fired.append(name)
            return defer.succeed(None)

        fired = []
        session = mock.Mock()
        session._realm = u'realm'
        session.fire = mock.Mock(side_effect=mock_fire)
        router_factory = mock.Mock()
        rap = RouterApplicationSession(session, router_factory)

        # we never fake out the 'Welcome' message, so there will be no
        # 'ready' notification...
        rap.send(message.Hello(u'realm', {u'caller': role.RoleCallerFeatures()}))
        rap.send(message.Goodbye(u'wamp.reason.logout', u'some custom message'))

        self.assertTrue('connect' in fired)
        self.assertTrue('join' in fired)
        self.assertTrue('ready' in fired)
        self.assertTrue('leave' in fired)
        self.assertTrue('disconnect' in fired)

    def test_add_and_subscribe(self):
        """
        Create an application session that subscribes to some
        topic and add it to a router to run embedded.
        """
        d = txaio.create_future()

        class TestSession(ApplicationSession):

            def onJoin(self, details):
                d2 = self.subscribe(lambda: None, u'com.example.topic1')

                def ok(_):
                    txaio.resolve(d, None)

                def error(err):
                    txaio.reject(d, err)

                txaio.add_callbacks(d2, ok, error)

        session = TestSession(types.ComponentConfig(u'realm1'))

        self.session_factory.add(session, authrole=u'test_role')

        return d

    def test_publish_closed_session(self):
        """
        ensure a session doesn't get Events if it's closed
        (see also issue #431)
        """
        # we want to trigger a deeply-nested condition in
        # processPublish in class Broker -- lets try w/o refactoring
        # anything first...

        class TestSession(ApplicationSession):
            pass
        session0 = TestSession()
        session1 = TestSession()
        router = mock.MagicMock()
        router.new_correlation_id = lambda: u'fake correlation id'
        broker = Broker(router, reactor)

        # let's just "cheat" our way a little to the right state by
        # injecting our subscription "directly" (e.g. instead of
        # faking out an entire Subscribe etc. flow
        # ...so we need _subscriptions_map to have at least one
        # subscription (our test one) for the topic we'll publish to
        broker._subscription_map.add_observer(session0, u'test.topic')

        # simulate the session state we want, which is that a
        # transport is connected (._transport != None) but there
        # _session_id *is* None (not joined yet, or left already)
        self.assertIs(None, session0._session_id)
        session0._transport = mock.MagicMock()
        session0._transport.get_channel_id = mock.MagicMock(return_value=b'deadbeef')
        session1._session_id = 1234  # "from" session should look connected + joined
        session1._transport = mock.MagicMock()
        session1._transport.channel_id = b'aaaabeef'

        # here's the main "cheat"; we're faking out the
        # router.authorize because we need it to callback immediately
        router.authorize = mock.MagicMock(return_value=txaio.create_future_success(dict(allow=True, cache=False, disclose=True)))

        # now we scan call "processPublish" such that we get to the
        # condition we're interested in (this "comes from" session1
        # beacuse by default publishes don't go to the same session)
        pubmsg = message.Publish(123, u'test.topic')
        broker.processPublish(session1, pubmsg)

        # neither session should have sent anything on its transport
        self.assertEquals(session0._transport.method_calls, [])
        self.assertEquals(session1._transport.method_calls, [])

    def test_publish_traced_events(self):
        """
        with two subscribers and message tracing the last event should
        have a magic flag
        """
        # we want to trigger a deeply-nested condition in
        # processPublish in class Broker -- lets try w/o refactoring
        # anything first...

        class TestSession(ApplicationSession):
            pass
        session0 = TestSession()
        session1 = TestSession()
        session2 = TestSession()
        router = mock.MagicMock()
        router.send = mock.Mock()
        router.new_correlation_id = lambda: u'fake correlation id'
        router.is_traced = True
        broker = Broker(router, reactor)

        # let's just "cheat" our way a little to the right state by
        # injecting our subscription "directly" (e.g. instead of
        # faking out an entire Subscribe etc. flow
        # ...so we need _subscriptions_map to have at least one
        # subscription (our test one) for the topic we'll publish to
        broker._subscription_map.add_observer(session0, u'test.topic')
        broker._subscription_map.add_observer(session1, u'test.topic')

        session0._session_id = 1000
        session0._transport = mock.MagicMock()
        session0._transport.get_channel_id = mock.MagicMock(return_value=b'deadbeef')

        session1._session_id = 1001
        session1._transport = mock.MagicMock()
        session1._transport.get_channel_id = mock.MagicMock(return_value=b'deadbeef')

        session2._session_id = 1002
        session2._transport = mock.MagicMock()
        session2._transport.get_channel_id = mock.MagicMock(return_value=b'deadbeef')

        # here's the main "cheat"; we're faking out the
        # router.authorize because we need it to callback immediately
        router.authorize = mock.MagicMock(return_value=txaio.create_future_success(dict(allow=True, cache=False, disclose=True)))

        # now we scan call "processPublish" such that we get to the
        # condition we're interested in (this "comes from" session1
        # beacuse by default publishes don't go to the same session)
        pubmsg = message.Publish(123, u'test.topic')
        broker.processPublish(session2, pubmsg)

        # extract all the event calls
        events = [
            call[1][1]
            for call in router.send.mock_calls
            if call[1][0] in [session0, session1, session2]
        ]

        self.assertEqual(2, len(events))
        self.assertFalse(events[0].correlation_is_last)
        self.assertTrue(events[1].correlation_is_last)

    def test_publish_traced_events_batched(self):
        """
        with two subscribers and message tracing the last event should
        have a magic flag
        """
        # we want to trigger a deeply-nested condition in
        # processPublish in class Broker -- lets try w/o refactoring
        # anything first...

        class TestSession(ApplicationSession):
            pass
        session0 = TestSession()
        session1 = TestSession()
        session2 = TestSession()
        session3 = TestSession()
        session4 = TestSession()
        # NOTE! We ensure that "session0" (the publishing session) is
        # *last* in the observation-list to trigger a (now fixed)
        # edge-case)
        sessions = [session1, session2, session3, session4, session0]
        router = mock.MagicMock()
        router.send = mock.Mock()
        router.new_correlation_id = lambda: u'fake correlation id'
        router.is_traced = True
        clock = Clock()
        with replace_loop(clock):
            broker = Broker(router, clock)
            broker._options.event_dispatching_chunk_size = 2

            # to ensure we get "session0" last, we turn on ordering in
            # the observations
            broker._subscription_map._ordered = 1

            # let's just "cheat" our way a little to the right state by
            # injecting our subscription "directly" (e.g. instead of
            # faking out an entire Subscribe etc. flow
            # ...so we need _subscriptions_map to have at least one
            # subscription (our test one) for the topic we'll publish to
            for session in sessions:
                broker._subscription_map.add_observer(session, u'test.topic')

            for i, sess in enumerate(sessions):
                sess._session_id = 1000 + i
                sess._transport = mock.MagicMock()
                sess._transport.get_channel_id = mock.MagicMock(return_value=b'deadbeef')

            # here's the main "cheat"; we're faking out the
            # router.authorize because we need it to callback immediately
            router.authorize = mock.MagicMock(return_value=txaio.create_future_success(dict(allow=True, cache=False, disclose=True)))

            # now we scan call "processPublish" such that we get to the
            # condition we're interested in; should go to all sessions
            # except session0
            pubmsg = message.Publish(123, u'test.topic')
            broker.processPublish(session0, pubmsg)
            clock.advance(1)
            clock.advance(1)

            # extract all the event calls
            events = [
                call[1][1]
                for call in router.send.mock_calls
                if call[1][0] in [session0, session1, session2, session3, session4]
            ]

            # all except session0 should have gotten an event, and
            # session4's should have the "last" flag set
            self.assertEqual(4, len(events))
            self.assertFalse(events[0].correlation_is_last)
            self.assertFalse(events[1].correlation_is_last)
            self.assertFalse(events[2].correlation_is_last)
            self.assertTrue(events[3].correlation_is_last)
コード例 #14
0
class TestEmbeddedSessions(unittest.TestCase):

    """
    Test cases for application session running embedded in router.
    """

    def setUp(self):
        """
        Setup router and router session factories.
        """

        # create a router factory
        self.router_factory = RouterFactory()

        # start a realm
        self.router_factory.start_realm(RouterRealm(None, {u'name': u'realm1'}))

        # allow everything
        permissions = RouterPermissions('', True, True, True, True, True)
        router = self.router_factory.get(u'realm1')
        router.add_role(RouterRoleStaticAuth(router, None, default_permissions=permissions))

        # create a router session factory
        self.session_factory = RouterSessionFactory(self.router_factory)

    def tearDown(self):
        pass

    def test_add(self):
        """
        Create an application session and add it to a router to
        run embedded.
        """
        d = txaio.create_future()

        class TestSession(ApplicationSession):

            def onJoin(self, details):
                txaio.resolve(d, None)

        session = TestSession(types.ComponentConfig(u'realm1'))

        self.session_factory.add(session)

        return d

    def test_add_and_subscribe(self):
        """
        Create an application session that subscribes to some
        topic and add it to a router to run embedded.
        """
        d = txaio.create_future()

        class TestSession(ApplicationSession):

            def onJoin(self, details):
                # noinspection PyUnusedLocal
                def on_event(*arg, **kwargs):
                    pass

                d2 = self.subscribe(on_event, u'com.example.topic1')

                def ok(_):
                    txaio.resolve(d, None)

                def error(err):
                    txaio.reject(d, err)

                txaio.add_callbacks(d2, ok, error)

        session = TestSession(types.ComponentConfig(u'realm1'))

        self.session_factory.add(session)

        return d
コード例 #15
0
ファイル: test_caller.py プロジェクト: vpineda7/crossbar
class CallerTestCase(TestCase):
    """
    Unit tests for L{CallerResource}.
    """
    def setUp(self):

        # create a router factory
        self.router_factory = RouterFactory(None, None)

        # start a realm
        self.realm = RouterRealm(None, {u'name': u'realm1'})
        self.router_factory.start_realm(self.realm)

        # allow everything
        self.router = self.router_factory.get(u'realm1')
        self.router.add_role(
            RouterRoleStaticAuth(self.router,
                                 u'test_role',
                                 default_permissions={
                                     u'uri': u'com.myapp.',
                                     u'match': u'prefix',
                                     u'allow': {
                                         u'call': True,
                                         u'register': True,
                                         u'publish': True,
                                         u'subscribe': True,
                                     }
                                 }))

        # create a router session factory
        self.session_factory = RouterSessionFactory(self.router_factory)

    @inlineCallbacks
    def test_add2(self):
        """
        Test a very basic call where you square root a number. This has one
        arg, no kwargs, and no authorisation.
        """
        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)

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

        self.assertEqual(request.code, 200)
        self.assertEqual(json.loads(native_string(request.get_written_data())),
                         {"args": [1.4142135623730951]})

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

    @inlineCallbacks
    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()

    @inlineCallbacks
    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()

    @inlineCallbacks
    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)

    @inlineCallbacks
    def test_no_body(self):
        """
        Test that calls with no 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"]})

        self.assertEqual(request.code, 400)

        errors = l.get_category("AR453")
        self.assertEqual(len(errors), 1)
        self.assertEqual(errors[0]["code"], 400)
コード例 #16
0
class TestEmbeddedSessions(unittest.TestCase):
    """
    Test cases for application session running embedded in router.
    """

    def setUp(self):
        """
        Setup router and router session factories.
        """

        # create a router factory
        self.router_factory = RouterFactory(u'mynode')

        # start a realm
        self.router_factory.start_realm(RouterRealm(None, {u'name': u'realm1'}))

        # allow everything
        default_permissions = {
            u'uri': u'',
            u'match': u'prefix',
            u'allow': {
                u'call': True,
                u'register': True,
                u'publish': True,
                u'subscribe': True
            }
        }
        self.router = self.router_factory.get(u'realm1')
        self.router.add_role(RouterRoleStaticAuth(self.router, u'test_role', default_permissions=default_permissions))
        self.router.add_role(RouterRoleStaticAuth(self.router, None, default_permissions=default_permissions))

        # create a router session factory
        self.session_factory = RouterSessionFactory(self.router_factory)

    def tearDown(self):
        pass

    def test_authorize_exception_call(self):
        """
        When a dynamic authorizor throws an exception (during processCall)
        we log it.
        """
        the_exception = RuntimeError("authorizer bug")

        def boom(*args, **kw):
            raise the_exception
        self.router._roles[u'test_role'].authorize = boom

        class TestSession(ApplicationSession):
            def __init__(self, *args, **kw):
                super(TestSession, self).__init__(*args, **kw)
                self._authrole = u'test_role'
                self._transport = mock.MagicMock()
        session0 = TestSession()
        self.router._dealer._registration_map.add_observer(session0, u'test.proc')

        # okay, we have an authorizer that will always explode and a
        # single procedure registered; when we call it, then
        # on_authorize_error (in dealer.py) should get called and our
        # error logged.

        call = message.Call(
            request=1234,
            procedure=u'test.proc',
            args=tuple(),
            kwargs=dict(),
        )
        # this should produce an error -- however processCall doesn't
        # itself return the Deferred, so we look for the side-effect
        # -- the router should have tried to send a message.Error (and
        # we should also have logged the error).
        self.router._dealer.processCall(session0, call)

        self.assertEqual(1, len(session0._transport.mock_calls))
        call = session0._transport.mock_calls[0]
        self.assertEqual('send', call[0])
        # ensure we logged our error (flushLoggedErrors also causes
        # trial to *not* fail the unit-test despite an error logged)
        errors = self.flushLoggedErrors()
        self.assertTrue(the_exception in [fail.value for fail in errors])

    def test_authorize_exception_register(self):
        """
        When a dynamic authorizor throws an exception (during processRegister)
        we log it.
        """
        the_exception = RuntimeError("authorizer bug")

        def boom(*args, **kw):
            raise the_exception
        self.router._roles[u'test_role'].authorize = boom

        class TestSession(ApplicationSession):
            def __init__(self, *args, **kw):
                super(TestSession, self).__init__(*args, **kw)
                self._authrole = u'test_role'
                self._transport = mock.MagicMock()
        session0 = TestSession()

        call = message.Register(
            request=1234,
            procedure=u'test.proc_reg',
        )
        # this should produce an error -- however processCall doesn't
        # itself return the Deferred, so we look for the side-effect
        # -- the router should have tried to send a message.Error (and
        # we should also have logged the error).
        self.router._dealer.processRegister(session0, call)

        self.assertEqual(1, len(session0._transport.mock_calls))
        call = session0._transport.mock_calls[0]
        self.assertEqual('send', call[0])
        # ensure we logged our error (flushLoggedErrors also causes
        # trial to *not* fail the unit-test despite an error logged)
        errors = self.flushLoggedErrors()
        self.assertTrue(the_exception in [fail.value for fail in errors])

    def test_add(self):
        """
        Create an application session and add it to a router to
        run embedded.
        """
        d = txaio.create_future()

        class TestSession(ApplicationSession):

            def onJoin(self, details):
                txaio.resolve(d, None)

        session = TestSession(types.ComponentConfig(u'realm1'))

        self.session_factory.add(session)

        return d

    def test_application_session_internal_error(self):
        """
        simulate an internal error triggering the 'onJoin' error-case from
        _RouterApplicationSession's send() method (from the Hello msg)
        """
        # setup
        the_exception = RuntimeError("sadness")
        errors = []

        class TestSession(ApplicationSession):
            def onJoin(self, *args, **kw):
                raise the_exception

            def onUserError(self, *args, **kw):
                errors.append((args, kw))
        session = TestSession(types.ComponentConfig(u'realm1'))

        # in this test, we are just looking for onUserError to get
        # called so we don't need to patch the logger. this should
        # call onJoin, triggering our error
        self.session_factory.add(session)

        # check we got the right log.failure() call
        self.assertTrue(len(errors) > 0, "expected onUserError call")
        fail = errors[0][0][0]
        self.assertTrue(fail.value == the_exception)

    def test_router_session_internal_error_onHello(self):
        """
        similar to above, but during _RouterSession's onMessage handling,
        where it calls self.onHello
        """
        # setup
        transport = mock.MagicMock()
        transport.get_channel_id = mock.MagicMock(return_value=b'deadbeef')
        the_exception = RuntimeError("kerblam")

        def boom(*args, **kw):
            raise the_exception
        session = self.session_factory()  # __call__ on the _RouterSessionFactory
        session.onHello = boom
        session.onOpen(transport)
        msg = message.Hello(u'realm1', dict(caller=role.RoleCallerFeatures()))

        # XXX think: why isn't this using _RouterSession.log?
        from crossbar.router.session import RouterSession
        with mock.patch.object(RouterSession, 'log') as logger:
            # do the test; should call onHello which is now "boom", above
            session.onMessage(msg)

            # check we got the right log.failure() call
            self.assertTrue(len(logger.method_calls) > 0)
            call = logger.method_calls[0]
            # for a MagicMock call-object, 0th thing is the method-name, 1st
            # thing is the arg-tuple, 2nd thing is the kwargs.
            self.assertEqual(call[0], 'failure')
            self.assertTrue('failure' in call[2])
            self.assertEqual(call[2]['failure'].value, the_exception)

    def test_router_session_internal_error_onAuthenticate(self):
        """
        similar to above, but during _RouterSession's onMessage handling,
        where it calls self.onAuthenticate)
        """
        # setup
        transport = mock.MagicMock()
        transport.get_channel_id = mock.MagicMock(return_value=b'deadbeef')
        the_exception = RuntimeError("kerblam")

        def boom(*args, **kw):
            raise the_exception
        session = self.session_factory()  # __call__ on the _RouterSessionFactory
        session.onAuthenticate = boom
        session.onOpen(transport)
        msg = message.Authenticate(u'bogus signature')

        # XXX think: why isn't this using _RouterSession.log?
        from crossbar.router.session import RouterSession
        with mock.patch.object(RouterSession, 'log') as logger:
            # do the test; should call onHello which is now "boom", above
            session.onMessage(msg)

            # check we got the right log.failure() call
            self.assertTrue(len(logger.method_calls) > 0)
            call = logger.method_calls[0]
            # for a MagicMock call-object, 0th thing is the method-name, 1st
            # thing is the arg-tuple, 2nd thing is the kwargs.
            self.assertEqual(call[0], 'failure')
            self.assertTrue('failure' in call[2])
            self.assertEqual(call[2]['failure'].value, the_exception)

    def test_add_and_subscribe(self):
        """
        Create an application session that subscribes to some
        topic and add it to a router to run embedded.
        """
        d = txaio.create_future()

        class TestSession(ApplicationSession):
            def onJoin(self, details):
                # noinspection PyUnusedLocal
                def on_event(*arg, **kwargs):
                    pass

                d2 = self.subscribe(on_event, u'com.example.topic1')

                def ok(_):
                    txaio.resolve(d, None)

                def error(err):
                    txaio.reject(d, err)

                txaio.add_callbacks(d2, ok, error)

        session = TestSession(types.ComponentConfig(u'realm1'))

        self.session_factory.add(session)

        return d
コード例 #17
0
ファイル: test_caller.py プロジェクト: NinjaMSP/crossbar
class CallerTestCase(TestCase):
    """
    Unit tests for L{CallerResource}.
    """
    def setUp(self):

        # create a router factory
        self.router_factory = RouterFactory(None, None)

        # start a realm
        self.realm = RouterRealm(None, {u'name': u'realm1'})
        self.router_factory.start_realm(self.realm)

        # allow everything
        self.router = self.router_factory.get(u'realm1')
        self.router.add_role(
            RouterRoleStaticAuth(
                self.router,
                u'test_role',
                default_permissions={
                    u'uri': u'com.myapp.',
                    u'match': u'prefix',
                    u'allow': {
                        u'call': True,
                        u'register': True,
                        u'publish': True,
                        u'subscribe': True,
                    }
                }
            )
        )

        # create a router session factory
        self.session_factory = RouterSessionFactory(self.router_factory)

    @inlineCallbacks
    def test_add2(self):
        """
        Test a very basic call where you square root a number. This has one
        arg, no kwargs, and no authorisation.
        """
        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)

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

        self.assertEqual(request.code, 200)
        self.assertEqual(json.loads(native_string(request.get_written_data())),
                         {"args": [1.4142135623730951]})

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

    @inlineCallbacks
    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()

    @inlineCallbacks
    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()

    @inlineCallbacks
    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)

    @inlineCallbacks
    def test_no_body(self):
        """
        Test that calls with no 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"]})

        self.assertEqual(request.code, 400)

        errors = l.get_category("AR453")
        self.assertEqual(len(errors), 1)
        self.assertEqual(errors[0]["code"], 400)