source: wokkel/test/test_xmppim.py @ 179:4d3066bc4034

0.7.1
Last change on this file since 179:4d3066bc4034 was 179:4d3066bc4034, checked in by Ralph Meijer <ralphm@…>, 9 years ago

Release Wokkel 0.7.1.

File size: 41.9 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,
[179]770                                  "wokkel.xmppim.RosterItem.jid was "
771                                  "deprecated in Wokkel 0.7.1; "
772                                  "please use RosterItem.entity instead.",
[172]773                                  xmppim.__file__,
774                                  getattr, item, 'jid')
775        self.assertIdentical(entity, item.entity)
776
777
778    def test_jidDeprecationSet(self):
779        """
780        Setting the jid attribute works as entity and warns deprecation.
781        """
782        item = xmppim.RosterItem(JID('user@example.org'))
783        self.assertWarns(DeprecationWarning,
[179]784                         "wokkel.xmppim.RosterItem.jid was deprecated "
785                         "in Wokkel 0.7.1; "
786                         "please use RosterItem.entity instead.",
[172]787                         xmppim.__file__,
788                         setattr, item, 'jid',
789                         JID('other@example.org'))
790        self.assertEqual(JID('other@example.org'), item.entity)
791
792
793    def test_askDeprecationGet(self):
794        """
795        Getting the ask attribute works as entity and warns deprecation.
796        """
797        item = xmppim.RosterItem(JID('user@example.org'))
798        item.pendingOut = True
799        ask = self.assertWarns(DeprecationWarning,
[179]800                               "wokkel.xmppim.RosterItem.ask was "
801                               "deprecated in Wokkel 0.7.1; "
802                               "please use RosterItem.pendingOut instead.",
[172]803                               xmppim.__file__,
804                               getattr, item, 'ask')
805        self.assertTrue(ask)
806
807
808    def test_askDeprecationSet(self):
809        """
810        Setting the ask attribute works as entity and warns deprecation.
811        """
812        item = xmppim.RosterItem(JID('user@example.org'))
813        self.assertWarns(DeprecationWarning,
[179]814                         "wokkel.xmppim.RosterItem.ask was "
815                         "deprecated in Wokkel 0.7.1; "
816                         "please use RosterItem.pendingOut instead.",
[172]817                         xmppim.__file__,
818                         setattr, item, 'ask',
819                         True)
820        self.assertTrue(item.pendingOut)
821
822
823
824class RosterRequestTest(unittest.TestCase):
825    """
826    Tests for L{xmppim.RosterRequest}.
827    """
828
829    def test_fromElement(self):
830        """
831        A bare roster request is parsed and missing information is None.
832        """
833        xml = """
834            <iq type='get' to='this@example.org/Home' from='this@example.org'>
835              <query xmlns='jabber:iq:roster'/>
836            </iq>
837        """
838
839        request = xmppim.RosterRequest.fromElement(parseXml(xml))
840        self.assertEqual('get', request.stanzaType)
841        self.assertEqual(JID('this@example.org/Home'), request.recipient)
842        self.assertEqual(JID('this@example.org'), request.sender)
843        self.assertEqual(None, request.item)
[173]844        self.assertEqual(None, request.version)
[172]845
846
847    def test_fromElementItem(self):
848        """
849        If an item is present, parse it and put it in the request item.
850        """
851        xml = """
852            <iq type='set' to='this@example.org/Home' from='this@example.org'>
853              <query xmlns='jabber:iq:roster'>
854                <item jid='user@example.org'/>
855              </query>
856            </iq>
857        """
858
859        request = xmppim.RosterRequest.fromElement(parseXml(xml))
860        self.assertNotIdentical(None, request.item)
861        self.assertEqual(JID('user@example.org'), request.item.entity)
862
863
[173]864    def test_fromElementVersion(self):
865        """
866        If a ver attribute is present, put it in the request version.
867        """
868        xml = """
869            <iq type='set' to='this@example.org/Home' from='this@example.org'>
870              <query xmlns='jabber:iq:roster' ver='ver72'>
871                <item jid='user@example.org'/>
872              </query>
873            </iq>
874        """
875        request = xmppim.RosterRequest.fromElement(parseXml(xml))
876        self.assertEqual('ver72', request.version)
877
878
879    def test_fromElementVersionEmpty(self):
880        """
881        The ver attribute may be empty.
882        """
883        xml = """
884            <iq type='get' to='this@example.org/Home' from='this@example.org'>
885              <query xmlns='jabber:iq:roster' ver=''/>
886            </iq>
887        """
888        request = xmppim.RosterRequest.fromElement(parseXml(xml))
889        self.assertEqual('', request.version)
890
891
[172]892    def test_toElement(self):
893        """
894        A roster request has a query element in the roster namespace.
895        """
896        request = xmppim.RosterRequest()
897        element = request.toElement()
898        children = element.elements()
899        child = children.next()
900        self.assertEqual(NS_ROSTER, child.uri)
901        self.assertEqual('query', child.name)
902
903
904    def test_toElementItem(self):
905        """
906        If an item is set, it is rendered as a child of the query.
907        """
908        request = xmppim.RosterRequest()
909        request.item = xmppim.RosterItem(JID('user@example.org'))
910        element = request.toElement()
911        children = element.query.elements()
912        child = children.next()
913        self.assertEqual(NS_ROSTER, child.uri)
914        self.assertEqual('item', child.name)
915
916
917
918class FakeClient(object):
919    """
920    Fake client stream manager for roster tests.
921    """
922
923    def __init__(self, xmlstream, jid):
924        self.xmlstream = xmlstream
925        self.jid = jid
926
927
928    def request(self, request):
929        element = request.toElement()
930        self.xmlstream.send(element)
931        return defer.Deferred()
932
933
934    def addHandler(self, handler):
935        handler.makeConnection(self.xmlstream)
936        handler.connectionInitialized()
937
938
[173]939    def test_toElementVersion(self):
940        """
941        If the roster version is set, a 'ver' attribute is added.
942        """
943        request = xmppim.RosterRequest()
944        request.version = 'ver72'
945        element = request.toElement()
946        self.assertEqual('ver72', element.query.getAttribute('ver'))
947
948
949    def test_toElementVersionEmpty(self):
950        """
951        If the roster version is the empty string, it should add 'ver', too.
952        """
953        request = xmppim.RosterRequest()
954        request.version = ''
955        element = request.toElement()
956        self.assertEqual('', element.query.getAttribute('ver'))
957
958
[172]959
960class RosterClientProtocolTest(unittest.TestCase, TestableRequestHandlerMixin):
[28]961    """
962    Tests for L{xmppim.RosterClientProtocol}.
963    """
964
965    def setUp(self):
966        self.stub = XmlStreamStub()
[172]967        self.client = FakeClient(self.stub.xmlstream, JID('this@example.org'))
968        self.service = xmppim.RosterClientProtocol()
969        self.service.setHandlerParent(self.client)
[28]970
971
[174]972    def test_setItem(self):
973        """
974        Setting a roster item renders the item and sends it out.
975        """
976        item = xmppim.RosterItem(JID('test@example.org'),
977                                 name='Joe User',
978                                 groups=set(['Friends', 'Jabber']))
979        d = self.service.setItem(item)
980
981        # Inspect outgoing iq request
982
983        iq = self.stub.output[-1]
984        self.assertEqual('set', iq.getAttribute('type'))
985        self.assertNotIdentical(None, iq.query)
986        self.assertEqual(NS_ROSTER, iq.query.uri)
987
988        children = list(domish.generateElementsQNamed(iq.query.children,
989                                                      'item', NS_ROSTER))
990        self.assertEqual(1, len(children))
991        child = children[0]
992        self.assertEqual('test@example.org', child['jid'])
993        self.assertIdentical(None, child.getAttribute('subscription'))
994
995        # Fake successful response
996
997        response = toResponse(iq, 'result')
998        d.callback(response)
999        return d
1000
1001
1002    def test_setItemIgnoreAttributes(self):
1003        """
1004        Certain attributes should be rendered for roster set.
1005        """
1006        item = xmppim.RosterItem(JID('test@example.org'),
1007                                 subscriptionTo=True,
1008                                 subscriptionFrom=False,
1009                                 name='Joe User',
1010                                 groups=set(['Friends', 'Jabber']))
1011        item.pendingOut = True
1012        item.approved = True
1013        d = self.service.setItem(item)
1014
1015        # Inspect outgoing iq request
1016
1017        iq = self.stub.output[-1]
1018        self.assertEqual('set', iq.getAttribute('type'))
1019        self.assertNotIdentical(None, iq.query)
1020        self.assertEqual(NS_ROSTER, iq.query.uri)
1021
1022        children = list(domish.generateElementsQNamed(iq.query.children,
1023                                                      'item', NS_ROSTER))
1024        self.assertEqual(1, len(children))
1025        child = children[0]
1026        self.assertIdentical(None, child.getAttribute('ask'))
1027        self.assertIdentical(None, child.getAttribute('approved'))
1028        self.assertIdentical(None, child.getAttribute('subscription'))
1029
1030        # Fake successful response
1031
1032        response = toResponse(iq, 'result')
1033        d.callback(response)
1034        return d
1035
1036
[28]1037    def test_removeItem(self):
1038        """
1039        Removing a roster item is setting an item with subscription C{remove}.
1040        """
[172]1041        d = self.service.removeItem(JID('test@example.org'))
[28]1042
1043        # Inspect outgoing iq request
1044
1045        iq = self.stub.output[-1]
[172]1046        self.assertEqual('set', iq.getAttribute('type'))
[28]1047        self.assertNotIdentical(None, iq.query)
[172]1048        self.assertEqual(NS_ROSTER, iq.query.uri)
[28]1049
1050        children = list(domish.generateElementsQNamed(iq.query.children,
1051                                                      'item', NS_ROSTER))
[172]1052        self.assertEqual(1, len(children))
[28]1053        child = children[0]
[172]1054        self.assertEqual('test@example.org', child['jid'])
1055        self.assertEqual('remove', child.getAttribute('subscription'))
[28]1056
1057        # Fake successful response
1058
1059        response = toResponse(iq, 'result')
[172]1060        d.callback(response)
[28]1061        return d
[172]1062
1063
1064    def test_getRoster(self):
1065        """
1066        A request for the roster is sent out and the response is parsed.
1067        """
1068        def cb(roster):
1069            self.assertIn(JID('user@example.org'), roster)
[173]1070            self.assertIdentical(None, getattr(roster, 'version'))
[172]1071
1072        d = self.service.getRoster()
1073        d.addCallback(cb)
1074
1075        # Inspect outgoing iq request
1076
1077        iq = self.stub.output[-1]
1078        self.assertEqual('get', iq.getAttribute('type'))
1079        self.assertNotIdentical(None, iq.query)
1080        self.assertEqual(NS_ROSTER, iq.query.uri)
[173]1081        self.assertFalse(iq.query.hasAttribute('ver'))
[172]1082
1083        # Fake successful response
1084        response = toResponse(iq, 'result')
1085        query = response.addElement((NS_ROSTER, 'query'))
1086        item = query.addElement('item')
1087        item['jid'] = 'user@example.org'
1088
1089        d.callback(response)
1090        return d
1091
1092
[173]1093    def test_getRosterVer(self):
1094        """
1095        A request for the roster with version passes the version on.
1096        """
1097        def cb(roster):
1098            self.assertEqual('ver96', getattr(roster, 'version'))
1099
1100        d = self.service.getRoster(version='ver72')
1101        d.addCallback(cb)
1102
1103        # Inspect outgoing iq request
1104
1105        iq = self.stub.output[-1]
1106        self.assertEqual('ver72', iq.query.getAttribute('ver'))
1107
1108        # Fake successful response
1109        response = toResponse(iq, 'result')
1110        query = response.addElement((NS_ROSTER, 'query'))
1111        query['ver'] = 'ver96'
1112        item = query.addElement('item')
1113        item['jid'] = 'user@example.org'
1114
1115        d.callback(response)
1116        return d
1117
1118
1119    def test_getRosterVerEmptyResult(self):
1120        """
1121        An empty response is returned as None.
1122        """
1123        def cb(response):
1124            self.assertIdentical(None, response)
1125
1126        d = self.service.getRoster(version='ver72')
1127        d.addCallback(cb)
1128
1129        # Inspect outgoing iq request
1130
1131        iq = self.stub.output[-1]
1132
1133        # Fake successful response
1134        response = toResponse(iq, 'result')
1135        d.callback(response)
1136        return d
1137
1138
[172]1139    def test_onRosterSet(self):
1140        """
1141        A roster push causes onRosterSet to be called with the parsed item.
1142        """
1143        xml = """
1144          <iq type='set'>
1145            <query xmlns='jabber:iq:roster'>
1146              <item jid='user@example.org'/>
1147            </query>
1148          </iq>
1149        """
1150
1151        items = []
1152
1153        def onRosterSet(item):
1154            items.append(item)
1155
1156        def cb(result):
1157            self.assertEqual(1, len(items))
1158            self.assertEqual(JID('user@example.org'), items[0].entity)
1159
1160        self.service.onRosterSet = onRosterSet
1161
1162        d = self.assertWarns(DeprecationWarning,
1163                             "wokkel.xmppim.RosterClientProtocol.onRosterSet "
[179]1164                             "was deprecated in Wokkel 0.7.1; "
1165                             "please use RosterClientProtocol.setReceived "
1166                             "instead.",
[172]1167                             xmppim.__file__,
1168                             self.handleRequest, xml)
1169        d.addCallback(cb)
1170        return d
1171
1172
1173    def test_onRosterRemove(self):
1174        """
1175        A roster push causes onRosterSet to be called with the parsed item.
1176        """
1177        xml = """
1178          <iq type='set'>
1179            <query xmlns='jabber:iq:roster'>
1180              <item jid='user@example.org' subscription='remove'/>
1181            </query>
1182          </iq>
1183        """
1184
1185        entities = []
1186
1187        def onRosterRemove(entity):
1188            entities.append(entity)
1189
1190        def cb(result):
1191            self.assertEqual([JID('user@example.org')], entities)
1192
1193        self.service.onRosterRemove = onRosterRemove
1194
1195        d = self.assertWarns(DeprecationWarning,
1196                             "wokkel.xmppim.RosterClientProtocol.onRosterRemove "
[179]1197                             "was deprecated in Wokkel 0.7.1; "
1198                             "please use RosterClientProtocol.removeReceived "
1199                             "instead.",
[172]1200                             xmppim.__file__,
1201                             self.handleRequest, xml)
1202        d.addCallback(cb)
1203        return d
1204
1205
1206    def test_setReceived(self):
1207        """
1208        A roster set push causes setReceived.
1209        """
1210        xml = """
1211          <iq type='set'>
1212            <query xmlns='jabber:iq:roster'>
1213              <item jid='user@example.org'/>
1214            </query>
1215          </iq>
1216        """
1217
1218        requests = []
1219
1220        def setReceived(request):
1221            requests.append(request)
1222
1223        def cb(result):
1224            self.assertEqual(1, len(requests), "setReceived was not called")
1225            self.assertEqual(JID('user@example.org'), requests[0].item.entity)
1226
1227        self.service.setReceived = setReceived
1228
1229        d = self.handleRequest(xml)
1230        d.addCallback(cb)
1231        return d
1232
1233
1234    def test_setReceivedOtherSource(self):
1235        """
1236        Roster pushes can be sent from other entities, too, ignore them.
1237        """
1238        xml = """
1239          <iq type='set' to='this@example.org/Home' from='other@example.org'>
1240            <query xmlns='jabber:iq:roster'>
1241              <item jid='user@example.org'/>
1242            </query>
1243          </iq>
1244        """
1245
1246        def cb(result):
1247            self.assertEquals('service-unavailable', result.condition)
1248
1249        d = self.handleRequest(xml)
1250        self.assertFailure(d, error.StanzaError)
1251        d.addCallback(cb)
1252        return d
1253
1254
1255    def test_setReceivedOtherSourceAllowed(self):
1256        """
1257        Roster pushes can be sent from other entities, allow them.
1258        """
1259        xml = """
1260          <iq type='set' to='this@example.org/Home' from='other@example.org'>
1261            <query xmlns='jabber:iq:roster'>
1262              <item jid='user@example.org'/>
1263            </query>
1264          </iq>
1265        """
1266
1267        self.service.allowAnySender = True
1268        requests = []
1269
1270        def setReceived(request):
1271            requests.append(request)
1272
1273        def cb(result):
1274            self.assertEqual(1, len(requests), "setReceived was not called")
1275
1276        self.service.setReceived = setReceived
1277
1278        d = self.handleRequest(xml)
1279        d.addCallback(cb)
1280        return d
1281
1282
1283    def test_setReceivedOtherSourceIgnored(self):
1284        """
1285        Roster pushes can be sent from other entities, allow them.
1286        """
1287        xml = """
1288          <iq type='set' to='this@example.org/Home' from='bad@example.org'>
1289            <query xmlns='jabber:iq:roster'>
1290              <item jid='user@example.org'/>
1291            </query>
1292          </iq>
1293        """
1294
1295        self.service.allowAnySender = True
1296
1297        def setReceived(request):
1298            if request.sender == JID('bad@example.org'):
1299                raise xmppim.RosterPushIgnored()
1300
1301        def cb(result):
1302            self.assertEquals('service-unavailable', result.condition)
1303
1304        self.service.setReceived = setReceived
1305
1306
1307        d = self.handleRequest(xml)
1308        self.assertFailure(d, error.StanzaError)
1309        d.addCallback(cb)
1310        return d
1311
1312
1313    def test_removeReceived(self):
1314        """
1315        A roster remove push causes removeReceived.
1316        """
1317        xml = """
1318          <iq type='set'>
1319            <query xmlns='jabber:iq:roster'>
1320              <item jid='user@example.org' subscription='remove'/>
1321            </query>
1322          </iq>
1323        """
1324
1325        requests = []
1326
1327        def removeReceived(request):
1328            requests.append(request)
1329
1330        def cb(result):
1331            self.assertEqual(1, len(requests), "removeReceived was not called")
1332            self.assertEqual(JID('user@example.org'), requests[0].item.entity)
1333
1334        self.service.removeReceived = removeReceived
1335
1336        d = self.handleRequest(xml)
1337        d.addCallback(cb)
1338        return d
Note: See TracBrowser for help on using the repository browser.