diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/test_stanza_xep_0030.py | 504 | ||||
-rw-r--r-- | tests/test_stream_handlers.py | 38 | ||||
-rw-r--r-- | tests/test_stream_xep_0030.py | 528 | ||||
-rw-r--r-- | tests/test_tostring.py | 18 |
4 files changed, 1005 insertions, 83 deletions
diff --git a/tests/test_stanza_xep_0030.py b/tests/test_stanza_xep_0030.py index e367c8d9..2d64988d 100644 --- a/tests/test_stanza_xep_0030.py +++ b/tests/test_stanza_xep_0030.py @@ -4,6 +4,11 @@ import sleekxmpp.plugins.xep_0030 as xep_0030 class TestDisco(SleekTest): + """ + Test creating and manipulating the disco#info and + disco#items stanzas from the XEP-0030 plugin. + """ + def setUp(self): register_stanza_plugin(Iq, xep_0030.DiscoInfo) register_stanza_plugin(Iq, xep_0030.DiscoItems) @@ -11,11 +16,10 @@ class TestDisco(SleekTest): def testCreateInfoQueryNoNode(self): """Testing disco#info query with no node.""" iq = self.Iq() - iq['id'] = "0" iq['disco_info']['node'] = '' self.check(iq, """ - <iq id="0"> + <iq> <query xmlns="http://jabber.org/protocol/disco#info" /> </iq> """) @@ -23,23 +27,22 @@ class TestDisco(SleekTest): def testCreateInfoQueryWithNode(self): """Testing disco#info query with a node.""" iq = self.Iq() - iq['id'] = "0" iq['disco_info']['node'] = 'foo' self.check(iq, """ - <iq id="0"> - <query xmlns="http://jabber.org/protocol/disco#info" node="foo" /> + <iq> + <query xmlns="http://jabber.org/protocol/disco#info" + node="foo" /> </iq> """) - def testCreateInfoQueryNoNode(self): + def testCreateItemsQueryNoNode(self): """Testing disco#items query with no node.""" iq = self.Iq() - iq['id'] = "0" iq['disco_items']['node'] = '' self.check(iq, """ - <iq id="0"> + <iq> <query xmlns="http://jabber.org/protocol/disco#items" /> </iq> """) @@ -47,130 +50,467 @@ class TestDisco(SleekTest): def testCreateItemsQueryWithNode(self): """Testing disco#items query with a node.""" iq = self.Iq() - iq['id'] = "0" iq['disco_items']['node'] = 'foo' self.check(iq, """ - <iq id="0"> - <query xmlns="http://jabber.org/protocol/disco#items" node="foo" /> + <iq> + <query xmlns="http://jabber.org/protocol/disco#items" + node="foo" /> </iq> """) - def testInfoIdentities(self): + def testIdentities(self): """Testing adding identities to disco#info.""" iq = self.Iq() - iq['id'] = "0" - iq['disco_info']['node'] = 'foo' - iq['disco_info'].addIdentity('conference', 'text', 'Chatroom') + iq['disco_info'].add_identity('conference', 'text', + name='Chatroom', + lang='en') + + self.check(iq, """ + <iq> + <query xmlns="http://jabber.org/protocol/disco#info"> + <identity category="conference" + type="text" + name="Chatroom" + xml:lang="en" /> + </query> + </iq> + """) + + def testDuplicateIdentities(self): + """ + Test adding multiple copies of the same category + and type combination. Only the first identity should + be kept. + """ + iq = self.Iq() + iq['disco_info'].add_identity('conference', 'text', + name='Chatroom') + iq['disco_info'].add_identity('conference', 'text', + name='MUC') + self.check(iq, """ + <iq> + <query xmlns="http://jabber.org/protocol/disco#info"> + <identity category="conference" + type="text" + name="Chatroom" /> + </query> + </iq> + """) + + def testDuplicateIdentitiesWithLangs(self): + """ + Test adding multiple copies of the same category, + type, and language combination. Only the first identity + should be kept. + """ + iq = self.Iq() + iq['disco_info'].add_identity('conference', 'text', + name='Chatroom', + lang='en') + iq['disco_info'].add_identity('conference', 'text', + name='MUC', + lang='en') + self.check(iq, """ + <iq> + <query xmlns="http://jabber.org/protocol/disco#info"> + <identity category="conference" + type="text" + name="Chatroom" + xml:lang="en" /> + </query> + </iq> + """) + + def testRemoveIdentitiesNoLang(self): + """Test removing identities from a disco#info stanza.""" + iq = self.Iq() + iq['disco_info'].add_identity('client', 'pc') + iq['disco_info'].add_identity('client', 'bot') + + iq['disco_info'].del_identity('client', 'pc') + + self.check(iq, """ + <iq> + <query xmlns="http://jabber.org/protocol/disco#info"> + <identity category="client" type="bot" /> + </query> + </iq> + """) + + def testRemoveIdentitiesWithLang(self): + """Test removing identities from a disco#info stanza.""" + iq = self.Iq() + iq['disco_info'].add_identity('client', 'pc') + iq['disco_info'].add_identity('client', 'bot') + iq['disco_info'].add_identity('client', 'pc', lang='no') + + iq['disco_info'].del_identity('client', 'pc') self.check(iq, """ - <iq id="0"> - <query xmlns="http://jabber.org/protocol/disco#info" node="foo"> - <identity category="conference" type="text" name="Chatroom" /> + <iq> + <query xmlns="http://jabber.org/protocol/disco#info"> + <identity category="client" type="bot" /> + <identity category="client" + type="pc" + xml:lang="no" /> </query> </iq> """) - def testInfoFeatures(self): + def testRemoveAllIdentitiesNoLang(self): + """Test removing all identities from a disco#info stanza.""" + iq = self.Iq() + iq['disco_info'].add_identity('client', 'bot', name='Bot') + iq['disco_info'].add_identity('client', 'bot', lang='no') + iq['disco_info'].add_identity('client', 'console') + + del iq['disco_info']['identities'] + + self.check(iq, """ + <iq> + <query xmlns="http://jabber.org/protocol/disco#info" /> + </iq> + """) + + def testRemoveAllIdentitiesWithLang(self): + """Test removing all identities from a disco#info stanza.""" + iq = self.Iq() + iq['disco_info'].add_identity('client', 'bot', name='Bot') + iq['disco_info'].add_identity('client', 'bot', lang='no') + iq['disco_info'].add_identity('client', 'console') + + iq['disco_info'].del_identities(lang='no') + + self.check(iq, """ + <iq> + <query xmlns="http://jabber.org/protocol/disco#info"> + <identity category="client" type="bot" name="Bot" /> + <identity category="client" type="console" /> + </query> + </iq> + """) + + def testAddBatchIdentitiesNoLang(self): + """Test adding multiple identities at once to a disco#info stanza.""" + iq = self.Iq() + identities = [('client', 'pc', 'no', 'PC Client'), + ('client', 'bot', None, 'Bot'), + ('client', 'console', None, None)] + + iq['disco_info']['identities'] = identities + + self.check(iq, """ + <iq> + <query xmlns="http://jabber.org/protocol/disco#info"> + <identity category="client" + type="pc" + xml:lang="no" + name="PC Client" /> + <identity category="client" type="bot" name="Bot" /> + <identity category="client" type="console" /> + </query> + </iq> + """) + + + def testAddBatchIdentitiesWithLang(self): + """Test selectively replacing identities based on language.""" + iq = self.Iq() + iq['disco_info'].add_identity('client', 'pc', lang='no') + iq['disco_info'].add_identity('client', 'pc', lang='en') + iq['disco_info'].add_identity('client', 'pc', lang='fr') + + identities = [('client', 'bot', 'fr', 'Bot'), + ('client', 'bot', 'en', 'Bot')] + + iq['disco_info'].set_identities(identities, lang='fr') + + self.check(iq, """ + <iq> + <query xmlns="http://jabber.org/protocol/disco#info"> + <identity category="client" type="pc" xml:lang="no" /> + <identity category="client" type="pc" xml:lang="en" /> + <identity category="client" + type="bot" + xml:lang="fr" + name="Bot" /> + <identity category="client" + type="bot" + xml:lang="en" + name="Bot" /> + </query> + </iq> + """) + + def testGetIdentitiesNoLang(self): + """Test getting all identities from a disco#info stanza.""" + iq = self.Iq() + iq['disco_info'].add_identity('client', 'pc') + iq['disco_info'].add_identity('client', 'pc', lang='no') + iq['disco_info'].add_identity('client', 'pc', lang='en') + iq['disco_info'].add_identity('client', 'pc', lang='fr') + + expected = set([('client', 'pc', None, None), + ('client', 'pc', 'no', None), + ('client', 'pc', 'en', None), + ('client', 'pc', 'fr', None)]) + self.failUnless(iq['disco_info']['identities'] == expected, + "Identities do not match:\n%s\n%s" % ( + expected, + iq['disco_info']['identities'])) + + def testGetIdentitiesWithLang(self): + """ + Test getting all identities of a given + lang from a disco#info stanza. + """ + iq = self.Iq() + iq['disco_info'].add_identity('client', 'pc') + iq['disco_info'].add_identity('client', 'pc', lang='no') + iq['disco_info'].add_identity('client', 'pc', lang='en') + iq['disco_info'].add_identity('client', 'pc', lang='fr') + + expected = set([('client', 'pc', 'no', None)]) + result = iq['disco_info'].get_identities(lang='no') + self.failUnless(result == expected, + "Identities do not match:\n%s\n%s" % ( + expected, result)) + + def testFeatures(self): """Testing adding features to disco#info.""" iq = self.Iq() - iq['id'] = "0" - iq['disco_info']['node'] = 'foo' - iq['disco_info'].addFeature('foo') - iq['disco_info'].addFeature('bar') + iq['disco_info'].add_feature('foo') + iq['disco_info'].add_feature('bar') self.check(iq, """ - <iq id="0"> - <query xmlns="http://jabber.org/protocol/disco#info" node="foo"> + <iq> + <query xmlns="http://jabber.org/protocol/disco#info"> <feature var="foo" /> <feature var="bar" /> </query> </iq> """) - def testItems(self): - """Testing adding features to disco#info.""" + def testFeaturesDuplicate(self): + """Test adding duplicate features to disco#info.""" iq = self.Iq() - iq['id'] = "0" - iq['disco_items']['node'] = 'foo' - iq['disco_items'].addItem('user@localhost') - iq['disco_items'].addItem('user@localhost', 'foo') - iq['disco_items'].addItem('user@localhost', 'bar', 'Testing') + iq['disco_info'].add_feature('foo') + iq['disco_info'].add_feature('bar') + iq['disco_info'].add_feature('foo') self.check(iq, """ - <iq id="0"> - <query xmlns="http://jabber.org/protocol/disco#items" node="foo"> - <item jid="user@localhost" /> - <item node="foo" jid="user@localhost" /> - <item node="bar" jid="user@localhost" name="Testing" /> + <iq> + <query xmlns="http://jabber.org/protocol/disco#info"> + <feature var="foo" /> + <feature var="bar" /> </query> </iq> """) - def testAddRemoveIdentities(self): - """Test adding and removing identities to disco#info stanza""" - ids = [('automation', 'commands', 'AdHoc'), - ('conference', 'text', 'ChatRoom')] + def testRemoveFeature(self): + """Test removing a feature from disco#info.""" + iq = self.Iq() + iq['disco_info'].add_feature('foo') + iq['disco_info'].add_feature('bar') + iq['disco_info'].add_feature('baz') - info = xep_0030.DiscoInfo() - info.addIdentity(*ids[0]) - self.failUnless(info.getIdentities() == [ids[0]]) + iq['disco_info'].del_feature('foo') - info.delIdentity('automation', 'commands') - self.failUnless(info.getIdentities() == []) + self.check(iq, """ + <iq> + <query xmlns="http://jabber.org/protocol/disco#info"> + <feature var="bar" /> + <feature var="baz" /> + </query> + </iq> + """) - info.setIdentities(ids) - self.failUnless(info.getIdentities() == ids) + def testGetFeatures(self): + """Test getting all features from a disco#info stanza.""" + iq = self.Iq() + iq['disco_info'].add_feature('foo') + iq['disco_info'].add_feature('bar') + iq['disco_info'].add_feature('baz') + + expected = set(['foo', 'bar', 'baz']) + self.failUnless(iq['disco_info']['features'] == expected, + "Features do not match:\n%s\n%s" % ( + expected, + iq['disco_info']['features'])) + + def testRemoveAllFeatures(self): + """Test removing all features from a disco#info stanza.""" + iq = self.Iq() + iq['disco_info'].add_feature('foo') + iq['disco_info'].add_feature('bar') + iq['disco_info'].add_feature('baz') - info.delIdentity('automation', 'commands') - self.failUnless(info.getIdentities() == [ids[1]]) + del iq['disco_info']['features'] - info.delIdentities() - self.failUnless(info.getIdentities() == []) + self.check(iq, """ + <iq> + <query xmlns="http://jabber.org/protocol/disco#info" /> + </iq> + """) - def testAddRemoveFeatures(self): - """Test adding and removing features to disco#info stanza""" + def testAddBatchFeatures(self): + """Test adding multiple features at once to a disco#info stanza.""" + iq = self.Iq() features = ['foo', 'bar', 'baz'] - info = xep_0030.DiscoInfo() - info.addFeature(features[0]) - self.failUnless(info.getFeatures() == [features[0]]) + iq['disco_info']['features'] = features + + self.check(iq, """ + <iq> + <query xmlns="http://jabber.org/protocol/disco#info"> + <feature var="foo" /> + <feature var="bar" /> + <feature var="baz" /> + </query> + </iq> + """) + + def testItems(self): + """Testing adding features to disco#info.""" + iq = self.Iq() + iq['disco_items'].add_item('user@localhost') + iq['disco_items'].add_item('user@localhost', 'foo') + iq['disco_items'].add_item('user@localhost', 'bar', name='Testing') + + self.check(iq, """ + <iq> + <query xmlns="http://jabber.org/protocol/disco#items"> + <item jid="user@localhost" /> + <item jid="user@localhost" + node="foo" /> + <item jid="user@localhost" + node="bar" + name="Testing" /> + </query> + </iq> + """) + + def testDuplicateItems(self): + """Test adding items with the same JID without any nodes.""" + iq = self.Iq() + iq['disco_items'].add_item('user@localhost', name='First') + iq['disco_items'].add_item('user@localhost', name='Second') + + self.check(iq, """ + <iq> + <query xmlns="http://jabber.org/protocol/disco#items"> + <item jid="user@localhost" name="First" /> + </query> + </iq> + """) - info.delFeature('foo') - self.failUnless(info.getFeatures() == []) - info.setFeatures(features) - self.failUnless(info.getFeatures() == features) + def testDuplicateItemsWithNodes(self): + """Test adding items with the same JID/node combination.""" + iq = self.Iq() + iq['disco_items'].add_item('user@localhost', + node='foo', + name='First') + iq['disco_items'].add_item('user@localhost', + node='foo', + name='Second') - info.delFeature('bar') - self.failUnless(info.getFeatures() == ['foo', 'baz']) + self.check(iq, """ + <iq> + <query xmlns="http://jabber.org/protocol/disco#items"> + <item jid="user@localhost" node="foo" name="First" /> + </query> + </iq> + """) - info.delFeatures() - self.failUnless(info.getFeatures() == []) + def testRemoveItemsNoNode(self): + """Test removing items without nodes from a disco#items stanza.""" + iq = self.Iq() + iq['disco_items'].add_item('user@localhost') + iq['disco_items'].add_item('user@localhost', node='foo') + iq['disco_items'].add_item('test@localhost') - def testAddRemoveItems(self): - """Test adding and removing items to disco#items stanza""" - items = [('user@localhost', None, None), - ('user@localhost', 'foo', None), - ('user@localhost', 'bar', 'Test')] + iq['disco_items'].del_item('user@localhost') - info = xep_0030.DiscoItems() - self.failUnless(True, ""+str(items[0])) + self.check(iq, """ + <iq> + <query xmlns="http://jabber.org/protocol/disco#items"> + <item jid="user@localhost" node="foo" /> + <item jid="test@localhost" /> + </query> + </iq> + """) - info.addItem(*(items[0])) - self.failUnless(info.getItems() == [items[0]], info.getItems()) + def testRemoveItemsWithNode(self): + """Test removing items with nodes from a disco#items stanza.""" + iq = self.Iq() + iq['disco_items'].add_item('user@localhost') + iq['disco_items'].add_item('user@localhost', node='foo') + iq['disco_items'].add_item('test@localhost') - info.delItem('user@localhost') - self.failUnless(info.getItems() == []) + iq['disco_items'].del_item('user@localhost', node='foo') - info.setItems(items) - self.failUnless(info.getItems() == items) + self.check(iq, """ + <iq> + <query xmlns="http://jabber.org/protocol/disco#items"> + <item jid="user@localhost" /> + <item jid="test@localhost" /> + </query> + </iq> + """) - info.delItem('user@localhost', 'foo') - self.failUnless(info.getItems() == [items[0], items[2]]) + def testGetItems(self): + """Test retrieving items from disco#items stanza.""" + iq = self.Iq() + iq['disco_items'].add_item('user@localhost') + iq['disco_items'].add_item('user@localhost', node='foo') + iq['disco_items'].add_item('test@localhost', + node='bar', + name='Tester') + + expected = set([('user@localhost', None, None), + ('user@localhost', 'foo', None), + ('test@localhost', 'bar', 'Tester')]) + self.failUnless(iq['disco_items']['items'] == expected, + "Items do not match:\n%s\n%s" % ( + expected, + iq['disco_items']['items'])) + + def testRemoveAllItems(self): + """Test removing all items from a disco#items stanza.""" + iq = self.Iq() + iq['disco_items'].add_item('user@localhost') + iq['disco_items'].add_item('user@localhost', node='foo') + iq['disco_items'].add_item('test@localhost', + node='bar', + name='Tester') - info.delItems() - self.failUnless(info.getItems() == []) + del iq['disco_items']['items'] + self.check(iq, """ + <iq> + <query xmlns="http://jabber.org/protocol/disco#items" /> + </iq> + """) + + def testAddBatchItems(self): + """Test adding multiple items to a disco#items stanza.""" + iq = self.Iq() + items = [('user@localhost', 'foo', 'Test'), + ('test@localhost', None, None), + ('other@localhost', None, 'Other')] + + iq['disco_items']['items'] = items + + self.check(iq, """ + <iq> + <query xmlns="http://jabber.org/protocol/disco#items"> + <item jid="user@localhost" node="foo" name="Test" /> + <item jid="test@localhost" /> + <item jid="other@localhost" name="Other" /> + </query> + </iq> + """) suite = unittest.TestLoader().loadTestsFromTestCase(TestDisco) diff --git a/tests/test_stream_handlers.py b/tests/test_stream_handlers.py index 2b878b37..a475b36c 100644 --- a/tests/test_stream_handlers.py +++ b/tests/test_stream_handlers.py @@ -1,3 +1,5 @@ +import time + from sleekxmpp.test import * from sleekxmpp.xmlstream.handler import * from sleekxmpp.xmlstream.matcher import * @@ -108,5 +110,41 @@ class TestHandlers(SleekTest): self.failUnless(waiter_exists == False, "Waiter handler was not removed.") + def testIqCallback(self): + """Test that iq.send(callback=handle_foo) works.""" + events = [] + + def handle_foo(iq): + events.append('foo') + + iq = self.Iq() + iq['type'] = 'get' + iq['id'] = 'test-foo' + iq['to'] = 'user@localhost' + iq['query'] = 'foo' + iq.send(callback=handle_foo) + + self.send(""" + <iq type="get" id="test-foo" to="user@localhost"> + <query xmlns="foo" /> + </iq> + """) + + self.recv(""" + <iq type="result" id="test-foo" + to="test@localhost" + from="user@localhost"> + <query xmlns="foo"> + <data /> + </query> + </iq> + """) + + # Give event queue time to process + time.sleep(0.1) + + self.failUnless(events == ['foo'], + "Iq callback was not executed: %s" % events) + suite = unittest.TestLoader().loadTestsFromTestCase(TestHandlers) diff --git a/tests/test_stream_xep_0030.py b/tests/test_stream_xep_0030.py new file mode 100644 index 00000000..1f989745 --- /dev/null +++ b/tests/test_stream_xep_0030.py @@ -0,0 +1,528 @@ +import time +import threading + +from sleekxmpp.test import * + + +class TestStreamDisco(SleekTest): + + """ + Test using the XEP-0030 plugin. + """ + + def tearDown(self): + self.stream_close() + + def testInfoEmptyDefaultNode(self): + """ + Info query result from an entity MUST have at least one identity + and feature, namely http://jabber.org/protocol/disco#info. + + Since the XEP-0030 plugin is loaded, a disco response should + be generated and not an error result. + """ + self.stream_start(mode='client', + plugins=['xep_0030']) + + self.recv(""" + <iq type="get" id="test"> + <query xmlns="http://jabber.org/protocol/disco#info" /> + </iq> + """) + + self.send(""" + <iq type="result" id="test"> + <query xmlns="http://jabber.org/protocol/disco#info"> + <identity category="client" type="bot" /> + <feature var="http://jabber.org/protocol/disco#info" /> + </query> + </iq> + """) + + def testInfoEmptyDefaultNodeComponent(self): + """ + Test requesting an empty, default node using a Component. + """ + self.stream_start(mode='component', + jid='tester.localhost', + plugins=['xep_0030']) + + self.recv(""" + <iq type="get" id="test"> + <query xmlns="http://jabber.org/protocol/disco#info" /> + </iq> + """) + + self.send(""" + <iq type="result" id="test"> + <query xmlns="http://jabber.org/protocol/disco#info"> + <identity category="component" type="generic" /> + <feature var="http://jabber.org/protocol/disco#info" /> + </query> + </iq> + """) + + def testInfoIncludeNode(self): + """ + Results for info queries directed to a particular node MUST + include the node in the query response. + """ + self.stream_start(mode='client', + plugins=['xep_0030']) + + + self.xmpp['xep_0030'].static.add_node(node='testing') + + self.recv(""" + <iq to="tester@localhost" type="get" id="test"> + <query xmlns="http://jabber.org/protocol/disco#info" + node="testing" /> + </iq> + """) + + self.send(""" + <iq type="result" id="test"> + <query xmlns="http://jabber.org/protocol/disco#info" + node="testing"> + </query> + </iq>""", + method='mask') + + def testItemsIncludeNode(self): + """ + Results for items queries directed to a particular node MUST + include the node in the query response. + """ + self.stream_start(mode='client', + plugins=['xep_0030']) + + + self.xmpp['xep_0030'].static.add_node(node='testing') + + self.recv(""" + <iq to="tester@localhost" type="get" id="test"> + <query xmlns="http://jabber.org/protocol/disco#items" + node="testing" /> + </iq> + """) + + self.send(""" + <iq type="result" id="test"> + <query xmlns="http://jabber.org/protocol/disco#items" + node="testing"> + </query> + </iq>""", + method='mask') + + def testDynamicInfoJID(self): + """ + Test using a dynamic info handler for a particular JID. + """ + self.stream_start(mode='client', + plugins=['xep_0030']) + + def dynamic_jid(jid, node, iq): + result = self.xmpp['xep_0030'].stanza.DiscoInfo() + result['node'] = node + result.add_identity('client', 'console', name='Dynamic Info') + return result + + self.xmpp['xep_0030'].set_node_handler('get_info', + jid='tester@localhost', + handler=dynamic_jid) + + self.recv(""" + <iq type="get" id="test" to="tester@localhost"> + <query xmlns="http://jabber.org/protocol/disco#info" + node="testing" /> + </iq> + """) + + self.send(""" + <iq type="result" id="test"> + <query xmlns="http://jabber.org/protocol/disco#info" + node="testing"> + <identity category="client" + type="console" + name="Dynamic Info" /> + </query> + </iq> + """) + + def testDynamicInfoGlobal(self): + """ + Test using a dynamic info handler for all requests. + """ + self.stream_start(mode='component', + jid='tester.localhost', + plugins=['xep_0030']) + + def dynamic_global(jid, node, iq): + result = self.xmpp['xep_0030'].stanza.DiscoInfo() + result['node'] = node + result.add_identity('component', 'generic', name='Dynamic Info') + return result + + self.xmpp['xep_0030'].set_node_handler('get_info', + handler=dynamic_global) + + self.recv(""" + <iq type="get" id="test" + to="user@tester.localhost" + from="tester@localhost"> + <query xmlns="http://jabber.org/protocol/disco#info" + node="testing" /> + </iq> + """) + + self.send(""" + <iq type="result" id="test" + to="tester@localhost" + from="user@tester.localhost"> + <query xmlns="http://jabber.org/protocol/disco#info" + node="testing"> + <identity category="component" + type="generic" + name="Dynamic Info" /> + </query> + </iq> + """) + + def testOverrideJIDInfoHandler(self): + """Test overriding a JID info handler.""" + self.stream_start(mode='client', + plugins=['xep_0030']) + + def dynamic_jid(jid, node, iq): + result = self.xmpp['xep_0030'].stanza.DiscoInfo() + result['node'] = node + result.add_identity('client', 'console', name='Dynamic Info') + return result + + self.xmpp['xep_0030'].set_node_handler('get_info', + jid='tester@localhost', + handler=dynamic_jid) + + + self.xmpp['xep_0030'].make_static(jid='tester@localhost', + node='testing') + + self.xmpp['xep_0030'].add_identity(jid='tester@localhost', + node='testing', + category='automation', + itype='command-list') + + self.recv(""" + <iq type="get" id="test" to="tester@localhost"> + <query xmlns="http://jabber.org/protocol/disco#info" + node="testing" /> + </iq> + """) + + self.send(""" + <iq type="result" id="test"> + <query xmlns="http://jabber.org/protocol/disco#info" + node="testing"> + <identity category="automation" + type="command-list" /> + </query> + </iq> + """) + + def testOverrideGlobalInfoHandler(self): + """Test overriding the global JID info handler.""" + self.stream_start(mode='component', + jid='tester.localhost', + plugins=['xep_0030']) + + def dynamic_global(jid, node, iq): + result = self.xmpp['xep_0030'].stanza.DiscoInfo() + result['node'] = node + result.add_identity('component', 'generic', name='Dynamic Info') + return result + + self.xmpp['xep_0030'].set_node_handler('get_info', + handler=dynamic_global) + + self.xmpp['xep_0030'].make_static(jid='user@tester.localhost', + node='testing') + + self.xmpp['xep_0030'].add_feature(jid='user@tester.localhost', + node='testing', + feature='urn:xmpp:ping') + + self.recv(""" + <iq type="get" id="test" + to="user@tester.localhost" + from="tester@localhost"> + <query xmlns="http://jabber.org/protocol/disco#info" + node="testing" /> + </iq> + """) + + self.send(""" + <iq type="result" id="test" + to="tester@localhost" + from="user@tester.localhost"> + <query xmlns="http://jabber.org/protocol/disco#info" + node="testing"> + <feature var="urn:xmpp:ping" /> + </query> + </iq> + """) + + def testGetInfoRemote(self): + """ + Test sending a disco#info query to another entity + and receiving the result. + """ + self.stream_start(mode='client', + plugins=['xep_0030']) + + events = set() + + def handle_disco_info(iq): + events.add('disco_info') + + + self.xmpp.add_event_handler('disco_info', handle_disco_info) + + t = threading.Thread(name="get_info", + target=self.xmpp['xep_0030'].get_info, + args=('user@localhost', 'foo')) + t.start() + + self.send(""" + <iq type="get" to="user@localhost" id="1"> + <query xmlns="http://jabber.org/protocol/disco#info" + node="foo" /> + </iq> + """) + + self.recv(""" + <iq type="result" to="tester@localhost" id="1"> + <query xmlns="http://jabber.org/protocol/disco#info" + node="foo"> + <identity category="client" type="bot" /> + <feature var="urn:xmpp:ping" /> + </query> + </iq> + """) + + # Wait for disco#info request to be received. + t.join() + + time.sleep(0.1) + + self.assertEqual(events, set(('disco_info',)), + "Disco info event was not triggered: %s" % events) + + def testDynamicItemsJID(self): + """ + Test using a dynamic items handler for a particular JID. + """ + self.stream_start(mode='client', + plugins=['xep_0030']) + + def dynamic_jid(jid, node, iq): + result = self.xmpp['xep_0030'].stanza.DiscoItems() + result['node'] = node + result.add_item('tester@localhost', node='foo', name='JID') + return result + + self.xmpp['xep_0030'].set_node_handler('get_items', + jid='tester@localhost', + handler=dynamic_jid) + + self.recv(""" + <iq type="get" id="test" to="tester@localhost"> + <query xmlns="http://jabber.org/protocol/disco#items" + node="testing" /> + </iq> + """) + + self.send(""" + <iq type="result" id="test"> + <query xmlns="http://jabber.org/protocol/disco#items" + node="testing"> + <item jid="tester@localhost" node="foo" name="JID" /> + </query> + </iq> + """) + + def testDynamicItemsGlobal(self): + """ + Test using a dynamic items handler for all requests. + """ + self.stream_start(mode='component', + jid='tester.localhost', + plugins=['xep_0030']) + + def dynamic_global(jid, node, iq): + result = self.xmpp['xep_0030'].stanza.DiscoItems() + result['node'] = node + result.add_item('tester@localhost', node='foo', name='Global') + return result + + self.xmpp['xep_0030'].set_node_handler('get_items', + handler=dynamic_global) + + self.recv(""" + <iq type="get" id="test" + to="user@tester.localhost" + from="tester@localhost"> + <query xmlns="http://jabber.org/protocol/disco#items" + node="testing" /> + </iq> + """) + + self.send(""" + <iq type="result" id="test" + to="tester@localhost" + from="user@tester.localhost"> + <query xmlns="http://jabber.org/protocol/disco#items" + node="testing"> + <item jid="tester@localhost" node="foo" name="Global" /> + </query> + </iq> + """) + + def testOverrideJIDItemsHandler(self): + """Test overriding a JID items handler.""" + self.stream_start(mode='client', + plugins=['xep_0030']) + + def dynamic_jid(jid, node, iq): + result = self.xmpp['xep_0030'].stanza.DiscoItems() + result['node'] = node + result.add_item('tester@localhost', node='foo', name='Global') + return result + + self.xmpp['xep_0030'].set_node_handler('get_items', + jid='tester@localhost', + handler=dynamic_jid) + + + self.xmpp['xep_0030'].make_static(jid='tester@localhost', + node='testing') + + self.xmpp['xep_0030'].add_item(jid='tester@localhost', + node='testing', + ijid='tester@localhost', + inode='foo', + name='Test') + + self.recv(""" + <iq type="get" id="test" to="tester@localhost"> + <query xmlns="http://jabber.org/protocol/disco#items" + node="testing" /> + </iq> + """) + + self.send(""" + <iq type="result" id="test"> + <query xmlns="http://jabber.org/protocol/disco#items" + node="testing"> + <item jid="tester@localhost" node="foo" name="Test" /> + </query> + </iq> + """) + + def testOverrideGlobalItemsHandler(self): + """Test overriding the global JID items handler.""" + self.stream_start(mode='component', + jid='tester.localhost', + plugins=['xep_0030']) + + def dynamic_global(jid, node, iq): + result = self.xmpp['xep_0030'].stanza.DiscoItems() + result['node'] = node + result.add_item('tester.localhost', node='foo', name='Global') + return result + + self.xmpp['xep_0030'].set_node_handler('get_items', + handler=dynamic_global) + + self.xmpp['xep_0030'].make_static(jid='user@tester.localhost', + node='testing') + + self.xmpp['xep_0030'].add_item(jid='user@tester.localhost', + node='testing', + ijid='user@tester.localhost', + inode='foo', + name='Test') + + self.recv(""" + <iq type="get" id="test" + to="user@tester.localhost" + from="tester@localhost"> + <query xmlns="http://jabber.org/protocol/disco#items" + node="testing" /> + </iq> + """) + + self.send(""" + <iq type="result" id="test" + to="tester@localhost" + from="user@tester.localhost"> + <query xmlns="http://jabber.org/protocol/disco#items" + node="testing"> + <item jid="user@tester.localhost" node="foo" name="Test" /> + </query> + </iq> + """) + + def testGetItemsRemote(self): + """ + Test sending a disco#items query to another entity + and receiving the result. + """ + self.stream_start(mode='client', + plugins=['xep_0030']) + + events = set() + results = set() + + def handle_disco_items(iq): + events.add('disco_items') + results.update(iq['disco_items']['items']) + + + self.xmpp.add_event_handler('disco_items', handle_disco_items) + + t = threading.Thread(name="get_items", + target=self.xmpp['xep_0030'].get_items, + args=('user@localhost', 'foo')) + t.start() + + self.send(""" + <iq type="get" to="user@localhost" id="1"> + <query xmlns="http://jabber.org/protocol/disco#items" + node="foo" /> + </iq> + """) + + self.recv(""" + <iq type="result" to="tester@localhost" id="1"> + <query xmlns="http://jabber.org/protocol/disco#items" + node="foo"> + <item jid="user@localhost" node="bar" name="Test" /> + <item jid="user@localhost" node="baz" name="Test 2" /> + </query> + </iq> + """) + + # Wait for disco#items request to be received. + t.join() + + time.sleep(0.1) + + items = set([('user@localhost', 'bar', 'Test'), + ('user@localhost', 'baz', 'Test 2')]) + self.assertEqual(events, set(('disco_items',)), + "Disco items event was not triggered: %s" % events) + self.assertEqual(results, items, + "Unexpected items: %s" % results) + + +suite = unittest.TestLoader().loadTestsFromTestCase(TestStreamDisco) diff --git a/tests/test_tostring.py b/tests/test_tostring.py index 3e9df524..638e613a 100644 --- a/tests/test_tostring.py +++ b/tests/test_tostring.py @@ -1,6 +1,6 @@ from sleekxmpp.test import * from sleekxmpp.stanza import Message -from sleekxmpp.xmlstream.stanzabase import ET +from sleekxmpp.xmlstream.stanzabase import ET, ElementBase from sleekxmpp.xmlstream.tostring import tostring, xml_escape @@ -10,6 +10,9 @@ class TestToString(SleekTest): Test the implementation of sleekxmpp.xmlstream.tostring """ + def tearDown(self): + self.stream_close() + def tryTostring(self, original='', expected=None, message='', **kwargs): """ Compare the result of calling tostring against an @@ -110,5 +113,18 @@ class TestToString(SleekTest): self.failUnless(result == expected, "Stanza Unicode handling is incorrect: %s" % result) + def testXMLLang(self): + """Test that serializing xml:lang works.""" + + self.stream_start() + + msg = self.Message() + msg._set_attr('{%s}lang' % msg.xml_ns, "no") + + expected = '<message xml:lang="no" />' + result = msg.__str__() + self.failUnless(expected == result, + "Serialization with xml:lang failed: %s" % result) + suite = unittest.TestLoader().loadTestsFromTestCase(TestToString) |