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
Line 
1# Copyright (c) Ralph Meijer.
2# See LICENSE for details
3
4"""
5Tests for L{wokkel.xmppim}.
6"""
7
8from twisted.internet import defer
9from twisted.trial import unittest
10from twisted.words.protocols.jabber import error
11from twisted.words.protocols.jabber.jid import JID
12from twisted.words.protocols.jabber.xmlstream import toResponse
13from twisted.words.xish import domish, utility
14
15from wokkel import xmppim
16from wokkel.generic import ErrorStanza, parseXml
17from wokkel.test.helpers import TestableRequestHandlerMixin, XmlStreamStub
18
19NS_XML = 'http://www.w3.org/XML/1998/namespace'
20NS_ROSTER = 'jabber:iq:roster'
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'))
80
81
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
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)
840        self.assertEqual(None, request.version)
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
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
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
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
955
956class RosterClientProtocolTest(unittest.TestCase, TestableRequestHandlerMixin):
957    """
958    Tests for L{xmppim.RosterClientProtocol}.
959    """
960
961    def setUp(self):
962        self.stub = XmlStreamStub()
963        self.client = FakeClient(self.stub.xmlstream, JID('this@example.org'))
964        self.service = xmppim.RosterClientProtocol()
965        self.service.setHandlerParent(self.client)
966
967
968    def test_removeItem(self):
969        """
970        Removing a roster item is setting an item with subscription C{remove}.
971        """
972        d = self.service.removeItem(JID('test@example.org'))
973
974        # Inspect outgoing iq request
975
976        iq = self.stub.output[-1]
977        self.assertEqual('set', iq.getAttribute('type'))
978        self.assertNotIdentical(None, iq.query)
979        self.assertEqual(NS_ROSTER, iq.query.uri)
980
981        children = list(domish.generateElementsQNamed(iq.query.children,
982                                                      'item', NS_ROSTER))
983        self.assertEqual(1, len(children))
984        child = children[0]
985        self.assertEqual('test@example.org', child['jid'])
986        self.assertEqual('remove', child.getAttribute('subscription'))
987
988        # Fake successful response
989
990        response = toResponse(iq, 'result')
991        d.callback(response)
992        return d
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)
1001            self.assertIdentical(None, getattr(roster, 'version'))
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)
1012        self.assertFalse(iq.query.hasAttribute('ver'))
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
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
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.