source: wokkel/test/test_muc.py @ 142:078a14ca2b3e

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

Add test for messages received from the room itself.

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