source: wokkel/test/test_xmppim.py @ 173:6b0eb01b5744

Last change on this file since 173:6b0eb01b5744 was 173:6b0eb01b5744, checked in by Ralph Meijer <ralphm@…>, 8 years ago

Add support for roster versioning.

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