plugin XEP_0033: fixes the server part and the tests

This commit is contained in:
souliane 2014-04-11 11:02:42 +02:00
commit f0fa9b699d
4 changed files with 88 additions and 40 deletions

View file

@ -58,7 +58,7 @@ class MessageSentAndStored(Exception):
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

View file

@ -82,13 +82,14 @@ class XEP_0033(object):
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 @@ class XEP_0033(object):
# 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 @@ class XEP_0033(object):
- 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 @@ class XEP_0033(object):
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)

View file

@ -93,7 +93,6 @@ class FakeSAT(object):
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 @@ class FakeSAT(object):
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"""

View file

@ -31,9 +31,15 @@ from wokkel.generic import parseXml
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 @@ class XEP_0033Test(helpers.SatTestCase):
<address type='bcc' jid='%s'/>
</addresses>
</message>
""" % (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 @@ class XEP_0033Test(helpers.SatTestCase):
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"""
<message type="chat" from="%s" to="%s" id="test_1">
<body>content</body>
</message>
""" % (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 @@ class XEP_0033Test(helpers.SatTestCase):
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 @@ class XEP_0033Test(helpers.SatTestCase):
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 @@ class XEP_0033Test(helpers.SatTestCase):
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 @@ class XEP_0033Test(helpers.SatTestCase):
# 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()