Compare commits
15 Commits
1.0-Beta1
...
sleek-1.0-
Author | SHA1 | Date | |
---|---|---|---|
![]() |
5bdcd9ef9d | ||
![]() |
2eff35cc7a | ||
![]() |
ac330b5c6c | ||
![]() |
46ffa8e9fe | ||
![]() |
03847497cc | ||
![]() |
185d7cf28e | ||
![]() |
8aa3d0c047 | ||
![]() |
9e3d506651 | ||
![]() |
2f3ff37a24 | ||
![]() |
1f09d60a52 | ||
![]() |
d528884723 | ||
![]() |
d9aff3d36f | ||
![]() |
04cc48775d | ||
![]() |
27ebb6e8f6 | ||
![]() |
8f55704928 |
@@ -553,6 +553,7 @@ class BaseXMPP(XMLStream):
|
||||
priority = presence['priority']
|
||||
|
||||
was_offline = False
|
||||
got_online = False
|
||||
old_roster = self.roster.get(jid, {}).get(resource, {})
|
||||
|
||||
# Create a new roster entry if needed.
|
||||
@@ -569,7 +570,7 @@ class BaseXMPP(XMLStream):
|
||||
# Determine if the user has just come online.
|
||||
if not resource in connections:
|
||||
if show == 'available' or show in presence.showtypes:
|
||||
self.event("got_online", presence)
|
||||
got_online = True
|
||||
was_offline = True
|
||||
connections[resource] = {}
|
||||
|
||||
@@ -601,6 +602,8 @@ class BaseXMPP(XMLStream):
|
||||
|
||||
# Presence state has changed.
|
||||
self.event("changed_status", presence)
|
||||
if got_online:
|
||||
self.event("got_online", presence)
|
||||
logging.debug("STATUS: %s%s/%s[%s]: %s" % (name, jid, resource,
|
||||
show, status))
|
||||
|
||||
@@ -618,8 +621,8 @@ class BaseXMPP(XMLStream):
|
||||
None * Disable automatic handling and use
|
||||
a custom handler.
|
||||
"""
|
||||
presence = self.Presence()
|
||||
presence['to'] = presence['from'].bare
|
||||
presence.reply()
|
||||
presence['to'] = presence['to'].bare
|
||||
|
||||
# We are using trinary logic, so conditions have to be
|
||||
# more explicit than usual.
|
||||
|
@@ -219,8 +219,8 @@ class ClientXMPP(BaseXMPP):
|
||||
iq['roster']['items'] = {jid: {'name': name,
|
||||
'subscription': subscription,
|
||||
'groups': groups}}
|
||||
resp = iq.send()
|
||||
return resp['type'] == 'result'
|
||||
response = iq.send()
|
||||
return response['type'] == 'result'
|
||||
|
||||
def del_roster_item(self, jid):
|
||||
"""
|
||||
@@ -235,8 +235,8 @@ class ClientXMPP(BaseXMPP):
|
||||
def get_roster(self):
|
||||
"""Request the roster from the server."""
|
||||
iq = self.Iq()._set_stanza_values({'type': 'get'}).enable('roster')
|
||||
iq.send()
|
||||
self._handle_roster(iq, request=True)
|
||||
response = iq.send()
|
||||
self._handle_roster(response, request=True)
|
||||
|
||||
def _handle_stream_features(self, features):
|
||||
"""
|
||||
|
@@ -67,6 +67,8 @@ class ComponentXMPP(BaseXMPP):
|
||||
self.server_port = port
|
||||
self.set_jid(jid)
|
||||
self.secret = secret
|
||||
self.plugin_config = plugin_config
|
||||
self.plugin_whitelist = plugin_whitelist
|
||||
self.is_component = True
|
||||
|
||||
self.register_handler(
|
||||
|
@@ -38,6 +38,9 @@ class XMPPError(Exception):
|
||||
element. Same as the additional arguments to
|
||||
the ET.Element constructor.
|
||||
"""
|
||||
if extension_args is None:
|
||||
extension_args = {}
|
||||
|
||||
self.condition = condition
|
||||
self.text = text
|
||||
self.etype = etype
|
||||
|
@@ -92,6 +92,12 @@ class Presence(RootStanza):
|
||||
|
||||
return StanzaBase.setup(self, xml)
|
||||
|
||||
def exception(self, e):
|
||||
"""
|
||||
Override exception passback for presence.
|
||||
"""
|
||||
pass
|
||||
|
||||
def set_show(self, show):
|
||||
"""
|
||||
Set the value of the <show> element.
|
||||
|
@@ -103,6 +103,41 @@ class SleekTest(unittest.TestCase):
|
||||
"""
|
||||
return Presence(None, *args, **kwargs)
|
||||
|
||||
|
||||
|
||||
def check_JID(self, jid, user=None, domain=None, resource=None,
|
||||
bare=None, full=None, string=None):
|
||||
"""
|
||||
Verify the components of a JID.
|
||||
|
||||
Arguments:
|
||||
jid -- The JID object to test.
|
||||
user -- Optional. The user name portion of the JID.
|
||||
domain -- Optional. The domain name portion of the JID.
|
||||
resource -- Optional. The resource portion of the JID.
|
||||
bare -- Optional. The bare JID.
|
||||
full -- Optional. The full JID.
|
||||
string -- Optional. The string version of the JID.
|
||||
"""
|
||||
if user is not None:
|
||||
self.assertEqual(jid.user, user,
|
||||
"User does not match: %s" % jid.user)
|
||||
if domain is not None:
|
||||
self.assertEqual(jid.domain, domain,
|
||||
"Domain does not match: %s" % jid.domain)
|
||||
if resource is not None:
|
||||
self.assertEqual(jid.resource, resource,
|
||||
"Resource does not match: %s" % jid.resource)
|
||||
if bare is not None:
|
||||
self.assertEqual(jid.bare, bare,
|
||||
"Bare JID does not match: %s" % jid.bare)
|
||||
if full is not None:
|
||||
self.assertEqual(jid.full, full,
|
||||
"Full JID does not match: %s" % jid.full)
|
||||
if string is not None:
|
||||
self.assertEqual(str(jid), string,
|
||||
"String does not match: %s" % str(jid))
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Methods for comparing stanza objects to XML strings
|
||||
|
||||
@@ -294,6 +329,7 @@ class SleekTest(unittest.TestCase):
|
||||
else:
|
||||
raise ValueError("Unknown socket type.")
|
||||
|
||||
self.xmpp.register_plugins()
|
||||
self.xmpp.process(threaded=True)
|
||||
if skip:
|
||||
# Clear startup stanzas
|
||||
|
@@ -57,7 +57,7 @@ class JID(object):
|
||||
full, or bare.
|
||||
"""
|
||||
if name == 'resource':
|
||||
if self._resource is None:
|
||||
if self._resource is None and '/' in self._jid:
|
||||
self._resource = self._jid.split('/', 1)[-1]
|
||||
return self._resource or ""
|
||||
elif name == 'user':
|
||||
@@ -94,21 +94,15 @@ class JID(object):
|
||||
elif name in ('server', 'domain', 'host'):
|
||||
self.domain = value
|
||||
elif name in ('full', 'jid'):
|
||||
if '@' not in value:
|
||||
if '/' in value:
|
||||
d, r = value.split('/', 1)
|
||||
object.__setattr__(self, "_resource", r)
|
||||
else:
|
||||
d = value
|
||||
object.__setattr__(self, "_domain", d)
|
||||
else:
|
||||
self.reset(value)
|
||||
self.reset(value)
|
||||
self.regenerate()
|
||||
elif name == 'bare':
|
||||
if '@' in value:
|
||||
u, d = value.split('@', 1)
|
||||
object.__setattr__(self, "_user", u)
|
||||
object.__setattr__(self, "_domain", d)
|
||||
else:
|
||||
object.__setattr__(self, "_user", '')
|
||||
object.__setattr__(self, "_domain", value)
|
||||
self.regenerate()
|
||||
else:
|
||||
|
@@ -68,9 +68,6 @@ def tostring(xml=None, xmlns='', stanza_ns='', stream=None, outbuffer=''):
|
||||
for child in xml.getchildren():
|
||||
output.append(tostring(child, tag_xmlns, stanza_ns, stream))
|
||||
output.append(u"</%s>" % tag_name)
|
||||
if xml.tail:
|
||||
# If there is additional text after the element.
|
||||
output.append(xml_escape(xml.tail))
|
||||
elif xml.text:
|
||||
# If we only have text content.
|
||||
output.append(u">%s</%s>" % (xml_escape(xml.text), tag_name))
|
||||
|
@@ -786,6 +786,23 @@ class XMLStream(object):
|
||||
if unhandled:
|
||||
stanza.unhandled()
|
||||
|
||||
def _threaded_event_wrapper(self, func, args):
|
||||
"""
|
||||
Capture exceptions for event handlers that run
|
||||
in individual threads.
|
||||
|
||||
Arguments:
|
||||
func -- The event handler to execute.
|
||||
args -- Arguments to the event handler.
|
||||
"""
|
||||
try:
|
||||
func(*args)
|
||||
except Exception as e:
|
||||
error_msg = 'Error processing event handler: %s'
|
||||
logging.exception(error_msg % str(func))
|
||||
if hasattr(args[0], 'exception'):
|
||||
args[0].exception(e)
|
||||
|
||||
def _event_runner(self):
|
||||
"""
|
||||
Process the event queue and execute handlers.
|
||||
@@ -825,14 +842,18 @@ class XMLStream(object):
|
||||
func, threaded, disposable = handler
|
||||
try:
|
||||
if threaded:
|
||||
x = threading.Thread(name="Event_%s" % str(func),
|
||||
target=func,
|
||||
args=args)
|
||||
x = threading.Thread(
|
||||
name="Event_%s" % str(func),
|
||||
target=self._threaded_event_wrapper,
|
||||
args=(func, args))
|
||||
x.start()
|
||||
else:
|
||||
func(*args)
|
||||
except:
|
||||
logging.exception('Error processing event handler: %s')
|
||||
except Exception as e:
|
||||
error_msg = 'Error processing event handler: %s'
|
||||
logging.exception(error_msg % str(func))
|
||||
if hasattr(args[0], 'exception'):
|
||||
args[0].exception(e)
|
||||
elif etype == 'quit':
|
||||
logging.debug("Quitting event runner thread")
|
||||
return False
|
||||
|
@@ -3,26 +3,126 @@ from sleekxmpp.xmlstream.jid import JID
|
||||
|
||||
|
||||
class TestJIDClass(SleekTest):
|
||||
def testJIDfromfull(self):
|
||||
j = JID('user@someserver/some/resource')
|
||||
self.assertEqual(j.user, 'user', "User does not match")
|
||||
self.assertEqual(j.domain, 'someserver', "Domain does not match")
|
||||
self.assertEqual(j.resource, 'some/resource', "Resource does not match")
|
||||
self.assertEqual(j.bare, 'user@someserver', "Bare does not match")
|
||||
self.assertEqual(j.full, 'user@someserver/some/resource', "Full does not match")
|
||||
self.assertEqual(str(j), 'user@someserver/some/resource', "String does not match")
|
||||
|
||||
"""Verify that the JID class can parse and manipulate JIDs."""
|
||||
|
||||
def testJIDFromFull(self):
|
||||
"""Test using JID of the form 'user@server/resource/with/slashes'."""
|
||||
self.check_JID(JID('user@someserver/some/resource'),
|
||||
'user',
|
||||
'someserver',
|
||||
'some/resource',
|
||||
'user@someserver',
|
||||
'user@someserver/some/resource',
|
||||
'user@someserver/some/resource')
|
||||
|
||||
def testJIDchange(self):
|
||||
"""Test changing JID of the form 'user@server/resource/with/slashes'"""
|
||||
j = JID('user1@someserver1/some1/resource1')
|
||||
j.user = 'user'
|
||||
j.domain = 'someserver'
|
||||
j.resource = 'some/resource'
|
||||
self.assertEqual(j.user, 'user', "User does not match")
|
||||
self.assertEqual(j.domain, 'someserver', "Domain does not match")
|
||||
self.assertEqual(j.resource, 'some/resource', "Resource does not match")
|
||||
self.assertEqual(j.bare, 'user@someserver', "Bare does not match")
|
||||
self.assertEqual(j.full, 'user@someserver/some/resource', "Full does not match")
|
||||
self.assertEqual(str(j), 'user@someserver/some/resource', "String does not match")
|
||||
self.check_JID(j,
|
||||
'user',
|
||||
'someserver',
|
||||
'some/resource',
|
||||
'user@someserver',
|
||||
'user@someserver/some/resource',
|
||||
'user@someserver/some/resource')
|
||||
|
||||
def testJIDaliases(self):
|
||||
"""Test changing JID using aliases for domain."""
|
||||
j = JID('user@someserver/resource')
|
||||
j.server = 'anotherserver'
|
||||
self.check_JID(j, domain='anotherserver')
|
||||
j.host = 'yetanother'
|
||||
self.check_JID(j, domain='yetanother')
|
||||
|
||||
def testJIDSetFullWithUser(self):
|
||||
"""Test setting the full JID with a user portion."""
|
||||
j = JID('user@domain/resource')
|
||||
j.full = 'otheruser@otherdomain/otherresource'
|
||||
self.check_JID(j,
|
||||
'otheruser',
|
||||
'otherdomain',
|
||||
'otherresource',
|
||||
'otheruser@otherdomain',
|
||||
'otheruser@otherdomain/otherresource',
|
||||
'otheruser@otherdomain/otherresource')
|
||||
|
||||
def testJIDFullNoUserWithResource(self):
|
||||
"""
|
||||
Test setting the full JID without a user
|
||||
portion and with a resource.
|
||||
"""
|
||||
j = JID('user@domain/resource')
|
||||
j.full = 'otherdomain/otherresource'
|
||||
self.check_JID(j,
|
||||
'',
|
||||
'otherdomain',
|
||||
'otherresource',
|
||||
'otherdomain',
|
||||
'otherdomain/otherresource',
|
||||
'otherdomain/otherresource')
|
||||
|
||||
def testJIDFullNoUserNoResource(self):
|
||||
"""
|
||||
Test setting the full JID without a user
|
||||
portion and without a resource.
|
||||
"""
|
||||
j = JID('user@domain/resource')
|
||||
j.full = 'otherdomain'
|
||||
self.check_JID(j,
|
||||
'',
|
||||
'otherdomain',
|
||||
'',
|
||||
'otherdomain',
|
||||
'otherdomain',
|
||||
'otherdomain')
|
||||
|
||||
def testJIDBareUser(self):
|
||||
"""Test setting the bare JID with a user."""
|
||||
j = JID('user@domain/resource')
|
||||
j.bare = 'otheruser@otherdomain'
|
||||
self.check_JID(j,
|
||||
'otheruser',
|
||||
'otherdomain',
|
||||
'resource',
|
||||
'otheruser@otherdomain',
|
||||
'otheruser@otherdomain/resource',
|
||||
'otheruser@otherdomain/resource')
|
||||
|
||||
def testJIDBareNoUser(self):
|
||||
"""Test setting the bare JID without a user."""
|
||||
j = JID('user@domain/resource')
|
||||
j.bare = 'otherdomain'
|
||||
self.check_JID(j,
|
||||
'',
|
||||
'otherdomain',
|
||||
'resource',
|
||||
'otherdomain',
|
||||
'otherdomain/resource',
|
||||
'otherdomain/resource')
|
||||
|
||||
def testJIDNoResource(self):
|
||||
"""Test using JID of the form 'user@domain'."""
|
||||
self.check_JID(JID('user@someserver'),
|
||||
'user',
|
||||
'someserver',
|
||||
'',
|
||||
'user@someserver',
|
||||
'user@someserver',
|
||||
'user@someserver')
|
||||
|
||||
def testJIDNoUser(self):
|
||||
"""Test JID of the form 'component.domain.tld'."""
|
||||
self.check_JID(JID('component.someserver'),
|
||||
'',
|
||||
'component.someserver',
|
||||
'',
|
||||
'component.someserver',
|
||||
'component.someserver',
|
||||
'component.someserver')
|
||||
|
||||
|
||||
suite = unittest.TestLoader().loadTestsFromTestCase(TestJIDClass)
|
||||
|
@@ -56,4 +56,21 @@ class TestErrorStanzas(SleekTest):
|
||||
</message>
|
||||
""", use_values=False)
|
||||
|
||||
def testDelText(self):
|
||||
"""Test deleting the text of an error."""
|
||||
msg = self.Message()
|
||||
msg['error']['test'] = 'Error!'
|
||||
msg['error']['condition'] = 'internal-server-error'
|
||||
|
||||
del msg['error']['text']
|
||||
|
||||
self.check_message(msg, """
|
||||
<message type="error">
|
||||
<error type="cancel">
|
||||
<internal-server-error xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" />
|
||||
</error>
|
||||
</message>
|
||||
""")
|
||||
|
||||
|
||||
suite = unittest.TestLoader().loadTestsFromTestCase(TestErrorStanzas)
|
||||
|
110
tests/test_stream_exceptions.py
Normal file
110
tests/test_stream_exceptions.py
Normal file
@@ -0,0 +1,110 @@
|
||||
import sys
|
||||
import sleekxmpp
|
||||
from sleekxmpp.exceptions import XMPPError
|
||||
from sleekxmpp.test import *
|
||||
|
||||
|
||||
class TestStreamExceptions(SleekTest):
|
||||
"""
|
||||
Test handling roster updates.
|
||||
"""
|
||||
|
||||
def tearDown(self):
|
||||
self.stream_close()
|
||||
|
||||
def testXMPPErrorException(self):
|
||||
"""Test raising an XMPPError exception."""
|
||||
|
||||
def message(msg):
|
||||
raise XMPPError(condition='feature-not-implemented',
|
||||
text="We don't do things that way here.",
|
||||
etype='cancel',
|
||||
extension='foo',
|
||||
extension_ns='foo:error',
|
||||
extension_args={'test': 'true'})
|
||||
|
||||
self.stream_start()
|
||||
self.xmpp.add_event_handler('message', message)
|
||||
|
||||
self.stream_recv("""
|
||||
<message>
|
||||
<body>This is going to cause an error.</body>
|
||||
</message>
|
||||
""")
|
||||
|
||||
self.stream_send_message("""
|
||||
<message type="error">
|
||||
<error type="cancel">
|
||||
<feature-not-implemented
|
||||
xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" />
|
||||
<text xmlns="urn:ietf:params:xml:ns:xmpp-stanzas">
|
||||
We don't do things that way here.
|
||||
</text>
|
||||
<foo xmlns="foo:error" test="true" />
|
||||
</error>
|
||||
</message>
|
||||
""", use_values=False)
|
||||
|
||||
def testThreadedXMPPErrorException(self):
|
||||
"""Test raising an XMPPError exception in a threaded handler."""
|
||||
|
||||
def message(msg):
|
||||
raise XMPPError(condition='feature-not-implemented',
|
||||
text="We don't do things that way here.",
|
||||
etype='cancel')
|
||||
|
||||
self.stream_start()
|
||||
self.xmpp.add_event_handler('message', message,
|
||||
threaded=True)
|
||||
|
||||
self.stream_recv("""
|
||||
<message>
|
||||
<body>This is going to cause an error.</body>
|
||||
</message>
|
||||
""")
|
||||
|
||||
self.stream_send_message("""
|
||||
<message type="error">
|
||||
<error type="cancel">
|
||||
<feature-not-implemented
|
||||
xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" />
|
||||
<text xmlns="urn:ietf:params:xml:ns:xmpp-stanzas">
|
||||
We don't do things that way here.
|
||||
</text>
|
||||
</error>
|
||||
</message>
|
||||
""")
|
||||
|
||||
def testUnknownException(self):
|
||||
"""Test raising an generic exception in a threaded handler."""
|
||||
|
||||
def message(msg):
|
||||
raise ValueError("Did something wrong")
|
||||
|
||||
self.stream_start()
|
||||
self.xmpp.add_event_handler('message', message)
|
||||
|
||||
self.stream_recv("""
|
||||
<message>
|
||||
<body>This is going to cause an error.</body>
|
||||
</message>
|
||||
""")
|
||||
|
||||
if sys.version_info < (3, 0):
|
||||
self.stream_send_message("""
|
||||
<message type="error">
|
||||
<error type="cancel">
|
||||
<undefined-condition
|
||||
xmlns="urn:ietf:params:xml:ns:xmpp-stanzas" />
|
||||
<text xmlns="urn:ietf:params:xml:ns:xmpp-stanzas">
|
||||
SleekXMPP got into trouble.
|
||||
</text>
|
||||
</error>
|
||||
</message>
|
||||
""")
|
||||
else:
|
||||
# Unfortunately, tracebacks do not make for very portable tests.
|
||||
pass
|
||||
|
||||
|
||||
suite = unittest.TestLoader().loadTestsFromTestCase(TestStreamExceptions)
|
188
tests/test_stream_presence.py
Normal file
188
tests/test_stream_presence.py
Normal file
@@ -0,0 +1,188 @@
|
||||
import time
|
||||
from sleekxmpp.test import *
|
||||
|
||||
|
||||
class TestStreamPresence(SleekTest):
|
||||
"""
|
||||
Test handling roster updates.
|
||||
"""
|
||||
|
||||
def tearDown(self):
|
||||
self.stream_close()
|
||||
|
||||
def testInitialUnavailablePresences(self):
|
||||
"""
|
||||
Test receiving unavailable presences from JIDs that
|
||||
are not online.
|
||||
"""
|
||||
events = set()
|
||||
|
||||
def got_offline(presence):
|
||||
# The got_offline event should not be triggered.
|
||||
events.add('got_offline')
|
||||
|
||||
def unavailable(presence):
|
||||
# The presence_unavailable event should be triggered.
|
||||
events.add('unavailable')
|
||||
|
||||
self.stream_start()
|
||||
self.xmpp.add_event_handler('got_offline', got_offline)
|
||||
self.xmpp.add_event_handler('presence_unavailable', unavailable)
|
||||
|
||||
self.stream_recv("""
|
||||
<presence type="unavailable" from="otheruser@localhost" />
|
||||
""")
|
||||
|
||||
# Give event queue time to process.
|
||||
time.sleep(0.1)
|
||||
|
||||
self.assertEqual(events, set(('unavailable',)),
|
||||
"Got offline incorrectly triggered: %s." % events)
|
||||
|
||||
def testGotOffline(self):
|
||||
"""Test that got_offline is triggered properly."""
|
||||
events = []
|
||||
|
||||
def got_offline(presence):
|
||||
events.append('got_offline')
|
||||
|
||||
self.stream_start()
|
||||
self.xmpp.add_event_handler('got_offline', got_offline)
|
||||
|
||||
# Setup roster. Use a 'set' instead of 'result' so we
|
||||
# don't have to handle get_roster() blocking.
|
||||
#
|
||||
# We use the stream to initialize the roster to make
|
||||
# the test independent of the roster implementation.
|
||||
self.stream_recv("""
|
||||
<iq type="set">
|
||||
<query xmlns="jabber:iq:roster">
|
||||
<item jid="otheruser@localhost"
|
||||
name="Other User"
|
||||
subscription="both">
|
||||
<group>Testers</group>
|
||||
</item>
|
||||
</query>
|
||||
</iq>
|
||||
""")
|
||||
|
||||
# Contact comes online.
|
||||
self.stream_recv("""
|
||||
<presence from="otheruser@localhost/foobar" />
|
||||
""")
|
||||
|
||||
# Contact goes offline, should trigger got_offline.
|
||||
self.stream_recv("""
|
||||
<presence from="otheruser@localhost/foobar"
|
||||
type="unavailable" />
|
||||
""")
|
||||
|
||||
# Give event queue time to process.
|
||||
time.sleep(0.1)
|
||||
|
||||
self.assertEqual(events, ['got_offline'],
|
||||
"Got offline incorrectly triggered: %s" % events)
|
||||
|
||||
def testGotOnline(self):
|
||||
"""Test that got_online is triggered properly."""
|
||||
|
||||
events = set()
|
||||
|
||||
def presence_available(p):
|
||||
events.add('presence_available')
|
||||
|
||||
def got_online(p):
|
||||
events.add('got_online')
|
||||
|
||||
self.stream_start()
|
||||
self.xmpp.add_event_handler('presence_available', presence_available)
|
||||
self.xmpp.add_event_handler('got_online', got_online)
|
||||
|
||||
self.stream_recv("""
|
||||
<presence from="user@localhost" />
|
||||
""")
|
||||
|
||||
# Give event queue time to process.
|
||||
time.sleep(0.1)
|
||||
|
||||
expected = set(('presence_available', 'got_online'))
|
||||
self.assertEqual(events, expected,
|
||||
"Incorrect events triggered: %s" % events)
|
||||
|
||||
def testAutoAuthorizeAndSubscribe(self):
|
||||
"""
|
||||
Test auto authorizing and auto subscribing
|
||||
to subscription requests.
|
||||
"""
|
||||
|
||||
events = set()
|
||||
|
||||
def presence_subscribe(p):
|
||||
events.add('presence_subscribe')
|
||||
|
||||
def changed_subscription(p):
|
||||
events.add('changed_subscription')
|
||||
|
||||
self.stream_start(jid='tester@localhost')
|
||||
|
||||
self.xmpp.add_event_handler('changed_subscription',
|
||||
changed_subscription)
|
||||
self.xmpp.add_event_handler('presence_subscribe',
|
||||
presence_subscribe)
|
||||
|
||||
# With these settings we should accept a subscription
|
||||
# and request a subscription in return.
|
||||
self.xmpp.auto_authorize = True
|
||||
self.xmpp.auto_subscribe = True
|
||||
|
||||
self.stream_recv("""
|
||||
<presence from="user@localhost" type="subscribe" />
|
||||
""")
|
||||
|
||||
self.stream_send_presence("""
|
||||
<presence to="user@localhost" type="subscribed" />
|
||||
""")
|
||||
|
||||
self.stream_send_presence("""
|
||||
<presence to="user@localhost" type="subscribe" />
|
||||
""")
|
||||
|
||||
expected = set(('presence_subscribe', 'changed_subscription'))
|
||||
self.assertEqual(events, expected,
|
||||
"Incorrect events triggered: %s" % events)
|
||||
|
||||
def testNoAutoAuthorize(self):
|
||||
"""Test auto rejecting subscription requests."""
|
||||
|
||||
events = set()
|
||||
|
||||
def presence_subscribe(p):
|
||||
events.add('presence_subscribe')
|
||||
|
||||
def changed_subscription(p):
|
||||
events.add('changed_subscription')
|
||||
|
||||
self.stream_start(jid='tester@localhost')
|
||||
|
||||
self.xmpp.add_event_handler('changed_subscription',
|
||||
changed_subscription)
|
||||
self.xmpp.add_event_handler('presence_subscribe',
|
||||
presence_subscribe)
|
||||
|
||||
# With this setting we should reject all subscriptions.
|
||||
self.xmpp.auto_authorize = False
|
||||
|
||||
self.stream_recv("""
|
||||
<presence from="user@localhost" type="subscribe" />
|
||||
""")
|
||||
|
||||
self.stream_send_presence("""
|
||||
<presence to="user@localhost" type="unsubscribed" />
|
||||
""")
|
||||
|
||||
expected = set(('presence_subscribe', 'changed_subscription'))
|
||||
self.assertEqual(events, expected,
|
||||
"Incorrect events triggered: %s" % events)
|
||||
|
||||
|
||||
suite = unittest.TestLoader().loadTestsFromTestCase(TestStreamPresence)
|
86
tests/test_stream_roster.py
Normal file
86
tests/test_stream_roster.py
Normal file
@@ -0,0 +1,86 @@
|
||||
from sleekxmpp.test import *
|
||||
import time
|
||||
import threading
|
||||
|
||||
|
||||
class TestStreamRoster(SleekTest):
|
||||
"""
|
||||
Test handling roster updates.
|
||||
"""
|
||||
|
||||
def tearDown(self):
|
||||
self.stream_close()
|
||||
|
||||
def testGetRoster(self):
|
||||
"""Test handling roster requests."""
|
||||
self.stream_start(mode='client')
|
||||
self.failUnless(self.xmpp.roster == {}, "Initial roster not empty.")
|
||||
|
||||
# Since get_roster blocks, we need to run it in a thread.
|
||||
t = threading.Thread(name='get_roster', target=self.xmpp.get_roster)
|
||||
t.start()
|
||||
|
||||
self.stream_send_iq("""
|
||||
<iq type="get" id="1">
|
||||
<query xmlns="jabber:iq:roster" />
|
||||
</iq>
|
||||
""")
|
||||
self.stream_recv("""
|
||||
<iq type="result" id="1">
|
||||
<query xmlns="jabber:iq:roster">
|
||||
<item jid="user@localhost"
|
||||
name="User"
|
||||
subscription="both">
|
||||
<group>Friends</group>
|
||||
<group>Examples</group>
|
||||
</item>
|
||||
</query>
|
||||
</iq>
|
||||
""")
|
||||
|
||||
# Wait for get_roster to return.
|
||||
t.join()
|
||||
|
||||
roster = {'user@localhost': {'name': 'User',
|
||||
'subscription': 'both',
|
||||
'groups': ['Friends', 'Examples'],
|
||||
'presence': {},
|
||||
'in_roster': True}}
|
||||
self.failUnless(self.xmpp.roster == roster,
|
||||
"Unexpected roster values: %s" % self.xmpp.roster)
|
||||
|
||||
def testRosterSet(self):
|
||||
"""Test handling pushed roster updates."""
|
||||
self.stream_start(mode='client')
|
||||
self.failUnless(self.xmpp.roster == {}, "Initial roster not empty.")
|
||||
|
||||
self.stream_recv("""
|
||||
<iq type="set" id="1">
|
||||
<query xmlns="jabber:iq:roster">
|
||||
<item jid="user@localhost"
|
||||
name="User"
|
||||
subscription="both">
|
||||
<group>Friends</group>
|
||||
<group>Examples</group>
|
||||
</item>
|
||||
</query>
|
||||
</iq>
|
||||
""")
|
||||
self.stream_send_iq("""
|
||||
<iq type="result" id="1">
|
||||
<query xmlns="jabber:iq:roster" />
|
||||
</iq>
|
||||
""")
|
||||
|
||||
roster = {'user@localhost': {'name': 'User',
|
||||
'subscription': 'both',
|
||||
'groups': ['Friends', 'Examples'],
|
||||
'presence': {},
|
||||
'in_roster': True}}
|
||||
self.failUnless(self.xmpp.roster == roster,
|
||||
"Unexpected roster values: %s" % self.xmpp.roster)
|
||||
|
||||
|
||||
|
||||
|
||||
suite = unittest.TestLoader().loadTestsFromTestCase(TestStreamRoster)
|
@@ -73,6 +73,16 @@ class TestToString(SleekTest):
|
||||
message="The xmlns parameter was not used properly.",
|
||||
xmlns='foo')
|
||||
|
||||
def testTailContent(self):
|
||||
"""
|
||||
Test that elements of the form <a>foo <b>bar</b> baz</a> only
|
||||
include " baz" once.
|
||||
"""
|
||||
self.tryTostring(
|
||||
original='<a>foo <b>bar</b> baz</a>',
|
||||
message='Element tail content is incorrect.')
|
||||
|
||||
|
||||
def testStanzaNs(self):
|
||||
"""
|
||||
Test using the stanza_ns tostring parameter, which will prevent
|
||||
|
Reference in New Issue
Block a user