source: wokkel/test/test_muc.py @ 135:516de1d26876

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

Rework existing patches to use XML snippets to prevent inderdependence.

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