source: wokkel/test/test_xmppim.py @ 203:7f8f69adf13b

Last change on this file since 203:7f8f69adf13b was 203:7f8f69adf13b, checked in by Ralph Meijer <ralphm@…>, 3 years ago

imported patch py3-xmppim.patch

File size: 42.0 KB
Line 
1# Copyright (c) Ralph Meijer.
2# See LICENSE for details
3
4"""
5Tests for L{wokkel.xmppim}.
6"""
7
8from __future__ import division, absolute_import
9
10from twisted.internet import defer
11from twisted.trial import unittest
12from twisted.python.compat import unicode
13from twisted.words.protocols.jabber import error
14from twisted.words.protocols.jabber.jid import JID
15from twisted.words.protocols.jabber.xmlstream import toResponse
16from twisted.words.xish import domish, utility
17
18from wokkel import xmppim
19from wokkel.generic import ErrorStanza, parseXml
20from wokkel.test.helpers import TestableRequestHandlerMixin, XmlStreamStub
21
22NS_XML = 'http://www.w3.org/XML/1998/namespace'
23NS_ROSTER = 'jabber:iq:roster'
24
25class PresenceClientProtocolTest(unittest.TestCase):
26    def setUp(self):
27        self.output = []
28        self.protocol = xmppim.PresenceClientProtocol()
29        self.protocol.parent = self
30
31    def send(self, obj):
32        self.output.append(obj)
33
34    def test_unavailableDirected(self):
35        """
36        Test sending of directed unavailable presence broadcast.
37        """
38
39        self.protocol.unavailable(JID('user@example.com'))
40        presence = self.output[-1]
41        self.assertEquals("presence", presence.name)
42        self.assertEquals(None, presence.uri)
43        self.assertEquals("user@example.com", presence.getAttribute('to'))
44        self.assertEquals("unavailable", presence.getAttribute('type'))
45
46    def test_unavailableWithStatus(self):
47        """
48        Test sending of directed unavailable presence broadcast with status.
49        """
50
51        self.protocol.unavailable(JID('user@example.com'),
52                                  {None: 'Disconnected'})
53        presence = self.output[-1]
54        self.assertEquals("presence", presence.name)
55        self.assertEquals(None, presence.uri)
56        self.assertEquals("user@example.com", presence.getAttribute('to'))
57        self.assertEquals("unavailable", presence.getAttribute('type'))
58        self.assertEquals("Disconnected", unicode(presence.status))
59
60    def test_unavailableBroadcast(self):
61        """
62        Test sending of unavailable presence broadcast.
63        """
64
65        self.protocol.unavailable(None)
66        presence = self.output[-1]
67        self.assertEquals("presence", presence.name)
68        self.assertEquals(None, presence.uri)
69        self.assertEquals(None, presence.getAttribute('to'))
70        self.assertEquals("unavailable", presence.getAttribute('type'))
71
72    def test_unavailableBroadcastNoEntityParameter(self):
73        """
74        Test sending of unavailable presence broadcast by not passing entity.
75        """
76
77        self.protocol.unavailable()
78        presence = self.output[-1]
79        self.assertEquals("presence", presence.name)
80        self.assertEquals(None, presence.uri)
81        self.assertEquals(None, presence.getAttribute('to'))
82        self.assertEquals("unavailable", presence.getAttribute('type'))
83
84
85
86class AvailabilityPresenceTest(unittest.TestCase):
87
88    def test_fromElement(self):
89        xml = """<presence from='user@example.org' to='user@example.com'>
90                   <show>chat</show>
91                   <status>Let's chat!</status>
92                   <priority>50</priority>
93                 </presence>
94              """
95
96        presence = xmppim.AvailabilityPresence.fromElement(parseXml(xml))
97        self.assertEquals(JID('user@example.org'), presence.sender)
98        self.assertEquals(JID('user@example.com'), presence.recipient)
99        self.assertTrue(presence.available)
100        self.assertEquals('chat', presence.show)
101        self.assertEquals({None: "Let's chat!"}, presence.statuses)
102        self.assertEquals(50, presence.priority)
103
104
105class PresenceProtocolTest(unittest.TestCase):
106    """
107    Tests for L{xmppim.PresenceProtocol}
108    """
109
110    def setUp(self):
111        self.output = []
112        self.protocol = xmppim.PresenceProtocol()
113        self.protocol.parent = self
114        self.protocol.xmlstream = utility.EventDispatcher()
115        self.protocol.connectionInitialized()
116
117
118    def send(self, obj):
119        self.output.append(obj)
120
121
122    def test_errorReceived(self):
123        """
124        Incoming presence stanzas are parsed and dispatched.
125        """
126        xml = """<presence type="error"/>"""
127
128        def errorReceived(error):
129            xmppim.PresenceProtocol.errorReceived(self.protocol, error)
130            try:
131                self.assertIsInstance(error, ErrorStanza)
132            except:
133                d.errback()
134            else:
135                d.callback(None)
136
137        d = defer.Deferred()
138        self.protocol.errorReceived = errorReceived
139        self.protocol.xmlstream.dispatch(parseXml(xml))
140        return d
141
142
143    def test_availableReceived(self):
144        """
145        Incoming presence stanzas are parsed and dispatched.
146        """
147        xml = """<presence/>"""
148
149        def availableReceived(presence):
150            xmppim.PresenceProtocol.availableReceived(self.protocol, presence)
151            try:
152                self.assertIsInstance(presence, xmppim.AvailabilityPresence)
153            except:
154                d.errback()
155            else:
156                d.callback(None)
157
158        d = defer.Deferred()
159        self.protocol.availableReceived = availableReceived
160        self.protocol.xmlstream.dispatch(parseXml(xml))
161        return d
162
163
164    def test_unavailableReceived(self):
165        """
166        Incoming presence stanzas are parsed and dispatched.
167        """
168        xml = """<presence type='unavailable'/>"""
169
170        def unavailableReceived(presence):
171            xmppim.PresenceProtocol.unavailableReceived(self.protocol, presence)
172            try:
173                self.assertIsInstance(presence, xmppim.AvailabilityPresence)
174            except:
175                d.errback()
176            else:
177                d.callback(None)
178
179        d = defer.Deferred()
180        self.protocol.unavailableReceived = unavailableReceived
181        self.protocol.xmlstream.dispatch(parseXml(xml))
182        return d
183
184
185    def test_subscribeReceived(self):
186        """
187        Incoming presence stanzas are parsed and dispatched.
188        """
189        xml = """<presence type='subscribe'/>"""
190
191        def subscribeReceived(presence):
192            xmppim.PresenceProtocol.subscribeReceived(self.protocol, presence)
193            try:
194                self.assertIsInstance(presence, xmppim.SubscriptionPresence)
195            except:
196                d.errback()
197            else:
198                d.callback(None)
199
200        d = defer.Deferred()
201        self.protocol.subscribeReceived = subscribeReceived
202        self.protocol.xmlstream.dispatch(parseXml(xml))
203        return d
204
205
206    def test_unsubscribeReceived(self):
207        """
208        Incoming presence stanzas are parsed and dispatched.
209        """
210        xml = """<presence type='unsubscribe'/>"""
211
212        def unsubscribeReceived(presence):
213            xmppim.PresenceProtocol.unsubscribeReceived(self.protocol, presence)
214            try:
215                self.assertIsInstance(presence, xmppim.SubscriptionPresence)
216            except:
217                d.errback()
218            else:
219                d.callback(None)
220
221        d = defer.Deferred()
222        self.protocol.unsubscribeReceived = unsubscribeReceived
223        self.protocol.xmlstream.dispatch(parseXml(xml))
224        return d
225
226
227    def test_subscribedReceived(self):
228        """
229        Incoming presence stanzas are parsed and dispatched.
230        """
231        xml = """<presence type='subscribed'/>"""
232
233        def subscribedReceived(presence):
234            xmppim.PresenceProtocol.subscribedReceived(self.protocol, presence)
235            try:
236                self.assertIsInstance(presence, xmppim.SubscriptionPresence)
237            except:
238                d.errback()
239            else:
240                d.callback(None)
241
242        d = defer.Deferred()
243        self.protocol.subscribedReceived = subscribedReceived
244        self.protocol.xmlstream.dispatch(parseXml(xml))
245        return d
246
247
248    def test_unsubscribedReceived(self):
249        """
250        Incoming presence stanzas are parsed and dispatched.
251        """
252        xml = """<presence type='unsubscribed'/>"""
253
254        def unsubscribedReceived(presence):
255            xmppim.PresenceProtocol.unsubscribedReceived(self.protocol,
256                                                         presence)
257            try:
258                self.assertIsInstance(presence, xmppim.SubscriptionPresence)
259            except:
260                d.errback()
261            else:
262                d.callback(None)
263
264        d = defer.Deferred()
265        self.protocol.unsubscribedReceived = unsubscribedReceived
266        self.protocol.xmlstream.dispatch(parseXml(xml))
267        return d
268
269
270    def test_probeReceived(self):
271        """
272        Incoming presence stanzas are parsed and dispatched.
273        """
274        xml = """<presence type='probe'/>"""
275
276        def probeReceived(presence):
277            xmppim.PresenceProtocol.probeReceived(self.protocol, presence)
278            try:
279                self.assertIsInstance(presence, xmppim.ProbePresence)
280            except:
281                d.errback()
282            else:
283                d.callback(None)
284
285        d = defer.Deferred()
286        self.protocol.probeReceived = probeReceived
287        self.protocol.xmlstream.dispatch(parseXml(xml))
288        return d
289
290    def test_available(self):
291        """
292        It should be possible to pass a sender address.
293        """
294        self.protocol.available(JID('user@example.com'),
295                                show=u'chat',
296                                status=u'Talk to me!',
297                                priority=50)
298        element = self.output[-1]
299        self.assertEquals("user@example.com", element.getAttribute('to'))
300        self.assertIdentical(None, element.getAttribute('type'))
301        self.assertEquals(u'chat', unicode(element.show))
302        self.assertEquals(u'Talk to me!', unicode(element.status))
303        self.assertEquals(u'50', unicode(element.priority))
304
305    def test_availableLanguages(self):
306        """
307        It should be possible to pass a sender address.
308        """
309        self.protocol.available(JID('user@example.com'),
310                                show=u'chat',
311                                statuses={None: u'Talk to me!',
312                                          'nl': u'Praat met me!'},
313                                priority=50)
314        element = self.output[-1]
315        self.assertEquals("user@example.com", element.getAttribute('to'))
316        self.assertIdentical(None, element.getAttribute('type'))
317        self.assertEquals(u'chat', unicode(element.show))
318
319        statuses = {}
320        for status in element.elements():
321            if status.name == 'status':
322                lang = status.getAttribute((NS_XML, 'lang'))
323                statuses[lang] = unicode(status)
324
325        self.assertIn(None, statuses)
326        self.assertEquals(u'Talk to me!', statuses[None])
327        self.assertIn('nl', statuses)
328        self.assertEquals(u'Praat met me!', statuses['nl'])
329        self.assertEquals(u'50', unicode(element.priority))
330
331
332    def test_availableSender(self):
333        """
334        It should be possible to pass a sender address.
335        """
336        self.protocol.available(JID('user@example.com'),
337                                sender=JID('user@example.org'))
338        element = self.output[-1]
339        self.assertEquals("user@example.org", element.getAttribute('from'))
340
341
342    def test_unavailableDirected(self):
343        """
344        Test sending of directed unavailable presence broadcast.
345        """
346
347        self.protocol.unavailable(JID('user@example.com'))
348        element = self.output[-1]
349        self.assertEquals("presence", element.name)
350        self.assertEquals(None, element.uri)
351        self.assertEquals("user@example.com", element.getAttribute('to'))
352        self.assertEquals("unavailable", element.getAttribute('type'))
353
354    def test_unavailableWithStatus(self):
355        """
356        Test sending of directed unavailable presence broadcast with status.
357        """
358
359        self.protocol.unavailable(JID('user@example.com'),
360                                  {None: 'Disconnected'})
361        element = self.output[-1]
362        self.assertEquals("presence", element.name)
363        self.assertEquals(None, element.uri)
364        self.assertEquals("user@example.com", element.getAttribute('to'))
365        self.assertEquals("unavailable", element.getAttribute('type'))
366        self.assertEquals("Disconnected", unicode(element.status))
367
368
369    def test_unavailableBroadcast(self):
370        """
371        Test sending of unavailable presence broadcast.
372        """
373
374        self.protocol.unavailable(None)
375        element = self.output[-1]
376        self.assertEquals("presence", element.name)
377        self.assertEquals(None, element.uri)
378        self.assertEquals(None, element.getAttribute('to'))
379        self.assertEquals("unavailable", element.getAttribute('type'))
380
381
382    def test_unavailableBroadcastNoRecipientParameter(self):
383        """
384        Test sending of unavailable presence broadcast by not passing entity.
385        """
386
387        self.protocol.unavailable()
388        element = self.output[-1]
389        self.assertEquals("presence", element.name)
390        self.assertEquals(None, element.uri)
391        self.assertEquals(None, element.getAttribute('to'))
392        self.assertEquals("unavailable", element.getAttribute('type'))
393
394
395    def test_unavailableSender(self):
396        """
397        It should be possible to pass a sender address.
398        """
399        self.protocol.unavailable(JID('user@example.com'),
400                                  sender=JID('user@example.org'))
401        element = self.output[-1]
402        self.assertEquals("user@example.org", element.getAttribute('from'))
403
404
405    def test_subscribeSender(self):
406        """
407        It should be possible to pass a sender address.
408        """
409        self.protocol.subscribe(JID('user@example.com'),
410                                sender=JID('user@example.org'))
411        element = self.output[-1]
412        self.assertEquals("user@example.org", element.getAttribute('from'))
413
414
415    def test_unsubscribeSender(self):
416        """
417        It should be possible to pass a sender address.
418        """
419        self.protocol.unsubscribe(JID('user@example.com'),
420                                  sender=JID('user@example.org'))
421        element = self.output[-1]
422        self.assertEquals("user@example.org", element.getAttribute('from'))
423
424
425    def test_subscribedSender(self):
426        """
427        It should be possible to pass a sender address.
428        """
429        self.protocol.subscribed(JID('user@example.com'),
430                                 sender=JID('user@example.org'))
431        element = self.output[-1]
432        self.assertEquals("user@example.org", element.getAttribute('from'))
433
434
435    def test_unsubscribedSender(self):
436        """
437        It should be possible to pass a sender address.
438        """
439        self.protocol.unsubscribed(JID('user@example.com'),
440                                   sender=JID('user@example.org'))
441        element = self.output[-1]
442        self.assertEquals("user@example.org", element.getAttribute('from'))
443
444
445    def test_probeSender(self):
446        """
447        It should be possible to pass a sender address.
448        """
449        self.protocol.probe(JID('user@example.com'),
450                            sender=JID('user@example.org'))
451        element = self.output[-1]
452        self.assertEquals("user@example.org", element.getAttribute('from'))
453
454
455
456class RosterItemTest(unittest.TestCase):
457    """
458    Tests for L{xmppim.RosterItem}.
459    """
460
461    def test_toElement(self):
462        """
463        A roster item has the correct namespace/name, lacks unset attributes.
464        """
465        item = xmppim.RosterItem(JID('user@example.org'))
466        element = item.toElement()
467        self.assertEqual('item', element.name)
468        self.assertEqual(NS_ROSTER, element.uri)
469        self.assertFalse(element.hasAttribute('subscription'))
470        self.assertFalse(element.hasAttribute('ask'))
471        self.assertEqual(u"", element.getAttribute('name', u""))
472        self.assertFalse(element.hasAttribute('approved'))
473        self.assertEquals(0, len(list(element.elements())))
474
475
476    def test_toElementMinimal(self):
477        """
478        A bare roster item only has a jid attribute.
479        """
480        item = xmppim.RosterItem(JID('user@example.org'))
481        element = item.toElement()
482        self.assertEqual(u'user@example.org', element.getAttribute('jid'))
483
484
485    def test_toElementSubscriptionNone(self):
486        """
487        A roster item with no subscription has no subscription attribute.
488        """
489        item = xmppim.RosterItem(JID('user@example.org'),
490                                 subscriptionTo=False,
491                                 subscriptionFrom=False)
492        element = item.toElement()
493        self.assertIdentical(None, element.getAttribute('subscription'))
494
495
496    def test_toElementSubscriptionTo(self):
497        """
498        A roster item with subscriptionTo set has subscription 'to'.
499        """
500        item = xmppim.RosterItem(JID('user@example.org'),
501                                 subscriptionTo=True,
502                                 subscriptionFrom=False)
503        element = item.toElement()
504        self.assertEqual('to', element.getAttribute('subscription'))
505
506
507    def test_toElementSubscriptionFrom(self):
508        """
509        A roster item with subscriptionFrom set has subscription 'to'.
510        """
511        item = xmppim.RosterItem(JID('user@example.org'),
512                                 subscriptionTo=False,
513                                 subscriptionFrom=True)
514        element = item.toElement()
515        self.assertEqual('from', element.getAttribute('subscription'))
516
517
518    def test_toElementSubscriptionBoth(self):
519        """
520        A roster item with mutual subscription has subscription 'both'.
521        """
522        item = xmppim.RosterItem(JID('user@example.org'),
523                                 subscriptionTo=True,
524                                 subscriptionFrom=True)
525        element = item.toElement()
526        self.assertEqual('both', element.getAttribute('subscription'))
527
528
529    def test_toElementSubscriptionRemove(self):
530        """
531        A roster item with remove set has subscription 'remove'.
532        """
533        item = xmppim.RosterItem(JID('user@example.org'))
534        item.remove = True
535        element = item.toElement()
536        self.assertEqual('remove', element.getAttribute('subscription'))
537
538
539    def test_toElementAsk(self):
540        """
541        A roster item with pendingOut set has subscription 'ask'.
542        """
543        item = xmppim.RosterItem(JID('user@example.org'))
544        item.pendingOut = True
545        element = item.toElement()
546        self.assertEqual('subscribe', element.getAttribute('ask'))
547
548
549    def test_toElementName(self):
550        """
551        A roster item's name is rendered to the 'name' attribute.
552        """
553        item = xmppim.RosterItem(JID('user@example.org'),
554                                 name='Joe User')
555        element = item.toElement()
556        self.assertEqual(u'Joe User', element.getAttribute('name'))
557
558
559    def test_toElementGroups(self):
560        """
561        A roster item's groups are rendered as 'group' child elements.
562        """
563        groups = set(['Friends', 'Jabber'])
564        item = xmppim.RosterItem(JID('user@example.org'),
565                                 groups=groups)
566
567        element = item.toElement()
568        foundGroups = set()
569        for child in element.elements():
570            if child.uri == NS_ROSTER and child.name == 'group':
571                foundGroups.add(unicode(child))
572
573        self.assertEqual(groups, foundGroups)
574
575
576    def test_toElementApproved(self):
577        """
578        A pre-approved subscription for a roster item has an 'approved' flag.
579        """
580        item = xmppim.RosterItem(JID('user@example.org'))
581        item.approved = True
582        element = item.toElement()
583        self.assertEqual(u'true', element.getAttribute('approved'))
584
585
586    def test_fromElementMinimal(self):
587        """
588        A minimal roster item has a reference to the JID of the contact.
589        """
590
591        xml = """
592            <item xmlns="jabber:iq:roster"
593                  jid="test@example.org"/>
594        """
595
596        item = xmppim.RosterItem.fromElement(parseXml(xml))
597        self.assertEqual(JID(u"test@example.org"), item.entity)
598        self.assertEqual(u"", item.name)
599        self.assertFalse(item.subscriptionTo)
600        self.assertFalse(item.subscriptionFrom)
601        self.assertFalse(item.pendingOut)
602        self.assertFalse(item.approved)
603        self.assertEqual(set(), item.groups)
604
605
606    def test_fromElementName(self):
607        """
608        A roster item may have an optional name.
609        """
610
611        xml = """
612            <item xmlns="jabber:iq:roster"
613                  jid="test@example.org"
614                  name="Test User"/>
615        """
616
617        item = xmppim.RosterItem.fromElement(parseXml(xml))
618        self.assertEqual(u"Test User", item.name)
619
620
621    def test_fromElementGroups(self):
622        """
623        A roster item may have one or more groups.
624        """
625
626        xml = """
627            <item xmlns="jabber:iq:roster"
628                  jid="test@example.org">
629              <group>Friends</group>
630              <group>Twisted</group>
631            </item>
632        """
633
634        item = xmppim.RosterItem.fromElement(parseXml(xml))
635        self.assertIn(u"Twisted", item.groups)
636        self.assertIn(u"Friends", item.groups)
637
638
639    def test_fromElementSubscriptionNone(self):
640        """
641        Subscription 'none' sets both attributes to False.
642        """
643
644        xml = """
645            <item xmlns="jabber:iq:roster"
646                  jid="test@example.org"
647                  subscription="none"/>
648        """
649
650        item = xmppim.RosterItem.fromElement(parseXml(xml))
651        self.assertFalse(item.remove)
652        self.assertFalse(item.subscriptionTo)
653        self.assertFalse(item.subscriptionFrom)
654
655
656    def test_fromElementSubscriptionTo(self):
657        """
658        Subscription 'to' sets the corresponding attribute to True.
659        """
660
661        xml = """
662            <item xmlns="jabber:iq:roster"
663                  jid="test@example.org"
664                  subscription="to"/>
665        """
666
667        item = xmppim.RosterItem.fromElement(parseXml(xml))
668        self.assertFalse(item.remove)
669        self.assertTrue(item.subscriptionTo)
670        self.assertFalse(item.subscriptionFrom)
671
672
673    def test_fromElementSubscriptionFrom(self):
674        """
675        Subscription 'from' sets the corresponding attribute to True.
676        """
677
678        xml = """
679            <item xmlns="jabber:iq:roster"
680                  jid="test@example.org"
681                  subscription="from"/>
682        """
683
684        item = xmppim.RosterItem.fromElement(parseXml(xml))
685        self.assertFalse(item.remove)
686        self.assertFalse(item.subscriptionTo)
687        self.assertTrue(item.subscriptionFrom)
688
689
690    def test_fromElementSubscriptionBoth(self):
691        """
692        Subscription 'both' sets both attributes to True.
693        """
694
695        xml = """
696            <item xmlns="jabber:iq:roster"
697                  jid="test@example.org"
698                  subscription="both"/>
699        """
700
701        item = xmppim.RosterItem.fromElement(parseXml(xml))
702        self.assertFalse(item.remove)
703        self.assertTrue(item.subscriptionTo)
704        self.assertTrue(item.subscriptionFrom)
705
706
707    def test_fromElementSubscriptionRemove(self):
708        """
709        Subscription 'remove' sets the remove attribute.
710        """
711
712        xml = """
713            <item xmlns="jabber:iq:roster"
714                  jid="test@example.org"
715                  subscription="remove"/>
716        """
717
718        item = xmppim.RosterItem.fromElement(parseXml(xml))
719        self.assertTrue(item.remove)
720
721
722    def test_fromElementPendingOut(self):
723        """
724        The ask attribute, if set to 'subscription', means pending out.
725        """
726
727        xml = """
728            <item xmlns="jabber:iq:roster"
729                  jid="test@example.org"
730                  ask="subscribe"/>
731        """
732
733        item = xmppim.RosterItem.fromElement(parseXml(xml))
734        self.assertTrue(item.pendingOut)
735
736
737    def test_fromElementApprovedTrue(self):
738        """
739        The approved attribute (true) signals a pre-approved subscription.
740        """
741
742        xml = """
743            <item xmlns="jabber:iq:roster"
744                  jid="test@example.org"
745                  approved="true"/>
746        """
747
748        item = xmppim.RosterItem.fromElement(parseXml(xml))
749        self.assertTrue(item.approved)
750
751
752    def test_fromElementApproved1(self):
753        """
754        The approved attribute (1) signals a pre-approved subscription.
755        """
756
757        xml = """
758            <item xmlns="jabber:iq:roster"
759                  jid="test@example.org"
760                  approved="1"/>
761        """
762
763        item = xmppim.RosterItem.fromElement(parseXml(xml))
764        self.assertTrue(item.approved)
765
766
767    def test_jidDeprecationGet(self):
768        """
769        Getting the jid attribute works as entity and warns deprecation.
770        """
771        item = xmppim.RosterItem(JID('user@example.org'))
772        entity = self.assertWarns(DeprecationWarning,
773                                  "wokkel.xmppim.RosterItem.jid was "
774                                  "deprecated in Wokkel 0.7.1; "
775                                  "please use RosterItem.entity instead.",
776                                  xmppim.__file__,
777                                  getattr, item, 'jid')
778        self.assertIdentical(entity, item.entity)
779
780
781    def test_jidDeprecationSet(self):
782        """
783        Setting the jid attribute works as entity and warns deprecation.
784        """
785        item = xmppim.RosterItem(JID('user@example.org'))
786        self.assertWarns(DeprecationWarning,
787                         "wokkel.xmppim.RosterItem.jid was deprecated "
788                         "in Wokkel 0.7.1; "
789                         "please use RosterItem.entity instead.",
790                         xmppim.__file__,
791                         setattr, item, 'jid',
792                         JID('other@example.org'))
793        self.assertEqual(JID('other@example.org'), item.entity)
794
795
796    def test_askDeprecationGet(self):
797        """
798        Getting the ask attribute works as entity and warns deprecation.
799        """
800        item = xmppim.RosterItem(JID('user@example.org'))
801        item.pendingOut = True
802        ask = self.assertWarns(DeprecationWarning,
803                               "wokkel.xmppim.RosterItem.ask was "
804                               "deprecated in Wokkel 0.7.1; "
805                               "please use RosterItem.pendingOut instead.",
806                               xmppim.__file__,
807                               getattr, item, 'ask')
808        self.assertTrue(ask)
809
810
811    def test_askDeprecationSet(self):
812        """
813        Setting the ask attribute works as entity and warns deprecation.
814        """
815        item = xmppim.RosterItem(JID('user@example.org'))
816        self.assertWarns(DeprecationWarning,
817                         "wokkel.xmppim.RosterItem.ask was "
818                         "deprecated in Wokkel 0.7.1; "
819                         "please use RosterItem.pendingOut instead.",
820                         xmppim.__file__,
821                         setattr, item, 'ask',
822                         True)
823        self.assertTrue(item.pendingOut)
824
825
826
827class RosterRequestTest(unittest.TestCase):
828    """
829    Tests for L{xmppim.RosterRequest}.
830    """
831
832    def test_fromElement(self):
833        """
834        A bare roster request is parsed and missing information is None.
835        """
836        xml = """
837            <iq type='get' to='this@example.org/Home' from='this@example.org'>
838              <query xmlns='jabber:iq:roster'/>
839            </iq>
840        """
841
842        request = xmppim.RosterRequest.fromElement(parseXml(xml))
843        self.assertEqual('get', request.stanzaType)
844        self.assertEqual(JID('this@example.org/Home'), request.recipient)
845        self.assertEqual(JID('this@example.org'), request.sender)
846        self.assertEqual(None, request.item)
847        self.assertEqual(None, request.version)
848
849
850    def test_fromElementItem(self):
851        """
852        If an item is present, parse it and put it in the request item.
853        """
854        xml = """
855            <iq type='set' to='this@example.org/Home' from='this@example.org'>
856              <query xmlns='jabber:iq:roster'>
857                <item jid='user@example.org'/>
858              </query>
859            </iq>
860        """
861
862        request = xmppim.RosterRequest.fromElement(parseXml(xml))
863        self.assertNotIdentical(None, request.item)
864        self.assertEqual(JID('user@example.org'), request.item.entity)
865
866
867    def test_fromElementVersion(self):
868        """
869        If a ver attribute is present, put it in the request version.
870        """
871        xml = """
872            <iq type='set' to='this@example.org/Home' from='this@example.org'>
873              <query xmlns='jabber:iq:roster' ver='ver72'>
874                <item jid='user@example.org'/>
875              </query>
876            </iq>
877        """
878        request = xmppim.RosterRequest.fromElement(parseXml(xml))
879        self.assertEqual('ver72', request.version)
880
881
882    def test_fromElementVersionEmpty(self):
883        """
884        The ver attribute may be empty.
885        """
886        xml = """
887            <iq type='get' to='this@example.org/Home' from='this@example.org'>
888              <query xmlns='jabber:iq:roster' ver=''/>
889            </iq>
890        """
891        request = xmppim.RosterRequest.fromElement(parseXml(xml))
892        self.assertEqual('', request.version)
893
894
895    def test_toElement(self):
896        """
897        A roster request has a query element in the roster namespace.
898        """
899        request = xmppim.RosterRequest()
900        element = request.toElement()
901        children = element.elements()
902        child = next(children)
903        self.assertEqual(NS_ROSTER, child.uri)
904        self.assertEqual('query', child.name)
905
906
907    def test_toElementItem(self):
908        """
909        If an item is set, it is rendered as a child of the query.
910        """
911        request = xmppim.RosterRequest()
912        request.item = xmppim.RosterItem(JID('user@example.org'))
913        element = request.toElement()
914        children = element.query.elements()
915        child = next(children)
916        self.assertEqual(NS_ROSTER, child.uri)
917        self.assertEqual('item', child.name)
918
919
920
921class FakeClient(object):
922    """
923    Fake client stream manager for roster tests.
924    """
925
926    def __init__(self, xmlstream, jid):
927        self.xmlstream = xmlstream
928        self.jid = jid
929
930
931    def request(self, request):
932        element = request.toElement()
933        self.xmlstream.send(element)
934        return defer.Deferred()
935
936
937    def addHandler(self, handler):
938        handler.makeConnection(self.xmlstream)
939        handler.connectionInitialized()
940
941
942    def test_toElementVersion(self):
943        """
944        If the roster version is set, a 'ver' attribute is added.
945        """
946        request = xmppim.RosterRequest()
947        request.version = 'ver72'
948        element = request.toElement()
949        self.assertEqual('ver72', element.query.getAttribute('ver'))
950
951
952    def test_toElementVersionEmpty(self):
953        """
954        If the roster version is the empty string, it should add 'ver', too.
955        """
956        request = xmppim.RosterRequest()
957        request.version = ''
958        element = request.toElement()
959        self.assertEqual('', element.query.getAttribute('ver'))
960
961
962
963class RosterClientProtocolTest(unittest.TestCase, TestableRequestHandlerMixin):
964    """
965    Tests for L{xmppim.RosterClientProtocol}.
966    """
967
968    def setUp(self):
969        self.stub = XmlStreamStub()
970        self.client = FakeClient(self.stub.xmlstream, JID('this@example.org'))
971        self.service = xmppim.RosterClientProtocol()
972        self.service.setHandlerParent(self.client)
973
974
975    def test_setItem(self):
976        """
977        Setting a roster item renders the item and sends it out.
978        """
979        item = xmppim.RosterItem(JID('test@example.org'),
980                                 name='Joe User',
981                                 groups=set(['Friends', 'Jabber']))
982        d = self.service.setItem(item)
983
984        # Inspect outgoing iq request
985
986        iq = self.stub.output[-1]
987        self.assertEqual('set', iq.getAttribute('type'))
988        self.assertNotIdentical(None, iq.query)
989        self.assertEqual(NS_ROSTER, iq.query.uri)
990
991        children = list(domish.generateElementsQNamed(iq.query.children,
992                                                      'item', NS_ROSTER))
993        self.assertEqual(1, len(children))
994        child = children[0]
995        self.assertEqual('test@example.org', child['jid'])
996        self.assertIdentical(None, child.getAttribute('subscription'))
997
998        # Fake successful response
999
1000        response = toResponse(iq, 'result')
1001        d.callback(response)
1002        return d
1003
1004
1005    def test_setItemIgnoreAttributes(self):
1006        """
1007        Certain attributes should be rendered for roster set.
1008        """
1009        item = xmppim.RosterItem(JID('test@example.org'),
1010                                 subscriptionTo=True,
1011                                 subscriptionFrom=False,
1012                                 name='Joe User',
1013                                 groups=set(['Friends', 'Jabber']))
1014        item.pendingOut = True
1015        item.approved = True
1016        d = self.service.setItem(item)
1017
1018        # Inspect outgoing iq request
1019
1020        iq = self.stub.output[-1]
1021        self.assertEqual('set', iq.getAttribute('type'))
1022        self.assertNotIdentical(None, iq.query)
1023        self.assertEqual(NS_ROSTER, iq.query.uri)
1024
1025        children = list(domish.generateElementsQNamed(iq.query.children,
1026                                                      'item', NS_ROSTER))
1027        self.assertEqual(1, len(children))
1028        child = children[0]
1029        self.assertIdentical(None, child.getAttribute('ask'))
1030        self.assertIdentical(None, child.getAttribute('approved'))
1031        self.assertIdentical(None, child.getAttribute('subscription'))
1032
1033        # Fake successful response
1034
1035        response = toResponse(iq, 'result')
1036        d.callback(response)
1037        return d
1038
1039
1040    def test_removeItem(self):
1041        """
1042        Removing a roster item is setting an item with subscription C{remove}.
1043        """
1044        d = self.service.removeItem(JID('test@example.org'))
1045
1046        # Inspect outgoing iq request
1047
1048        iq = self.stub.output[-1]
1049        self.assertEqual('set', iq.getAttribute('type'))
1050        self.assertNotIdentical(None, iq.query)
1051        self.assertEqual(NS_ROSTER, iq.query.uri)
1052
1053        children = list(domish.generateElementsQNamed(iq.query.children,
1054                                                      'item', NS_ROSTER))
1055        self.assertEqual(1, len(children))
1056        child = children[0]
1057        self.assertEqual('test@example.org', child['jid'])
1058        self.assertEqual('remove', child.getAttribute('subscription'))
1059
1060        # Fake successful response
1061
1062        response = toResponse(iq, 'result')
1063        d.callback(response)
1064        return d
1065
1066
1067    def test_getRoster(self):
1068        """
1069        A request for the roster is sent out and the response is parsed.
1070        """
1071        def cb(roster):
1072            self.assertIn(JID('user@example.org'), roster)
1073            self.assertIdentical(None, getattr(roster, 'version'))
1074
1075        d = self.service.getRoster()
1076        d.addCallback(cb)
1077
1078        # Inspect outgoing iq request
1079
1080        iq = self.stub.output[-1]
1081        self.assertEqual('get', iq.getAttribute('type'))
1082        self.assertNotIdentical(None, iq.query)
1083        self.assertEqual(NS_ROSTER, iq.query.uri)
1084        self.assertFalse(iq.query.hasAttribute('ver'))
1085
1086        # Fake successful response
1087        response = toResponse(iq, 'result')
1088        query = response.addElement((NS_ROSTER, 'query'))
1089        item = query.addElement('item')
1090        item['jid'] = 'user@example.org'
1091
1092        d.callback(response)
1093        return d
1094
1095
1096    def test_getRosterVer(self):
1097        """
1098        A request for the roster with version passes the version on.
1099        """
1100        def cb(roster):
1101            self.assertEqual('ver96', getattr(roster, 'version'))
1102
1103        d = self.service.getRoster(version='ver72')
1104        d.addCallback(cb)
1105
1106        # Inspect outgoing iq request
1107
1108        iq = self.stub.output[-1]
1109        self.assertEqual('ver72', iq.query.getAttribute('ver'))
1110
1111        # Fake successful response
1112        response = toResponse(iq, 'result')
1113        query = response.addElement((NS_ROSTER, 'query'))
1114        query['ver'] = 'ver96'
1115        item = query.addElement('item')
1116        item['jid'] = 'user@example.org'
1117
1118        d.callback(response)
1119        return d
1120
1121
1122    def test_getRosterVerEmptyResult(self):
1123        """
1124        An empty response is returned as None.
1125        """
1126        def cb(response):
1127            self.assertIdentical(None, response)
1128
1129        d = self.service.getRoster(version='ver72')
1130        d.addCallback(cb)
1131
1132        # Inspect outgoing iq request
1133
1134        iq = self.stub.output[-1]
1135
1136        # Fake successful response
1137        response = toResponse(iq, 'result')
1138        d.callback(response)
1139        return d
1140
1141
1142    def test_onRosterSet(self):
1143        """
1144        A roster push causes onRosterSet to be called with the parsed item.
1145        """
1146        xml = """
1147          <iq type='set'>
1148            <query xmlns='jabber:iq:roster'>
1149              <item jid='user@example.org'/>
1150            </query>
1151          </iq>
1152        """
1153
1154        items = []
1155
1156        def onRosterSet(item):
1157            items.append(item)
1158
1159        def cb(result):
1160            self.assertEqual(1, len(items))
1161            self.assertEqual(JID('user@example.org'), items[0].entity)
1162
1163        self.service.onRosterSet = onRosterSet
1164
1165        d = self.assertWarns(DeprecationWarning,
1166                             "wokkel.xmppim.RosterClientProtocol.onRosterSet "
1167                             "was deprecated in Wokkel 0.7.1; "
1168                             "please use RosterClientProtocol.setReceived "
1169                             "instead.",
1170                             xmppim.__file__,
1171                             self.handleRequest, xml)
1172        d.addCallback(cb)
1173        return d
1174
1175
1176    def test_onRosterRemove(self):
1177        """
1178        A roster push causes onRosterSet to be called with the parsed item.
1179        """
1180        xml = """
1181          <iq type='set'>
1182            <query xmlns='jabber:iq:roster'>
1183              <item jid='user@example.org' subscription='remove'/>
1184            </query>
1185          </iq>
1186        """
1187
1188        entities = []
1189
1190        def onRosterRemove(entity):
1191            entities.append(entity)
1192
1193        def cb(result):
1194            self.assertEqual([JID('user@example.org')], entities)
1195
1196        self.service.onRosterRemove = onRosterRemove
1197
1198        d = self.assertWarns(DeprecationWarning,
1199                             "wokkel.xmppim.RosterClientProtocol.onRosterRemove "
1200                             "was deprecated in Wokkel 0.7.1; "
1201                             "please use RosterClientProtocol.removeReceived "
1202                             "instead.",
1203                             xmppim.__file__,
1204                             self.handleRequest, xml)
1205        d.addCallback(cb)
1206        return d
1207
1208
1209    def test_setReceived(self):
1210        """
1211        A roster set push causes setReceived.
1212        """
1213        xml = """
1214          <iq type='set'>
1215            <query xmlns='jabber:iq:roster'>
1216              <item jid='user@example.org'/>
1217            </query>
1218          </iq>
1219        """
1220
1221        requests = []
1222
1223        def setReceived(request):
1224            requests.append(request)
1225
1226        def cb(result):
1227            self.assertEqual(1, len(requests), "setReceived was not called")
1228            self.assertEqual(JID('user@example.org'), requests[0].item.entity)
1229
1230        self.service.setReceived = setReceived
1231
1232        d = self.handleRequest(xml)
1233        d.addCallback(cb)
1234        return d
1235
1236
1237    def test_setReceivedOtherSource(self):
1238        """
1239        Roster pushes can be sent from other entities, too, ignore them.
1240        """
1241        xml = """
1242          <iq type='set' to='this@example.org/Home' from='other@example.org'>
1243            <query xmlns='jabber:iq:roster'>
1244              <item jid='user@example.org'/>
1245            </query>
1246          </iq>
1247        """
1248
1249        def cb(result):
1250            self.assertEquals('service-unavailable', result.condition)
1251
1252        d = self.handleRequest(xml)
1253        self.assertFailure(d, error.StanzaError)
1254        d.addCallback(cb)
1255        return d
1256
1257
1258    def test_setReceivedOtherSourceAllowed(self):
1259        """
1260        Roster pushes can be sent from other entities, allow them.
1261        """
1262        xml = """
1263          <iq type='set' to='this@example.org/Home' from='other@example.org'>
1264            <query xmlns='jabber:iq:roster'>
1265              <item jid='user@example.org'/>
1266            </query>
1267          </iq>
1268        """
1269
1270        self.service.allowAnySender = True
1271        requests = []
1272
1273        def setReceived(request):
1274            requests.append(request)
1275
1276        def cb(result):
1277            self.assertEqual(1, len(requests), "setReceived was not called")
1278
1279        self.service.setReceived = setReceived
1280
1281        d = self.handleRequest(xml)
1282        d.addCallback(cb)
1283        return d
1284
1285
1286    def test_setReceivedOtherSourceIgnored(self):
1287        """
1288        Roster pushes can be sent from other entities, allow them.
1289        """
1290        xml = """
1291          <iq type='set' to='this@example.org/Home' from='bad@example.org'>
1292            <query xmlns='jabber:iq:roster'>
1293              <item jid='user@example.org'/>
1294            </query>
1295          </iq>
1296        """
1297
1298        self.service.allowAnySender = True
1299
1300        def setReceived(request):
1301            if request.sender == JID('bad@example.org'):
1302                raise xmppim.RosterPushIgnored()
1303
1304        def cb(result):
1305            self.assertEquals('service-unavailable', result.condition)
1306
1307        self.service.setReceived = setReceived
1308
1309
1310        d = self.handleRequest(xml)
1311        self.assertFailure(d, error.StanzaError)
1312        d.addCallback(cb)
1313        return d
1314
1315
1316    def test_removeReceived(self):
1317        """
1318        A roster remove push causes removeReceived.
1319        """
1320        xml = """
1321          <iq type='set'>
1322            <query xmlns='jabber:iq:roster'>
1323              <item jid='user@example.org' subscription='remove'/>
1324            </query>
1325          </iq>
1326        """
1327
1328        requests = []
1329
1330        def removeReceived(request):
1331            requests.append(request)
1332
1333        def cb(result):
1334            self.assertEqual(1, len(requests), "removeReceived was not called")
1335            self.assertEqual(JID('user@example.org'), requests[0].item.entity)
1336
1337        self.service.removeReceived = removeReceived
1338
1339        d = self.handleRequest(xml)
1340        d.addCallback(cb)
1341        return d
Note: See TracBrowser for help on using the repository browser.