source: wokkel/test/test_muc.py @ 150:2f7237d9c537

wokkel-muc-client-support-24
Last change on this file since 150:2f7237d9c537 was 150:2f7237d9c537, checked in by Ralph Meijer <ralphm@…>, 9 years ago

Redo admin requests using generic.Request.

This changes all role and affiliation management functionality to use a
subclass of generic.Request.

The methods for retrieving affiliation or role lists now return lists of
AdminItems, instead of modifying a Room instance and returning that. This
is because affiliation changes can also be done when not in the room.

File size: 33.6 KB
Line 
1# Copyright (c) Ralph Meijer.
2# See LICENSE for details.
3
4"""
5Tests for L{wokkel.muc}
6"""
7
8from datetime import datetime
9from dateutil.tz import tzutc
10
11from zope.interface import verify
12
13from twisted.trial import unittest
14from twisted.internet import defer, task
15from twisted.words.xish import domish, xpath
16from twisted.words.protocols.jabber.jid import JID
17from twisted.words.protocols.jabber.error import StanzaError
18from twisted.words.protocols.jabber.xmlstream import TimeoutError, toResponse
19
20from wokkel import data_form, iwokkel, muc
21from wokkel.generic import parseXml
22from wokkel.test.helpers import TestableStreamManager
23
24
25NS_MUC_ADMIN = 'http://jabber.org/protocol/muc#admin'
26
27def calledAsync(fn):
28    """
29    Function wrapper that fires a deferred upon calling the given function.
30    """
31    d = defer.Deferred()
32
33    def func(*args, **kwargs):
34        try:
35            result = fn(*args, **kwargs)
36        except:
37            d.errback()
38        else:
39            d.callback(result)
40
41    return d, func
42
43
44
45class MUCClientTest(unittest.TestCase):
46    timeout = 2
47
48    def setUp(self):
49        self.clock = task.Clock()
50        self.sessionManager = TestableStreamManager(reactor=self.clock)
51        self.stub = self.sessionManager.stub
52        self.protocol = muc.MUCClient(reactor=self.clock)
53        self.protocol.setHandlerParent(self.sessionManager)
54
55        self.roomIdentifier = 'test'
56        self.service  = 'conference.example.org'
57        self.nick = 'Nick'
58
59        self.occupantJID = JID(tuple=(self.roomIdentifier,
60                                      self.service,
61                                      self.nick))
62        self.roomJID = self.occupantJID.userhostJID()
63        self.userJID = JID('test@example.org/Testing')
64
65
66    def _createRoom(self):
67        """
68        A helper method to create a test room.
69        """
70        # create a room
71        room = muc.Room(self.roomIdentifier,
72                        self.service,
73                        self.nick)
74        self.protocol._addRoom(room)
75
76
77    def test_interface(self):
78        """
79        Do instances of L{muc.MUCClient} provide L{iwokkel.IMUCClient}?
80        """
81        verify.verifyObject(iwokkel.IMUCClient, self.protocol)
82
83
84    def test_userJoinedRoom(self):
85        """
86        Joins by others to a room we're in are passed to userJoinedRoom
87        """
88        xml = """
89            <presence to='%s' from='%s'>
90              <x xmlns='http://jabber.org/protocol/muc#user'>
91                <item affiliation='member' role='participant'/>
92              </x>
93            </presence>
94        """ % (self.userJID.full(), self.occupantJID.full())
95
96        # create a room
97        self._createRoom()
98
99        def userJoinedRoom(room, user):
100            self.assertEquals(self.roomIdentifier, room.roomIdentifier,
101                              'Wrong room name')
102            self.assertTrue(room.inRoster(user), 'User not in roster')
103
104        d, self.protocol.userJoinedRoom = calledAsync(userJoinedRoom)
105        self.stub.send(parseXml(xml))
106        return d
107
108
109    def test_receivedSubject(self):
110        """
111        Subject received from a room we're in are passed to receivedSubject.
112        """
113        xml = u"""
114            <message to='%s' from='%s' type='groupchat'>
115              <subject>test</subject>
116            </message>
117        """ % (self.userJID, self.occupantJID)
118
119        self._createRoom()
120
121        # add user to room
122        user = muc.User(self.nick)
123        room = self.protocol._getRoom(self.roomJID)
124        room.addUser(user)
125
126        def receivedSubject(room, user, subject):
127            self.assertEquals('test', subject, "Wrong group chat message")
128            self.assertEquals(self.roomIdentifier, room.roomIdentifier,
129                              'Wrong room name')
130            self.assertEquals(self.nick, user.nick)
131
132        d, self.protocol.receivedSubject = calledAsync(receivedSubject)
133        self.stub.send(parseXml(xml))
134        return d
135
136
137    def test_receivedGroupChat(self):
138        """
139        Messages received from a room we're in are passed to receivedGroupChat.
140        """
141        xml = u"""
142            <message to='test@test.com' from='%s' type='groupchat'>
143              <body>test</body>
144            </message>
145        """ % (self.occupantJID)
146
147        self._createRoom()
148
149        def receivedGroupChat(room, user, message):
150            self.assertEquals('test', message.body, "Wrong group chat message")
151            self.assertEquals(self.roomIdentifier, room.roomIdentifier,
152                              'Wrong room name')
153
154        d, self.protocol.receivedGroupChat = calledAsync(receivedGroupChat)
155        self.stub.send(parseXml(xml))
156        return d
157
158
159    def test_receivedGroupChatRoom(self):
160        """
161        Messages received from the room itself have C{user} set to C{None}.
162        """
163        xml = u"""
164            <message to='test@test.com' from='%s' type='groupchat'>
165              <body>test</body>
166            </message>
167        """ % (self.roomJID)
168
169        self._createRoom()
170
171        def receivedGroupChat(room, user, message):
172            self.assertIdentical(None, user)
173
174        d, self.protocol.receivedGroupChat = calledAsync(receivedGroupChat)
175        self.stub.send(parseXml(xml))
176        return d
177
178
179    def test_join(self):
180        """
181        Joining a room waits for confirmation, deferred fires room.
182        """
183
184        def cb(room):
185            self.assertEquals(self.roomIdentifier, room.roomIdentifier)
186
187        d = self.protocol.join(self.service, self.roomIdentifier, self.nick)
188        d.addCallback(cb)
189
190        element = self.stub.output[-1]
191        self.assertEquals('presence', element.name, "Need to be presence")
192        self.assertNotIdentical(None, element.x, 'No muc x element')
193
194        # send back user presence, they joined
195        xml = """
196            <presence from='%s@%s/%s'>
197              <x xmlns='http://jabber.org/protocol/muc#user'>
198                <item affiliation='member' role='participant'/>
199              </x>
200            </presence>
201        """ % (self.roomIdentifier, self.service, self.nick)
202        self.stub.send(parseXml(xml))
203        return d
204
205
206    def test_joinHistory(self):
207        """
208        Passing a history parameter sends a 'maxstanzas' history limit.
209        """
210
211        def cb(room):
212            self.assertEquals(self.roomIdentifier, room.roomIdentifier)
213
214        d = self.protocol.join(self.service, self.roomIdentifier, self.nick,
215                               history=10)
216        d.addCallback(cb)
217
218        element = self.stub.output[-1]
219        query = "/*/x[@xmlns='%s']/history[@xmlns='%s']" % (muc.NS_MUC,
220                                                            muc.NS_MUC)
221        result = xpath.queryForNodes(query, element)
222        history = result[0]
223        self.assertEquals('10', history.getAttribute('maxstanzas'))
224
225        # send back user presence, they joined
226        xml = """
227            <presence from='%s@%s/%s'>
228              <x xmlns='http://jabber.org/protocol/muc#user'>
229                <item affiliation='member' role='participant'/>
230              </x>
231            </presence>
232        """ % (self.roomIdentifier, self.service, self.nick)
233        self.stub.send(parseXml(xml))
234        return d
235
236
237    def test_joinForbidden(self):
238        """
239        A forbidden error in response to a join errbacks with L{StanzaError}.
240        """
241
242        def cb(error):
243            self.assertEquals('forbidden', error.condition,
244                              'Wrong muc condition')
245
246        d = self.protocol.join(self.service, self.roomIdentifier, self.nick)
247        self.assertFailure(d, StanzaError)
248        d.addCallback(cb)
249
250        # send back error, forbidden
251        xml = u"""
252            <presence from='%s' type='error'>
253              <error type='auth'>
254                <forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
255              </error>
256            </presence>
257        """ % (self.occupantJID)
258        self.stub.send(parseXml(xml))
259        return d
260
261
262    def test_joinForbiddenFromRoomJID(self):
263        """
264        An error response to a join sent from the room JID should errback.
265
266        Some service implementations send error stanzas from the room JID
267        instead of the JID the join presence was sent to.
268        """
269
270        d = self.protocol.join(self.service, self.roomIdentifier, self.nick)
271        self.assertFailure(d, StanzaError)
272
273        # send back error, forbidden
274        xml = u"""
275            <presence from='%s' type='error'>
276              <error type='auth'>
277                <forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
278              </error>
279            </presence>
280        """ % (self.roomJID)
281        self.stub.send(parseXml(xml))
282        return d
283
284
285    def test_joinBadJID(self):
286        """
287        Client joining a room and getting a jid-malformed error.
288        """
289
290        def cb(error):
291            self.assertEquals('jid-malformed', error.condition,
292                              'Wrong muc condition')
293
294        d = self.protocol.join(self.service, self.roomIdentifier, self.nick)
295        self.assertFailure(d, StanzaError)
296        d.addCallback(cb)
297
298        # send back error, bad JID
299        xml = u"""
300            <presence from='%s' type='error'>
301              <error type='modify'>
302                <jid-malformed xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
303              </error>
304            </presence>
305        """ % (self.occupantJID)
306        self.stub.send(parseXml(xml))
307        return d
308
309
310    def test_joinTimeout(self):
311        """
312        After not receiving a response to a join, errback with L{TimeoutError}.
313        """
314
315        d = self.protocol.join(self.service, self.roomIdentifier, self.nick)
316        self.assertFailure(d, TimeoutError)
317        self.clock.advance(muc.DEFER_TIMEOUT)
318        return d
319
320
321    def test_leave(self):
322        """
323        Client leaves a room
324        """
325        def cb(left):
326            self.assertTrue(left, 'did not leave room')
327
328        self._createRoom()
329        d = self.protocol.leave(self.roomJID)
330        d.addCallback(cb)
331
332        element = self.stub.output[-1]
333
334        self.assertEquals('unavailable', element['type'],
335                          'Unavailable is not being sent')
336
337        xml = u"""
338            <presence to='%s' from='%s' type='unavailable'/>
339        """ % (self.userJID, self.occupantJID)
340        self.stub.send(parseXml(xml))
341        return d
342
343
344    def test_userLeftRoom(self):
345        """
346        Unavailable presence from a participant removes it from the room.
347        """
348
349        xml = u"""
350            <presence to='%s' from='%s' type='unavailable'/>
351        """ % (self.userJID, self.occupantJID)
352
353        # create a room
354        self._createRoom()
355
356        # add user to room
357        user = muc.User(self.nick)
358        room = self.protocol._getRoom(self.roomJID)
359        room.addUser(user)
360
361        def userLeftRoom(room, user):
362            self.assertEquals(self.roomIdentifier, room.roomIdentifier,
363                              'Wrong room name')
364            self.assertFalse(room.inRoster(user), 'User in roster')
365
366        d, self.protocol.userLeftRoom = calledAsync(userLeftRoom)
367        self.stub.send(parseXml(xml))
368        return d
369
370
371    def test_ban(self):
372        """
373        Ban an entity in a room.
374        """
375        banned = JID('ban@jabber.org/TroubleMaker')
376
377        def cb(banned):
378            self.assertTrue(banned, 'Did not ban user')
379
380        d = self.protocol.ban(self.roomJID, banned, reason='Spam',
381                              sender=self.userJID)
382        d.addCallback(cb)
383
384        iq = self.stub.output[-1]
385
386        self.assertTrue(xpath.matches(
387                u"/iq[@type='set' and @to='%s']/query/item"
388                    "[@affiliation='outcast']" % (self.roomJID,),
389                iq),
390            'Wrong ban stanza')
391
392        response = toResponse(iq, 'result')
393        self.stub.send(response)
394
395        return d
396
397
398    def test_kick(self):
399        """
400        Kick an entity from a room.
401        """
402        nick = 'TroubleMaker'
403
404        def cb(kicked):
405            self.assertTrue(kicked, 'Did not kick user')
406
407        d = self.protocol.kick(self.roomJID, nick, reason='Spam',
408                               sender=self.userJID)
409        d.addCallback(cb)
410
411        iq = self.stub.output[-1]
412
413        self.assertTrue(xpath.matches(
414                u"/iq[@type='set' and @to='%s']/query/item"
415                    "[@role='none']" % (self.roomJID,),
416                iq),
417            'Wrong kick stanza')
418
419        response = toResponse(iq, 'result')
420        self.stub.send(response)
421
422        return d
423
424
425    def test_password(self):
426        """
427        Sending a password via presence to a password protected room.
428        """
429
430        self.protocol.password(self.occupantJID, 'secret')
431
432        element = self.stub.output[-1]
433
434        self.assertTrue(xpath.matches(
435                u"/presence[@to='%s']/x/password"
436                    "[text()='secret']" % (self.occupantJID,),
437                element),
438            'Wrong presence stanza')
439
440
441    def test_receivedHistory(self):
442        """
443        Receiving history on room join.
444        """
445        xml = u"""
446            <message to='test@test.com' from='%s' type='groupchat'>
447              <body>test</body>
448              <delay xmlns='urn:xmpp:delay' stamp="2002-10-13T23:58:37Z"
449                                            from="%s"/>
450            </message>
451        """ % (self.occupantJID, self.userJID)
452
453        self._createRoom()
454
455
456        def receivedHistory(room, user, message):
457            self.assertEquals('test', message.body, "wrong message body")
458            stamp = datetime(2002, 10, 13, 23, 58, 37, tzinfo=tzutc())
459            self.assertEquals(stamp, message.delay.stamp,
460                             'Does not have a history stamp')
461
462        d, self.protocol.receivedHistory = calledAsync(receivedHistory)
463        self.stub.send(parseXml(xml))
464        return d
465
466
467    def test_oneToOneChat(self):
468        """
469        Converting a one to one chat to a multi-user chat.
470        """
471        archive = []
472        thread = "e0ffe42b28561960c6b12b944a092794b9683a38"
473        # create messages
474        element = domish.Element((None, 'message'))
475        element['to'] = 'testing@example.com'
476        element['type'] = 'chat'
477        element.addElement('body', None, 'test')
478        element.addElement('thread', None, thread)
479
480        archive.append({'stanza': element,
481                        'timestamp': datetime(2002, 10, 13, 23, 58, 37,
482                                              tzinfo=tzutc())})
483
484        element = domish.Element((None, 'message'))
485        element['to'] = 'testing2@example.com'
486        element['type'] = 'chat'
487        element.addElement('body', None, 'yo')
488        element.addElement('thread', None, thread)
489
490        archive.append({'stanza': element,
491                        'timestamp': datetime(2002, 10, 13, 23, 58, 43,
492                                              tzinfo=tzutc())})
493
494        self.protocol.history(self.occupantJID, archive)
495
496
497        while len(self.stub.output)>0:
498            element = self.stub.output.pop()
499            # check for delay element
500            self.assertEquals('message', element.name, 'Wrong stanza')
501            self.assertTrue(xpath.matches("/message/delay", element),
502                            'Invalid history stanza')
503
504
505    def test_invite(self):
506        """
507        Invite a user to a room
508        """
509        invitee = JID('other@example.org')
510
511        self.protocol.invite(self.roomJID, invitee, u'This is a test')
512
513        message = self.stub.output[-1]
514
515        self.assertEquals('message', message.name)
516        self.assertEquals(self.roomJID.full(), message.getAttribute('to'))
517        self.assertEquals(muc.NS_MUC_USER, message.x.uri)
518        self.assertEquals(muc.NS_MUC_USER, message.x.invite.uri)
519        self.assertEquals(invitee.full(), message.x.invite.getAttribute('to'))
520        self.assertEquals(muc.NS_MUC_USER, message.x.invite.reason.uri)
521        self.assertEquals(u'This is a test', unicode(message.x.invite.reason))
522
523
524    def test_groupChat(self):
525        """
526        Send private messages to muc entities.
527        """
528        self.protocol.groupChat(self.roomJID, u'This is a test')
529
530        message = self.stub.output[-1]
531
532        self.assertEquals('message', message.name)
533        self.assertEquals(self.roomJID.full(), message.getAttribute('to'))
534        self.assertEquals('groupchat', message.getAttribute('type'))
535        self.assertEquals(u'This is a test', unicode(message.body))
536
537
538    def test_chat(self):
539        """
540        Send private messages to muc entities.
541        """
542        otherOccupantJID = JID(self.occupantJID.userhost()+'/OtherNick')
543
544        self.protocol.chat(otherOccupantJID, u'This is a test')
545
546        message = self.stub.output[-1]
547
548        self.assertEquals('message', message.name)
549        self.assertEquals(otherOccupantJID.full(), message.getAttribute('to'))
550        self.assertEquals('chat', message.getAttribute('type'))
551        self.assertEquals(u'This is a test', unicode(message.body))
552
553
554    def test_register(self):
555        """
556        Client registering with a room.
557
558        http://xmpp.org/extensions/xep-0045.html#register
559        """
560
561        # FIXME: this doesn't really test the registration
562
563        def cb(iq):
564            # check for a result
565            self.assertEquals('result', iq['type'], 'We did not get a result')
566
567        d = self.protocol.register(self.roomJID)
568        d.addCallback(cb)
569
570        iq = self.stub.output[-1]
571        query = "/iq/query[@xmlns='%s']" % muc.NS_REQUEST
572        self.assertTrue(xpath.matches(query, iq), 'Invalid iq register request')
573
574        response = toResponse(iq, 'result')
575        self.stub.send(response)
576        return d
577
578
579    def test_voice(self):
580        """
581        Client requesting voice for a room.
582        """
583        self.protocol.voice(self.occupantJID)
584
585        m = self.stub.output[-1]
586
587        query = ("/message/x[@type='submit']/field/value"
588                    "[text()='%s']") % muc.NS_MUC_REQUEST
589        self.assertTrue(xpath.matches(query, m), 'Invalid voice message stanza')
590
591
592    def test_configure(self):
593        """
594        Default configure and changing the room name.
595        """
596
597        def cb(iq):
598            self.assertEquals('result', iq['type'], 'Not a result')
599
600        values = {'muc#roomconfig_roomname': self.roomIdentifier}
601
602        d = self.protocol.configure(self.roomJID, values)
603        d.addCallback(cb)
604
605        iq = self.stub.output[-1]
606
607        self.assertEquals('set', iq.getAttribute('type'))
608        self.assertEquals(self.roomJID.full(), iq.getAttribute('to'))
609
610        query = "/iq/query[@xmlns='%s']" % (muc.NS_MUC_OWNER)
611        nodes = xpath.queryForNodes(query, iq)
612        self.assertNotIdentical(None, nodes, 'Bad configure request')
613
614        form = data_form.findForm(nodes[0], muc.NS_MUC_CONFIG)
615        self.assertNotIdentical(None, form, 'Missing configuration form')
616        self.assertEquals('submit', form.formType)
617
618        response = toResponse(iq, 'result')
619        self.stub.send(response)
620        return d
621
622
623    def test_configureCancel(self):
624        """
625        Cancelling room configuration should send a cancel form.
626        """
627
628        d = self.protocol.configure(self.roomJID, None)
629
630        iq = self.stub.output[-1]
631
632        query = "/iq/query[@xmlns='%s']" % (muc.NS_MUC_OWNER)
633        nodes = xpath.queryForNodes(query, iq)
634
635        form = data_form.findForm(nodes[0], muc.NS_MUC_CONFIG)
636        self.assertNotIdentical(None, form, 'Missing configuration form')
637        self.assertEquals('cancel', form.formType)
638
639        response = toResponse(iq, 'result')
640        self.stub.send(response)
641        return d
642
643
644    def test_getConfiguration(self):
645        """
646        The response of a configure form request should extract the form.
647        """
648
649        def cb(form):
650            self.assertEquals('form', form.formType)
651
652        d = self.protocol.getConfiguration(self.roomJID)
653        d.addCallback(cb)
654
655        iq = self.stub.output[-1]
656
657        query = "/iq/query[@xmlns='%s']" % (muc.NS_MUC_OWNER)
658        nodes = xpath.queryForNodes(query, iq)
659        self.assertNotIdentical(None, nodes, 'Missing query element')
660
661        self.assertRaises(StopIteration, nodes[0].elements().next)
662
663        xml = u"""
664            <iq from='%s' id='%s' to='%s' type='result'>
665              <query xmlns='http://jabber.org/protocol/muc#owner'>
666                <x xmlns='jabber:x:data' type='form'>
667                  <field type='hidden'
668                         var='FORM_TYPE'>
669                    <value>http://jabber.org/protocol/muc#roomconfig</value>
670                  </field>
671                  <field label='Natural-Language Room Name'
672                         type='text-single'
673                         var='muc#roomconfig_roomname'/>
674                </x>
675              </query>
676            </iq>
677        """ % (self.roomJID, iq['id'], self.userJID)
678        self.stub.send(parseXml(xml))
679
680        return d
681
682
683    def test_getConfigurationNoOptions(self):
684        """
685        The response of a configure form request should extract the form.
686        """
687
688        def cb(form):
689            self.assertIdentical(None, form)
690
691        d = self.protocol.getConfiguration(self.roomJID)
692        d.addCallback(cb)
693
694        iq = self.stub.output[-1]
695
696        xml = u"""
697            <iq from='%s' id='%s' to='%s' type='result'>
698              <query xmlns='http://jabber.org/protocol/muc#owner'/>
699            </iq>
700        """ % (self.roomJID, iq['id'], self.userJID)
701        self.stub.send(parseXml(xml))
702
703        return d
704
705
706    def test_destroy(self):
707        """
708        Destroy a room.
709        """
710        d = self.protocol.destroy(self.occupantJID, reason='Time to leave',
711                                  alternate=JID('other@%s' % self.service),
712                                  password='secret')
713
714        iq = self.stub.output[-1]
715
716        query = ("/iq/query[@xmlns='%s']/destroy[@xmlns='%s']" %
717                 (muc.NS_MUC_OWNER, muc.NS_MUC_OWNER))
718
719        nodes = xpath.queryForNodes(query, iq)
720        self.assertNotIdentical(None, nodes, 'Bad configure request')
721        destroy = nodes[0]
722        self.assertEquals('Time to leave', unicode(destroy.reason))
723
724        response = toResponse(iq, 'result')
725        self.stub.send(response)
726        return d
727
728
729    def test_subject(self):
730        """
731        Change subject of the room.
732        """
733        self.protocol.subject(self.roomJID, u'This is a test')
734
735        message = self.stub.output[-1]
736
737        self.assertEquals('message', message.name)
738        self.assertEquals(self.roomJID.full(), message.getAttribute('to'))
739        self.assertEquals('groupchat', message.getAttribute('type'))
740        self.assertEquals(u'This is a test', unicode(message.subject))
741
742
743    def test_nick(self):
744        """
745        Send a nick change to the server.
746        """
747        newNick = 'newNick'
748
749        self._createRoom()
750
751        def cb(room):
752            self.assertEquals(self.roomIdentifier, room.roomIdentifier)
753            self.assertEquals(newNick, room.nick)
754
755        d = self.protocol.nick(self.roomJID, newNick)
756        d.addCallback(cb)
757
758        element = self.stub.output[-1]
759        self.assertEquals('presence', element.name, "Need to be presence")
760        self.assertNotIdentical(None, element.x, 'No muc x element')
761
762        # send back user presence, nick changed
763        xml = u"""
764            <presence from='%s/%s'>
765              <x xmlns='http://jabber.org/protocol/muc#user'>
766                <item affiliation='member' role='participant'/>
767              </x>
768            </presence>
769        """ % (self.roomJID, newNick)
770        self.stub.send(parseXml(xml))
771        return d
772
773
774    def test_nickConflict(self):
775        """
776        If the server finds the new nick in conflict, the errback is called.
777        """
778        newNick = 'newNick'
779
780        self._createRoom()
781
782        d = self.protocol.nick(self.roomJID, newNick)
783        self.assertFailure(d, StanzaError)
784
785        element = self.stub.output[-1]
786        self.assertEquals('presence', element.name, "Need to be presence")
787        self.assertNotIdentical(None, element.x, 'No muc x element')
788
789        # send back user presence, nick changed
790        xml = u"""
791            <presence from='%s/%s' type='error'>
792                <x xmlns='http://jabber.org/protocol/muc'/>
793                <error type='cancel'>
794                  <conflict xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
795                </error>
796            </presence>
797        """ % (self.roomJID, newNick)
798        self.stub.send(parseXml(xml))
799        return d
800
801
802    def test_modifyAffiliationList(self):
803
804        entities = [JID('user1@test.example.org'),
805                    JID('user2@test.example.org')]
806        d = self.protocol.modifyAffiliationList(self.roomJID, entities,
807                                                'admin')
808
809        iq = self.stub.output[-1]
810        query = "/iq/query[@xmlns='%s']/item[@xmlns='%s']" % (muc.NS_MUC_ADMIN,
811                                                              muc.NS_MUC_ADMIN)
812        items = xpath.queryForNodes(query, iq)
813        self.assertNotIdentical(None, items)
814        self.assertEquals(entities[0], JID(items[0].getAttribute('jid')))
815        self.assertEquals('admin', items[0].getAttribute('affiliation'))
816        self.assertEquals(entities[1], JID(items[1].getAttribute('jid')))
817        self.assertEquals('admin', items[1].getAttribute('affiliation'))
818
819        # Send a response to have the deferred fire.
820        response = toResponse(iq, 'result')
821        self.stub.send(response)
822        return d
823
824
825    def test_grantVoice(self):
826        """
827        Granting voice sends request to set role to 'participant'.
828        """
829        nick = 'TroubleMaker'
830        def cb(give_voice):
831            self.assertTrue(give_voice, 'Did not give voice user')
832
833        d = self.protocol.grantVoice(self.roomJID, nick,
834                                     sender=self.userJID)
835        d.addCallback(cb)
836
837        iq = self.stub.output[-1]
838
839        query = (u"/iq[@type='set' and @to='%s']/query/item"
840                     "[@role='participant']") % self.roomJID
841        self.assertTrue(xpath.matches(query, iq), 'Wrong voice stanza')
842
843        response = toResponse(iq, 'result')
844        self.stub.send(response)
845        return d
846
847
848    def test_revokeVoice(self):
849        """
850        Revoking voice sends request to set role to 'visitor'.
851        """
852        nick = 'TroubleMaker'
853
854        d = self.protocol.revokeVoice(self.roomJID, nick,
855                                      reason="Trouble maker",
856                                      sender=self.userJID)
857
858        iq = self.stub.output[-1]
859
860        query = (u"/iq[@type='set' and @to='%s']/query/item"
861                     "[@role='visitor']") % self.roomJID
862        self.assertTrue(xpath.matches(query, iq), 'Wrong voice stanza')
863
864        response = toResponse(iq, 'result')
865        self.stub.send(response)
866        return d
867
868
869    def test_grantModerator(self):
870        """
871        Granting moderator privileges sends request to set role to 'moderator'.
872        """
873        nick = 'TroubleMaker'
874
875        d = self.protocol.grantModerator(self.roomJID, nick,
876                                         sender=self.userJID)
877
878        iq = self.stub.output[-1]
879
880        query = (u"/iq[@type='set' and @to='%s']/query/item"
881                     "[@role='moderator']") % self.roomJID
882        self.assertTrue(xpath.matches(query, iq), 'Wrong voice stanza')
883
884        response = toResponse(iq, 'result')
885        self.stub.send(response)
886        return d
887
888
889    def test_status(self):
890        """
891        Change status
892        """
893        self._createRoom()
894        room = self.protocol._getRoom(self.roomJID)
895        user = muc.User(self.nick)
896        room.addUser(user)
897
898        def cb(room):
899            self.assertEquals(self.roomIdentifier, room.roomIdentifier)
900            user = room.getUser(self.nick)
901            self.assertNotIdentical(None, user, 'User not found')
902            self.assertEquals('testing MUC', user.status, 'Wrong status')
903            self.assertEquals('xa', user.show, 'Wrong show')
904
905        d = self.protocol.status(self.roomJID, 'xa', 'testing MUC')
906        d.addCallback(cb)
907
908        element = self.stub.output[-1]
909
910        self.assertEquals('presence', element.name, "Need to be presence")
911        self.assertTrue(getattr(element, 'x', None), 'No muc x element')
912
913        # send back user presence, status changed
914        xml = u"""
915            <presence from='%s'>
916              <x xmlns='http://jabber.org/protocol/muc#user'>
917                <item affiliation='member' role='participant'/>
918              </x>
919              <show>xa</show>
920              <status>testing MUC</status>
921            </presence>
922        """ % self.occupantJID
923        self.stub.send(parseXml(xml))
924        return d
925
926
927    def test_getMemberList(self):
928        """
929        Retrieving the member list returns a list of L{muc.AdminItem}s
930
931        The request asks for the affiliation C{'member'}.
932        """
933        def cb(items):
934            self.assertEquals(1, len(items))
935            item = items[0]
936            self.assertEquals(JID(u'hag66@shakespeare.lit'), item.entity)
937            self.assertEquals(u'thirdwitch', item.nick)
938            self.assertEquals(u'member', item.affiliation)
939
940        d = self.protocol.getMemberList(self.roomJID)
941        d.addCallback(cb)
942
943        iq = self.stub.output[-1]
944        self.assertEquals('get', iq.getAttribute('type'))
945        query = "/iq/query[@xmlns='%s']/item[@xmlns='%s']" % (muc.NS_MUC_ADMIN,
946                                                              muc.NS_MUC_ADMIN)
947        items = xpath.queryForNodes(query, iq)
948        self.assertNotIdentical(None, items)
949        self.assertEquals(1, len(items))
950        self.assertEquals('member', items[0].getAttribute('affiliation'))
951
952        response = toResponse(iq, 'result')
953        query = response.addElement((NS_MUC_ADMIN, 'query'))
954        item = query.addElement('item')
955        item['affiliation'] ='member'
956        item['jid'] = 'hag66@shakespeare.lit'
957        item['nick'] = 'thirdwitch'
958        item['role'] = 'participant'
959        self.stub.send(response)
960
961        return d
962
963
964    def test_getAdminList(self):
965        """
966        Retrieving the admin list returns a list of L{muc.AdminItem}s
967
968        The request asks for the affiliation C{'admin'}.
969        """
970        d = self.protocol.getAdminList(self.roomJID)
971
972        iq = self.stub.output[-1]
973        query = "/iq/query[@xmlns='%s']/item[@xmlns='%s']" % (muc.NS_MUC_ADMIN,
974                                                              muc.NS_MUC_ADMIN)
975        items = xpath.queryForNodes(query, iq)
976        self.assertEquals('admin', items[0].getAttribute('affiliation'))
977
978        response = toResponse(iq, 'result')
979        query = response.addElement((NS_MUC_ADMIN, 'query'))
980        self.stub.send(response)
981
982        return d
983
984
985    def test_getBanList(self):
986        """
987        Retrieving the ban list returns a list of L{muc.AdminItem}s
988
989        The request asks for the affiliation C{'outcast'}.
990        """
991        def cb(items):
992            self.assertEquals(1, len(items))
993            item = items[0]
994            self.assertEquals(JID(u'hag66@shakespeare.lit'), item.entity)
995            self.assertEquals(u'outcast', item.affiliation)
996            self.assertEquals(u'Trouble making', item.reason)
997
998        d = self.protocol.getBanList(self.roomJID)
999        d.addCallback(cb)
1000
1001        iq = self.stub.output[-1]
1002        query = "/iq/query[@xmlns='%s']/item[@xmlns='%s']" % (muc.NS_MUC_ADMIN,
1003                                                              muc.NS_MUC_ADMIN)
1004        items = xpath.queryForNodes(query, iq)
1005        self.assertEquals('outcast', items[0].getAttribute('affiliation'))
1006
1007        response = toResponse(iq, 'result')
1008        query = response.addElement((NS_MUC_ADMIN, 'query'))
1009        item = query.addElement('item')
1010        item['affiliation'] ='outcast'
1011        item['jid'] = 'hag66@shakespeare.lit'
1012        item.addElement('reason', content='Trouble making')
1013        self.stub.send(response)
1014
1015        return d
1016
1017
1018    def test_getOwnerList(self):
1019        """
1020        Retrieving the owner list returns a list of L{muc.AdminItem}s
1021
1022        The request asks for the affiliation C{'owner'}.
1023        """
1024        d = self.protocol.getOwnerList(self.roomJID)
1025
1026        iq = self.stub.output[-1]
1027        query = "/iq/query[@xmlns='%s']/item[@xmlns='%s']" % (muc.NS_MUC_ADMIN,
1028                                                              muc.NS_MUC_ADMIN)
1029        items = xpath.queryForNodes(query, iq)
1030        self.assertEquals('owner', items[0].getAttribute('affiliation'))
1031
1032        response = toResponse(iq, 'result')
1033        query = response.addElement((NS_MUC_ADMIN, 'query'))
1034        self.stub.send(response)
1035
1036        return d
1037
1038
1039    def test_getModeratorList(self):
1040        """
1041        Retrieving the moderator returns a list of L{muc.AdminItem}s.
1042
1043        The request asks for the role C{'moderator'}.
1044        """
1045
1046        def cb(items):
1047            self.assertEquals(1, len(items))
1048            item = items[0]
1049            self.assertEquals(JID(u'hag66@shakespeare.lit'), item.entity)
1050            self.assertEquals(u'thirdwitch', item.nick)
1051            self.assertEquals(u'moderator', item.role)
1052
1053        d = self.protocol.getModeratorList(self.roomJID)
1054        d.addCallback(cb)
1055
1056        iq = self.stub.output[-1]
1057        self.assertEquals('get', iq.getAttribute('type'))
1058        query = "/iq/query[@xmlns='%s']/item[@xmlns='%s']" % (muc.NS_MUC_ADMIN,
1059                                                              muc.NS_MUC_ADMIN)
1060        items = xpath.queryForNodes(query, iq)
1061        self.assertNotIdentical(None, items)
1062        self.assertEquals(1, len(items))
1063        self.assertEquals('moderator', items[0].getAttribute('role'))
1064
1065        response = toResponse(iq, 'result')
1066        query = response.addElement((NS_MUC_ADMIN, 'query'))
1067        item = query.addElement('item')
1068        item['affiliation'] ='member'
1069        item['jid'] = 'hag66@shakespeare.lit'
1070        item['nick'] = 'thirdwitch'
1071        item['role'] = 'moderator'
1072        self.stub.send(response)
1073
1074        return d
Note: See TracBrowser for help on using the repository browser.