changeset 787:dd656d745d6a

test: added tests for XEP-0033
author souliane <souliane@mailoo.org>
date Sun, 05 Jan 2014 13:05:31 +0100
parents c3acc1298a2f
children d6652683c572
files src/core/sat_main.py src/plugins/plugin_xep_0033.py src/test/test_plugin_xep_0033.py
diffstat 3 files changed, 175 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/src/core/sat_main.py	Sun Jan 05 13:04:54 2014 +0100
+++ b/src/core/sat_main.py	Sun Jan 05 13:05:31 2014 +0100
@@ -68,7 +68,9 @@
     """ Exception to raise if the message has been already sent and stored in the
     history by the trigger, so the rest of the process should be stopped. This
     should normally be raised by the trigger with the minimal priority """
-    pass
+    def __init__(self, reason, mess_data):
+        Exception(reason)
+        self.mess_data = mess_data  # added for testing purpose
 
 
 class AbortSendMessage(Exception):
@@ -652,10 +654,10 @@
     def requestServerDisco(self, feature, jid_=None, cache_only=False, profile_key="@NONE"):
         """Discover if a server or its items offer a given feature
         @param feature: the feature to check
-        @param jid_: the jid of the server
+        @param jid_: the jid of the server, local server if None
         @param cache_only: expect the result to be in cache and don't actually
-        make any request to avoid returning a Deferred. This can be used anytime
-        for requesting the local server because the data are cached for sure.
+        make any request. This can be used anytime for requesting a feature on
+        the local server because the data are cached for sure.
         @result: the Deferred entity jid offering the feature, or None
         """
         profile = self.memory.getProfileName(profile_key)
--- a/src/plugins/plugin_xep_0033.py	Sun Jan 05 13:04:54 2014 +0100
+++ b/src/plugins/plugin_xep_0033.py	Sun Jan 05 13:05:31 2014 +0100
@@ -96,7 +96,7 @@
                     element.addChild(domish.Element((None, 'address'), None, {'type': type_, 'jid': jid_}))
                 # when the prosody plugin is completed, we can immediately return mess_data from here
                 self.sendAndStoreMessage(mess_data, entries, profile)
-                return Failure(MessageSentAndStored("XEP-0033 took over"))
+                return Failure(MessageSentAndStored("XEP-0033 took over", mess_data))
             d = self.host.requestServerDisco(NS_ADDRESS, profile_key=profile)
             d.addCallbacks(discoCallback, lambda dummy: discoCallback(None))
             return d
@@ -151,7 +151,10 @@
         def post_treat_addr(data, addresses):
             data['extra']['addresses'] = ""
             for address in addresses:
-                data['extra']['addresses'] += '%s:%s\n' % (address['type'], address['jid'])
+                # Depending how message has been constructed, we could get here
+                # some noise like "\n        " instead of an address element.
+                if isinstance(address, domish.Element):
+                    data['extra']['addresses'] += '%s:%s\n' % (address['type'], address['jid'])
             return data
 
         try:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/test/test_plugin_xep_0033.py	Sun Jan 05 13:05:31 2014 +0100
