source: wokkel/test/test_xmppim.py @ 172:ea2774e1c71c

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

Clean up of RosterItem? and RosterClientProtocol?.

RosterItem:

  • Renamed attributes jid and ask to entity and pendingOut respectively.
  • Can represent roster items to be removed or that have been removed.
  • Now has fromElement and toElement methods.

RosterRequest is a new class to represent roster request stanzas.

RosterClientProtocol:

  • Roster returned from getRoster is now indexed by JIDs (instead of the unicode representation of the JID).
  • Outgoing requests are now done using RosterRequest.
  • onRosterSet and onRosterRemove are deprecated in favor of setReceived and removeReceived, respectively. These are called with a RosterRequest to have access to addressing and roster version information.

RosterPushIgnored can be raised to return a service-unavailable stanza
error for unwanted pushes.

This also fixes a problem with checking the sender address for roster pushes.

Author: ralphm.
Fixes: #71.

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