# HG changeset patch # User souliane # Date 1397206962 -7200 # Node ID c37a24922f27377f95a1aaa3cad95506dca86b27 # Parent f5761534e0f383c813ebe70499551f86da86c4f4 plugin XEP_0033: fixes the server part and the tests diff -r f5761534e0f3 -r c37a24922f27 src/core/sat_main.py --- a/src/core/sat_main.py Wed Apr 23 12:01:59 2014 +0200 +++ b/src/core/sat_main.py Fri Apr 11 11:02:42 2014 +0200 @@ -58,7 +58,7 @@ 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 """ def __init__(self, reason, mess_data): - Exception(reason) + Exception.__init__(self, reason) self.mess_data = mess_data # added for testing purpose diff -r f5761534e0f3 -r c37a24922f27 src/plugins/plugin_xep_0033.py --- a/src/plugins/plugin_xep_0033.py Wed Apr 23 12:01:59 2014 +0200 +++ b/src/plugins/plugin_xep_0033.py Fri Apr 11 11:02:42 2014 +0200 @@ -82,13 +82,14 @@ if not 'address' in mess_data['extra']: return mess_data - def discoCallback(entity): - if entity is None: + def discoCallback(entities): + if not entities: return Failure(AbortSendMessage(_("XEP-0033 is being used but the server doesn't support it!"))) - if mess_data["to"] != entity: - log.warning(_("Stanzas using XEP-0033 should be addressed to %(expected)s, not %(current)s!") % {'expected': entity, 'current': mess_data["to"]}) - log.warning(_("TODO: addressing has be fixed by the backend... fix it in the frontend!")) - mess_data["to"] = entity + if mess_data["to"] not in entities: + expected = _(' or ').join([entity.userhost() for entity in entities]) + log.warning(_("Stanzas using XEP-0033 should be addressed to %(expected)s, not %(current)s!") % {'expected': expected, 'current': mess_data["to"]}) + log.warning(_("TODO: addressing has been fixed by the backend... fix it in the frontend!")) + mess_data["to"] = list(entities)[0].userhostJID() element = mess_data['xml'].addElement('addresses', NS_ADDRESS) entries = [entry.split(':') for entry in mess_data['extra']['address'].split('\n') if entry != ''] for type_, jid_ in entries: @@ -96,7 +97,7 @@ # 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", mess_data)) - d = self.host.requestServerDisco(NS_ADDRESS, profile_key=profile) + d = self.host.findFeaturesSet([NS_ADDRESS], profile_key=profile) d.addCallbacks(discoCallback, lambda dummy: discoCallback(None)) return d @@ -116,19 +117,22 @@ - redesign the database to save only one entry to the database - change the newMessage signal to eventually pass more than one recipient """ - def discoCallback(entity, to_jid): - new_data = copy.deepcopy(mess_data) - new_data['to'] = JID(to_jid) - new_data['xml']['to'] = to_jid - if entity: - if entity not in self.internal_data[timestamp]: - self.host.sendAndStoreMessage(mess_data, False, profile) - self.internal_data[timestamp].append(entity) + def discoCallback(entities, to_jid_s): + history_data = copy.deepcopy(mess_data) + history_data['to'] = JID(to_jid_s) + history_data['xml']['to'] = to_jid_s + if entities: + if entities not in self.internal_data[timestamp]: + sent_data = copy.deepcopy(mess_data) + sent_data['to'] = JID(JID(to_jid_s).host) + sent_data['xml']['to'] = JID(to_jid_s).host + self.host.sendAndStoreMessage(sent_data, False, profile) + self.internal_data[timestamp].append(entities) # we still need to fill the history and signal the echo... - self.host.sendAndStoreMessage(new_data, True, profile) + self.host.sendAndStoreMessage(history_data, True, profile) else: # target server misses the addressing feature - self.host.sendAndStoreMessage(new_data, False, profile) + self.host.sendAndStoreMessage(history_data, False, profile) def errback(failure, to_jid): discoCallback(None, to_jid) @@ -138,9 +142,9 @@ defer_list = [] for type_, jid_ in entries: d = defer.Deferred() - d.addCallback(self.host.requestServerDisco, JID(JID(jid_).host), profile_key=profile) + d.addCallback(self.host.findFeaturesSet, jid_=JID(JID(jid_).host), profile_key=profile) d.addCallbacks(discoCallback, errback, callbackArgs=[jid_], errbackArgs=[jid_]) - d.callback(NS_ADDRESS) + d.callback([NS_ADDRESS]) defer_list.append(d) d = defer.Deferred().addCallback(lambda dummy: self.internal_data.pop(timestamp)) defer.DeferredList(defer_list).chainDeferred(d) diff -r f5761534e0f3 -r c37a24922f27 src/test/helpers.py --- a/src/test/helpers.py Wed Apr 23 12:01:59 2014 +0200 +++ b/src/test/helpers.py Fri Apr 11 11:02:42 2014 +0200 @@ -93,7 +93,6 @@ if not skip_send: self.sent_messages.append(mess_data["to"]) self.stored_messages.append(mess_data["to"]) - pass def getClient(self, profile_key): """Convenient method to get client from profile key @@ -131,6 +130,33 @@ entry = self.getSentMessageRaw(profile_index) return entry.toXml() if entry else None + def findFeaturesSet(self, features, category=None, type_=None, jid_=None, profile_key=None): + """Call self.addFeature from your tests to change the return value. + + @return: a set of entities + """ + client = self.getClient(profile_key) + if jid_ is None: + jid_ = JID(client.jid.host) + try: + if set(features).issubset(client.features[jid_]): + return defer.succeed(set([jid_])) + except (TypeError, AttributeError, KeyError): + pass + return defer.succeed(set()) + + def addFeature(self, jid_, feature, profile_key): + """Add a feature to an entity. + + To be called from your tests when needed. + """ + client = self.getClient(profile_key) + if not hasattr(client, 'features'): + client.features = {} + if jid_ not in client.features: + client.features[jid_] = set() + client.features[jid_].add(feature) + class FakeBridge(object): """Class to simulate and test bridge calls""" diff -r f5761534e0f3 -r c37a24922f27 src/test/test_plugin_xep_0033.py --- a/src/test/test_plugin_xep_0033.py Wed Apr 23 12:01:59 2014 +0200 +++ b/src/test/test_plugin_xep_0033.py Fri Apr 11 11:02:42 2014 +0200 @@ -31,9 +31,15 @@ from twisted.words.protocols.jabber.jid import JID from logging import ERROR +PROFILE = Const.PROFILE[0] +JID_STR_FROM = Const.JID_STR[1] +JID_STR_TO = Const.PROFILE_DICT[PROFILE].host +JID_STR_X_TO = Const.JID_STR[0] +JID_STR_X_CC = Const.JID_STR[1] +JID_STR_X_BCC = Const.JID_STR[2] + class XEP_0033Test(helpers.SatTestCase): - skip = "Must be fixed after disco changes" def setUp(self): self.host = helpers.FakeSAT() @@ -50,15 +56,14 @@
- """ % (Const.JID_STR[1], self.host.getClientHostJid(Const.PROFILE[0]), - Const.JID_STR[0], Const.JID_STR[1], Const.JID_STR[2]) + """ % (JID_STR_FROM, JID_STR_TO, JID_STR_X_TO, JID_STR_X_CC, JID_STR_X_BCC) stanza = parseXml(xml.encode("utf-8")) treatments = defer.Deferred() - self.plugin.messageReceivedTrigger(stanza, treatments, Const.PROFILE[0]) + self.plugin.messageReceivedTrigger(stanza, treatments, PROFILE) data = {'extra': {}} def cb(data): - expected = ('to', Const.JID_STR[0], 'cc', Const.JID_STR[1], 'bcc', Const.JID_STR[2]) + expected = ('to', JID_STR_X_TO, 'cc', JID_STR_X_CC, 'bcc', JID_STR_X_BCC) msg = 'Expected: %s\nGot: %s' % (expected, data['extra']['addresses']) self.assertEqual(data['extra']['addresses'], '%s:%s\n%s:%s\n%s:%s\n' % expected, msg) @@ -66,18 +71,18 @@ treatments.callback(data) def test_sendMessageTrigger(self): - mess_data = {"to": self.host.getClientHostJid(Const.PROFILE[0]), + mess_data = {"to": JID(JID_STR_TO), "type": "chat", "message": "content", "extra": {} } - addresses = ('to', Const.JID_STR[0], 'cc', Const.JID_STR[1], 'bcc', Const.JID_STR[2]) + addresses = ('to', JID_STR_X_TO, 'cc', JID_STR_X_CC, 'bcc', JID_STR_X_BCC) mess_data["extra"]["address"] = '%s:%s\n%s:%s\n%s:%s\n' % addresses original_stanza = u""" content - """ % (Const.JID_STR[1], self.host.getClientHostJid(Const.PROFILE[0])) + """ % (JID_STR_FROM, JID_STR_TO) mess_data['xml'] = parseXml(original_stanza.encode("utf-8")) expected = deepcopy(mess_data['xml']) addresses_extra = """ @@ -97,29 +102,28 @@ def sendMessageErrback(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""" + failure.trap(exception_class) if exception_class == MessageSentAndStored: assertAddresses(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]]: + for to_s in (JID_STR_X_TO, JID_STR_X_CC, JID_STR_X_BCC): to_jid = JID(to_s) host = JID(to_jid.host) logger = getLogger() level = logger.getEffectiveLevel() logger.setLevel(ERROR) # remove log.warning pollution - if self.host.memory.hasServerFeature(plugin.NS_ADDRESS, host, Const.PROFILE[0]): - if host not in cache: + entities = yield self.host.findFeaturesSet([plugin.NS_ADDRESS], jid_=host, profile_key=PROFILE) + if host in entities: + if host not in sent: # send the message to the entity offering the feature sent.append(host) stored.append(host) - cache.add(host) - stored.append(to_jid) - else: + stored.append(to_jid) # store in history for each recipient + else: # feature not supported, use normal behavior sent.append(to_jid) stored.append(to_jid) logger.setLevel(level) @@ -140,7 +144,7 @@ logger.setLevel(ERROR) # remove log.warning pollution pre_treatments = defer.Deferred() post_treatments = defer.Deferred() - self.plugin.sendMessageTrigger(data, pre_treatments, post_treatments, Const.PROFILE[0]) + self.plugin.sendMessageTrigger(data, pre_treatments, post_treatments, PROFILE) post_treatments.callback(data) logger.setLevel(level) post_treatments.addCallbacks(assertAddresses, lambda failure: sendMessageErrback(failure, exception)) @@ -150,9 +154,20 @@ data = deepcopy(mess_data) trigger(data, AbortSendMessage) - # feature is supported + # feature is supported by the main target server self.host.init() self.host.memory.init() + self.host.addFeature(JID(JID_STR_TO), plugin.NS_ADDRESS, PROFILE) + data = deepcopy(mess_data) + trigger(data, MessageSentAndStored) + checkSentAndStored() + + # feature is supported by all target servers + self.host.init() + self.host.memory.init() + self.host.addFeature(JID(JID_STR_TO), plugin.NS_ADDRESS, PROFILE) + for dest in (JID_STR_X_TO, JID_STR_X_CC, JID_STR_X_BCC): + self.host.addFeature(JID(JID(dest).host), plugin.NS_ADDRESS, PROFILE) data = deepcopy(mess_data) trigger(data, MessageSentAndStored) checkSentAndStored() @@ -160,7 +175,10 @@ # check that a wrong recipient entity is fixed by the backend self.host.init() self.host.memory.init() + self.host.addFeature(JID(JID_STR_TO), plugin.NS_ADDRESS, PROFILE) + for dest in (JID_STR_X_TO, JID_STR_X_CC, JID_STR_X_BCC): + self.host.addFeature(JID(JID(dest).host), plugin.NS_ADDRESS, PROFILE) data = deepcopy(mess_data) - data["to"] = Const.JID[0] + data["to"] = JID(JID_STR_X_TO) trigger(data, MessageSentAndStored) checkSentAndStored()