source: wokkel/test/test_muc.py @ 157:2b80688b5688

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

Pyflakes cleanups.

File size: 55.5 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, delay, 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 GroupChatTest(unittest.TestCase):
46    """
47    Tests for {muc.GroupChat}.
48    """
49
50
51    def test_toElementDelay(self):
52        """
53        If the delay attribute is set, toElement has it rendered.
54        """
55        message = muc.GroupChat()
56        message.delay = delay.Delay(stamp=datetime(2002, 10, 13, 23, 58, 37,
57                                                   tzinfo=tzutc()))
58
59        element = message.toElement()
60
61        query = "/message/delay[@xmlns='%s']" % (delay.NS_DELAY,)
62        nodes = xpath.queryForNodes(query, element)
63        self.assertNotIdentical(None, nodes, "Missing delay element")
64
65
66    def test_toElementDelayLegacy(self):
67        """
68        If legacy delay is requested, the legacy format is rendered.
69        """
70        message = muc.GroupChat()
71        message.delay = delay.Delay(stamp=datetime(2002, 10, 13, 23, 58, 37,
72                                                   tzinfo=tzutc()))
73
74        element = message.toElement(legacyDelay=True)
75
76        query = "/message/x[@xmlns='%s']" % (delay.NS_JABBER_DELAY,)
77        nodes = xpath.queryForNodes(query, element)
78        self.assertNotIdentical(None, nodes, "Missing legacy delay element")
79
80
81
82class HistoryOptionsTest(unittest.TestCase):
83    """
84    Tests for L{muc.HistoryOptionsTest}.
85    """
86
87    def test_toElement(self):
88        """
89        toElement renders the history element in the right namespace.
90        """
91        history = muc.HistoryOptions()
92
93        element = history.toElement()
94
95        self.assertEqual(muc.NS_MUC, element.uri)
96        self.assertEqual('history', element.name)
97
98
99    def test_toElementMaxStanzas(self):
100        """
101        If C{maxStanzas} is set, the element has the attribute C{'maxstanzas'}.
102        """
103        history = muc.HistoryOptions(maxStanzas=10)
104
105        element = history.toElement()
106
107        self.assertEqual(u'10', element.getAttribute('maxstanzas'))
108
109
110    def test_toElementSince(self):
111        """
112        If C{since} is set, the attribute C{'since'} has a rendered timestamp.
113        """
114        history = muc.HistoryOptions(since=datetime(2002, 10, 13, 23, 58, 37,
115                                                   tzinfo=tzutc()))
116
117        element = history.toElement()
118
119        self.assertEqual(u'2002-10-13T23:58:37Z',
120                         element.getAttribute('since'))
121
122
123class UserPresenceTest(unittest.TestCase):
124    """
125    Tests for L{muc.UserPresence}.
126    """
127
128
129    def test_toElementUnknownChild(self):
130        """
131        Unknown child elements are ignored.
132        """
133        xml = """
134            <presence from='coven@chat.shakespeare.lit/thirdwitch'
135                      id='026B3509-2CCE-4D69-96D6-25F41FFDC408'
136                      to='hag66@shakespeare.lit/pda'>
137              <x xmlns='http://jabber.org/protocol/muc#user'>
138                <status xmlns='myns' code='110'/>
139              </x>
140            </presence>
141        """
142
143        element = parseXml(xml)
144        presence = muc.UserPresence.fromElement(element)
145
146        self.assertIdentical(None, presence.statusCodes)
147
148
149    def test_toElementStatusOne(self):
150        """
151        Status codes are extracted.
152        """
153        xml = """
154            <presence from='coven@chat.shakespeare.lit/thirdwitch'
155                      id='026B3509-2CCE-4D69-96D6-25F41FFDC408'
156                      to='hag66@shakespeare.lit/pda'>
157              <x xmlns='http://jabber.org/protocol/muc#user'>
158                <item affiliation='member' role='participant'/>
159                <status code='110'/>
160              </x>
161            </presence>
162        """
163
164        element = parseXml(xml)
165        presence = muc.UserPresence.fromElement(element)
166
167        self.assertIn(110, presence.statusCodes)
168
169
170    def test_toElementStatusMultiple(self):
171        """
172        Multiple status codes are all extracted.
173        """
174        xml = """
175            <presence from='coven@chat.shakespeare.lit/thirdwitch'
176                      id='026B3509-2CCE-4D69-96D6-25F41FFDC408'
177                      to='hag66@shakespeare.lit/pda'>
178              <x xmlns='http://jabber.org/protocol/muc#user'>
179                <item affiliation='member' role='participant'/>
180                <status code='100'/>
181                <status code='110'/>
182              </x>
183            </presence>
184        """
185
186        element = parseXml(xml)
187        presence = muc.UserPresence.fromElement(element)
188
189        self.assertIn(110, presence.statusCodes)
190        self.assertIn(100, presence.statusCodes)
191
192
193    def test_toElementStatusEmpty(self):
194        """
195        Empty status elements are ignored.
196        """
197        xml = """
198            <presence from='coven@chat.shakespeare.lit/thirdwitch'
199                      id='026B3509-2CCE-4D69-96D6-25F41FFDC408'
200                      to='hag66@shakespeare.lit/pda'>
201              <x xmlns='http://jabber.org/protocol/muc#user'>
202                <item affiliation='member' role='participant'/>
203                <status/>
204              </x>
205            </presence>
206        """
207
208        element = parseXml(xml)
209        presence = muc.UserPresence.fromElement(element)
210
211        self.assertIdentical(None, presence.statusCodes)
212
213
214    def test_toElementStatusBad(self):
215        """
216        Bad status codes are ignored.
217        """
218        xml = """
219            <presence from='coven@chat.shakespeare.lit/thirdwitch'
220                      id='026B3509-2CCE-4D69-96D6-25F41FFDC408'
221                      to='hag66@shakespeare.lit/pda'>
222              <x xmlns='http://jabber.org/protocol/muc#user'>
223                <item affiliation='member' role='participant'/>
224                <status code="badvalue"/>
225              </x>
226            </presence>
227        """
228
229        element = parseXml(xml)
230        presence = muc.UserPresence.fromElement(element)
231
232        self.assertIdentical(None, presence.statusCodes)
233
234
235    def test_toElementStatusUnknown(self):
236        """
237        Unknown status codes are still recorded in C{statusCodes}.
238        """
239        xml = """
240            <presence from='coven@chat.shakespeare.lit/thirdwitch'
241                      id='026B3509-2CCE-4D69-96D6-25F41FFDC408'
242                      to='hag66@shakespeare.lit/pda'>
243              <x xmlns='http://jabber.org/protocol/muc#user'>
244                <item affiliation='member' role='participant'/>
245                <status code="999"/>
246              </x>
247            </presence>
248        """
249
250        element = parseXml(xml)
251        presence = muc.UserPresence.fromElement(element)
252
253        self.assertIn(999, presence.statusCodes)
254
255
256    def test_toElementItem(self):
257        """
258        Item attributes are parsed properly.
259        """
260        xml = """
261            <presence from='coven@chat.shakespeare.lit/thirdwitch'
262                      to='crone1@shakespeare.lit/desktop'>
263              <x xmlns='http://jabber.org/protocol/muc#user'>
264                <item affiliation='member'
265                      jid='hag66@shakespeare.lit/pda'
266                      role='participant'
267                      nick='thirdwitch'/>
268              </x>
269            </presence>
270        """
271
272        element = parseXml(xml)
273        presence = muc.UserPresence.fromElement(element)
274        self.assertEqual(u'member', presence.affiliation)
275        self.assertEqual(u'participant', presence.role)
276        self.assertEqual(JID('hag66@shakespeare.lit/pda'), presence.entity)
277        self.assertEqual(u'thirdwitch', presence.nick)
278
279
280
281class MUCClientProtocolTest(unittest.TestCase):
282    """
283    Tests for L{muc.MUCClientProtocol}.
284    """
285
286    def setUp(self):
287        self.clock = task.Clock()
288        self.sessionManager = TestableStreamManager(reactor=self.clock)
289        self.stub = self.sessionManager.stub
290        self.protocol = muc.MUCClientProtocol(reactor=self.clock)
291        self.protocol.setHandlerParent(self.sessionManager)
292
293        self.roomIdentifier = 'test'
294        self.service  = 'conference.example.org'
295        self.nick = 'Nick'
296
297        self.occupantJID = JID(tuple=(self.roomIdentifier,
298                                      self.service,
299                                      self.nick))
300        self.roomJID = self.occupantJID.userhostJID()
301        self.userJID = JID('test@example.org/Testing')
302
303
304    def test_initNoReactor(self):
305        """
306        If no reactor is passed, the default reactor is used.
307        """
308        protocol = muc.MUCClientProtocol()
309        from twisted.internet import reactor
310        self.assertEqual(reactor, protocol._reactor)
311
312
313    def test_groupChatReceived(self):
314        """
315        Messages of type groupchat are parsed and passed to L{groupChatReceived}.
316        """
317        xml = u"""
318            <message to='test@test.com' from='%s' type='groupchat'>
319              <body>test</body>
320            </message>
321        """ % (self.occupantJID)
322
323        def groupChatReceived(message):
324            self.assertEquals('test', message.body, "Wrong group chat message")
325            self.assertEquals(self.roomIdentifier, message.sender.user,
326                              'Wrong room identifier')
327
328        d, self.protocol.groupChatReceived = calledAsync(groupChatReceived)
329        self.stub.send(parseXml(xml))
330        return d
331
332
333    def test_groupChatReceivedNotOverridden(self):
334        """
335        If L{groupChatReceived} has not been overridden, no errors should occur.
336        """
337        xml = u"""
338            <message to='test@test.com' from='%s' type='groupchat'>
339              <body>test</body>
340            </message>
341        """ % (self.occupantJID)
342
343        self.stub.send(parseXml(xml))
344
345
346    def test_join(self):
347        """
348        Joining a room waits for confirmation, deferred fires user presence.
349        """
350
351        def cb(presence):
352            self.assertEquals(self.occupantJID, presence.sender)
353
354        # Join the room
355        d = self.protocol.join(self.roomJID, self.nick)
356        d.addCallback(cb)
357
358        element = self.stub.output[-1]
359
360        self.assertEquals('presence', element.name, "Need to be presence")
361        self.assertNotIdentical(None, element.x, 'No muc x element')
362
363        # send back user presence, they joined
364        xml = """
365            <presence from='%s@%s/%s'>
366              <x xmlns='http://jabber.org/protocol/muc#user'>
367                <item affiliation='member' role='participant'/>
368              </x>
369            </presence>
370        """ % (self.roomIdentifier, self.service, self.nick)
371        self.stub.send(parseXml(xml))
372        return d
373
374
375    def test_joinHistory(self):
376        """
377        Passing a history parameter sends a 'maxStanzas' history limit.
378        """
379
380        historyOptions = muc.HistoryOptions(maxStanzas=10)
381        d = self.protocol.join(self.roomJID, self.nick,
382                               historyOptions)
383
384        element = self.stub.output[-1]
385        query = "/*/x[@xmlns='%s']/history[@xmlns='%s']" % (muc.NS_MUC,
386                                                            muc.NS_MUC)
387        result = xpath.queryForNodes(query, element)
388        history = result[0]
389        self.assertEquals('10', history.getAttribute('maxstanzas'))
390
391        # send back user presence, they joined
392        xml = """
393            <presence from='%s@%s/%s'>
394              <x xmlns='http://jabber.org/protocol/muc#user'>
395                <item affiliation='member' role='participant'/>
396              </x>
397            </presence>
398        """ % (self.roomIdentifier, self.service, self.nick)
399        self.stub.send(parseXml(xml))
400        return d
401
402
403    def test_joinForbidden(self):
404        """
405        A forbidden error in response to a join errbacks with L{StanzaError}.
406        """
407
408        def cb(error):
409            self.assertEquals('forbidden', error.condition,
410                              'Wrong muc condition')
411
412        d = self.protocol.join(self.roomJID, self.nick)
413        self.assertFailure(d, StanzaError)
414        d.addCallback(cb)
415
416        # send back error, forbidden
417        xml = u"""
418            <presence from='%s' type='error'>
419              <error type='auth'>
420                <forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
421              </error>
422            </presence>
423        """ % (self.occupantJID)
424        self.stub.send(parseXml(xml))
425        return d
426
427
428    def test_joinForbiddenFromRoomJID(self):
429        """
430        An error response to a join sent from the room JID should errback.
431
432        Some service implementations send error stanzas from the room JID
433        instead of the JID the join presence was sent to.
434        """
435
436        d = self.protocol.join(self.roomJID, self.nick)
437        self.assertFailure(d, StanzaError)
438
439        # send back error, forbidden
440        xml = u"""
441            <presence from='%s' type='error'>
442              <error type='auth'>
443                <forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
444              </error>
445            </presence>
446        """ % (self.roomJID)
447        self.stub.send(parseXml(xml))
448        return d
449
450
451    def test_joinBadJID(self):
452        """
453        Client joining a room and getting a jid-malformed error.
454        """
455
456        def cb(error):
457            self.assertEquals('jid-malformed', error.condition,
458                              'Wrong muc condition')
459
460        d = self.protocol.join(self.roomJID, self.nick)
461        self.assertFailure(d, StanzaError)
462        d.addCallback(cb)
463
464        # send back error, bad JID
465        xml = u"""
466            <presence from='%s' type='error'>
467              <error type='modify'>
468                <jid-malformed xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
469              </error>
470            </presence>
471        """ % (self.occupantJID)
472        self.stub.send(parseXml(xml))
473        return d
474
475
476    def test_joinTimeout(self):
477        """
478        After not receiving a response to a join, errback with L{TimeoutError}.
479        """
480
481        d = self.protocol.join(self.roomJID, self.nick)
482        self.assertFailure(d, TimeoutError)
483        self.clock.advance(muc.DEFER_TIMEOUT)
484        return d
485
486
487    def test_joinPassword(self):
488        """
489        Sending a password via presence to a password protected room.
490        """
491
492        self.protocol.join(self.roomJID, self.nick, password='secret')
493
494        element = self.stub.output[-1]
495
496        self.assertTrue(xpath.matches(
497                u"/presence[@to='%s']/x/password"
498                    "[text()='secret']" % (self.occupantJID,),
499                element),
500            'Wrong presence stanza')
501
502
503    def test_nick(self):
504        """
505        Send a nick change to the server.
506        """
507        newNick = 'newNick'
508
509        def cb(presence):
510            self.assertEquals(JID(tuple=(self.roomIdentifier,
511                                         self.service,
512                                         newNick)),
513                              presence.sender)
514
515        d = self.protocol.nick(self.roomJID, newNick)
516        d.addCallback(cb)
517
518        element = self.stub.output[-1]
519        self.assertEquals('presence', element.name, "Need to be presence")
520        self.assertNotIdentical(None, element.x, 'No muc x element')
521
522        # send back user presence, nick changed
523        xml = u"""
524            <presence from='%s/%s'>
525              <x xmlns='http://jabber.org/protocol/muc#user'>
526                <item affiliation='member' role='participant'/>
527              </x>
528            </presence>
529        """ % (self.roomJID, newNick)
530        self.stub.send(parseXml(xml))
531        return d
532
533
534    def test_nickConflict(self):
535        """
536        If the server finds the new nick in conflict, the errback is called.
537        """
538        newNick = 'newNick'
539
540        d = self.protocol.nick(self.roomJID, newNick)
541        self.assertFailure(d, StanzaError)
542
543        element = self.stub.output[-1]
544        self.assertEquals('presence', element.name, "Need to be presence")
545        self.assertNotIdentical(None, element.x, 'No muc x element')
546
547        # send back error presence, nick conflicted
548        xml = u"""
549            <presence from='%s/%s' type='error'>
550                <x xmlns='http://jabber.org/protocol/muc'/>
551                <error type='cancel'>
552                  <conflict xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
553                </error>
554            </presence>
555        """ % (self.roomJID, newNick)
556        self.stub.send(parseXml(xml))
557        return d
558
559
560    def test_status(self):
561        """
562        Change status
563        """
564        def joined(_):
565            d = self.protocol.status(self.roomJID, 'xa', 'testing MUC')
566            d.addCallback(statusChanged)
567            return d
568
569        def statusChanged(presence):
570            self.assertEqual(self.occupantJID, presence.sender)
571
572        # Join the room
573        d = self.protocol.join(self.roomJID, self.nick)
574        d.addCallback(joined)
575
576        # Receive presence back from the room: joined.
577        xml = u"""
578            <presence to='%s' from='%s'/>
579        """ % (self.userJID, self.occupantJID)
580        self.stub.send(parseXml(xml))
581
582        # The presence for the status change should have been sent now.
583        element = self.stub.output[-1]
584
585        self.assertEquals('presence', element.name, "Need to be presence")
586        self.assertTrue(getattr(element, 'x', None), 'No muc x element')
587
588        # send back user presence, status changed
589        xml = u"""
590            <presence from='%s'>
591              <x xmlns='http://jabber.org/protocol/muc#user'>
592                <item affiliation='member' role='participant'/>
593              </x>
594              <show>xa</show>
595              <status>testing MUC</status>
596            </presence>
597        """ % self.occupantJID
598        self.stub.send(parseXml(xml))
599
600        return d
601
602
603    def test_leave(self):
604        """
605        Client leaves a room
606        """
607        def joined(_):
608            return self.protocol.leave(self.roomJID)
609
610        # Join the room
611        d = self.protocol.join(self.roomJID, self.nick)
612        d.addCallback(joined)
613
614        # Receive presence back from the room: joined.
615        xml = u"""
616            <presence to='%s' from='%s'/>
617        """ % (self.userJID, self.occupantJID)
618        self.stub.send(parseXml(xml))
619
620        # The presence for leaving the room should have been sent now.
621        element = self.stub.output[-1]
622
623        self.assertEquals('unavailable', element['type'],
624                          'Unavailable is not being sent')
625
626        # Receive presence back from the room: left.
627        xml = u"""
628            <presence to='%s' from='%s' type='unavailable'/>
629        """ % (self.userJID, self.occupantJID)
630        self.stub.send(parseXml(xml))
631
632        return d
633
634
635    def test_groupChat(self):
636        """
637        Send private messages to muc entities.
638        """
639        self.protocol.groupChat(self.roomJID, u'This is a test')
640
641        message = self.stub.output[-1]
642
643        self.assertEquals('message', message.name)
644        self.assertEquals(self.roomJID.full(), message.getAttribute('to'))
645        self.assertEquals('groupchat', message.getAttribute('type'))
646        self.assertEquals(u'This is a test', unicode(message.body))
647
648
649    def test_chat(self):
650        """
651        Send private messages to muc entities.
652        """
653        otherOccupantJID = JID(self.occupantJID.userhost()+'/OtherNick')
654
655        self.protocol.chat(otherOccupantJID, u'This is a test')
656
657        message = self.stub.output[-1]
658
659        self.assertEquals('message', message.name)
660        self.assertEquals(otherOccupantJID.full(), message.getAttribute('to'))
661        self.assertEquals('chat', message.getAttribute('type'))
662        self.assertEquals(u'This is a test', unicode(message.body))
663
664
665    def test_subject(self):
666        """
667        Change subject of the room.
668        """
669        self.protocol.subject(self.roomJID, u'This is a test')
670
671        message = self.stub.output[-1]
672
673        self.assertEquals('message', message.name)
674        self.assertEquals(self.roomJID.full(), message.getAttribute('to'))
675        self.assertEquals('groupchat', message.getAttribute('type'))
676        self.assertEquals(u'This is a test', unicode(message.subject))
677
678
679    def test_invite(self):
680        """
681        Invite a user to a room
682        """
683        invitee = JID('other@example.org')
684
685        self.protocol.invite(self.roomJID, invitee, u'This is a test')
686
687        message = self.stub.output[-1]
688
689        self.assertEquals('message', message.name)
690        self.assertEquals(self.roomJID.full(), message.getAttribute('to'))
691        self.assertEquals(muc.NS_MUC_USER, message.x.uri)
692        self.assertEquals(muc.NS_MUC_USER, message.x.invite.uri)
693        self.assertEquals(invitee.full(), message.x.invite.getAttribute('to'))
694        self.assertEquals(muc.NS_MUC_USER, message.x.invite.reason.uri)
695        self.assertEquals(u'This is a test', unicode(message.x.invite.reason))
696
697
698    def test_getRegisterForm(self):
699        """
700        The response of a register form request should extract the form.
701        """
702
703        def cb(form):
704            self.assertEquals('form', form.formType)
705
706        d = self.protocol.getRegisterForm(self.roomJID)
707        d.addCallback(cb)
708
709        iq = self.stub.output[-1]
710
711        query = "/iq/query[@xmlns='%s']" % (muc.NS_REGISTER)
712        nodes = xpath.queryForNodes(query, iq)
713        self.assertNotIdentical(None, nodes, 'Missing query element')
714
715        self.assertRaises(StopIteration, nodes[0].elements().next)
716
717        xml = u"""
718            <iq from='%s' id='%s' to='%s' type='result'>
719              <query xmlns='jabber:iq:register'>
720                <x xmlns='jabber:x:data' type='form'>
721                  <field type='hidden'
722                         var='FORM_TYPE'>
723                    <value>http://jabber.org/protocol/muc#register</value>
724                  </field>
725                  <field label='Desired Nickname'
726                         type='text-single'
727                         var='muc#register_roomnick'>
728                    <required/>
729                  </field>
730                </x>
731              </query>
732            </iq>
733        """ % (self.roomJID, iq['id'], self.userJID)
734        self.stub.send(parseXml(xml))
735
736        return d
737
738
739    def test_register(self):
740        """
741        Client registering with a room.
742
743        http://xmpp.org/extensions/xep-0045.html#register
744        """
745
746        def cb(iq):
747            # check for a result
748            self.assertEquals('result', iq['type'], 'We did not get a result')
749
750        d = self.protocol.register(self.roomJID,
751                                   {'muc#register_roomnick': 'thirdwitch'})
752        d.addCallback(cb)
753
754        iq = self.stub.output[-1]
755
756        query = "/iq/query[@xmlns='%s']" % muc.NS_REGISTER
757        nodes = xpath.queryForNodes(query, iq)
758        self.assertNotIdentical(None, nodes, 'Invalid registration request')
759
760        form = data_form.findForm(nodes[0], muc.NS_MUC_REGISTER)
761        self.assertNotIdentical(None, form, 'Missing registration form')
762        self.assertEquals('submit', form.formType)
763        self.assertIn('muc#register_roomnick', form.fields)
764
765
766        response = toResponse(iq, 'result')
767        self.stub.send(response)
768        return d
769
770
771    def test_voice(self):
772        """
773        Client requesting voice for a room.
774        """
775        self.protocol.voice(self.occupantJID)
776
777        m = self.stub.output[-1]
778
779        query = ("/message/x[@type='submit']/field/value"
780                    "[text()='%s']") % muc.NS_MUC_REQUEST
781        self.assertTrue(xpath.matches(query, m), 'Invalid voice message stanza')
782
783
784    def test_history(self):
785        """
786        Converting a one to one chat to a multi-user chat.
787        """
788        archive = []
789        thread = "e0ffe42b28561960c6b12b944a092794b9683a38"
790        # create messages
791        element = domish.Element((None, 'message'))
792        element['to'] = 'testing@example.com'
793        element['type'] = 'chat'
794        element.addElement('body', None, 'test')
795        element.addElement('thread', None, thread)
796
797        archive.append({'stanza': element,
798                        'timestamp': datetime(2002, 10, 13, 23, 58, 37,
799                                              tzinfo=tzutc())})
800
801        element = domish.Element((None, 'message'))
802        element['to'] = 'testing2@example.com'
803        element['type'] = 'chat'
804        element.addElement('body', None, 'yo')
805        element.addElement('thread', None, thread)
806
807        archive.append({'stanza': element,
808                        'timestamp': datetime(2002, 10, 13, 23, 58, 43,
809                                              tzinfo=tzutc())})
810
811        self.protocol.history(self.occupantJID, archive)
812
813
814        while len(self.stub.output)>0:
815            element = self.stub.output.pop()
816            # check for delay element
817            self.assertEquals('message', element.name, 'Wrong stanza')
818            self.assertTrue(xpath.matches("/message/delay", element),
819                            'Invalid history stanza')
820
821
822    def test_getConfiguration(self):
823        """
824        The response of a configure form request should extract the form.
825        """
826
827        def cb(form):
828            self.assertEquals('form', form.formType)
829
830        d = self.protocol.getConfiguration(self.roomJID)
831        d.addCallback(cb)
832
833        iq = self.stub.output[-1]
834
835        query = "/iq/query[@xmlns='%s']" % (muc.NS_MUC_OWNER)
836        nodes = xpath.queryForNodes(query, iq)
837        self.assertNotIdentical(None, nodes, 'Missing query element')
838
839        self.assertRaises(StopIteration, nodes[0].elements().next)
840
841        xml = u"""
842            <iq from='%s' id='%s' to='%s' type='result'>
843              <query xmlns='http://jabber.org/protocol/muc#owner'>
844                <x xmlns='jabber:x:data' type='form'>
845                  <field type='hidden'
846                         var='FORM_TYPE'>
847                    <value>http://jabber.org/protocol/muc#roomconfig</value>
848                  </field>
849                  <field label='Natural-Language Room Name'
850                         type='text-single'
851                         var='muc#roomconfig_roomname'/>
852                </x>
853              </query>
854            </iq>
855        """ % (self.roomJID, iq['id'], self.userJID)
856        self.stub.send(parseXml(xml))
857
858        return d
859
860
861    def test_getConfigurationNoOptions(self):
862        """
863        The response of a configure form request should extract the form.
864        """
865
866        def cb(form):
867            self.assertIdentical(None, form)
868
869        d = self.protocol.getConfiguration(self.roomJID)
870        d.addCallback(cb)
871
872        iq = self.stub.output[-1]
873
874        xml = u"""
875            <iq from='%s' id='%s' to='%s' type='result'>
876              <query xmlns='http://jabber.org/protocol/muc#owner'/>
877            </iq>
878        """ % (self.roomJID, iq['id'], self.userJID)
879        self.stub.send(parseXml(xml))
880
881        return d
882
883
884    def test_configure(self):
885        """
886        Default configure and changing the room name.
887        """
888
889        def cb(iq):
890            self.assertEquals('result', iq['type'], 'Not a result')
891
892        values = {'muc#roomconfig_roomname': self.roomIdentifier}
893
894        d = self.protocol.configure(self.roomJID, values)
895        d.addCallback(cb)
896
897        iq = self.stub.output[-1]
898
899        self.assertEquals('set', iq.getAttribute('type'))
900        self.assertEquals(self.roomJID.full(), iq.getAttribute('to'))
901
902        query = "/iq/query[@xmlns='%s']" % (muc.NS_MUC_OWNER)
903        nodes = xpath.queryForNodes(query, iq)
904        self.assertNotIdentical(None, nodes, 'Bad configure request')
905
906        form = data_form.findForm(nodes[0], muc.NS_MUC_CONFIG)
907        self.assertNotIdentical(None, form, 'Missing configuration form')
908        self.assertEquals('submit', form.formType)
909
910        response = toResponse(iq, 'result')
911        self.stub.send(response)
912        return d
913
914
915    def test_configureCancel(self):
916        """
917        Cancelling room configuration should send a cancel form.
918        """
919
920        d = self.protocol.configure(self.roomJID, None)
921
922        iq = self.stub.output[-1]
923
924        query = "/iq/query[@xmlns='%s']" % (muc.NS_MUC_OWNER)
925        nodes = xpath.queryForNodes(query, iq)
926
927        form = data_form.findForm(nodes[0], muc.NS_MUC_CONFIG)
928        self.assertNotIdentical(None, form, 'Missing configuration form')
929        self.assertEquals('cancel', form.formType)
930
931        response = toResponse(iq, 'result')
932        self.stub.send(response)
933        return d
934
935
936    def test_getMemberList(self):
937        """
938        Retrieving the member list returns a list of L{muc.AdminItem}s
939
940        The request asks for the affiliation C{'member'}.
941        """
942        def cb(items):
943            self.assertEquals(1, len(items))
944            item = items[0]
945            self.assertEquals(JID(u'hag66@shakespeare.lit'), item.entity)
946            self.assertEquals(u'thirdwitch', item.nick)
947            self.assertEquals(u'member', item.affiliation)
948
949        d = self.protocol.getMemberList(self.roomJID)
950        d.addCallback(cb)
951
952        iq = self.stub.output[-1]
953        self.assertEquals('get', iq.getAttribute('type'))
954        query = "/iq/query[@xmlns='%s']/item[@xmlns='%s']" % (muc.NS_MUC_ADMIN,
955                                                              muc.NS_MUC_ADMIN)
956        items = xpath.queryForNodes(query, iq)
957        self.assertNotIdentical(None, items)
958        self.assertEquals(1, len(items))
959        self.assertEquals('member', items[0].getAttribute('affiliation'))
960
961        response = toResponse(iq, 'result')
962        query = response.addElement((NS_MUC_ADMIN, 'query'))
963        item = query.addElement('item')
964        item['affiliation'] ='member'
965        item['jid'] = 'hag66@shakespeare.lit'
966        item['nick'] = 'thirdwitch'
967        item['role'] = 'participant'
968        self.stub.send(response)
969
970        return d
971
972
973    def test_getAdminList(self):
974        """
975        Retrieving the admin list returns a list of L{muc.AdminItem}s
976
977        The request asks for the affiliation C{'admin'}.
978        """
979        d = self.protocol.getAdminList(self.roomJID)
980
981        iq = self.stub.output[-1]
982        query = "/iq/query[@xmlns='%s']/item[@xmlns='%s']" % (muc.NS_MUC_ADMIN,
983                                                              muc.NS_MUC_ADMIN)
984        items = xpath.queryForNodes(query, iq)
985        self.assertEquals('admin', items[0].getAttribute('affiliation'))
986
987        response = toResponse(iq, 'result')
988        query = response.addElement((NS_MUC_ADMIN, 'query'))
989        self.stub.send(response)
990
991        return d
992
993
994    def test_getBanList(self):
995        """
996        Retrieving the ban list returns a list of L{muc.AdminItem}s
997
998        The request asks for the affiliation C{'outcast'}.
999        """
1000        def cb(items):
1001            self.assertEquals(1, len(items))
1002            item = items[0]
1003            self.assertEquals(JID(u'hag66@shakespeare.lit'), item.entity)
1004            self.assertEquals(u'outcast', item.affiliation)
1005            self.assertEquals(u'Trouble making', item.reason)
1006
1007        d = self.protocol.getBanList(self.roomJID)
1008        d.addCallback(cb)
1009
1010        iq = self.stub.output[-1]
1011        query = "/iq/query[@xmlns='%s']/item[@xmlns='%s']" % (muc.NS_MUC_ADMIN,
1012                                                              muc.NS_MUC_ADMIN)
1013        items = xpath.queryForNodes(query, iq)
1014        self.assertEquals('outcast', items[0].getAttribute('affiliation'))
1015
1016        response = toResponse(iq, 'result')
1017        query = response.addElement((NS_MUC_ADMIN, 'query'))
1018        item = query.addElement('item')
1019        item['affiliation'] ='outcast'
1020        item['jid'] = 'hag66@shakespeare.lit'
1021        item.addElement('reason', content='Trouble making')
1022        self.stub.send(response)
1023
1024        return d
1025
1026
1027    def test_getOwnerList(self):
1028        """
1029        Retrieving the owner list returns a list of L{muc.AdminItem}s
1030
1031        The request asks for the affiliation C{'owner'}.
1032        """
1033        d = self.protocol.getOwnerList(self.roomJID)
1034
1035        iq = self.stub.output[-1]
1036        query = "/iq/query[@xmlns='%s']/item[@xmlns='%s']" % (muc.NS_MUC_ADMIN,
1037                                                              muc.NS_MUC_ADMIN)
1038        items = xpath.queryForNodes(query, iq)
1039        self.assertEquals('owner', items[0].getAttribute('affiliation'))
1040
1041        response = toResponse(iq, 'result')
1042        query = response.addElement((NS_MUC_ADMIN, 'query'))
1043        self.stub.send(response)
1044
1045        return d
1046
1047
1048    def test_getModeratorList(self):
1049        """
1050        Retrieving the moderator returns a list of L{muc.AdminItem}s.
1051
1052        The request asks for the role C{'moderator'}.
1053        """
1054
1055        def cb(items):
1056            self.assertEquals(1, len(items))
1057            item = items[0]
1058            self.assertEquals(JID(u'hag66@shakespeare.lit'), item.entity)
1059            self.assertEquals(u'thirdwitch', item.nick)
1060            self.assertEquals(u'moderator', item.role)
1061
1062        d = self.protocol.getModeratorList(self.roomJID)
1063        d.addCallback(cb)
1064
1065        iq = self.stub.output[-1]
1066        self.assertEquals('get', iq.getAttribute('type'))
1067        query = "/iq/query[@xmlns='%s']/item[@xmlns='%s']" % (muc.NS_MUC_ADMIN,
1068                                                              muc.NS_MUC_ADMIN)
1069        items = xpath.queryForNodes(query, iq)
1070        self.assertNotIdentical(None, items)
1071        self.assertEquals(1, len(items))
1072        self.assertEquals('moderator', items[0].getAttribute('role'))
1073
1074        response = toResponse(iq, 'result')
1075        query = response.addElement((NS_MUC_ADMIN, 'query'))
1076        item = query.addElement('item')
1077        item['affiliation'] ='member'
1078        item['jid'] = 'hag66@shakespeare.lit'
1079        item['nick'] = 'thirdwitch'
1080        item['role'] = 'moderator'
1081        self.stub.send(response)
1082
1083        return d
1084
1085
1086    def test_modifyAffiliationList(self):
1087
1088        entities = [JID('user1@test.example.org'),
1089                    JID('user2@test.example.org')]
1090        d = self.protocol.modifyAffiliationList(self.roomJID, entities,
1091                                                'admin')
1092
1093        iq = self.stub.output[-1]
1094        query = "/iq/query[@xmlns='%s']/item[@xmlns='%s']" % (muc.NS_MUC_ADMIN,
1095                                                              muc.NS_MUC_ADMIN)
1096        items = xpath.queryForNodes(query, iq)
1097        self.assertNotIdentical(None, items)
1098        self.assertEquals(entities[0], JID(items[0].getAttribute('jid')))
1099        self.assertEquals('admin', items[0].getAttribute('affiliation'))
1100        self.assertEquals(entities[1], JID(items[1].getAttribute('jid')))
1101        self.assertEquals('admin', items[1].getAttribute('affiliation'))
1102
1103        # Send a response to have the deferred fire.
1104        response = toResponse(iq, 'result')
1105        self.stub.send(response)
1106        return d
1107
1108
1109    def test_grantVoice(self):
1110        """
1111        Granting voice sends request to set role to 'participant'.
1112        """
1113        nick = 'TroubleMaker'
1114        def cb(give_voice):
1115            self.assertTrue(give_voice, 'Did not give voice user')
1116
1117        d = self.protocol.grantVoice(self.roomJID, nick,
1118                                     sender=self.userJID)
1119        d.addCallback(cb)
1120
1121        iq = self.stub.output[-1]
1122
1123        query = (u"/iq[@type='set' and @to='%s']/query/item"
1124                     "[@role='participant']") % self.roomJID
1125        self.assertTrue(xpath.matches(query, iq), 'Wrong voice stanza')
1126
1127        response = toResponse(iq, 'result')
1128        self.stub.send(response)
1129        return d
1130
1131
1132    def test_revokeVoice(self):
1133        """
1134        Revoking voice sends request to set role to 'visitor'.
1135        """
1136        nick = 'TroubleMaker'
1137
1138        d = self.protocol.revokeVoice(self.roomJID, nick,
1139                                      reason="Trouble maker",
1140                                      sender=self.userJID)
1141
1142        iq = self.stub.output[-1]
1143
1144        query = (u"/iq[@type='set' and @to='%s']/query/item"
1145                     "[@role='visitor']") % self.roomJID
1146        self.assertTrue(xpath.matches(query, iq), 'Wrong voice stanza')
1147
1148        response = toResponse(iq, 'result')
1149        self.stub.send(response)
1150        return d
1151
1152
1153    def test_grantModerator(self):
1154        """
1155        Granting moderator privileges sends request to set role to 'moderator'.
1156        """
1157        nick = 'TroubleMaker'
1158
1159        d = self.protocol.grantModerator(self.roomJID, nick,
1160                                         sender=self.userJID)
1161
1162        iq = self.stub.output[-1]
1163
1164        query = (u"/iq[@type='set' and @to='%s']/query/item"
1165                     "[@role='moderator']") % self.roomJID
1166        self.assertTrue(xpath.matches(query, iq), 'Wrong voice stanza')
1167
1168        response = toResponse(iq, 'result')
1169        self.stub.send(response)
1170        return d
1171
1172
1173    def test_ban(self):
1174        """
1175        Ban an entity in a room.
1176        """
1177        banned = JID('ban@jabber.org/TroubleMaker')
1178
1179        def cb(banned):
1180            self.assertTrue(banned, 'Did not ban user')
1181
1182        d = self.protocol.ban(self.roomJID, banned, reason='Spam',
1183                              sender=self.userJID)
1184        d.addCallback(cb)
1185
1186        iq = self.stub.output[-1]
1187
1188        self.assertTrue(xpath.matches(
1189                u"/iq[@type='set' and @to='%s']/query/item"
1190                    "[@affiliation='outcast']" % (self.roomJID,),
1191                iq),
1192            'Wrong ban stanza')
1193
1194        response = toResponse(iq, 'result')
1195        self.stub.send(response)
1196
1197        return d
1198
1199
1200    def test_kick(self):
1201        """
1202        Kick an entity from a room.
1203        """
1204        nick = 'TroubleMaker'
1205
1206        def cb(kicked):
1207            self.assertTrue(kicked, 'Did not kick user')
1208
1209        d = self.protocol.kick(self.roomJID, nick, reason='Spam',
1210                               sender=self.userJID)
1211        d.addCallback(cb)
1212
1213        iq = self.stub.output[-1]
1214
1215        self.assertTrue(xpath.matches(
1216                u"/iq[@type='set' and @to='%s']/query/item"
1217                    "[@role='none']" % (self.roomJID,),
1218                iq),
1219            'Wrong kick stanza')
1220
1221        response = toResponse(iq, 'result')
1222        self.stub.send(response)
1223
1224        return d
1225
1226
1227    def test_destroy(self):
1228        """
1229        Destroy a room.
1230        """
1231        d = self.protocol.destroy(self.occupantJID, reason='Time to leave',
1232                                  alternate=JID('other@%s' % self.service),
1233                                  password='secret')
1234
1235        iq = self.stub.output[-1]
1236
1237        query = ("/iq/query[@xmlns='%s']/destroy[@xmlns='%s']" %
1238                 (muc.NS_MUC_OWNER, muc.NS_MUC_OWNER))
1239
1240        nodes = xpath.queryForNodes(query, iq)
1241        self.assertNotIdentical(None, nodes, 'Bad configure request')
1242        destroy = nodes[0]
1243        self.assertEquals('Time to leave', unicode(destroy.reason))
1244
1245        response = toResponse(iq, 'result')
1246        self.stub.send(response)
1247        return d
1248
1249
1250
1251class MUCClientTest(unittest.TestCase):
1252    """
1253    Tests for C{muc.MUCClient}.
1254    """
1255
1256    def setUp(self):
1257        self.clock = task.Clock()
1258        self.sessionManager = TestableStreamManager(reactor=self.clock)
1259        self.stub = self.sessionManager.stub
1260        self.protocol = muc.MUCClient(reactor=self.clock)
1261        self.protocol.setHandlerParent(self.sessionManager)
1262
1263        self.roomIdentifier = 'test'
1264        self.service  = 'conference.example.org'
1265        self.nick = 'Nick'
1266
1267        self.occupantJID = JID(tuple=(self.roomIdentifier,
1268                                      self.service,
1269                                      self.nick))
1270        self.roomJID = self.occupantJID.userhostJID()
1271        self.userJID = JID('test@example.org/Testing')
1272
1273
1274    def _createRoom(self):
1275        """
1276        A helper method to create a test room.
1277        """
1278        # create a room
1279        room = muc.Room(self.roomJID, self.nick)
1280        self.protocol._addRoom(room)
1281        return room
1282
1283
1284    def test_interface(self):
1285        """
1286        Do instances of L{muc.MUCClient} provide L{iwokkel.IMUCClient}?
1287        """
1288        verify.verifyObject(iwokkel.IMUCClient, self.protocol)
1289
1290
1291    def _testPresence(self, sender='', available=True):
1292        """
1293        Helper for presence tests.
1294        """
1295        def userUpdatedStatus(room, user, show, status):
1296            self.fail("Unexpected call to userUpdatedStatus")
1297
1298        def userJoinedRoom(room, user):
1299            self.fail("Unexpected call to userJoinedRoom")
1300
1301        if available:
1302            available = ""
1303        else:
1304            available = " type='unavailable'"
1305
1306        if sender:
1307            sender = u" from='%s'" % sender
1308
1309        xml = u"""
1310            <presence to='%s'%s%s>
1311              <x xmlns='http://jabber.org/protocol/muc#user'>
1312                <item affiliation='member' role='participant'/>
1313              </x>
1314            </presence>
1315        """ % (self.userJID, sender, available)
1316
1317        self.protocol.userUpdatedStatus = userUpdatedStatus
1318        self.protocol.userJoinedRoom = userJoinedRoom
1319        self.stub.send(parseXml(xml))
1320
1321
1322    def test_availableReceivedEmptySender(self):
1323        """
1324        Availability presence from empty sender is ignored.
1325        """
1326        self._testPresence(sender='')
1327
1328
1329    def test_availableReceivedNotInRoom(self):
1330        """
1331        Availability presence from unknown entities is ignored.
1332        """
1333        otherOccupantJID = JID(self.occupantJID.userhost()+'/OtherNick')
1334        self._testPresence(sender=otherOccupantJID)
1335
1336
1337    def test_unavailableReceivedEmptySender(self):
1338        """
1339        Availability presence from empty sender is ignored.
1340        """
1341        self._testPresence(sender='', available=False)
1342
1343
1344    def test_unavailableReceivedNotInRoom(self):
1345        """
1346        Availability presence from unknown entities is ignored.
1347        """
1348        otherOccupantJID = JID(self.occupantJID.userhost()+'/OtherNick')
1349        self._testPresence(sender=otherOccupantJID, available=False)
1350
1351
1352    def test_unavailableReceivedNotInRoster(self):
1353        """
1354        Availability presence from unknown entities is ignored.
1355        """
1356        room = self._createRoom()
1357        user = muc.User(self.nick)
1358        room.addUser(user)
1359        otherOccupantJID = JID(self.occupantJID.userhost()+'/OtherNick')
1360        self._testPresence(sender=otherOccupantJID, available=False)
1361
1362
1363    def test_userJoinedRoom(self):
1364        """
1365        Joins by others to a room we're in are passed to userJoinedRoom
1366        """
1367        xml = """
1368            <presence to='%s' from='%s'>
1369              <x xmlns='http://jabber.org/protocol/muc#user'>
1370                <item affiliation='member' role='participant'/>
1371              </x>
1372            </presence>
1373        """ % (self.userJID.full(), self.occupantJID.full())
1374
1375        # create a room
1376        self._createRoom()
1377
1378        def userJoinedRoom(room, user):
1379            self.assertEquals(self.roomJID, room.roomJID,
1380                              'Wrong room name')
1381            self.assertTrue(room.inRoster(user), 'User not in roster')
1382
1383        d, self.protocol.userJoinedRoom = calledAsync(userJoinedRoom)
1384        self.stub.send(parseXml(xml))
1385        return d
1386
1387
1388    def test_receivedSubject(self):
1389        """
1390        Subject received from a room we're in are passed to receivedSubject.
1391        """
1392        xml = u"""
1393            <message to='%s' from='%s' type='groupchat'>
1394              <subject>test</subject>
1395            </message>
1396        """ % (self.userJID, self.occupantJID)
1397
1398        self._createRoom()
1399
1400        # add user to room
1401        user = muc.User(self.nick)
1402        room = self.protocol._getRoom(self.roomJID)
1403        room.addUser(user)
1404
1405        def receivedSubject(room, user, subject):
1406            self.assertEquals('test', subject, "Wrong group chat message")
1407            self.assertEquals(self.roomJID, room.roomJID,
1408                              'Wrong room name')
1409            self.assertEquals(self.nick, user.nick)
1410
1411        d, self.protocol.receivedSubject = calledAsync(receivedSubject)
1412        self.stub.send(parseXml(xml))
1413        return d
1414
1415
1416    def test_receivedSubjectNotOverridden(self):
1417        """
1418        Not overriding receivedSubject is ok.
1419        """
1420        xml = u"""
1421            <message to='%s' from='%s' type='groupchat'>
1422              <subject>test</subject>
1423            </message>
1424        """ % (self.userJID, self.occupantJID)
1425
1426        self._createRoom()
1427        self.stub.send(parseXml(xml))
1428
1429
1430    def test_receivedGroupChat(self):
1431        """
1432        Messages received from a room we're in are passed to receivedGroupChat.
1433        """
1434        xml = u"""
1435            <message to='test@test.com' from='%s' type='groupchat'>
1436              <body>test</body>
1437            </message>
1438        """ % (self.occupantJID)
1439
1440        self._createRoom()
1441
1442        def receivedGroupChat(room, user, message):
1443            self.assertEquals('test', message.body, "Wrong group chat message")
1444            self.assertEquals(self.roomJID, room.roomJID,
1445                              'Wrong room name')
1446
1447        d, self.protocol.receivedGroupChat = calledAsync(receivedGroupChat)
1448        self.stub.send(parseXml(xml))
1449        return d
1450
1451
1452    def test_receivedGroupChatRoom(self):
1453        """
1454        Messages received from the room itself have C{user} set to C{None}.
1455        """
1456        xml = u"""
1457            <message to='test@test.com' from='%s' type='groupchat'>
1458              <body>test</body>
1459            </message>
1460        """ % (self.roomJID)
1461
1462        self._createRoom()
1463
1464        def receivedGroupChat(room, user, message):
1465            self.assertIdentical(None, user)
1466
1467        d, self.protocol.receivedGroupChat = calledAsync(receivedGroupChat)
1468        self.stub.send(parseXml(xml))
1469        return d
1470
1471
1472    def test_receivedGroupChatNotInRoom(self):
1473        """
1474        Messages received from a room we're not in are ignored.
1475        """
1476        xml = u"""
1477            <message to='test@test.com' from='%s' type='groupchat'>
1478              <body>test</body>
1479            </message>
1480        """ % (self.occupantJID)
1481
1482        def receivedGroupChat(room, user, message):
1483            self.fail("Unexpected call to receivedGroupChat")
1484
1485        self.protocol.receivedGroupChat = receivedGroupChat
1486        self.stub.send(parseXml(xml))
1487
1488
1489    def test_receivedGroupChatNotOverridden(self):
1490        """
1491        Not overriding receivedGroupChat is ok.
1492        """
1493        xml = u"""
1494            <message to='test@test.com' from='%s' type='groupchat'>
1495              <body>test</body>
1496            </message>
1497        """ % (self.occupantJID)
1498
1499        self._createRoom()
1500        self.stub.send(parseXml(xml))
1501
1502
1503    def test_join(self):
1504        """
1505        Joining a room waits for confirmation, deferred fires room.
1506        """
1507
1508        def cb(room):
1509            self.assertEqual(self.roomJID, room.roomJID)
1510            self.assertTrue('joined', room.state)
1511
1512        d = self.protocol.join(self.roomJID, self.nick)
1513        d.addCallback(cb)
1514
1515        # send back user presence, they joined
1516        xml = """
1517            <presence from='%s@%s/%s'>
1518              <x xmlns='http://jabber.org/protocol/muc#user'>
1519                <item affiliation='member' role='participant'/>
1520              </x>
1521            </presence>
1522        """ % (self.roomIdentifier, self.service, self.nick)
1523        self.stub.send(parseXml(xml))
1524        return d
1525
1526
1527    def test_joinForbidden(self):
1528        """
1529        A forbidden error in response to a join errbacks with L{StanzaError}.
1530        """
1531
1532        def cb(error):
1533            self.assertEquals('forbidden', error.condition,
1534                              'Wrong muc condition')
1535            self.assertIdentical(None, self.protocol._getRoom(self.roomJID))
1536
1537
1538        d = self.protocol.join(self.roomJID, self.nick)
1539        self.assertFailure(d, StanzaError)
1540        d.addCallback(cb)
1541
1542        # send back error, forbidden
1543        xml = u"""
1544            <presence from='%s' type='error'>
1545              <error type='auth'>
1546                <forbidden xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
1547              </error>
1548            </presence>
1549        """ % (self.occupantJID)
1550        self.stub.send(parseXml(xml))
1551        return d
1552
1553
1554    def test_userLeftRoom(self):
1555        """
1556        Unavailable presence from a participant removes it from the room.
1557        """
1558
1559        xml = u"""
1560            <presence to='%s' from='%s' type='unavailable'/>
1561        """ % (self.userJID, self.occupantJID)
1562
1563        # create a room
1564        self._createRoom()
1565
1566        # add user to room
1567        user = muc.User(self.nick)
1568        room = self.protocol._getRoom(self.roomJID)
1569        room.addUser(user)
1570
1571        def userLeftRoom(room, user):
1572            self.assertEquals(self.roomJID, room.roomJID,
1573                              'Wrong room name')
1574            self.assertFalse(room.inRoster(user), 'User in roster')
1575
1576        d, self.protocol.userLeftRoom = calledAsync(userLeftRoom)
1577        self.stub.send(parseXml(xml))
1578        return d
1579
1580
1581    def test_receivedHistory(self):
1582        """
1583        Receiving history on room join.
1584        """
1585        xml = u"""
1586            <message to='test@test.com' from='%s' type='groupchat'>
1587              <body>test</body>
1588              <delay xmlns='urn:xmpp:delay' stamp="2002-10-13T23:58:37Z"
1589                                            from="%s"/>
1590            </message>
1591        """ % (self.occupantJID, self.userJID)
1592
1593        self._createRoom()
1594
1595
1596        def receivedHistory(room, user, message):
1597            self.assertEquals('test', message.body, "wrong message body")
1598            stamp = datetime(2002, 10, 13, 23, 58, 37, tzinfo=tzutc())
1599            self.assertEquals(stamp, message.delay.stamp,
1600                             'Does not have a history stamp')
1601
1602        d, self.protocol.receivedHistory = calledAsync(receivedHistory)
1603        self.stub.send(parseXml(xml))
1604        return d
1605
1606
1607    def test_receivedHistoryNotOverridden(self):
1608        """
1609        Not overriding receivedHistory is ok.
1610        """
1611        xml = u"""
1612            <message to='test@test.com' from='%s' type='groupchat'>
1613              <body>test</body>
1614              <delay xmlns='urn:xmpp:delay' stamp="2002-10-13T23:58:37Z"
1615                                            from="%s"/>
1616            </message>
1617        """ % (self.occupantJID, self.userJID)
1618
1619        self._createRoom()
1620        self.stub.send(parseXml(xml))
1621
1622
1623    def test_nickConflict(self):
1624        """
1625        If the server finds the new nick in conflict, the errback is called.
1626        """
1627
1628        def cb(failure, room):
1629            user = room.getUser(otherNick)
1630            self.assertNotIdentical(None, user)
1631            self.assertEqual(otherJID, user.entity)
1632
1633        def joined(room):
1634            d = self.protocol.nick(room.roomJID, otherNick)
1635            self.assertFailure(d, StanzaError)
1636            d.addCallback(cb, room)
1637
1638        otherJID = JID('other@example.org/Home')
1639        otherNick = 'otherNick'
1640
1641        d = self.protocol.join(self.roomJID, self.nick)
1642        d.addCallback(joined)
1643
1644        # Send back other partipant's presence.
1645        xml = u"""
1646            <presence from='%s/%s'>
1647              <x xmlns='http://jabber.org/protocol/muc#user'>
1648                <item affiliation='member' role='participant' jid='%s'/>
1649              </x>
1650            </presence>
1651        """ % (self.roomJID, otherNick, otherJID)
1652        self.stub.send(parseXml(xml))
1653
1654        # send back user presence, they joined
1655        xml = u"""
1656            <presence from='%s/%s'>
1657              <x xmlns='http://jabber.org/protocol/muc#user'>
1658                <item affiliation='member' role='participant'/>
1659              </x>
1660            </presence>
1661        """ % (self.roomJID, self.nick)
1662        self.stub.send(parseXml(xml))
1663
1664        # send back error presence, nick conflicted
1665        xml = u"""
1666            <presence from='%s/%s' type='error'>
1667                <x xmlns='http://jabber.org/protocol/muc'/>
1668                <error type='cancel'>
1669                  <conflict xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
1670                </error>
1671            </presence>
1672        """ % (self.roomJID, otherNick)
1673        self.stub.send(parseXml(xml))
1674        return d
1675
1676
1677    def test_nick(self):
1678        """
1679        Send a nick change to the server.
1680        """
1681        newNick = 'newNick'
1682
1683        room = self._createRoom()
1684
1685        def joined(room):
1686            self.assertEqual(self.roomJID, room.roomJID)
1687            self.assertEqual(newNick, room.nick)
1688            user = room.getUser(newNick)
1689            self.assertNotIdentical(None, user)
1690            self.assertEqual(newNick, user.nick)
1691
1692        d = self.protocol.nick(self.roomJID, newNick)
1693        d.addCallback(joined)
1694
1695        # Nick should not have been changed, yet, as we haven't gotten
1696        # confirmation, yet.
1697
1698        self.assertEquals(self.nick, room.nick)
1699
1700        # send back user presence, nick changed
1701        xml = u"""
1702            <presence from='%s/%s'>
1703              <x xmlns='http://jabber.org/protocol/muc#user'>
1704                <item affiliation='member' role='participant'/>
1705              </x>
1706            </presence>
1707        """ % (self.roomJID, newNick)
1708
1709        self.stub.send(parseXml(xml))
1710        return d
1711
1712
1713    def test_leave(self):
1714        """
1715        Client leaves a room
1716        """
1717        def joined(_):
1718            return self.protocol.leave(self.roomJID)
1719
1720        def left(_):
1721            self.assertIdentical(None, self.protocol._getRoom(self.roomJID))
1722
1723        # Join the room
1724        d = self.protocol.join(self.roomJID, self.nick)
1725        d.addCallback(joined)
1726        d.addCallback(left)
1727
1728        # Receive presence back from the room: joined.
1729        xml = u"""
1730            <presence to='%s' from='%s'/>
1731        """ % (self.userJID, self.occupantJID)
1732        self.stub.send(parseXml(xml))
1733
1734        # Receive presence back from the room: left.
1735        xml = u"""
1736            <presence to='%s' from='%s' type='unavailable'/>
1737        """ % (self.userJID, self.occupantJID)
1738        self.stub.send(parseXml(xml))
1739
1740        return d
1741
1742
1743    def test_status(self):
1744        """
1745        Change status
1746        """
1747        def joined(_):
1748            d = self.protocol.status(self.roomJID, 'xa', 'testing MUC')
1749            d.addCallback(statusChanged)
1750            return d
1751
1752        def statusChanged(room):
1753            self.assertEqual(self.roomJID, room.roomJID)
1754            user = room.getUser(self.nick)
1755            self.assertNotIdentical(None, user, 'User not found')
1756            self.assertEqual('testing MUC', user.status, 'Wrong status')
1757            self.assertEqual('xa', user.show, 'Wrong show')
1758
1759        # Join the room
1760        d = self.protocol.join(self.roomJID, self.nick)
1761        d.addCallback(joined)
1762
1763        # Receive presence back from the room: joined.
1764        xml = u"""
1765            <presence to='%s' from='%s'/>
1766        """ % (self.userJID, self.occupantJID)
1767        self.stub.send(parseXml(xml))
1768
1769        # send back user presence, status changed
1770        xml = u"""
1771            <presence from='%s'>
1772              <x xmlns='http://jabber.org/protocol/muc#user'>
1773                <item affiliation='member' role='participant'/>
1774              </x>
1775              <show>xa</show>
1776              <status>testing MUC</status>
1777            </presence>
1778        """ % self.occupantJID
1779
1780        self.stub.send(parseXml(xml))
1781        return d
1782
1783
1784    def test_destroy(self):
1785        """
1786        Destroy a room.
1787        """
1788        def destroyed(_):
1789            self.assertIdentical(None, self.protocol._getRoom(self.roomJID))
1790
1791        d = self.protocol.destroy(self.occupantJID, reason='Time to leave',
1792                                  alternate=JID('other@%s' % self.service),
1793                                  password='secret')
1794        d.addCallback(destroyed)
1795
1796        iq = self.stub.output[-1]
1797        response = toResponse(iq, 'result')
1798        self.stub.send(response)
1799        return d
Note: See TracBrowser for help on using the repository browser.