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@…>, 7 years ago

Release Wokkel 0.7.1.

File size: 41.9 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 was "
771                                  "deprecated in Wokkel 0.7.1; "
772                                  "please use RosterItem.entity instead.",
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,
784                         "wokkel.xmppim.RosterItem.jid was deprecated "
785                         "in Wokkel 0.7.1; "
786                         "please use RosterItem.entity instead.",
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,
800                               "wokkel.xmppim.RosterItem.ask was "
801                               "deprecated in Wokkel 0.7.1; "
802                               "please use RosterItem.pendingOut instead.",
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,
814                         "wokkel.xmppim.RosterItem.ask was "
815                         "deprecated in Wokkel 0.7.1; "
816                         "please use RosterItem.pendingOut instead.",
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)
844        self.assertEqual(None, request.version)
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
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
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
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
959
960class RosterClientProtocolTest(unittest.TestCase, TestableRequestHandlerMixin):
961    """
962    Tests for L{xmppim.RosterClientProtocol}.
963    """
964
965    def setUp(self):
966        self.stub = XmlStreamStub()
967        self.client = FakeClient(self.stub.xmlstream, JID('this@example.org'))
968        self.service = xmppim.RosterClientProtocol()
969        self.service.setHandlerParent(self.client)
970
971
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
1037    def test_removeItem(self):
1038        """
1039        Removing a roster item is setting an item with subscription C{remove}.
1040        """
1041        d = self.service.removeItem(JID('test@example.org'))
1042
1043        # Inspect outgoing iq request
1044
1045        iq = self.stub.output[-1]
1046        self.assertEqual('set', iq.getAttribute('type'))
1047        self.assertNotIdentical(None, iq.query)
1048        self.assertEqual(NS_ROSTER, iq.query.uri)
1049
1050        children = list(domish.generateElementsQNamed(iq.query.children,
1051                                                      'item', NS_ROSTER))
1052        self.assertEqual(1, len(children))
1053        child = children[0]
1054        self.assertEqual('test@example.org', child['jid'])
1055        self.assertEqual('remove', child.getAttribute('subscription'))
1056
1057        # Fake successful response
1058
1059        response = toResponse(iq, 'result')
1060        d.callback(response)
1061        return d
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)
1070            self.assertIdentical(None, getattr(roster, 'version'))
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)
1081        self.assertFalse(iq.query.hasAttribute('ver'))
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
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
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 "
1164                             "was deprecated in Wokkel 0.7.1; "
1165                             "please use RosterClientProtocol.setReceived "
1166                             "instead.",
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 "
1197                             "was deprecated in Wokkel 0.7.1; "
1198                             "please use RosterClientProtocol.removeReceived "
1199                             "instead.",
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.