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 _unsubscribe(self, subscription): """ Called from :meth:`autobahn.wamp.protocol.Subscription.unsubscribe` """ assert (isinstance(subscription, Subscription)) assert subscription.active assert (subscription.id in self._subscriptions) assert (subscription in self._subscriptions[subscription.id]) if not self._transport: raise exception.TransportLost() # remove handler subscription and mark as inactive self._subscriptions[subscription.id].remove(subscription) subscription.active = False # number of handler subscriptions left .. scount = len(self._subscriptions[subscription.id]) if scount == 0: # if the last handler was removed, unsubscribe from broker .. request_id = self._request_id_gen.next() on_reply = txaio.create_future() self._unsubscribe_reqs[request_id] = UnsubscribeRequest( request_id, on_reply, subscription.id) msg = message.Unsubscribe(request_id, subscription.id) self._transport.send(msg) return on_reply else: # there are still handlers active on the subscription! return txaio.create_future_success(scount)
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_as_future_recursive(framework): ''' Returns another Future from as_future ''' errors = [] results = [] calls = [] f1 = txaio.create_future_success(42) def method(*args, **kw): calls.append((args, kw)) return f1 f0 = txaio.as_future(method, 1, 2, 3, key='word') def cb(x): results.append(x) def errback(f): errors.append(f) txaio.add_callbacks(f0, cb, errback) run_once() assert len(results) == 1 assert len(errors) == 0 assert results[0] == 42 assert calls[0] == ((1, 2, 3), dict(key='word'))
def _unsubscribe(self, subscription): """ Called from :meth:`autobahn.wamp.protocol.Subscription.unsubscribe` """ assert(isinstance(subscription, Subscription)) assert subscription.active assert(subscription.id in self._subscriptions) assert(subscription in self._subscriptions[subscription.id]) if not self._transport: raise exception.TransportLost() # remove handler subscription and mark as inactive self._subscriptions[subscription.id].remove(subscription) subscription.active = False # number of handler subscriptions left .. scount = len(self._subscriptions[subscription.id]) if scount == 0: # if the last handler was removed, unsubscribe from broker .. request_id = util.id() on_reply = txaio.create_future() self._unsubscribe_reqs[request_id] = UnsubscribeRequest(request_id, on_reply, subscription.id) msg = message.Unsubscribe(request_id, subscription.id) self._transport.send(msg) return on_reply else: # there are still handlers active on the subscription! return txaio.create_future_success(scount)
def create_connection(protocol_factory=None, server_hostname=None, host=None, port=None, ssl=False): if actual_protocol[0] is None: protocol = protocol_factory() actual_protocol[0] = protocol protocol.connection_made(fake_transport) return txaio.create_future_success((fake_transport, protocol)) else: return txaio.create_future_error(RuntimeError("second connection fails completely"))
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.transport_details = TransportDetails( channel_id={'tls-unique': b'deadbeef'}) session1._session_id = 1234 # "from" session should look connected + joined session1._transport = mock.MagicMock() session1._transport.transport_details = TransportDetails( channel_id={'tls-unique': 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.assertEqual(session0._transport.method_calls, []) self.assertEqual(session1._transport.method_calls, [])
def authorize(self, session, uri, action, options): """ Authorizes a session for an action on an URI. Implements :func:`autobahn.wamp.interfaces.IRouter.authorize` """ assert (type(uri) == str) assert (action in [u'call', u'register', u'publish', u'subscribe']) # the role under which the session that wishes to perform the given action on # the given URI was authenticated under role = session._authrole if role in self._roles: # the authorizer procedure of the role which we will call .. authorize = self._roles[role].authorize d = txaio.as_future(authorize, session, uri, action, options) else: # normally, the role should exist on the router (and hence we should not arrive # here), but the role might have been dynamically removed - and anyway, safety first! d = txaio.create_future_success(False) # XXX would be nicer for dynamic-authorizer authors if we # sanity-checked the return-value ('authorization') here # (i.e. is it a dict? does it have 'allow' in it? does it have # disallowed keys in it?) def got_authorization(authorization): # backward compatibility if isinstance(authorization, bool): authorization = {u'allow': authorization, u'cache': False} if action in [u'call', u'publish']: authorization[u'disclose'] = False auto_disclose_trusted = False if auto_disclose_trusted and role == u'trusted' and action in [ u'call', u'publish' ]: authorization[u'disclose'] = True self.log.debug( "Authorized action '{action}' for URI '{uri}' by session {session_id} with authid '{authid}' and authrole '{authrole}' -> authorization: {authorization}", session_id=session._session_id, uri=uri, action=action, authid=session._authid, authrole=session._authrole, authorization=authorization) return authorization d.addCallback(got_authorization) return d
def authorize(self, session, uri, action, options): """ Authorizes a session for an action on an URI. Implements :func:`autobahn.wamp.interfaces.IRouter.authorize` """ assert(type(uri) == str) assert(action in [u'call', u'register', u'publish', u'subscribe']) # the role under which the session that wishes to perform the given action on # the given URI was authenticated under role = session._authrole if role in self._roles: # the authorizer procedure of the role which we will call .. authorize = self._roles[role].authorize d = txaio.as_future(authorize, session, uri, action, options) else: # normally, the role should exist on the router (and hence we should not arrive # here), but the role might have been dynamically removed - and anyway, safety first! d = txaio.create_future_success(False) # XXX would be nicer for dynamic-authorizer authors if we # sanity-checked the return-value ('authorization') here # (i.e. is it a dict? does it have 'allow' in it? does it have # disallowed keys in it?) def got_authorization(authorization): # backward compatibility if isinstance(authorization, bool): authorization = { u'allow': authorization, u'cache': False } if action in [u'call', u'publish']: authorization[u'disclose'] = False auto_disclose_trusted = False if auto_disclose_trusted and role == u'trusted' and action in [u'call', u'publish']: authorization[u'disclose'] = True self.log.debug("Authorized action '{action}' for URI '{uri}' by session {session_id} with authid '{authid}' and authrole '{authrole}' -> authorization: {authorization}", session_id=session._session_id, uri=uri, action=action, authid=session._authid, authrole=session._authrole, authorization=authorization) return authorization d.addCallback(got_authorization) return d
def test_immediate_result(framework): f = txaio.create_future_success("it worked") results = [] def cb(f): results.append(f) txaio.add_callbacks(f, cb, None) run_once() assert len(results) == 1 assert results[0] == "it worked"
def stop(self): self._stopping = True if self._session and self._session.is_attached(): return self._session.leave() elif self._delay_f: # This cancel request will actually call the "error" callback of # the _delay_f future. Nothing to worry about. return txaio.as_future(txaio.cancel, self._delay_f) # if (for some reason -- should we log warning here to figure # out if this can evern happen?) we've not fired _done_f, we # do that now (causing our "main" to exit, and thus react() to # quit) if not txaio.is_called(self._done_f): txaio.resolve(self._done_f, None) return txaio.create_future_success(None)
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 sign(self, data): """ Sign some data. :param data: The data to be signed. :type data: bytes :returns: The signature. :rtype: bytes """ if not self._is_private: raise Exception("private key required to sign") if type(data) != six.binary_type: raise Exception("data to be signed must be binary") return create_future_success(self._key.sign(data))
def authorize(self, session, uri, action): """ Authorizes a session for an action on an URI. Implements :func:`autobahn.wamp.interfaces.IRouter.authorize` """ assert(type(uri) == six.text_type) assert(action in [u'call', u'register', u'publish', u'subscribe']) # the role under which the session that wishes to perform the given action on # the given URI was authenticated under role = session._authrole if role in self._roles: # the authorizer procedure of the role which we will call .. authorize = self._roles[role].authorize d = txaio.as_future(authorize, session, uri, action) else: # normally, the role should exist on the router (and hence we should not arrive # here), but the role might have been dynamically removed - and anyway, safety first! d = txaio.create_future_success(False) def got_authorization(authorization): # backward compatibility if type(authorization) == bool: authorization = { u'allow': authorization, u'cache': False } if action in [u'call', u'publish']: authorization[u'disclose'] = False self.log.debug("Authorized action '{action}' for URI '{uri}' by session {session_id} with authid '{authid}' and authrole '{authrole}' -> authorization: {authorization}", session_id=session._session_id, uri=uri, action=action, authid=session._authid, authrole=session._authrole, authorization=authorization) return authorization d.addCallback(got_authorization) return d
def authorize(self, session, uri, action): """ Authorizes a session for an action on an URI. Implements :func:`autobahn.wamp.interfaces.IRouter.authorize` """ assert (type(uri) == six.text_type) assert (action in [u'call', u'register', u'publish', u'subscribe']) # the role under which the session that wishes to perform the given action on # the given URI was authenticated under role = session._authrole if role in self._roles: # the authorizer procedure of the role which we will call .. authorize = self._roles[role].authorize d = txaio.as_future(authorize, session, uri, action) else: # normally, the role should exist on the router (and hence we should not arrive # here), but the role might have been dynamically removed - and anyway, safety first! d = txaio.create_future_success(False) def got_authorization(authorization): # backward compatibility if type(authorization) == bool: authorization = {u'allow': authorization, u'cache': False} if action in [u'call', u'publish']: authorization[u'disclose'] = False self.log.info( "Authorized action '{action}' for URI '{uri}' by session {session_id} with authid '{authid}' and authrole '{authrole}' -> authorization: {authorization}", session_id=session._session_id, uri=uri, action=action, authid=session._authid, authrole=session._authrole, authorization=authorization) return authorization d.addCallback(got_authorization) return d
def callRemoteMessage(self, mcall, timeout = None): """ Uses the specified L{message.MethodCallMessage} to call a remote method. @rtype: L{twisted.internet.defer.Deferred} @returns: a Deferred to the result of the remote method call """ assert isinstance(mcall, message.MethodCallMessage) if mcall.expectReply: d = txaio.create_future() if timeout: timeout = txaio.call_later(timeout, self._onMethodTimeout, mcall.serial, d) self._pendingCalls[ mcall.serial ] = (d, timeout) self.sendMessage( mcall ) return d else: self.sendMessage( mcall ) return txaio.create_future_success(None)
def sign(self, data): """ Sign some data. :param data: The data to be signed. :type data: bytes :returns: The signature. :rtype: bytes """ if not self._can_sign: raise Exception("a signing key required to sign") if type(data) != six.binary_type: raise Exception("data to be signed must be binary") # sig is a nacl.signing.SignedMessage sig = self._key.sign(data) # we only return the actual signature! if we return "sig", # it get coerced into the concatenation of message + signature # not sure which order, but we don't want that. we only want # the signature return create_future_success(sig.signature)
def authorize(self, session: ISession, uri: str, action: str, options: Dict[str, Any]): """ Authorizes a session for an action on an URI. Implements :func:`autobahn.wamp.interfaces.IRouter.authorize` """ assert (action in ['call', 'register', 'publish', 'subscribe']) # the realm, authid and authrole under which the session that wishes to perform the # given action on the given URI was authenticated under realm = session._realm # authid = session._authid authrole = session._authrole # the permission of a WAMP client is always determined (only) from # WAMP realm, authrole, URI and action already cache_key = (realm, authrole, uri, action) # if we do have a cache entry, use the authorization cached cached_authorization = self._authorization_cache.get(cache_key, None) # normally, the role should exist on the router (and hence we should not arrive # here), but the role might have been dynamically removed - and anyway, safety first! if authrole in self._roles: if cached_authorization: self.log.debug( '{func} authorization cache entry found key {cache_key}:\n{authorization}', func=hltype(self.authorize), cache_key=hlval(cache_key), authorization=pformat(cached_authorization)) d = txaio.create_future_success(cached_authorization) else: # the authorizer procedure of the role which we will call authorize = self._roles[authrole].authorize d = txaio.as_future(authorize, session, uri, action, options) else: # remove cache entry if cached_authorization: del self._authorization_cache[cache_key] # outright deny, since the role isn't active anymore d = txaio.create_future_success(False) # XXX would be nicer for dynamic-authorizer authors if we # sanity-checked the return-value ('authorization') here # (i.e. is it a dict? does it have 'allow' in it? does it have # disallowed keys in it?) def got_authorization(authorization): # backward compatibility if isinstance(authorization, bool): authorization = {'allow': authorization, 'cache': False} if action in ['call', 'publish']: authorization['disclose'] = False auto_disclose_trusted = True if auto_disclose_trusted and authrole == 'trusted' and action in [ 'call', 'publish' ]: authorization['disclose'] = True if not cached_authorization and authorization.get('cache', False): self._authorization_cache[cache_key] = authorization self.log.debug( '{func} add authorization cache entry for key {cache_key}:\n{authorization}', func=hltype(got_authorization), cache_key=hlval(cache_key), authorization=pformat(authorization)) self.log.debug( "Authorized action '{action}' for URI '{uri}' by session {session_id} with authid '{authid}' and " "authrole '{authrole}' -> authorization: {authorization}", session_id=session._session_id, uri=uri, action=action, authid=session._authid, authrole=session._authrole, authorization=authorization) return authorization d.addCallback(got_authorization) return d
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 getRemoteObject(self, busName, objectPath, interfaces = None, replaceKnownInterfaces = False): """ Creates a L{RemoteDBusObject} instance to represent the specified DBus object. If explicit interfaces are not supplied, DBus object introspection will be used to obtain them automatically. @type busName: C{string} @param busName: Name of the bus exporting the desired object @type objectPath: C{string} @param objectPath: DBus path of the desired object @type interfaces: None, C{string} or L{interface.DBusInterface} or a list of C{string}/L{interface.DBusInterface} @param interfaces: May be None, a single value, or a list of string interface names and/or instances of L{interface.DBusInterface}. If None or any of the specified interface names are unknown, full introspection will be attempted. If interfaces consists of solely of L{interface.DBusInterface} instances and/or known interfacep names, no introspection will be preformed. @type replaceKnownInterfaces: C{bool} @param replaceKnownInterfaces: If True (defaults to False), any interfaces discovered during the introspection process will override any previous, cached values. @rtype: L{twisted.internet.defer.Deferred} @returns: A Deferred to the L{RemoteDBusObject} instance """ weak_id = (busName, objectPath, interfaces) need_introspection = False required_interfaces = set() if interfaces is not None: ifl = list() if not isinstance(interfaces, list): interfaces = [interfaces] for i in interfaces: if isinstance(i, interface.DBusInterface ): ifl.append(i) required_interfaces.add(i.name) else: required_interfaces.add(i) if i in interface.DBusInterface.knownInterfaces: ifl.append( interface.DBusInterface.knownInterfaces[i] ) else: need_introspection = True if not need_introspection: return txaio.create_future_success( RemoteDBusObject( self, busName, objectPath, ifl ) ) d = self.conn.introspectRemoteObject( busName, objectPath, replaceKnownInterfaces ) def ok( ifaces ): missing = required_interfaces - set( [ q.name for q in ifaces ] ) if missing: raise error.IntrospectionFailed('Introspection failed to find interfaces: ' + ','.join(missing)) prox = RemoteDBusObject( self, busName, objectPath, ifaces ) self._weakProxies[ weak_id ] = prox return prox txaio.add_callbacks(d, ok, None) return d
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)
def test_is_called(framework): f = txaio.create_future_success(None) assert txaio.is_called(f)