Commits

Alexander Shorin  committed 6c04523

Refactor XMPPWSGI server: split XMPP driver interface from server logic.

Introduced XmppWsgiServerInterface class which should be used to implement
interface to additional XMPP libraries and to allow XmppWsgiServer communicate
with them.

Also this tricks helps(should) with testing due to we could easily be abstracted
from any XMPP realisaion (see MockXmppWsgiServerInterface) and wouldn't bind
XMPP library and XmppFlask app.

Additionaly, XmppWsgiServer environ has extended by new variables:
- APP_JID: XmppFlask app bounded full JID. App should know his own JID.
- APP_MUCNICK: XmppFlask app MUC nick if message type is groupchat.
- XMPP_ID: XMPP stanza id. If not specified by income event, it would be
created by using 'xmppflask' as prefix and uuid1 first 8 bytes as suffix.
- XMPP_DELAY: Shows delay in seconds between current UTC time and point when
income event was occurred.

  • Participants
  • Parent commits 92a1f3c

Comments (0)

Files changed (4)

File xmppflask/run.py

 import getpass
 import os
 import sys
-from .server import XmpppyServer, SleekXmppServer
+from xmppflask.server import XmppWsgiServer
+from xmppflask.ext.xmpppy import XmpppyWsgiInterface
+from xmppflask.ext.sleekxmpp import SleekXmppWsgiInterface
 
 _HELP = '''
 usage: %(name)s [-h] [--jid=JID] [--password=PASSWORD] [--engine=ENGINE] app
         raise ValueError('Unknown xmpp engine %s' % engine)
 
 def run_xmpppy_server(app, jid, pwd):
-    server = XmpppyServer(app)
+    server = XmppWsgiServer(app, XmpppyWsgiInterface())
     server.connect(jid, pwd)
     server.serve_forever()
 
 def run_sleek_server(app, jid, pwd):
-    server = SleekXmppServer(app)
+    server = XmppWsgiServer(app, SleekXmppWsgiInterface())
     server.connect(jid, pwd)
     server.serve_forever()
 

File xmppflask/server.py

     :license: BSD.
 """
 
+import time
+import uuid
 from collections import namedtuple
 from pprint import pformat
 
 JID = namedtuple('JID', 'user,domain,resource')
 
-class XmppWsgiServer(object):
-    """Base XMPPWSGI server."""
+def make_event_id(prefix):
+    return '%s_%s' % (prefix, str(uuid.uuid1()).split('-')[0])
 
-    #: Proxy to XMPP module
-    module = None
-    #: Proxy to active XMPP client instance
-    client = None
+def maybe_unicode(value):
+    if value is None:
+        return value
+    if not value:
+        return None
+    return unicode(value)
+
+class XmppWsgiServerInterface(object):
+    """Base XMPP interface for server."""
+    #: Proxy to XMPP module. For interface internal usage only.
+    _module = None
+    #: Proxy to active XMPP client instance. For interface internal usage only.
+    _client = None
+
     message_class = None
     presence_class = None
     iq_class = None
 
-    def __init__(self, app):
-        self.app = app
+    def __init__(self):
+        self.event_handlers = {}
 
-    def _connect(self, *args, **kwargs):
-        """Handles internal connection routine specific."""
-        raise NotImplementedError
+    def register_handler(self, event, func):
+        self.event_handlers[event] = func
 
     def make_environ(self, event):
         """Creates XMPPWSGI environ.
 
         :param event: XMPP event that would be base for environ.
         """
-        environ = {}
         if isinstance(event, self.message_class):
             event_type = u'message'
         elif isinstance(event, self.presence_class):
             event_type = u'presence'
         elif isinstance(event, self.iq_class):
             event_type = u'iq'
-        environ['XMPP_EVENT'] = event_type
+        else:
+            event_type = None
+
+        environ = {
+            #: XmppFlask app bounded full JID.
+            'APP_JID': None,
+            #: XmppFlask app MUC nick if message type is groupchat.
+            'APP_MUCNICK': None,
+            #: XMPP stanza id.
+            'XMPP_ID': make_event_id('xmppflask'),
+            #: XMPP stanza type: message, presence or iq.
+            'XMPP_EVENT': event_type,
+            #: Bare JID of stanza sender.
+            'XMPP_JID': None,
+            #: XMPP event receiving UTC timestamp.
+            'XMPP_TIMESTAMP': time.time(),
+            #: XMPP event delay in seconds since time of receiving.
+            'XMPP_DELAY': 0
+        }
         return environ
 
+    def connect(self, *args, **kwargs):
+        """Should establish connection for specified credentials."""
+        raise NotImplementedError
+
+    def serve_forever(self):
+        """Should implement infinity loop if needed."""
+        raise NotImplementedError
+
+    def send_message(self, environ, payload):
+        """Sends XMPP message stanza. Should be implemented in interface
+        realization.
+
+        :param environ: XMPPWSGI environ.
+        :type environ: dict
+
+        :param payload: Message payload data.
+        :type payload: dict
+        """
+        raise NotImplementedError
+
+    def send_presence(self, environ, payload):
+        """Sends XMPP presence stanza. Should be implemented in interface
+        realization.
+
+        :param environ: XMPPWSGI environ.
+        :type environ: dict
+
+        :param payload: Presence payload data.
+        :type payload: dict
+        """
+        raise NotImplementedError
+
+    def send_iq(self, environ, payload):
+        """Sends XMPP iq stanza. Should be implemented in interface
+        realization.
+
+        :param environ: XMPPWSGI environ.
+        :type environ: dict
+
+        :param payload: IQ payload data.
+        :type payload: dict
+        """
+        raise NotImplementedError
+
+
+class XmppWsgiServer(object):
+    """Base XMPPWSGI server."""
+
+    def __init__(self, app, xmpp_interface):
+        self.app = app
+        self.xmpp = xmpp_interface
+        self.xmpp.register_handler('message', self.handle_message)
+        self.xmpp.register_handler('presence', self.handle_presence)
+        self.xmpp.register_handler('iq', self.handle_iq)
+
     def connect(self, jid, pwd, use_tls=True, use_ssl=False):
         """Activates connection for XMPPWSGI server."""
-        self.app.logger.info('connecting...')
-        self._connect(jid, pwd, use_tls, use_ssl)
+        self.xmpp.connect(jid, pwd, use_tls, use_ssl)
 
     def serve_forever(self):
-        raise NotImplementedError
+        self.xmpp.serve_forever()
 
     def dispatch_app_response(self, environ, response):
         """Response object dispatcher."""
 
     def handle_message(self, message):
         """Handles message events."""
-        environ = self.make_environ(message)
+        environ = self.xmpp.make_environ(message)
         self.app.logger.debug(pformat(environ))
-        if not environ['MESSAGE']:
+        if not environ['body']:
+            return
+        if environ['XMPP_TIMESTAMP'] < time.time() - 3: # skip old messages
             return
         notification_queue = []
         response = self.app(environ, notification_queue)
         for jid, resp in notification_queue:
             self.dispatch_app_response({'XMPP_JID': jid}, resp)
 
-    def send_message(self, environ, body):
-        """Sends XMPP messages. Should be overriden with library specific
-        routine.
+    def send_message(self, environ, payload):
+        """Sends XMPP messages.
 
         :param environ: XMPPWSGI environ.
         :type environ: dict
 
-        :param body: Message body.
-        :type body: unicode
+        :param payload: Presence payload data.
+        :type payload: dict
         """
-        raise NotImplementedError
+        if not isinstance(payload, dict):
+            payload = {'body': payload}
+
+        if 'to' not in payload:
+            if environ.get('type') == 'groupchat':
+                to_jid = environ['mucroom']
+            else:
+                to_jid = environ.get('from', environ['XMPP_JID'])
+            payload['to'] = to_jid
+
+        return self.xmpp.send_message(environ, payload)
 
     def handle_presence(self, presence):
         """Handles presence events."""
-        environ = self.make_environ(presence)
+        environ = self.xmpp.make_environ(presence)
         self.app.logger.debug(pformat(environ))
         if environ['from'] == environ['to']: # we don't want infinity loop there
             return
     def send_presence(self, environ, payload):
         """Sends XMPP presence event.
 
-        :param environ: XMPPWSGI environ. Should contains 'XMPP_JID' key.
+        :param environ: XMPPWSGI environ.
         :type environ: dict
 
         :param payload: Presence payload data.
         :type payload: dict
         """
-        raise NotImplementedError
+        assert isinstance(payload, dict)
+
+        if 'to' not in payload:
+            payload['to'] = environ.get('from', environ['XMPP_JID'])
+
+        return self.xmpp.send_presence(environ, payload)
 
     def handle_iq(self, iq):
+        """Handles IQ events."""
         raise NotImplementedError
 
     def send_iq(self, environ, payload):
-        raise NotImplementedError
+        """Sends IQ request.
 
-
-class XmpppyServer(XmppWsgiServer):
-    """XMPPWSGI Server based on xmpppy library."""
-    def __init__(self, app):
-        import xmpp
-        self.module = xmpp
-        self.message_class = xmpp.Message
-        self.presence_class = xmpp.Presence
-        self.iq_class = xmpp.Iq
-        super(XmpppyServer, self).__init__(app)
-
-    def _connect(self, jid, pwd, use_tls=True, use_ssl=False):
-        jid = self.module.JID(jid)
-        user, server, password = jid.getNode(), jid.getDomain(), pwd
-
-        self.client = self.module.Client(server)
-
-        conn_type = self.client.connect()
-
-        if conn_type is None:
-            raise Exception(u'Unable to connect to server %s!' % server)
-
-        auth_type = self.client.auth(user, password)
-
-        if auth_type is None:
-            raise Exception((u'Unable to authorize on %s - '
-                             u'check login/password.') % server)
-        self.app.logger.info('done.')
-
-        self.client.RegisterHandler('message',
-                                    lambda c, m: self.handle_message(m))
-        self.client.RegisterHandler('presence',
-                                    lambda c, m: self.handle_presence(m))
-        self.on_start()
-
-    def on_start(self, *args, **kwargs):
-        self.client.sendInitPresence()
-        self.app.logger.info('started!')
-
-    def serve_forever(self):
-        def step_on(conn):
-            try:
-                conn.Process(1)
-            except KeyboardInterrupt:
-                return False
-            return True
-
-        while step_on(self.client):
-            pass
-
-    def make_environ(self, event):
-        """Creates XMPPWSGI environ.
-
-        :param event: XMPP event that would be base for environ.
-        """
-        environ = super(XmpppyServer, self).make_environ(event)
-        environ['id'] = maybe_unicode(event.getID())
-        environ['from'] = maybe_unicode(event.getFrom())
-        environ['to'] = maybe_unicode(event.getTo())
-        environ['type'] = maybe_unicode(event.getType())
-
-        xmpp_user = event.getFrom()
-        jid = JID(
-            xmpp_user.getNode(),
-            xmpp_user.getDomain(),
-            xmpp_user.getResource()
-        )
-        environ['XMPP_JID'] = u'%s@%s' % (jid.user, jid.domain)
-
-        if isinstance(event, self.message_class):
-            environ['body'] = environ['MESSAGE'] = maybe_unicode(event.getBody())
-            environ['subject'] = maybe_unicode(event.getSubject())
-            if environ['type'] == 'groupchat':
-                environ['mucroom'] = u'%s@%s' % (jid.user, jid.domain)
-                environ['mucnick'] = maybe_unicode(jid.resource)
-            else:
-                environ['mucroom'] = None
-                environ['mucnick'] = None
-        elif isinstance(event, self.presence_class):
-            environ['priority'] = maybe_unicode(event.getPriority())
-            environ['status'] = maybe_unicode(event.getStatus())
-            environ['type'] = maybe_unicode(environ['type'] or event.getShow())
-
-        return environ
-
-    def send_message(self, environ, body):
-        """Sends XMPP messages.
-
-        :param environ: XMPPWSGI environ. Should contains 'XMPP_JID' key.
+        :param environ: XMPPWSGI environ.
         :type environ: dict
 
-        :param body: Message body.
-        :type body: unicode
-        """
-        jid = self.module.JID(environ['XMPP_JID'])
-        self.client.send(self.message_class(jid, body))
-
-    def send_presence(self, environ, payload):
-        """Sends XMPP presence event.
-
-        :param environ: XMPPWSGI environ. Should contains 'XMPP_JID' key.
-        :type environ: dict
-
-        :param payload: Presence payload data. Mostly, could contain keys:
-                        ``to``, ``type`` and ``status``.
+        :param payload: IQ payload data.
         :type payload: dict
         """
-        to_jid = payload.get('to', environ['XMPP_JID'])
-        if to_jid == 'all':
-            to_jid = None
-        if 'type' not in payload:
-            presence = self.presence_class(to=to_jid)
-            self.client.send(presence)
-            return
-        if payload['type'] in ('available', 'unavailable'):
-            presence = self.presence_class(to=to_jid, typ=payload['type'])
-            self.client.send(presence)
-        elif payload['type'] in ('subscribe', 'subscribed',
-                                 'unsubscribe', 'unsubscribed'):
-            presence = self.presence_class(to=to_jid, typ=payload['type'])
-            self.client.send(presence)
-        elif payload['type'] in ('probe',):
-            presence = self.presence_class(to=to_jid, typ=payload['type'])
-            self.client.send(presence)
-        else:
-            presence = self.presence_class(to=to_jid,
-                                           status=payload.get('status', ''),
-                                           show=payload['type'])
-            self.client.send(presence)
-
-
-class SleekXmppServer(XmppWsgiServer):
-    """XMPPWSGI Server based on SleekXMPP library."""
-    def __init__(self, app):
-        import sleekxmpp
-        self.module = sleekxmpp
-        self.message_class = sleekxmpp.Message
-        self.presence_class = sleekxmpp.Presence
-        self.iq_class = sleekxmpp.Iq
-        super(SleekXmppServer, self).__init__(app)
-
-    def _connect(self, jid, pwd, use_tls=True, use_ssl=False):
-        self.client = self.module.ClientXMPP(jid, pwd)
-        self.client.connect(use_tls=use_tls, use_ssl=use_ssl)
-
-        self.app.logger.info('done.')
-
-        self.client.add_event_handler('session_start', self.on_start)
-        self.client.add_event_handler('message', self.handle_message)
-        self.client.add_event_handler('presence', self.handle_presence)
-
-    def on_start(self, *args, **kwargs):
-        self.client.send_presence()
-        self.client.get_roster()
-        self.app.logger.info('started!')
-
-    def serve_forever(self):
-        self.client.process(threading=False)
-
-    def make_environ(self, event):
-        """Creates XMPPWSGI environ.
-
-        :param event: XMPP event that would be base for environ.
-        """
-        environ = super(SleekXmppServer, self).make_environ(event)
-        environ['id'] = maybe_unicode(event['id'])
-        environ['from'] = maybe_unicode(event['from'])
-        environ['to'] = maybe_unicode(event['to'])
-        environ['type'] = maybe_unicode(event['type'])
-
-        jid = JID(
-            event['from'].user,
-            event['from'].domain,
-            event['from'].resource
-        )
-        environ['XMPP_JID'] = u'%s@%s' % (jid.user, jid.domain)
-
-        if isinstance(event, self.message_class):
-            environ['body'] = environ['MESSAGE'] = maybe_unicode(event['body'])
-            environ['subject'] = maybe_unicode(event['subject'])
-            environ['mucroom'] = maybe_unicode(event['mucroom'])
-            environ['mucnick'] = maybe_unicode(event['mucnick'])
-        elif isinstance(event, self.presence_class):
-            environ['priority'] = maybe_unicode(event['priority'])
-            environ['status'] = maybe_unicode(event['status'])
-            environ['type'] = maybe_unicode(environ['type'] or event['show'])
-
-        return environ
-
-    def send_message(self, environ, body):
-        """Sends XMPP messages.
-
-        :param environ: XMPPWSGI environ. Should contains 'XMPP_JID' key.
-        :type environ: dict
-
-        :param body: Message body.
-        :type body: unicode
-        """
-        self.client.send_message(mto=environ['XMPP_JID'], mbody=body)
-
-    def send_presence(self, environ, payload):
-        """Sends XMPP presence event.
-
-        :param environ: XMPPWSGI environ. Should contains 'XMPP_JID' key.
-        :type environ: dict
-
-        :param payload: Presence payload data. Mostly, could contain keys:
-                        ``to``, ``type`` and ``status``.
-        :type payload: dict
-        """
-        to_jid = payload.get('to', environ['XMPP_JID'])
-        if to_jid == 'all':
-            to_jid = None
-        if 'type' not in payload:
-            self.client.send_presence(pto=to_jid)
-            return
-        if payload['type'] in ('available', 'unavailable'):
-            self.client.send_presence(pto=to_jid, ptype=payload['type'])
-        elif payload['type'] in ('subscribe', 'subscribed',
-                                 'unsubscribe', 'unsubscribed'):
-            self.client.send_presence(pto=to_jid,
-                                      ptype=payload['type'])
-        elif payload['type'] in ('probe',):
-            self.client.send_presence(pto=to_jid,
-                                      ptype=payload['type'])
-        else:
-            self.client.send_presence(pto=to_jid,
-                                      pstatus=payload.get('status', ''),
-                                      pshow=payload['type'])
-
-def maybe_unicode(value):
-    if value is None:
-        return value
-    if isinstance(value, str):
-        if not value:
-            return None
-        return unicode(value)
-    return value
+        assert isinstance(payload, dict)
+        return self.xmpp.send_iq(environ, payload)

File xmppflask/tests/helpers.py

 # -*- coding: utf-8 -*-
 
+import logging
 import sys
 
 if sys.version_info >= (2, 7):
 else:
     unittest = __import__('unittest2')
 
+logging.disable(logging.CRITICAL)
+
+from xmppflask.server import XmppWsgiServerInterface, maybe_unicode
+from collections import deque
+
+class MockStanza(dict):
+
+    def __getitem__(self, item):
+        return self.get(item)
+
+class MockMessage(MockStanza):
+    pass
+
+class MockPresence(MockStanza):
+    pass
+
+class MockIq(MockStanza):
+    pass
+
+class MockXmppWsgiServerInterface(XmppWsgiServerInterface):
+
+    message_class = MockMessage
+    presence_class = MockPresence
+    iq_class = MockIq
+
+    def __init__(self, *args, **kwargs):
+        self.m_queue = deque()
+        self.m_started = False
+        self.m_connected = False
+        super(MockXmppWsgiServerInterface, self).__init__(*args, **kwargs)
+
+    def connect(self, jid, pwd, *args, **kwargs):
+        self.app_jid = jid
+        self.m_connected = True
+
+    def serve_forever(self):
+        self.m_started = True
+
+    def make_environ(self, event):
+        environ = super(MockXmppWsgiServerInterface, self).make_environ(event)
+
+        environ['APP_JID'] = unicode(self.app_jid)
+
+        if event['id']:
+            environ['XMPP_ID'] = maybe_unicode(event['id'])
+        environ['from'] = maybe_unicode(event['from'])
+        environ['to'] = maybe_unicode(event['to'])
+        environ['type'] = maybe_unicode(event['type'])
+        if 'delay' in event:
+            environ['XMPP_DELAY'] =  event['delay']
+
+        if environ['from']:
+            environ['XMPP_JID'] = environ['from'].split('/')[0]
+
+        if isinstance(event, self.message_class):
+            environ['body'] = environ['MESSAGE'] = maybe_unicode(event['body'])
+            environ['subject'] = maybe_unicode(event['subject'])
+            environ['mucroom'] = maybe_unicode(event['mucroom'])
+            environ['mucnick'] = maybe_unicode(event['mucnick'])
+        elif isinstance(event, self.presence_class):
+            environ['priority'] = event['priority']
+            environ['status'] = maybe_unicode(event['status'])
+            environ['type'] = maybe_unicode(environ['type'] or event['show'])
+
+        return environ
+
+    def m_make_message(self, body, type='chat', **kwargs):
+        return MockMessage(body=body, type=type, **kwargs)
+
+    def send_message(self, environ, payload):
+        self.m_queue.append((
+            'message',
+            {
+                'to': payload['to'],
+                'body': payload['body'],
+                'type': environ.get('type', 'chat'),
+            }
+        ))
+
+    def send_presence(self, environ, payload):
+        if 'status' in payload:
+            self.m_queue.append((
+                'presence',
+                {
+                    'to': payload['to'],
+                    'status': payload['status'],
+                    'show': payload['type']
+                }
+            ))
+        else:
+            self.m_queue.append((
+                'presence',
+                    {
+                    'to': payload['to'],
+                    'type': payload['type']
+                }
+            ))
+
+

File xmppflask/tests/test_server.py

     :license: BSD.
 """
 
-from xmppflask.tests.helpers import unittest
+from xmppflask.tests.helpers import unittest, MockXmppWsgiServerInterface
 from xmppflask import XmppFlask
 from xmppflask.server import XmppWsgiServer
 
-def counted(fun):
-    def wrapper(*args, **kwargs):
-        wrapper.times_called += 1
-        return fun(*args, **kwargs)
-    wrapper.times_called = 0
-    wrapper.__name__= fun.__name__
-    return wrapper
-
-
-class MockXmppWsgiServer(XmppWsgiServer):
-    connected = False
-    running = False
-    last_message = None
-
-    def _connect(self, *args, **kwargs):
-        self.connected = True
-
-    def serve_forever(self):
-        self.running = True
-
-    def dispatch_app_response(self, environ, response):
-        self.message_sent = False
-        self.presence_sent = False
-        self.iq_sent = False
-        return super(MockXmppWsgiServer, self).dispatch_app_response(environ, response)
-
-    def send_message(self, environ, body):
-        pass
-
-    def send_presence(self, environ, payload):
-        pass
-
-    def send_iq(self, environ, payload):
-        pass
-
-
 class XmppWsgiServerTestCase(unittest.TestCase):
 
     def setUp(self):
         app = XmppFlask('xmppflask.test')
-        self.server = MockXmppWsgiServer(app)
-        self.server.send_message = counted(self.server.send_message)
-        self.server.send_presence = counted(self.server.send_presence)
-        self.server.send_iq = counted(self.server.send_iq)
+        self.server = XmppWsgiServer(app, MockXmppWsgiServerInterface())
 
     def test_connect(self):
         self.server.connect('foo', 'bar')
-        assert self.server.connected
+        assert self.server.xmpp.m_connected
 
     def test_dispatch_message_response(self):
         environ = {'XMPP_JID': 'foo@bar'}
 
         self.server.dispatch_app_response(environ, resp)
 
-        self.assertEqual(self.server.send_message.times_called, 1)
+        queue = self.server.xmpp.m_queue
+        self.assertEqual(len(queue), 1)
+
+        cmd, payload = queue[0]
+        self.assertEqual(cmd, 'message')
+        self.assertEqual(payload['to'], environ['XMPP_JID'])
+        self.assertEqual(payload['body'], 'baz')
+        self.assertEqual(payload['type'], 'chat')
 
     def test_dispatch_new_style_message_response(self):
         environ = {'XMPP_JID': 'foo@bar'}
 
         self.server.dispatch_app_response(environ, resp)
 
-        self.assertEqual(self.server.send_message.times_called, 1)
+        queue = self.server.xmpp.m_queue
+        self.assertEqual(len(queue), 1)
 
-    def test_dispatch_new_style_signle_message_response(self):
+        cmd, payload = queue[0]
+        self.assertEqual(cmd, 'message')
+        self.assertEqual(payload['to'], environ['XMPP_JID'])
+        self.assertEqual(payload['body'], 'baz')
+        self.assertEqual(payload['type'], 'chat')
+
+    def test_dispatch_multi_message_response(self):
         def pong():
             return 'message', 'pong'
         environ = {'XMPP_JID': 'foo@bar'}
-        resp = self.server.app.response_class(pong())
+        resp = self.server.app.response_class([('message', 'foo'),
+                                               ('message', 'bar'),
+                                               'baz'])
 
         self.server.dispatch_app_response(environ, resp)
 
-        self.assertEqual(self.server.send_message.times_called, 1)
+        queue = self.server.xmpp.m_queue
+        self.assertEqual(len(queue), 3)
+
+        seq = ['foo', 'bar', 'baz']
+        for idx, msg, in enumerate(seq):
+            cmd, payload = queue[idx]
+            self.assertEqual(cmd, 'message')
+            self.assertEqual(payload['to'], environ['XMPP_JID'])
+            self.assertEqual(payload['body'], msg)
+            self.assertEqual(payload['type'], 'chat')
 
     def test_dispatch_presence_response(self):
         environ = {'XMPP_JID': 'foo@bar'}
-        resp = self.server.app.response_class([('presence', 'baz')])
+        resp = self.server.app.response_class([('presence', {'type': 'baz'})])
 
         self.server.dispatch_app_response(environ, resp)
 
-        self.assertEqual(self.server.send_presence.times_called, 1)
+        queue = self.server.xmpp.m_queue
+        self.assertEqual(len(queue), 1)
 
-    def test_dispatch_iq_response(self):
-        environ = {'XMPP_JID': 'foo@bar'}
-        resp = self.server.app.response_class([('iq', 'baz')])
-
-        self.server.dispatch_app_response(environ, resp)
-
-        self.assertEqual(self.server.send_iq.times_called, 1)
+        cmd, payload = queue[0]
+        self.assertEqual(cmd, 'presence')
+        self.assertEqual(payload['to'], environ['XMPP_JID'])
+        self.assertEqual(payload['type'], 'baz')
 
     def test_dispatch_weird_response(self):
         environ = {'XMPP_JID': 'foo@bar'}
 
     def test_dispatch_generator_of_responses(self):
         def ping():
-            yield 'presence', '1'
-            yield 'presence', '2'
-            yield 'presence', '3'
+            yield 'presence', {'type': 'foo'}
+            yield 'presence', {'type': 'bar'}
+            yield 'presence', {'type': 'baz'}
             yield 'PONG!'
 
         environ = {'XMPP_JID': 'foo@bar'}
 
         self.server.dispatch_app_response(environ, resp)
 
-        self.assertEqual(self.server.send_message.times_called, 1)
-        self.assertEqual(self.server.send_presence.times_called, 3)
+        queue = self.server.xmpp.m_queue
+        self.assertEqual(len(queue), 4)
+
+        seq = ['foo', 'bar', 'baz']
+        for idx, type_ in enumerate(seq):
+            cmd, payload = queue[idx]
+            self.assertEqual(cmd, 'presence')
+            self.assertEqual(payload['to'], environ['XMPP_JID'])
+            self.assertEqual(payload['type'], type_)
+            if idx == 2:
+                break
+
+        cmd, payload = queue[-1]
+        self.assertEqual(cmd, 'message')
+        self.assertEqual(payload['to'], environ['XMPP_JID'])
+        self.assertEqual(payload['body'], 'PONG!')
+        self.assertEqual(payload['type'], 'chat')
+
+if __name__ == '__main__':
+    unittest.main()