source: wokkel/test/test_muc.py @ 148:75cf9e14b776

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

Redo room configuration.

  • The underlying request object now derives from generic.Request.
  • MUCClient.getConfiguration now returns a wokkel.data_form.Form.
  • MUCClient.configure now takes a simple dictionary of values.
File size: 27.0 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.occupantJID, 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.occupantJID, 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                    "[@affiliation='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_grantVoice(self):
803        """
804        Test granting voice to a user.
805
806        """
807        nick = 'TroubleMaker'
808        def cb(give_voice):
809            self.assertTrue(give_voice, 'Did not give voice user')
810
811        d = self.protocol.grantVoice(self.occupantJID, nick,
812                                     sender=self.userJID)
813        d.addCallback(cb)
814
815        iq = self.stub.output[-1]
816
817        query = (u"/iq[@type='set' and @to='%s']/query/item"
818                     "[@role='participant']") % self.roomJID
819        self.assertTrue(xpath.matches(query, iq), 'Wrong voice stanza')
820
821        response = toResponse(iq, 'result')
822        self.stub.send(response)
823        return d
824
825
826    def test_status(self):
827        """
828        Change status
829        """
830        self._createRoom()
831        room = self.protocol._getRoom(self.roomJID)
832        user = muc.User(self.nick)
833        room.addUser(user)
834
835        def cb(room):
836            self.assertEquals(self.roomIdentifier, room.roomIdentifier)
837            user = room.getUser(self.nick)
838            self.assertNotIdentical(None, user, 'User not found')
839            self.assertEquals('testing MUC', user.status, 'Wrong status')
840            self.assertEquals('xa', user.show, 'Wrong show')
841
842        d = self.protocol.status(self.roomJID, 'xa', 'testing MUC')
843        d.addCallback(cb)
844
845        element = self.stub.output[-1]
846
847        self.assertEquals('presence', element.name, "Need to be presence")
848        self.assertTrue(getattr(element, 'x', None), 'No muc x element')
849
850        # send back user presence, status changed
851        xml = u"""
852            <presence from='%s'>
853              <x xmlns='http://jabber.org/protocol/muc#user'>
854                <item affiliation='member' role='participant'/>
855              </x>
856              <show>xa</show>
857              <status>testing MUC</status>
858            </presence>
859        """ % self.occupantJID
860        self.stub.send(parseXml(xml))
861        return d
862
863
864    def test_getMemberList(self):
865        def cb(room):
866            members = room.members
867            self.assertEquals(1, len(members))
868            user = members[0]
869            self.assertEquals(JID(u'hag66@shakespeare.lit'), user.entity)
870            self.assertEquals(u'thirdwitch', user.nick)
871            self.assertEquals(u'participant', user.role)
872
873        self._createRoom()
874        d = self.protocol.getMemberList(self.roomJID)
875        d.addCallback(cb)
876
877        iq = self.stub.output[-1]
878        query = iq.query
879        self.assertNotIdentical(None, query)
880        self.assertEquals(NS_MUC_ADMIN, query.uri)
881
882        response = toResponse(iq, 'result')
883        query = response.addElement((NS_MUC_ADMIN, 'query'))
884        item = query.addElement('item')
885        item['affiliation'] ='member'
886        item['jid'] = 'hag66@shakespeare.lit'
887        item['nick'] = 'thirdwitch'
888        item['role'] = 'participant'
889        self.stub.send(response)
890
891        return d
Note: See TracBrowser for help on using the repository browser.