plugin XEP_0033: fixes the server part and the tests
This commit is contained in:
parent
d89044dbbc
commit
f0fa9b699d
4 changed files with 88 additions and 40 deletions
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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"""
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
Loading…
Reference in a new issue