source: wokkel/test/test_muc.py @ 151:fb6eaad60bb8

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

Reimplement RegisterRequest? by generalizing the form handling for room config.

File size: 33.9 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        def cb(iq):
562            # check for a result
563            self.assertEquals('result', iq['type'], 'We did not get a result')
564
565        d = self.protocol.register(self.roomJID,
566                                   {'muc#register_roomnick': 'thirdwitch'})
567        d.addCallback(cb)
568
569        iq = self.stub.output[-1]
570
571        query = "/iq/query[@xmlns='%s']" % muc.NS_REGISTER
572        nodes = xpath.queryForNodes(query, iq)
573        self.assertNotIdentical(None, nodes, 'Invalid registration request')
574
575        form = data_form.findForm(nodes[0], muc.NS_MUC_REGISTER)
576        self.assertNotIdentical(None, form, 'Missing registration form')
577        self.assertEquals('submit', form.formType)
578        self.assertIn('muc#register_roomnick', form.fields)
579
580
581        response = toResponse(iq, 'result')
582        self.stub.send(response)
583        return d
584
585
586    def test_voice(self):
587        """
588        Client requesting voice for a room.
589        """
590        self.protocol.voice(self.occupantJID)
591
592        m = self.stub.output[-1]
593
594        query = ("/message/x[@type='submit']/field/value"
595                    "[text()='%s']") % muc.NS_MUC_REQUEST
596        self.assertTrue(xpath.matches(query, m), 'Invalid voice message stanza')
597
598
599    def test_configure(self):
600        """
601        Default configure and changing the room name.
602        """
603
604        def cb(iq):
605            self.assertEquals('result', iq['type'], 'Not a result')
606
607        values = {'muc#roomconfig_roomname': self.roomIdentifier}
608
609        d = self.protocol.configure(self.roomJID, values)
610        d.addCallback(cb)
611
612        iq = self.stub.output[-1]
613
614        self.assertEquals('set', iq.getAttribute('type'))
615        self.assertEquals(self.roomJID.full(), iq.getAttribute('to'))
616
617        query = "/iq/query[@xmlns='%s']" % (muc.NS_MUC_OWNER)
618        nodes = xpath.queryForNodes(query, iq)
619        self.assertNotIdentical(None, nodes, 'Bad configure request')
620
621        form = data_form.findForm(nodes[0], muc.NS_MUC_CONFIG)
622        self.assertNotIdentical(None, form, 'Missing configuration form')
623        self.assertEquals('submit', form.formType)
624
625        response = toResponse(iq, 'result')
626        self.stub.send(response)
627        return d
628
629
630    def test_configureCancel(self):
631        """
632        Cancelling room configuration should send a cancel form.
633        """
634
635        d = self.protocol.configure(self.roomJID, None)
636
637        iq = self.stub.output[-1]
638
639        query = "/iq/query[@xmlns='%s']" % (muc.NS_MUC_OWNER)
640        nodes = xpath.queryForNodes(query, iq)
641
642        form = data_form.findForm(nodes[0], muc.NS_MUC_CONFIG)
643        self.assertNotIdentical(None, form, 'Missing configuration form')
644        self.assertEquals('cancel', form.formType)
645
646        response = toResponse(iq, 'result')
647        self.stub.send(response)
648        return d
649
650
651    def test_getConfiguration(self):
652        """
653        The response of a configure form request should extract the form.
654        """
655
656        def cb(form):
657            self.assertEquals('form', form.formType)
658
659        d = self.protocol.getConfiguration(self.roomJID)
660        d.addCallback(cb)
661
662        iq = self.stub.output[-1]
663
664        query = "/iq/query[@xmlns='%s']" % (muc.NS_MUC_OWNER)
665        nodes = xpath.queryForNodes(query, iq)
666        self.assertNotIdentical(None, nodes, 'Missing query element')
667
668        self.assertRaises(StopIteration, nodes[0].elements().next)
669
670        xml = u"""
671            <iq from='%s' id='%s' to='%s' type='result'>
672              <query xmlns='http://jabber.org/protocol/muc#owner'>
673                <x xmlns='jabber:x:data' type='form'>
674                  <field type='hidden'
675                         var='FORM_TYPE'>
676                    <value>http://jabber.org/protocol/muc#roomconfig</value>
677                  </field>
678                  <field label='Natural-Language Room Name'
679                         type='text-single'
680                         var='muc#roomconfig_roomname'/>
681                </x>
682              </query>
683            </iq>
684        """ % (self.roomJID, iq['id'], self.userJID)
685        self.stub.send(parseXml(xml))
686
687        return d
688
689
690    def test_getConfigurationNoOptions(self):
691        """
692        The response of a configure form request should extract the form.
693        """
694
695        def cb(form):
696            self.assertIdentical(None, form)
697
698        d = self.protocol.getConfiguration(self.roomJID)
699        d.addCallback(cb)
700
701        iq = self.stub.output[-1]
702
703        xml = u"""
704            <iq from='%s' id='%s' to='%s' type='result'>
705              <query xmlns='http://jabber.org/protocol/muc#owner'/>
706            </iq>
707        """ % (self.roomJID, iq['id'], self.userJID)
708        self.stub.send(parseXml(xml))
709
710        return d
711
712
713    def test_destroy(self):
714        """
715        Destroy a room.
716        """
717        d = self.protocol.destroy(self.occupantJID, reason='Time to leave',
718                                  alternate=JID('other@%s' % self.service),
719                                  password='secret')
720
721        iq = self.stub.output[-1]
722
723        query = ("/iq/query[@xmlns='%s']/destroy[@xmlns='%s']" %
724                 (muc.NS_MUC_OWNER, muc.NS_MUC_OWNER))
725
726        nodes = xpath.queryForNodes(query, iq)
727        self.assertNotIdentical(None, nodes, 'Bad configure request')
728        destroy = nodes[0]
729        self.assertEquals('Time to leave', unicode(destroy.reason))
730
731        response = toResponse(iq, 'result')
732        self.stub.send(response)
733        return d
734
735
736    def test_subject(self):
737        """
738        Change subject of the room.
739        """
740        self.protocol.subject(self.roomJID, u'This is a test')
741
742        message = self.stub.output[-1]
743
744        self.assertEquals('message', message.name)
745        self.assertEquals(self.roomJID.full(), message.getAttribute('to'))
746        self.assertEquals('groupchat', message.getAttribute('type'))
747        self.assertEquals(u'This is a test', unicode(message.subject))
748
749
750    def test_nick(self):
751        """
752        Send a nick change to the server.
753        """
754        newNick = 'newNick'
755
756        self._createRoom()
757
758        def cb(room):
759            self.assertEquals(self.roomIdentifier, room.roomIdentifier)
760            self.assertEquals(newNick, room.nick)
761
762        d = self.protocol.nick(self.roomJID, newNick)
763        d.addCallback(cb)
764
765        element = self.stub.output[-1]
766        self.assertEquals('presence', element.name, "Need to be presence")
767        self.assertNotIdentical(None, element.x, 'No muc x element')
768
769        # send back user presence, nick changed
770        xml = u"""
771            <presence from='%s/%s'>
772              <x xmlns='http://jabber.org/protocol/muc#user'>
773                <item affiliation='member' role='participant'/>
774              </x>
775            </presence>
776        """ % (self.roomJID, newNick)
777        self.stub.send(parseXml(xml))
778        return d
779
780
781    def test_nickConflict(self):
782        """
783        If the server finds the new nick in conflict, the errback is called.
784        """
785        newNick = 'newNick'
786
787        self._createRoom()
788
789        d = self.protocol.nick(self.roomJID, newNick)
790        self.assertFailure(d, StanzaError)
791
792        element = self.stub.output[-1]
793        self.assertEquals('presence', element.name, "Need to be presence")
794        self.assertNotIdentical(None, element.x, 'No muc x element')
795
796        # send back user presence, nick changed
797        xml = u"""
798            <presence from='%s/%s' type='error'>
799                <x xmlns='http://jabber.org/protocol/muc'/>
800                <error type='cancel'>
801                  <conflict xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
802                </error>
803            </presence>
804        """ % (self.roomJID, newNick)
805        self.stub.send(parseXml(xml))
806        return d
807
808
809    def test_modifyAffiliationList(self):
810
811        entities = [JID('user1@test.example.org'),
812                    JID('user2@test.example.org')]
813        d = self.protocol.modifyAffiliationList(self.roomJID, entities,
814                                                'admin')
815
816        iq = self.stub.output[-1]
817        query = "/iq/query[@xmlns='%s']/item[@xmlns='%s']" % (muc.NS_MUC_ADMIN,
818                                                              muc.NS_MUC_ADMIN)
819        items = xpath.queryForNodes(query, iq)
820        self.assertNotIdentical(None, items)
821        self.assertEquals(entities[0], JID(items[0].getAttribute('jid')))
822        self.assertEquals('admin', items[0].getAttribute('affiliation'))
823        self.assertEquals(entities[1], JID(items[1].getAttribute('jid')))
824        self.assertEquals('admin', items[1].getAttribute('affiliation'))
825
826        # Send a response to have the deferred fire.
827        response = toResponse(iq, 'result')
828        self.stub.send(response)
829        return d
830
831
832    def test_grantVoice(self):
833        """
834        Granting voice sends request to set role to 'participant'.
835        """
836        nick = 'TroubleMaker'
837        def cb(give_voice):
838            self.assertTrue(give_voice, 'Did not give voice user')
839
840        d = self.protocol.grantVoice(self.roomJID, nick,
841                                     sender=self.userJID)
842        d.addCallback(cb)
843
844        iq = self.stub.output[-1]
845
846        query = (u"/iq[@type='set' and @to='%s']/query/item"
847                     "[@role='participant']") % self.roomJID
848        self.assertTrue(xpath.matches(query, iq), 'Wrong voice stanza')
849
850        response = toResponse(iq, 'result')
851        self.stub.send(response)
852        return d
853
854
855    def test_revokeVoice(self):
856        """
857        Revoking voice sends request to set role to 'visitor'.
858        """
859        nick = 'TroubleMaker'
860
861        d = self.protocol.revokeVoice(self.roomJID, nick,
862                                      reason="Trouble maker",
863                                      sender=self.userJID)
864
865        iq = self.stub.output[-1]
866
867        query = (u"/iq[@type='set' and @to='%s']/query/item"
868                     "[@role='visitor']") % self.roomJID
869        self.assertTrue(xpath.matches(query, iq), 'Wrong voice stanza')
870
871        response = toResponse(iq, 'result')
872        self.stub.send(response)
873        return d
874
875
876    def test_grantModerator(self):
877        """
878        Granting moderator privileges sends request to set role to 'moderator'.
879        """
880        nick = 'TroubleMaker'
881
882        d = self.protocol.grantModerator(self.roomJID, nick,
883                                         sender=self.userJID)
884
885        iq = self.stub.output[-1]
886
887        query = (u"/iq[@type='set' and @to='%s']/query/item"
888                     "[@role='moderator']") % self.roomJID
889        self.assertTrue(xpath.matches(query, iq), 'Wrong voice stanza')
890
891        response = toResponse(iq, 'result')
892        self.stub.send(response)
893        return d
894
895
896    def test_status(self):
897        """
898        Change status
899        """
900        self._createRoom()
901        room = self.protocol._getRoom(self.roomJID)
902        user = muc.User(self.nick)
903        room.addUser(user)
904
905        def cb(room):
906            self.assertEquals(self.roomIdentifier, room.roomIdentifier)
907            user = room.getUser(self.nick)
908            self.assertNotIdentical(None, user, 'User not found')
909            self.assertEquals('testing MUC', user.status, 'Wrong status')
910            self.assertEquals('xa', user.show, 'Wrong show')
911
912        d = self.protocol.status(self.roomJID, 'xa', 'testing MUC')
913        d.addCallback(cb)
914
915        element = self.stub.output[-1]
916
917        self.assertEquals('presence', element.name, "Need to be presence")
918        self.assertTrue(getattr(element, 'x', None), 'No muc x element')
919
920        # send back user presence, status changed
921        xml = u"""
922            <presence from='%s'>
923              <x xmlns='http://jabber.org/protocol/muc#user'>
924                <item affiliation='member' role='participant'/>
925              </x>
926              <show>xa</show>
927              <status>testing MUC</status>
928            </presence>
929        """ % self.occupantJID
930        self.stub.send(parseXml(xml))
931        return d
932
933
934    def test_getMemberList(self):
935        """
936        Retrieving the member list returns a list of L{muc.AdminItem}s
937
938        The request asks for the affiliation C{'member'}.
939        """
940        def cb(items):
941            self.assertEquals(1, len(items))
942            item = items[0]
943            self.assertEquals(JID(u'hag66@shakespeare.lit'), item.entity)
944            self.assertEquals(u'thirdwitch', item.nick)
945            self.assertEquals(u'member', item.affiliation)
946
947        d = self.protocol.getMemberList(self.roomJID)
948        d.addCallback(cb)
949
950        iq = self.stub.output[-1]
951        self.assertEquals('get', iq.getAttribute('type'))
952        query = "/iq/query[@xmlns='%s']/item[@xmlns='%s']" % (muc.NS_MUC_ADMIN,
953                                                              muc.NS_MUC_ADMIN)
954        items = xpath.queryForNodes(query, iq)
955        self.assertNotIdentical(None, items)
956        self.assertEquals(1, len(items))
957        self.assertEquals('member', items[0].getAttribute('affiliation'))
958
959        response = toResponse(iq, 'result')
960        query = response.addElement((NS_MUC_ADMIN, 'query'))
961        item = query.addElement('item')
962        item['affiliation'] ='member'
963        item['jid'] = 'hag66@shakespeare.lit'
964        item['nick'] = 'thirdwitch'
965        item['role'] = 'participant'
966        self.stub.send(response)
967
968        return d
969
970
971    def test_getAdminList(self):
972        """
973        Retrieving the admin list returns a list of L{muc.AdminItem}s
974
975        The request asks for the affiliation C{'admin'}.
976        """
977        d = self.protocol.getAdminList(self.roomJID)
978
979        iq = self.stub.output[-1]
980        query = "/iq/query[@xmlns='%s']/item[@xmlns='%s']" % (muc.NS_MUC_ADMIN,
981                                                              muc.NS_MUC_ADMIN)
982        items = xpath.queryForNodes(query, iq)
983        self.assertEquals('admin', items[0].getAttribute('affiliation'))
984
985        response = toResponse(iq, 'result')
986        query = response.addElement((NS_MUC_ADMIN, 'query'))
987        self.stub.send(response)
988
989        return d
990
991
992    def test_getBanList(self):
993        """
994        Retrieving the ban list returns a list of L{muc.AdminItem}s
995
996        The request asks for the affiliation C{'outcast'}.
997        """
998        def cb(items):
999            self.assertEquals(1, len(items))
1000            item = items[0]
1001            self.assertEquals(JID(u'hag66@shakespeare.lit'), item.entity)
1002            self.assertEquals(u'outcast', item.affiliation)
1003            self.assertEquals(u'Trouble making', item.reason)
1004
1005        d = self.protocol.getBanList(self.roomJID)
1006        d.addCallback(cb)
1007
1008        iq = self.stub.output[-1]
1009        query = "/iq/query[@xmlns='%s']/item[@xmlns='%s']" % (muc.NS_MUC_ADMIN,
1010                                                              muc.NS_MUC_ADMIN)
1011        items = xpath.queryForNodes(query, iq)
1012        self.assertEquals('outcast', items[0].getAttribute('affiliation'))
1013
1014        response = toResponse(iq, 'result')
1015        query = response.addElement((NS_MUC_ADMIN, 'query'))
1016        item = query.addElement('item')
1017        item['affiliation'] ='outcast'
1018        item['jid'] = 'hag66@shakespeare.lit'
1019        item.addElement('reason', content='Trouble making')
1020        self.stub.send(response)
1021
1022        return d
1023
1024
1025    def test_getOwnerList(self):
1026        """
1027        Retrieving the owner list returns a list of L{muc.AdminItem}s
1028
1029        The request asks for the affiliation C{'owner'}.
1030        """
1031        d = self.protocol.getOwnerList(self.roomJID)
1032
1033        iq = self.stub.output[-1]
1034        query = "/iq/query[@xmlns='%s']/item[@xmlns='%s']" % (muc.NS_MUC_ADMIN,
1035                                                              muc.NS_MUC_ADMIN)
1036        items = xpath.queryForNodes(query, iq)
1037        self.assertEquals('owner', items[0].getAttribute('affiliation'))
1038
1039        response = toResponse(iq, 'result')
1040        query = response.addElement((NS_MUC_ADMIN, 'query'))
1041        self.stub.send(response)
1042
1043        return d
1044
1045
1046    def test_getModeratorList(self):
1047        """
1048        Retrieving the moderator returns a list of L{muc.AdminItem}s.
1049
1050        The request asks for the role C{'moderator'}.
1051        """
1052
1053        def cb(items):
1054            self.assertEquals(1, len(items))
1055            item = items[0]
1056            self.assertEquals(JID(u'hag66@shakespeare.lit'), item.entity)
1057            self.assertEquals(u'thirdwitch', item.nick)
1058            self.assertEquals(u'moderator', item.role)
1059
1060        d = self.protocol.getModeratorList(self.roomJID)
1061        d.addCallback(cb)
1062
1063        iq = self.stub.output[-1]
1064        self.assertEquals('get', iq.getAttribute('type'))
1065        query = "/iq/query[@xmlns='%s']/item[@xmlns='%s']" % (muc.NS_MUC_ADMIN,
1066                                                              muc.NS_MUC_ADMIN)
1067        items = xpath.queryForNodes(query, iq)
1068        self.assertNotIdentical(None, items)
1069        self.assertEquals(1, len(items))
1070        self.assertEquals('moderator', items[0].getAttribute('role'))
1071
1072        response = toResponse(iq, 'result')
1073        query = response.addElement((NS_MUC_ADMIN, 'query'))
1074        item = query.addElement('item')
1075        item['affiliation'] ='member'
1076        item['jid'] = 'hag66@shakespeare.lit'
1077        item['nick'] = 'thirdwitch'
1078        item['role'] = 'moderator'
1079        self.stub.send(response)
1080
1081        return d
Note: See TracBrowser for help on using the repository browser.