source: wokkel/test/test_muc.py @ 138:8332717e2739

wokkel-muc-client-support-24
Last change on this file since 138:8332717e2739 was 138:8332717e2739, checked in by Goffi <goffi@…>, 9 years ago

Replace custom error classes by StanzaError?.

File size: 18.2 KB
Line 
1# Copyright (c) 2003-2009 Ralph Meijer
2# See LICENSE for details.
3
4"""
5Tests for L{wokkel.muc}
6"""
7
8from zope.interface import verify
9
10from twisted.trial import unittest
11from twisted.internet import defer
12from twisted.words.xish import domish, xpath
13from twisted.words.protocols.jabber.jid import JID
14
15from wokkel import data_form, iwokkel, muc
16from wokkel.generic import parseXml
17from wokkel.test.helpers import XmlStreamStub
18
19from twisted.words.protocols.jabber.xmlstream import toResponse
20from twisted.words.protocols.jabber.error import StanzaError
21
22NS_MUC_ADMIN = 'http://jabber.org/protocol/muc#admin'
23
24def calledAsync(fn):
25    """
26    Function wrapper that fires a deferred upon calling the given function.
27    """
28    d = defer.Deferred()
29
30    def func(*args, **kwargs):
31        try:
32            result = fn(*args, **kwargs)
33        except:
34            d.errback()
35        else:
36            d.callback(result)
37
38    return d, func
39
40class MucClientTest(unittest.TestCase):
41    timeout = 2
42
43    def setUp(self):
44        self.stub = XmlStreamStub()
45        self.protocol = muc.MUCClient()
46        self.protocol.xmlstream = self.stub.xmlstream
47        self.protocol.connectionInitialized()
48        self.test_room = 'test'
49        self.test_srv  = 'conference.example.org'
50        self.test_nick = 'Nick'
51
52        self.room_jid = JID(tuple=(self.test_room,
53                                   self.test_srv,
54                                   self.test_nick))
55        self.user_jid = JID('test@jabber.org/Testing')
56
57
58    def _createRoom(self):
59        """
60        A helper method to create a test room.
61        """
62        # create a room
63        self.current_room = muc.Room(self.test_room,
64                                     self.test_srv,
65                                     self.test_nick)
66        self.protocol._addRoom(self.current_room)
67
68
69    def test_interface(self):
70        """
71        Do instances of L{muc.MUCClient} provide L{iwokkel.IMUCClient}?
72        """
73        verify.verifyObject(iwokkel.IMUCClient, self.protocol)
74
75
76    def test_userJoinedRoom(self):
77        """
78        The client receives presence from an entity joining the room.
79
80        This tests the class L{muc.UserPresence} and the userJoinedRoom event method.
81
82        The test sends the user presence and tests if the event method is called.
83
84        """
85        xml = """
86            <presence to='%s' from='%s'>
87              <x xmlns='http://jabber.org/protocol/muc#user'>
88                <item affiliation='member' role='participant'/>
89              </x>
90            </presence>
91        """ % (self.user_jid.full(), self.room_jid.full())
92
93        # create a room
94        self._createRoom()
95
96        def userJoinedRoom(room, user):
97            self.assertEquals(self.test_room, room.roomIdentifier,
98                              'Wrong room name')
99            self.assertTrue(room.inRoster(user), 'User not in roster')
100
101        d, self.protocol.userJoinedRoom = calledAsync(userJoinedRoom)
102        self.stub.send(parseXml(xml))
103        return d
104
105
106    def test_groupChat(self):
107        """
108        The client receives a groupchat message from an entity in the room.
109        """
110        xml = """
111            <message to='test@test.com' from='%s' type='groupchat'>
112              <body>test</body>
113            </message>
114        """ % (self.room_jid.full())
115
116        self._createRoom()
117
118        def groupChat(room, user, message):
119            self.assertEquals('test', message, "Wrong group chat message")
120            self.assertEquals(self.test_room, room.roomIdentifier,
121                              'Wrong room name')
122
123        d, self.protocol.receivedGroupChat = calledAsync(groupChat)
124        self.stub.send(parseXml(xml))
125        return d
126
127
128    def test_joinRoom(self):
129        """
130        Joining a room
131        """
132
133        def cb(room):
134            self.assertEquals(self.test_room, room.roomIdentifier)
135
136        d = self.protocol.join(self.test_srv, self.test_room, self.test_nick)
137        d.addCallback(cb)
138
139        prs = self.stub.output[-1]
140        self.assertEquals('presence', prs.name, "Need to be presence")
141        self.assertNotIdentical(None, prs.x, 'No muc x element')
142
143        # send back user presence, they joined
144        xml = """
145            <presence from='%s@%s/%s'>
146              <x xmlns='http://jabber.org/protocol/muc#user'>
147                <item affiliation='member' role='participant'/>
148              </x>
149            </presence>
150        """ % (self.test_room, self.test_srv, self.test_nick)
151        self.stub.send(parseXml(xml))
152        return d
153
154
155    def test_joinRoomForbidden(self):
156        """
157        Client joining a room and getting a forbidden error.
158        """
159
160        def cb(error):
161            self.assertEquals('forbidden', error.value.condition,
162                              'Wrong muc condition')
163
164        d = self.protocol.join(self.test_srv, self.test_room, self.test_nick)
165        d.addBoth(cb)
166
167        prs = self.stub.output[-1]
168        self.assertEquals('presence', prs.name, "Need to be presence")
169        self.assertNotIdentical(None, prs.x, 'No muc x element')
170
171        # send back error, forbidden
172        xml = """
173            <presence from='%s' type='error'>
174              <error type='auth'>
175                <forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
176              </error>
177            </presence>
178        """ % (self.room_jid.full())
179        self.stub.send(parseXml(xml))
180        return d
181
182
183    def test_joinRoomBadJID(self):
184        """
185        Client joining a room and getting a jid-malformed error.
186        """
187
188        def cb(error):
189            self.assertEquals('jid-malformed', error.value.condition,
190                              'Wrong muc condition')
191
192        d = self.protocol.join(self.test_srv, self.test_room, self.test_nick)
193        d.addBoth(cb)
194
195        prs = self.stub.output[-1]
196        self.assertEquals('presence', prs.name, "Need to be presence")
197        self.assertNotIdentical(None, prs.x, 'No muc x element')
198
199        # send back error, bad JID
200        xml = """
201            <presence from='%s' type='error'>
202              <error type='modify'>
203                <jid-malformed xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
204              </error>
205            </presence>
206        """ % (self.room_jid.full())
207        self.stub.send(parseXml(xml))
208        return d
209
210
211    def test_partRoom(self):
212        """
213        Client leaves a room
214        """
215        def cb(left):
216            self.assertTrue(left, 'did not leave room')
217
218        self._createRoom()
219        d = self.protocol.leave(self.room_jid)
220        d.addCallback(cb)
221
222        prs = self.stub.output[-1]
223
224        self.assertEquals('unavailable', prs['type'],
225                          'Unavailable is not being sent')
226
227        xml = """
228            <presence to='test@jabber.org' from='%s' type='unavailable'/>
229        """ % (self.room_jid.full())
230        self.stub.send(parseXml(xml))
231        return d
232
233
234    def test_userPartsRoom(self):
235        """
236        An entity leaves the room, a presence of type unavailable is received by the client.
237        """
238
239        xml = """
240            <presence to='%s' from='%s' type='unavailable'/>
241        """ % (self.user_jid.full(), self.room_jid.full())
242
243        # create a room
244        self._createRoom()
245
246        # add user to room
247        user = muc.User(self.room_jid.resource)
248        room = self.protocol._getRoom(self.room_jid)
249        room.addUser(user)
250
251        def userPresence(room, user):
252            self.assertEquals(self.test_room, room.roomIdentifier,
253                              'Wrong room name')
254            self.assertFalse(room.inRoster(user), 'User in roster')
255
256        d, self.protocol.userLeftRoom = calledAsync(userPresence)
257        self.stub.send(parseXml(xml))
258        return d
259
260
261    def test_ban(self):
262        """
263        Ban an entity in a room.
264        """
265        banned = JID('ban@jabber.org/TroubleMaker')
266
267        def cb(banned):
268            self.assertTrue(banned, 'Did not ban user')
269
270        d = self.protocol.ban(self.room_jid, banned, reason='Spam',
271                              sender=self.user_jid)
272        d.addCallback(cb)
273
274        iq = self.stub.output[-1]
275
276        self.assertTrue(xpath.matches(
277                "/iq[@type='set' and @to='%s']/query/item"
278                    "[@affiliation='outcast']" % (self.room_jid.userhost(),),
279                iq),
280            'Wrong ban stanza')
281
282        response = toResponse(iq, 'result')
283        self.stub.send(response)
284
285        return d
286
287
288    def test_kick(self):
289        """
290        Kick an entity from a room.
291        """
292        nick = 'TroubleMaker'
293
294        def cb(kicked):
295            self.assertTrue(kicked, 'Did not kick user')
296
297        d = self.protocol.kick(self.room_jid, nick, reason='Spam',
298                               sender=self.user_jid)
299        d.addCallback(cb)
300
301        iq = self.stub.output[-1]
302
303        self.assertTrue(xpath.matches(
304                "/iq[@type='set' and @to='%s']/query/item"
305                    "[@affiliation='none']" % (self.room_jid.userhost(),),
306                iq),
307            'Wrong kick stanza')
308
309        response = toResponse(iq, 'result')
310        self.stub.send(response)
311
312        return d
313
314
315    def test_password(self):
316        """
317        Sending a password via presence to a password protected room.
318        """
319
320        self.protocol.password(self.room_jid, 'secret')
321
322        prs = self.stub.output[-1]
323
324        self.assertTrue(xpath.matches(
325                "/presence[@to='%s']/x/password"
326                    "[text()='secret']" % (self.room_jid.full(),),
327                prs),
328            'Wrong presence stanza')
329
330
331    def test_historyReceived(self):
332        """
333        Receiving history on room join.
334        """
335        xml = """
336            <message to='test@test.com' from='%s' type='groupchat'>
337              <body>test</body>
338              <delay xmlns='urn:xmpp:delay' stamp="2002-10-13T23:58:37Z"
339                                            from="%s"/>
340            </message>
341        """ % (self.room_jid.full(), self.user_jid.full())
342
343        self._createRoom()
344
345
346        def historyReceived(room, user, body, stamp, frm=None):
347            self.assertEquals('test', body, "wrong message body")
348            self.assertTrue(stamp, 'Does not have a history stamp')
349
350        d, self.protocol.receivedHistory = calledAsync(historyReceived)
351        self.stub.send(parseXml(xml))
352        return d
353
354
355    def test_oneToOneChat(self):
356        """
357        Converting a one to one chat to a multi-user chat.
358        """
359        archive = []
360        thread = "e0ffe42b28561960c6b12b944a092794b9683a38"
361        # create messages
362        msg = domish.Element((None, 'message'))
363        msg['to'] = 'testing@example.com'
364        msg['type'] = 'chat'
365        msg.addElement('body', None, 'test')
366        msg.addElement('thread', None, thread)
367
368        archive.append({'stanza': msg, 'timestamp': '2002-10-13T23:58:37Z'})
369
370        msg = domish.Element((None, 'message'))
371        msg['to'] = 'testing2@example.com'
372        msg['type'] = 'chat'
373        msg.addElement('body', None, 'yo')
374        msg.addElement('thread', None, thread)
375
376        archive.append({'stanza': msg, 'timestamp': '2002-10-13T23:58:43Z'})
377
378        self.protocol.history(self.room_jid, archive)
379
380
381        while len(self.stub.output)>0:
382            m = self.stub.output.pop()
383            # check for delay element
384            self.assertEquals('message', m.name, 'Wrong stanza')
385            self.assertTrue(xpath.matches("/message/delay", m), 'Invalid history stanza')
386
387
388    def test_invite(self):
389        """
390        Invite a user to a room
391        """
392        bareRoomJID = self.room_jid.userhostJID()
393        invitee = JID('test@jabber.org')
394
395        self.protocol.invite(bareRoomJID, 'This is a test', invitee)
396
397        msg = self.stub.output[-1]
398
399        query = u"/message[@to='%s']/x/invite/reason" % bareRoomJID
400        self.assertTrue(xpath.matches(query, msg), 'Wrong message type')
401
402
403    def test_privateMessage(self):
404        """
405        Send private messages to muc entities.
406        """
407        other_nick = JID(self.room_jid.userhost()+'/OtherNick')
408
409        self.protocol.chat(other_nick, 'This is a test')
410
411        msg = self.stub.output[-1]
412
413        query = "/message[@type='chat' and @to='%s']/body" % other_nick.full()
414        self.assertTrue(xpath.matches(query, msg), 'Wrong message type')
415
416
417    def test_register(self):
418        """
419        Client registering with a room.
420
421        http://xmpp.org/extensions/xep-0045.html#register
422        """
423
424        def cb(iq):
425            # check for a result
426            self.assertEquals('result', iq['type'], 'We did not get a result')
427
428        d = self.protocol.register(self.room_jid)
429        d.addCallback(cb)
430
431        iq = self.stub.output[-1]
432        query = "/iq/query[@xmlns='%s']" % muc.NS_REQUEST
433        self.assertTrue(xpath.matches(query, iq), 'Invalid iq register request')
434
435        response = toResponse(iq, 'result')
436        self.stub.send(response)
437        return d
438
439
440    def test_voice(self):
441        """
442        Client requesting voice for a room.
443        """
444        self.protocol.voice(self.room_jid)
445
446        m = self.stub.output[-1]
447
448        query = ("/message/x[@type='submit']/field/value"
449                    "[text()='%s']") % muc.NS_MUC_REQUEST
450        self.assertTrue(xpath.matches(query, m), 'Invalid voice message stanza')
451
452
453    def test_roomConfigure(self):
454        """
455        Default configure and changing the room name.
456        """
457
458        def cb(iq):
459            self.assertEquals('result', iq['type'], 'Not a result')
460
461
462        fields = []
463
464        fields.append(data_form.Field(label='Natural-Language Room Name',
465                                      var='muc#roomconfig_roomname',
466                                      value=self.test_room))
467
468        d = self.protocol.configure(self.room_jid.userhost(), fields)
469        d.addCallback(cb)
470
471        iq = self.stub.output[-1]
472        query = "/iq/query[@xmlns='%s']/x"% muc.NS_MUC_OWNER
473        self.assertTrue(xpath.matches(query, iq), 'Bad configure request')
474
475        response = toResponse(iq, 'result')
476        self.stub.send(response)
477        return d
478
479
480    def test_roomDestroy(self):
481        """
482        Destroy a room.
483        """
484
485        def cb(destroyed):
486            self.assertTrue(destroyed, 'Room not destroyed.')
487
488        d = self.protocol.destroy(self.room_jid)
489        d.addCallback(cb)
490
491        iq = self.stub.output[-1]
492        query = "/iq/query[@xmlns='%s']/destroy"% muc.NS_MUC_OWNER
493        self.assertTrue(xpath.matches(query, iq), 'Bad configure request')
494
495        response = toResponse(iq, 'result')
496        self.stub.send(response)
497        return d
498
499
500    def test_nickChange(self):
501        """
502        Send a nick change to the server.
503        """
504        test_nick = 'newNick'
505
506        self._createRoom()
507
508        def cb(room):
509            self.assertEquals(self.test_room, room.roomIdentifier)
510            self.assertEquals(test_nick, room.nick)
511
512        d = self.protocol.nick(self.room_jid, test_nick)
513        d.addCallback(cb)
514
515        prs = self.stub.output[-1]
516        self.assertEquals('presence', prs.name, "Need to be presence")
517        self.assertNotIdentical(None, prs.x, 'No muc x element')
518
519        # send back user presence, nick changed
520        xml = """
521            <presence from='%s@%s/%s'>
522              <x xmlns='http://jabber.org/protocol/muc#user'>
523                <item affiliation='member' role='participant'/>
524              </x>
525            </presence>
526        """ % (self.test_room, self.test_srv, test_nick)
527        self.stub.send(parseXml(xml))
528        return d
529
530
531    def test_grantVoice(self):
532        """
533        Test granting voice to a user.
534
535        """
536        nick = 'TroubleMaker'
537        def cb(give_voice):
538            self.assertTrue(give_voice, 'Did not give voice user')
539
540        d = self.protocol.grantVoice(self.room_jid, nick, sender=self.user_jid)
541        d.addCallback(cb)
542
543        iq = self.stub.output[-1]
544
545        query = ("/iq[@type='set' and @to='%s']/query/item"
546                     "[@role='participant']") % self.room_jid.userhost()
547        self.assertTrue(xpath.matches(query, iq), 'Wrong voice stanza')
548
549        response = toResponse(iq, 'result')
550        self.stub.send(response)
551        return d
552
553
554    def test_changeStatus(self):
555        """
556        Change status
557        """
558        self._createRoom()
559        room = self.protocol._getRoom(self.room_jid)
560        user = muc.User(self.room_jid.resource)
561        room.addUser(user)
562
563        def cb(room):
564            self.assertEquals(self.test_room, room.roomIdentifier)
565            user = room.getUser(self.room_jid.resource)
566            self.assertNotIdentical(None, user, 'User not found')
567            self.assertEquals('testing MUC', user.status, 'Wrong status')
568            self.assertEquals('xa', user.show, 'Wrong show')
569
570        d = self.protocol.status(self.room_jid, 'xa', 'testing MUC')
571        d.addCallback(cb)
572
573        prs = self.stub.output[-1]
574
575        self.assertEquals('presence', prs.name, "Need to be presence")
576        self.assertTrue(getattr(prs, 'x', None), 'No muc x element')
577
578        # send back user presence, status changed
579        xml = """
580            <presence from='%s'>
581              <x xmlns='http://jabber.org/protocol/muc#user'>
582                <item affiliation='member' role='participant'/>
583              </x>
584              <show>xa</show>
585              <status>testing MUC</status>
586            </presence>
587        """ % self.room_jid.full()
588        self.stub.send(parseXml(xml))
589        return d
590
591
592    def test_getMemberList(self):
593        def cb(room):
594            members = room.members
595            self.assertEquals(1, len(members))
596            user = members[0]
597            self.assertEquals(JID(u'hag66@shakespeare.lit'), user.entity)
598            self.assertEquals(u'thirdwitch', user.nick)
599            self.assertEquals(u'participant', user.role)
600
601        self._createRoom()
602        bareRoomJID = self.room_jid.userhostJID()
603        d = self.protocol.getMemberList(bareRoomJID)
604        d.addCallback(cb)
605
606        iq = self.stub.output[-1]
607        query = iq.query
608        self.assertNotIdentical(None, query)
609        self.assertEquals(NS_MUC_ADMIN, query.uri)
610
611        response = toResponse(iq, 'result')
612        query = response.addElement((NS_MUC_ADMIN, 'query'))
613        item = query.addElement('item')
614        item['affiliation'] ='member'
615        item['jid'] = 'hag66@shakespeare.lit'
616        item['nick'] = 'thirdwitch'
617        item['role'] = 'participant'
618        self.stub.send(response)
619
620        return d
621
622
Note: See TracBrowser for help on using the repository browser.