@@ -0,0 +1,164 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# SAT: a jabber client
+# Copyright (C) 2009, 2010, 2011, 2012, 2013  Jérôme Poisson (goffi@goffi.org)
+# Copyright (C) 2013  Adrien Cossa (souliane@mailoo.org)
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+""" Plugin extended addressing stanzas """
+
+from constants import Const
+from sat.test import helpers
+from sat.plugins import plugin_xep_0033 as plugin
+from sat.memory.memory import NO_SECURITY_LIMIT
+from sat.core.sat_main import AbortSendMessage, MessageSentAndStored
+from copy import deepcopy
+from twisted.internet import defer
+from wokkel.generic import parseXml
+from twisted.words.protocols.jabber.jid import JID
+
+
+class XEP_0033Test(helpers.SatTestCase):
+
+    def setUp(self):
+        self.host = helpers.FakeSAT()
+        self.plugin = plugin.XEP_0033(self.host)
+
+    def test_messageReceived(self):
+        self.host.memory.init()
+        xml = u"""
+        <message type="chat" from="%s" to="%s" id="test_1">
+            <body>test</body>
+            <addresses xmlns='http://jabber.org/protocol/address'>
+                <address type='to' jid='%s'/>
+                <address type='cc' jid='%s'/>
+                <address type='bcc' jid='%s'/>
+            </addresses>
+        </message>
+        """ % (Const.TEST_JID_2_STR, self.host.getClientHostJid(Const.TEST_PROFILE),
+               Const.TEST_JID_STR, Const.TEST_JID_2_STR, Const.TEST_JID_3_STR)
+        stanza = parseXml(xml.encode("utf-8"))
+        treatments = defer.Deferred()
+        self.plugin.messageReceivedTrigger(stanza, treatments, Const.TEST_PROFILE)
+        data = {'extra': {}}
+
+        def assert_(data):
+            expected = ('to', Const.TEST_JID_STR, 'cc', Const.TEST_JID_2_STR, 'bcc', Const.TEST_JID_3_STR)
+            assert(data['extra']['addresses'] == '%s:%s\n%s:%s\n%s:%s\n' % expected)
+
+        treatments.addCallback(assert_)
+        treatments.callback(data)
+
+    def test_sendMessageTrigger(self):
+        mess_data = {"to": self.host.getClientHostJid(Const.TEST_PROFILE),
+                     "type": "chat",
+                     "message": "content",
+                     "extra": {}
+                     }
+        addresses = ('to', Const.TEST_JID_STR, 'cc', Const.TEST_JID_2_STR, 'bcc', Const.TEST_JID_3_STR)
+        mess_data["extra"]["address"] = '%s:%s\n%s:%s\n%s:%s\n' % addresses
+        original_stanza = u"""
+        <message type="chat" from="%s" to="%s" id="test_1">
+            <body>content</body>
+        </message>
+        """ % (Const.TEST_JID_2_STR, self.host.getClientHostJid(Const.TEST_PROFILE))
+        mess_data['xml'] = parseXml(original_stanza.encode("utf-8"))
+        expected = deepcopy(mess_data['xml'])
+        addresses_extra = """
+        <addresses xmlns='http://jabber.org/protocol/address'>
+            <address type='%s' jid='%s'/>
+            <address type='%s' jid='%s'/>
+            <address type='%s' jid='%s'/>
+        </addresses>""" % addresses
+        addresses_element = parseXml(addresses_extra.encode('utf-8'))
+        expected.addChild(addresses_element)
+
+        def assert_(mess_data):
+            """The mess_data that we got here as been modified by self.plugin.sendMessageTrigger,
+            check that the addresses element has been added to the stanza."""
+            self.assertEqualXML(mess_data['xml'].toXml().encode("utf-8"), expected.toXml().encode("utf-8"))
+
+        def fail_(failure, exception_class):
+            """If the failure does encapsulate the expected exception, it will be silently
+            trapped, otherwise it will be re-raised and will make the test fail"""
+            if exception_class == MessageSentAndStored:
+                assert_(failure.value.mess_data)
+            failure.trap(exception_class)
+
+        def checkSentAndStored():
+            """Check that all the recipients got their messages and that the history has been filled.
+            /!\ see the comments in XEP_0033.sendAndStoreMessage"""
+            sent = []
+            stored = []
+            cache = set()
+            for to_s in [addresses[1], addresses[3], addresses[5]]:
+                to_jid = JID(to_s)
+                host = JID(to_jid.host)
+                if self.host.memory.hasServerFeature(plugin.NS_ADDRESS, host, Const.TEST_PROFILE):
+                    if host not in cache:
+                        sent.append(host)
+                        stored.append(host)
+                        cache.add(host)
+                    stored.append(to_jid)
+                else:
+                    sent.append(to_jid)
+                    stored.append(to_jid)
+            try:
+                assert(len(self.host.sent_messages) == len(sent))
+                assert(len(self.host.stored_messages) == len(stored))
+                assert(set(self.host.sent_messages) == set(sent))
+                assert(set(self.host.stored_messages) == set(stored))
+            except AssertionError as e:
+                print "----------------------------------------------------"
+                print "Comparing sent and stored messages failed!"
+                print self.host.sent_messages
+                print sent
+                print self.host.stored_messages
+                print stored
+                print "/!\ see the comments in XEP_0033.sendAndStoreMessage"
+                print "----------------------------------------------------"
+                raise e
+
+        # feature is not supported, abort the message
+        self.host.memory.init()
+        treatments = defer.Deferred()
+        data = deepcopy(mess_data)
+        self.plugin.sendMessageTrigger(data, treatments, Const.TEST_PROFILE)
+        treatments.addCallbacks(assert_, lambda failure: fail_(failure, AbortSendMessage))
+        treatments.callback(data)
+
+        # feature is supported
+        self.host.init()
+        self.host.memory.init()
+        self.host.memory.addServerFeature(plugin.NS_ADDRESS, self.host.getClientHostJid(Const.TEST_PROFILE), Const.TEST_PROFILE)
+        treatments = defer.Deferred()
+        data = deepcopy(mess_data)
+        self.plugin.sendMessageTrigger(data, treatments, Const.TEST_PROFILE)
+        treatments.addCallbacks(assert_, lambda failure: fail_(failure, MessageSentAndStored))
+        treatments.callback(data)
+        checkSentAndStored()
+
+        # check that a wrong recipient entity is fixed by the backend
+        self.host.init()
+        self.host.memory.init()
+        self.host.memory.addServerFeature(plugin.NS_ADDRESS, self.host.getClientHostJid(Const.TEST_PROFILE), Const.TEST_PROFILE)
+        treatments = defer.Deferred()
+        data = deepcopy(mess_data)
+        data["to"] = Const.TEST_JID
+        self.plugin.sendMessageTrigger(data, treatments, Const.TEST_PROFILE)
+        treatments.addCallbacks(assert_, lambda failure: fail_(failure, MessageSentAndStored))
+        treatments.callback(mess_data)
+        checkSentAndStored()