# Copyright (c) Ralph Meijer. # See LICENSE for details """ Tests for L{wokkel.xmppim}. """ from twisted.internet import defer from twisted.trial import unittest from twisted.words.protocols.jabber import error from twisted.words.protocols.jabber.jid import JID from twisted.words.protocols.jabber.xmlstream import toResponse from twisted.words.xish import domish, utility from wokkel import xmppim from wokkel.generic import ErrorStanza, parseXml from wokkel.test.helpers import TestableRequestHandlerMixin, XmlStreamStub NS_XML = 'http://www.w3.org/XML/1998/namespace' NS_ROSTER = 'jabber:iq:roster' class PresenceClientProtocolTest(unittest.TestCase): def setUp(self): self.output = [] self.protocol = xmppim.PresenceClientProtocol() self.protocol.parent = self def send(self, obj): self.output.append(obj) def test_unavailableDirected(self): """ Test sending of directed unavailable presence broadcast. """ self.protocol.unavailable(JID('user@example.com')) presence = self.output[-1] self.assertEquals("presence", presence.name) self.assertEquals(None, presence.uri) self.assertEquals("user@example.com", presence.getAttribute('to')) self.assertEquals("unavailable", presence.getAttribute('type')) def test_unavailableWithStatus(self): """ Test sending of directed unavailable presence broadcast with status. """ self.protocol.unavailable(JID('user@example.com'), {None: 'Disconnected'}) presence = self.output[-1] self.assertEquals("presence", presence.name) self.assertEquals(None, presence.uri) self.assertEquals("user@example.com", presence.getAttribute('to')) self.assertEquals("unavailable", presence.getAttribute('type')) self.assertEquals("Disconnected", unicode(presence.status)) def test_unavailableBroadcast(self): """ Test sending of unavailable presence broadcast. """ self.protocol.unavailable(None) presence = self.output[-1] self.assertEquals("presence", presence.name) self.assertEquals(None, presence.uri) self.assertEquals(None, presence.getAttribute('to')) self.assertEquals("unavailable", presence.getAttribute('type')) def test_unavailableBroadcastNoEntityParameter(self): """ Test sending of unavailable presence broadcast by not passing entity. """ self.protocol.unavailable() presence = self.output[-1] self.assertEquals("presence", presence.name) self.assertEquals(None, presence.uri) self.assertEquals(None, presence.getAttribute('to')) self.assertEquals("unavailable", presence.getAttribute('type')) class AvailabilityPresenceTest(unittest.TestCase): def test_fromElement(self): xml = """ chat Let's chat! 50 """ presence = xmppim.AvailabilityPresence.fromElement(parseXml(xml)) self.assertEquals(JID('user@example.org'), presence.sender) self.assertEquals(JID('user@example.com'), presence.recipient) self.assertTrue(presence.available) self.assertEquals('chat', presence.show) self.assertEquals({None: "Let's chat!"}, presence.statuses) self.assertEquals(50, presence.priority) class PresenceProtocolTest(unittest.TestCase): """ Tests for L{xmppim.PresenceProtocol} """ def setUp(self): self.output = [] self.protocol = xmppim.PresenceProtocol() self.protocol.parent = self self.protocol.xmlstream = utility.EventDispatcher() self.protocol.connectionInitialized() def send(self, obj): self.output.append(obj) def test_errorReceived(self): """ Incoming presence stanzas are parsed and dispatched. """ xml = """""" def errorReceived(error): xmppim.PresenceProtocol.errorReceived(self.protocol, error) try: self.assertIsInstance(error, ErrorStanza) except: d.errback() else: d.callback(None) d = defer.Deferred() self.protocol.errorReceived = errorReceived self.protocol.xmlstream.dispatch(parseXml(xml)) return d def test_availableReceived(self): """ Incoming presence stanzas are parsed and dispatched. """ xml = """""" def availableReceived(presence): xmppim.PresenceProtocol.availableReceived(self.protocol, presence) try: self.assertIsInstance(presence, xmppim.AvailabilityPresence) except: d.errback() else: d.callback(None) d = defer.Deferred() self.protocol.availableReceived = availableReceived self.protocol.xmlstream.dispatch(parseXml(xml)) return d def test_unavailableReceived(self): """ Incoming presence stanzas are parsed and dispatched. """ xml = """""" def unavailableReceived(presence): xmppim.PresenceProtocol.unavailableReceived(self.protocol, presence) try: self.assertIsInstance(presence, xmppim.AvailabilityPresence) except: d.errback() else: d.callback(None) d = defer.Deferred() self.protocol.unavailableReceived = unavailableReceived self.protocol.xmlstream.dispatch(parseXml(xml)) return d def test_subscribeReceived(self): """ Incoming presence stanzas are parsed and dispatched. """ xml = """""" def subscribeReceived(presence): xmppim.PresenceProtocol.subscribeReceived(self.protocol, presence) try: self.assertIsInstance(presence, xmppim.SubscriptionPresence) except: d.errback() else: d.callback(None) d = defer.Deferred() self.protocol.subscribeReceived = subscribeReceived self.protocol.xmlstream.dispatch(parseXml(xml)) return d def test_unsubscribeReceived(self): """ Incoming presence stanzas are parsed and dispatched. """ xml = """""" def unsubscribeReceived(presence): xmppim.PresenceProtocol.unsubscribeReceived(self.protocol, presence) try: self.assertIsInstance(presence, xmppim.SubscriptionPresence) except: d.errback() else: d.callback(None) d = defer.Deferred() self.protocol.unsubscribeReceived = unsubscribeReceived self.protocol.xmlstream.dispatch(parseXml(xml)) return d def test_subscribedReceived(self): """ Incoming presence stanzas are parsed and dispatched. """ xml = """""" def subscribedReceived(presence): xmppim.PresenceProtocol.subscribedReceived(self.protocol, presence) try: self.assertIsInstance(presence, xmppim.SubscriptionPresence) except: d.errback() else: d.callback(None) d = defer.Deferred() self.protocol.subscribedReceived = subscribedReceived self.protocol.xmlstream.dispatch(parseXml(xml)) return d def test_unsubscribedReceived(self): """ Incoming presence stanzas are parsed and dispatched. """ xml = """""" def unsubscribedReceived(presence): xmppim.PresenceProtocol.unsubscribedReceived(self.protocol, presence) try: self.assertIsInstance(presence, xmppim.SubscriptionPresence) except: d.errback() else: d.callback(None) d = defer.Deferred() self.protocol.unsubscribedReceived = unsubscribedReceived self.protocol.xmlstream.dispatch(parseXml(xml)) return d def test_probeReceived(self): """ Incoming presence stanzas are parsed and dispatched. """ xml = """""" def probeReceived(presence): xmppim.PresenceProtocol.probeReceived(self.protocol, presence) try: self.assertIsInstance(presence, xmppim.ProbePresence) except: d.errback() else: d.callback(None) d = defer.Deferred() self.protocol.probeReceived = probeReceived self.protocol.xmlstream.dispatch(parseXml(xml)) return d def test_available(self): """ It should be possible to pass a sender address. """ self.protocol.available(JID('user@example.com'), show=u'chat', status=u'Talk to me!', priority=50) element = self.output[-1] self.assertEquals("user@example.com", element.getAttribute('to')) self.assertIdentical(None, element.getAttribute('type')) self.assertEquals(u'chat', unicode(element.show)) self.assertEquals(u'Talk to me!', unicode(element.status)) self.assertEquals(u'50', unicode(element.priority)) def test_availableLanguages(self): """ It should be possible to pass a sender address. """ self.protocol.available(JID('user@example.com'), show=u'chat', statuses={None: u'Talk to me!', 'nl': u'Praat met me!'}, priority=50) element = self.output[-1] self.assertEquals("user@example.com", element.getAttribute('to')) self.assertIdentical(None, element.getAttribute('type')) self.assertEquals(u'chat', unicode(element.show)) statuses = {} for status in element.elements(): if status.name == 'status': lang = status.getAttribute((NS_XML, 'lang')) statuses[lang] = unicode(status) self.assertIn(None, statuses) self.assertEquals(u'Talk to me!', statuses[None]) self.assertIn('nl', statuses) self.assertEquals(u'Praat met me!', statuses['nl']) self.assertEquals(u'50', unicode(element.priority)) def test_availableSender(self): """ It should be possible to pass a sender address. """ self.protocol.available(JID('user@example.com'), sender=JID('user@example.org')) element = self.output[-1] self.assertEquals("user@example.org", element.getAttribute('from')) def test_unavailableDirected(self): """ Test sending of directed unavailable presence broadcast. """ self.protocol.unavailable(JID('user@example.com')) element = self.output[-1] self.assertEquals("presence", element.name) self.assertEquals(None, element.uri) self.assertEquals("user@example.com", element.getAttribute('to')) self.assertEquals("unavailable", element.getAttribute('type')) def test_unavailableWithStatus(self): """ Test sending of directed unavailable presence broadcast with status. """ self.protocol.unavailable(JID('user@example.com'), {None: 'Disconnected'}) element = self.output[-1] self.assertEquals("presence", element.name) self.assertEquals(None, element.uri) self.assertEquals("user@example.com", element.getAttribute('to')) self.assertEquals("unavailable", element.getAttribute('type')) self.assertEquals("Disconnected", unicode(element.status)) def test_unavailableBroadcast(self): """ Test sending of unavailable presence broadcast. """ self.protocol.unavailable(None) element = self.output[-1] self.assertEquals("presence", element.name) self.assertEquals(None, element.uri) self.assertEquals(None, element.getAttribute('to')) self.assertEquals("unavailable", element.getAttribute('type')) def test_unavailableBroadcastNoRecipientParameter(self): """ Test sending of unavailable presence broadcast by not passing entity. """ self.protocol.unavailable() element = self.output[-1] self.assertEquals("presence", element.name) self.assertEquals(None, element.uri) self.assertEquals(None, element.getAttribute('to')) self.assertEquals("unavailable", element.getAttribute('type')) def test_unavailableSender(self): """ It should be possible to pass a sender address. """ self.protocol.unavailable(JID('user@example.com'), sender=JID('user@example.org')) element = self.output[-1] self.assertEquals("user@example.org", element.getAttribute('from')) def test_subscribeSender(self): """ It should be possible to pass a sender address. """ self.protocol.subscribe(JID('user@example.com'), sender=JID('user@example.org')) element = self.output[-1] self.assertEquals("user@example.org", element.getAttribute('from')) def test_unsubscribeSender(self): """ It should be possible to pass a sender address. """ self.protocol.unsubscribe(JID('user@example.com'), sender=JID('user@example.org')) element = self.output[-1] self.assertEquals("user@example.org", element.getAttribute('from')) def test_subscribedSender(self): """ It should be possible to pass a sender address. """ self.protocol.subscribed(JID('user@example.com'), sender=JID('user@example.org')) element = self.output[-1] self.assertEquals("user@example.org", element.getAttribute('from')) def test_unsubscribedSender(self): """ It should be possible to pass a sender address. """ self.protocol.unsubscribed(JID('user@example.com'), sender=JID('user@example.org')) element = self.output[-1] self.assertEquals("user@example.org", element.getAttribute('from')) def test_probeSender(self): """ It should be possible to pass a sender address. """ self.protocol.probe(JID('user@example.com'), sender=JID('user@example.org')) element = self.output[-1] self.assertEquals("user@example.org", element.getAttribute('from')) class RosterItemTest(unittest.TestCase): """ Tests for L{xmppim.RosterItem}. """ def test_toElement(self): """ A roster item has the correct namespace/name, lacks unset attributes. """ item = xmppim.RosterItem(JID('user@example.org')) element = item.toElement() self.assertEqual('item', element.name) self.assertEqual(NS_ROSTER, element.uri) self.assertFalse(element.hasAttribute('subscription')) self.assertFalse(element.hasAttribute('ask')) self.assertEqual(u"", element.getAttribute('name', u"")) self.assertFalse(element.hasAttribute('approved')) self.assertEquals(0, len(list(element.elements()))) def test_toElementMinimal(self): """ A bare roster item only has a jid attribute. """ item = xmppim.RosterItem(JID('user@example.org')) element = item.toElement() self.assertEqual(u'user@example.org', element.getAttribute('jid')) def test_toElementSubscriptionNone(self): """ A roster item with no subscription has no subscription attribute. """ item = xmppim.RosterItem(JID('user@example.org'), subscriptionTo=False, subscriptionFrom=False) element = item.toElement() self.assertIdentical(None, element.getAttribute('subscription')) def test_toElementSubscriptionTo(self): """ A roster item with subscriptionTo set has subscription 'to'. """ item = xmppim.RosterItem(JID('user@example.org'), subscriptionTo=True, subscriptionFrom=False) element = item.toElement() self.assertEqual('to', element.getAttribute('subscription')) def test_toElementSubscriptionFrom(self): """ A roster item with subscriptionFrom set has subscription 'to'. """ item = xmppim.RosterItem(JID('user@example.org'), subscriptionTo=False, subscriptionFrom=True) element = item.toElement() self.assertEqual('from', element.getAttribute('subscription')) def test_toElementSubscriptionBoth(self): """ A roster item with mutual subscription has subscription 'both'. """ item = xmppim.RosterItem(JID('user@example.org'), subscriptionTo=True, subscriptionFrom=True) element = item.toElement() self.assertEqual('both', element.getAttribute('subscription')) def test_toElementSubscriptionRemove(self): """ A roster item with remove set has subscription 'remove'. """ item = xmppim.RosterItem(JID('user@example.org')) item.remove = True element = item.toElement() self.assertEqual('remove', element.getAttribute('subscription')) def test_toElementAsk(self): """ A roster item with pendingOut set has subscription 'ask'. """ item = xmppim.RosterItem(JID('user@example.org')) item.pendingOut = True element = item.toElement() self.assertEqual('subscribe', element.getAttribute('ask')) def test_toElementName(self): """ A roster item's name is rendered to the 'name' attribute. """ item = xmppim.RosterItem(JID('user@example.org'), name='Joe User') element = item.toElement() self.assertEqual(u'Joe User', element.getAttribute('name')) def test_toElementGroups(self): """ A roster item's groups are rendered as 'group' child elements. """ groups = set(['Friends', 'Jabber']) item = xmppim.RosterItem(JID('user@example.org'), groups=groups) element = item.toElement() foundGroups = set() for child in element.elements(): if child.uri == NS_ROSTER and child.name == 'group': foundGroups.add(unicode(child)) self.assertEqual(groups, foundGroups) def test_toElementApproved(self): """ A pre-approved subscription for a roster item has an 'approved' flag. """ item = xmppim.RosterItem(JID('user@example.org')) item.approved = True element = item.toElement() self.assertEqual(u'true', element.getAttribute('approved')) def test_fromElementMinimal(self): """ A minimal roster item has a reference to the JID of the contact. """ xml = """ """ item = xmppim.RosterItem.fromElement(parseXml(xml)) self.assertEqual(JID(u"test@example.org"), item.entity) self.assertEqual(u"", item.name) self.assertFalse(item.subscriptionTo) self.assertFalse(item.subscriptionFrom) self.assertFalse(item.pendingOut) self.assertFalse(item.approved) self.assertEqual(set(), item.groups) def test_fromElementName(self): """ A roster item may have an optional name. """ xml = """ """ item = xmppim.RosterItem.fromElement(parseXml(xml)) self.assertEqual(u"Test User", item.name) def test_fromElementGroups(self): """ A roster item may have one or more groups. """ xml = """ Friends Twisted """ item = xmppim.RosterItem.fromElement(parseXml(xml)) self.assertIn(u"Twisted", item.groups) self.assertIn(u"Friends", item.groups) def test_fromElementSubscriptionNone(self): """ Subscription 'none' sets both attributes to False. """ xml = """ """ item = xmppim.RosterItem.fromElement(parseXml(xml)) self.assertFalse(item.remove) self.assertFalse(item.subscriptionTo) self.assertFalse(item.subscriptionFrom) def test_fromElementSubscriptionTo(self): """ Subscription 'to' sets the corresponding attribute to True. """ xml = """ """ item = xmppim.RosterItem.fromElement(parseXml(xml)) self.assertFalse(item.remove) self.assertTrue(item.subscriptionTo) self.assertFalse(item.subscriptionFrom) def test_fromElementSubscriptionFrom(self): """ Subscription 'from' sets the corresponding attribute to True. """ xml = """ """ item = xmppim.RosterItem.fromElement(parseXml(xml)) self.assertFalse(item.remove) self.assertFalse(item.subscriptionTo) self.assertTrue(item.subscriptionFrom) def test_fromElementSubscriptionBoth(self): """ Subscription 'both' sets both attributes to True. """ xml = """ """ item = xmppim.RosterItem.fromElement(parseXml(xml)) self.assertFalse(item.remove) self.assertTrue(item.subscriptionTo) self.assertTrue(item.subscriptionFrom) def test_fromElementSubscriptionRemove(self): """ Subscription 'remove' sets the remove attribute. """ xml = """ """ item = xmppim.RosterItem.fromElement(parseXml(xml)) self.assertTrue(item.remove) def test_fromElementPendingOut(self): """ The ask attribute, if set to 'subscription', means pending out. """ xml = """ """ item = xmppim.RosterItem.fromElement(parseXml(xml)) self.assertTrue(item.pendingOut) def test_fromElementApprovedTrue(self): """ The approved attribute (true) signals a pre-approved subscription. """ xml = """ """ item = xmppim.RosterItem.fromElement(parseXml(xml)) self.assertTrue(item.approved) def test_fromElementApproved1(self): """ The approved attribute (1) signals a pre-approved subscription. """ xml = """ """ item = xmppim.RosterItem.fromElement(parseXml(xml)) self.assertTrue(item.approved) def test_jidDeprecationGet(self): """ Getting the jid attribute works as entity and warns deprecation. """ item = xmppim.RosterItem(JID('user@example.org')) entity = self.assertWarns(DeprecationWarning, "wokkel.xmppim.RosterItem.jid was " "deprecated in Wokkel 0.7.1; " "please use RosterItem.entity instead.", xmppim.__file__, getattr, item, 'jid') self.assertIdentical(entity, item.entity) def test_jidDeprecationSet(self): """ Setting the jid attribute works as entity and warns deprecation. """ item = xmppim.RosterItem(JID('user@example.org')) self.assertWarns(DeprecationWarning, "wokkel.xmppim.RosterItem.jid was deprecated " "in Wokkel 0.7.1; " "please use RosterItem.entity instead.", xmppim.__file__, setattr, item, 'jid', JID('other@example.org')) self.assertEqual(JID('other@example.org'), item.entity) def test_askDeprecationGet(self): """ Getting the ask attribute works as entity and warns deprecation. """ item = xmppim.RosterItem(JID('user@example.org')) item.pendingOut = True ask = self.assertWarns(DeprecationWarning, "wokkel.xmppim.RosterItem.ask was " "deprecated in Wokkel 0.7.1; " "please use RosterItem.pendingOut instead.", xmppim.__file__, getattr, item, 'ask') self.assertTrue(ask) def test_askDeprecationSet(self): """ Setting the ask attribute works as entity and warns deprecation. """ item = xmppim.RosterItem(JID('user@example.org')) self.assertWarns(DeprecationWarning, "wokkel.xmppim.RosterItem.ask was " "deprecated in Wokkel 0.7.1; " "please use RosterItem.pendingOut instead.", xmppim.__file__, setattr, item, 'ask', True) self.assertTrue(item.pendingOut) class RosterRequestTest(unittest.TestCase): """ Tests for L{xmppim.RosterRequest}. """ def test_fromElement(self): """ A bare roster request is parsed and missing information is None. """ xml = """ """ request = xmppim.RosterRequest.fromElement(parseXml(xml)) self.assertEqual('get', request.stanzaType) self.assertEqual(JID('this@example.org/Home'), request.recipient) self.assertEqual(JID('this@example.org'), request.sender) self.assertEqual(None, request.item) self.assertEqual(None, request.version) def test_fromElementItem(self): """ If an item is present, parse it and put it in the request item. """ xml = """ """ request = xmppim.RosterRequest.fromElement(parseXml(xml)) self.assertNotIdentical(None, request.item) self.assertEqual(JID('user@example.org'), request.item.entity) def test_fromElementVersion(self): """ If a ver attribute is present, put it in the request version. """ xml = """ """ request = xmppim.RosterRequest.fromElement(parseXml(xml)) self.assertEqual('ver72', request.version) def test_fromElementVersionEmpty(self): """ The ver attribute may be empty. """ xml = """ """ request = xmppim.RosterRequest.fromElement(parseXml(xml)) self.assertEqual('', request.version) def test_toElement(self): """ A roster request has a query element in the roster namespace. """ request = xmppim.RosterRequest() element = request.toElement() children = element.elements() child = children.next() self.assertEqual(NS_ROSTER, child.uri) self.assertEqual('query', child.name) def test_toElementItem(self): """ If an item is set, it is rendered as a child of the query. """ request = xmppim.RosterRequest() request.item = xmppim.RosterItem(JID('user@example.org')) element = request.toElement() children = element.query.elements() child = children.next() self.assertEqual(NS_ROSTER, child.uri) self.assertEqual('item', child.name) class FakeClient(object): """ Fake client stream manager for roster tests. """ def __init__(self, xmlstream, jid): self.xmlstream = xmlstream self.jid = jid def request(self, request): element = request.toElement() self.xmlstream.send(element) return defer.Deferred() def addHandler(self, handler): handler.makeConnection(self.xmlstream) handler.connectionInitialized() def test_toElementVersion(self): """ If the roster version is set, a 'ver' attribute is added. """ request = xmppim.RosterRequest() request.version = 'ver72' element = request.toElement() self.assertEqual('ver72', element.query.getAttribute('ver')) def test_toElementVersionEmpty(self): """ If the roster version is the empty string, it should add 'ver', too. """ request = xmppim.RosterRequest() request.version = '' element = request.toElement() self.assertEqual('', element.query.getAttribute('ver')) class RosterClientProtocolTest(unittest.TestCase, TestableRequestHandlerMixin): """ Tests for L{xmppim.RosterClientProtocol}. """ def setUp(self): self.stub = XmlStreamStub() self.client = FakeClient(self.stub.xmlstream, JID('this@example.org')) self.service = xmppim.RosterClientProtocol() self.service.setHandlerParent(self.client) def test_setItem(self): """ Setting a roster item renders the item and sends it out. """ item = xmppim.RosterItem(JID('test@example.org'), name='Joe User', groups=set(['Friends', 'Jabber'])) d = self.service.setItem(item) # Inspect outgoing iq request iq = self.stub.output[-1] self.assertEqual('set', iq.getAttribute('type')) self.assertNotIdentical(None, iq.query) self.assertEqual(NS_ROSTER, iq.query.uri) children = list(domish.generateElementsQNamed(iq.query.children, 'item', NS_ROSTER)) self.assertEqual(1, len(children)) child = children[0] self.assertEqual('test@example.org', child['jid']) self.assertIdentical(None, child.getAttribute('subscription')) # Fake successful response response = toResponse(iq, 'result') d.callback(response) return d def test_setItemIgnoreAttributes(self): """ Certain attributes should be rendered for roster set. """ item = xmppim.RosterItem(JID('test@example.org'), subscriptionTo=True, subscriptionFrom=False, name='Joe User', groups=set(['Friends', 'Jabber'])) item.pendingOut = True item.approved = True d = self.service.setItem(item) # Inspect outgoing iq request iq = self.stub.output[-1] self.assertEqual('set', iq.getAttribute('type')) self.assertNotIdentical(None, iq.query) self.assertEqual(NS_ROSTER, iq.query.uri) children = list(domish.generateElementsQNamed(iq.query.children, 'item', NS_ROSTER)) self.assertEqual(1, len(children)) child = children[0] self.assertIdentical(None, child.getAttribute('ask')) self.assertIdentical(None, child.getAttribute('approved')) self.assertIdentical(None, child.getAttribute('subscription')) # Fake successful response response = toResponse(iq, 'result') d.callback(response) return d def test_removeItem(self): """ Removing a roster item is setting an item with subscription C{remove}. """ d = self.service.removeItem(JID('test@example.org')) # Inspect outgoing iq request iq = self.stub.output[-1] self.assertEqual('set', iq.getAttribute('type')) self.assertNotIdentical(None, iq.query) self.assertEqual(NS_ROSTER, iq.query.uri) children = list(domish.generateElementsQNamed(iq.query.children, 'item', NS_ROSTER)) self.assertEqual(1, len(children)) child = children[0] self.assertEqual('test@example.org', child['jid']) self.assertEqual('remove', child.getAttribute('subscription')) # Fake successful response response = toResponse(iq, 'result') d.callback(response) return d def test_getRoster(self): """ A request for the roster is sent out and the response is parsed. """ def cb(roster): self.assertIn(JID('user@example.org'), roster) self.assertIdentical(None, getattr(roster, 'version')) d = self.service.getRoster() d.addCallback(cb) # Inspect outgoing iq request iq = self.stub.output[-1] self.assertEqual('get', iq.getAttribute('type')) self.assertNotIdentical(None, iq.query) self.assertEqual(NS_ROSTER, iq.query.uri) self.assertFalse(iq.query.hasAttribute('ver')) # Fake successful response response = toResponse(iq, 'result') query = response.addElement((NS_ROSTER, 'query')) item = query.addElement('item') item['jid'] = 'user@example.org' d.callback(response) return d def test_getRosterVer(self): """ A request for the roster with version passes the version on. """ def cb(roster): self.assertEqual('ver96', getattr(roster, 'version')) d = self.service.getRoster(version='ver72') d.addCallback(cb) # Inspect outgoing iq request iq = self.stub.output[-1] self.assertEqual('ver72', iq.query.getAttribute('ver')) # Fake successful response response = toResponse(iq, 'result') query = response.addElement((NS_ROSTER, 'query')) query['ver'] = 'ver96' item = query.addElement('item') item['jid'] = 'user@example.org' d.callback(response) return d def test_getRosterVerEmptyResult(self): """ An empty response is returned as None. """ def cb(response): self.assertIdentical(None, response) d = self.service.getRoster(version='ver72') d.addCallback(cb) # Inspect outgoing iq request iq = self.stub.output[-1] # Fake successful response response = toResponse(iq, 'result') d.callback(response) return d def test_onRosterSet(self): """ A roster push causes onRosterSet to be called with the parsed item. """ xml = """ """ items = [] def onRosterSet(item): items.append(item) def cb(result): self.assertEqual(1, len(items)) self.assertEqual(JID('user@example.org'), items[0].entity) self.service.onRosterSet = onRosterSet d = self.assertWarns(DeprecationWarning, "wokkel.xmppim.RosterClientProtocol.onRosterSet " "was deprecated in Wokkel 0.7.1; " "please use RosterClientProtocol.setReceived " "instead.", xmppim.__file__, self.handleRequest, xml) d.addCallback(cb) return d def test_onRosterRemove(self): """ A roster push causes onRosterSet to be called with the parsed item. """ xml = """ """ entities = [] def onRosterRemove(entity): entities.append(entity) def cb(result): self.assertEqual([JID('user@example.org')], entities) self.service.onRosterRemove = onRosterRemove d = self.assertWarns(DeprecationWarning, "wokkel.xmppim.RosterClientProtocol.onRosterRemove " "was deprecated in Wokkel 0.7.1; " "please use RosterClientProtocol.removeReceived " "instead.", xmppim.__file__, self.handleRequest, xml) d.addCallback(cb) return d def test_setReceived(self): """ A roster set push causes setReceived. """ xml = """ """ requests = [] def setReceived(request): requests.append(request) def cb(result): self.assertEqual(1, len(requests), "setReceived was not called") self.assertEqual(JID('user@example.org'), requests[0].item.entity) self.service.setReceived = setReceived d = self.handleRequest(xml) d.addCallback(cb) return d def test_setReceivedOtherSource(self): """ Roster pushes can be sent from other entities, too, ignore them. """ xml = """ """ def cb(result): self.assertEquals('service-unavailable', result.condition) d = self.handleRequest(xml) self.assertFailure(d, error.StanzaError) d.addCallback(cb) return d def test_setReceivedOtherSourceAllowed(self): """ Roster pushes can be sent from other entities, allow them. """ xml = """ """ self.service.allowAnySender = True requests = [] def setReceived(request): requests.append(request) def cb(result): self.assertEqual(1, len(requests), "setReceived was not called") self.service.setReceived = setReceived d = self.handleRequest(xml) d.addCallback(cb) return d def test_setReceivedOtherSourceIgnored(self): """ Roster pushes can be sent from other entities, allow them. """ xml = """ """ self.service.allowAnySender = True def setReceived(request): if request.sender == JID('bad@example.org'): raise xmppim.RosterPushIgnored() def cb(result): self.assertEquals('service-unavailable', result.condition) self.service.setReceived = setReceived d = self.handleRequest(xml) self.assertFailure(d, error.StanzaError) d.addCallback(cb) return d def test_removeReceived(self): """ A roster remove push causes removeReceived. """ xml = """ """ requests = [] def removeReceived(request): requests.append(request) def cb(result): self.assertEqual(1, len(requests), "removeReceived was not called") self.assertEqual(JID('user@example.org'), requests[0].item.entity) self.service.removeReceived = removeReceived d = self.handleRequest(xml) d.addCallback(cb) return d