source: wokkel/test/test_subprotocols.py @ 100:e7199edcb6f5

Last change on this file since 100:e7199edcb6f5 was 100:e7199edcb6f5, checked in by Ralph Meijer <ralphm@…>, 9 years ago

Add iq request (set/get) tracking for StreamManager?.

This adds a new request method to StreamManager? to serialize an iq get or iq
set request object (using toElement), send it over the stream (or queue it
until the next time a stream is initialized) and track the response stanza by
returning a Deferred.

Requests may have a timeout, so that the deferred errbacks after the timeout
(counting from the moment of calling request) has expired.

This implementation is similar to the response tracking done in
twisted.words.protocols.jabber.xmlstream.IQ, but slighly different:

  • Requests can be queued until the connection is initialized, even before a XmlStream? object is available. I.e. it is tied to the StreamManager?, not the stream.
  • Response timeout starts counting from the moment of calling request, not from the moment request is sent over the wire.
  • Property exe set to *
File size: 29.4 KB
Line 
1# Copyright (c) Ralph Meijer.
2# See LICENSE for details.
3
4"""
5Tests for L{wokkel.subprotocols}
6"""
7
8from zope.interface.verify import verifyObject
9
10from twisted.trial import unittest
11from twisted.test import proto_helpers
12from twisted.internet import defer, task
13from twisted.internet.error import ConnectionDone
14from twisted.python import failure
15from twisted.words.xish import domish
16from twisted.words.protocols.jabber import error, xmlstream
17
18from wokkel import generic, iwokkel, subprotocols
19
20class DummyFactory(object):
21    """
22    Dummy XmlStream factory that only registers bootstrap observers.
23    """
24    def __init__(self):
25        self.callbacks = {}
26
27
28    def addBootstrap(self, event, callback):
29        self.callbacks[event] = callback
30
31
32
33class DummyXMPPHandler(subprotocols.XMPPHandler):
34    """
35    Dummy XMPP subprotocol handler to count the methods are called on it.
36    """
37    def __init__(self):
38        self.doneMade = 0
39        self.doneInitialized = 0
40        self.doneLost = 0
41
42
43    def makeConnection(self, xs):
44        self.connectionMade()
45
46
47    def connectionMade(self):
48        self.doneMade += 1
49
50
51    def connectionInitialized(self):
52        self.doneInitialized += 1
53
54
55    def connectionLost(self, reason):
56        self.doneLost += 1
57
58
59
60class FailureReasonXMPPHandler(subprotocols.XMPPHandler):
61    """
62    Dummy handler specifically for failure Reason tests.
63    """
64    def __init__(self):
65        self.gotFailureReason = False
66
67
68    def connectionLost(self, reason):
69        if isinstance(reason, failure.Failure):
70            self.gotFailureReason = True
71
72
73
74class XMPPHandlerTest(unittest.TestCase):
75    """
76    Tests for L{subprotocols.XMPPHandler}.
77    """
78
79    def test_interface(self):
80        """
81        L{xmlstream.XMPPHandler} implements L{iwokkel.IXMPPHandler}.
82        """
83        verifyObject(iwokkel.IXMPPHandler, subprotocols.XMPPHandler())
84
85
86    def test_send(self):
87        """
88        Test that data is passed on for sending by the stream manager.
89        """
90        class DummyStreamManager(object):
91            def __init__(self):
92                self.outlist = []
93
94            def send(self, data):
95                self.outlist.append(data)
96
97        handler = subprotocols.XMPPHandler()
98        handler.parent = DummyStreamManager()
99        handler.send('<presence/>')
100        self.assertEquals(['<presence/>'], handler.parent.outlist)
101
102
103    def test_makeConnection(self):
104        """
105        Test that makeConnection saves the XML stream and calls connectionMade.
106        """
107        class TestXMPPHandler(subprotocols.XMPPHandler):
108            def connectionMade(self):
109                self.doneMade = True
110
111        handler = TestXMPPHandler()
112        xs = xmlstream.XmlStream(xmlstream.Authenticator())
113        handler.makeConnection(xs)
114        self.assertTrue(handler.doneMade)
115        self.assertIdentical(xs, handler.xmlstream)
116
117
118    def test_connectionLost(self):
119        """
120        Test that connectionLost forgets the XML stream.
121        """
122        handler = subprotocols.XMPPHandler()
123        xs = xmlstream.XmlStream(xmlstream.Authenticator())
124        handler.makeConnection(xs)
125        handler.connectionLost(Exception())
126        self.assertIdentical(None, handler.xmlstream)
127
128
129
130class XMPPHandlerCollectionTest(unittest.TestCase):
131    """
132    Tests for L{subprotocols.XMPPHandlerCollection}.
133    """
134
135    def setUp(self):
136        self.collection = subprotocols.XMPPHandlerCollection()
137
138
139    def test_interface(self):
140        """
141        L{subprotocols.StreamManager} implements L{iwokkel.IXMPPHandlerCollection}.
142        """
143        verifyObject(iwokkel.IXMPPHandlerCollection, self.collection)
144
145
146    def test_addHandler(self):
147        """
148        Test the addition of a protocol handler.
149        """
150        handler = DummyXMPPHandler()
151        handler.setHandlerParent(self.collection)
152        self.assertIn(handler, self.collection)
153        self.assertIdentical(self.collection, handler.parent)
154
155
156    def test_removeHandler(self):
157        """
158        Test removal of a protocol handler.
159        """
160        handler = DummyXMPPHandler()
161        handler.setHandlerParent(self.collection)
162        handler.disownHandlerParent(self.collection)
163        self.assertNotIn(handler, self.collection)
164        self.assertIdentical(None, handler.parent)
165
166
167
168class IQGetStanza(generic.Stanza):
169    stanzaKind = 'iq'
170    stanzaType = 'get'
171    stanzaID = 'test'
172
173
174
175class StreamManagerTest(unittest.TestCase):
176    """
177    Tests for L{subprotocols.StreamManager}.
178    """
179
180    def setUp(self):
181        factory = xmlstream.XmlStreamFactory(xmlstream.Authenticator())
182        self.clock = task.Clock()
183        self.streamManager = subprotocols.StreamManager(factory, self.clock)
184        self.xmlstream = factory.buildProtocol(None)
185        self.transport = proto_helpers.StringTransport()
186        self.xmlstream.transport = self.transport
187
188        self.request = IQGetStanza()
189
190    def _streamStarted(self):
191        """
192        Bring the test stream to the initialized state.
193        """
194        self.xmlstream.connectionMade()
195        self.xmlstream.dataReceived(
196                "<stream:stream xmlns='jabber:client' "
197                    "xmlns:stream='http://etherx.jabber.org/streams' "
198                    "from='example.com' id='12345'>")
199        self.xmlstream.dispatch(self.xmlstream, "//event/stream/authd")
200
201
202    def test_basic(self):
203        """
204        Test correct initialization and setup of factory observers.
205        """
206        factory = DummyFactory()
207        sm = subprotocols.StreamManager(factory)
208        self.assertIdentical(None, sm.xmlstream)
209        self.assertEquals([], sm.handlers)
210        self.assertEquals(sm._connected,
211                          sm.factory.callbacks['//event/stream/connected'])
212        self.assertEquals(sm._authd,
213                          sm.factory.callbacks['//event/stream/authd'])
214        self.assertEquals(sm._disconnected,
215                          sm.factory.callbacks['//event/stream/end'])
216        self.assertEquals(sm.initializationFailed,
217                          sm.factory.callbacks['//event/xmpp/initfailed'])
218
219
220    def test_connected(self):
221        """
222        Test that protocol handlers have their connectionMade method called
223        when the XML stream is connected.
224        """
225        sm = self.streamManager
226        handler = DummyXMPPHandler()
227        handler.setHandlerParent(sm)
228        xs = xmlstream.XmlStream(xmlstream.Authenticator())
229        sm._connected(xs)
230        self.assertEquals(1, handler.doneMade)
231        self.assertEquals(0, handler.doneInitialized)
232        self.assertEquals(0, handler.doneLost)
233
234
235    def test_connectedLogTrafficFalse(self):
236        """
237        Test raw data functions unset when logTraffic is set to False.
238        """
239        sm = self.streamManager
240        handler = DummyXMPPHandler()
241        handler.setHandlerParent(sm)
242        xs = xmlstream.XmlStream(xmlstream.Authenticator())
243        sm._connected(xs)
244        self.assertIdentical(None, xs.rawDataInFn)
245        self.assertIdentical(None, xs.rawDataOutFn)
246
247
248    def test_connectedLogTrafficTrue(self):
249        """
250        Test raw data functions set when logTraffic is set to True.
251        """
252        sm = self.streamManager
253        sm.logTraffic = True
254        handler = DummyXMPPHandler()
255        handler.setHandlerParent(sm)
256        xs = xmlstream.XmlStream(xmlstream.Authenticator())
257        sm._connected(xs)
258        self.assertNotIdentical(None, xs.rawDataInFn)
259        self.assertNotIdentical(None, xs.rawDataOutFn)
260
261
262    def test_authd(self):
263        """
264        Test that protocol handlers have their connectionInitialized method
265        called when the XML stream is initialized.
266        """
267        sm = self.streamManager
268        handler = DummyXMPPHandler()
269        handler.setHandlerParent(sm)
270        xs = xmlstream.XmlStream(xmlstream.Authenticator())
271        sm._authd(xs)
272        self.assertEquals(0, handler.doneMade)
273        self.assertEquals(1, handler.doneInitialized)
274        self.assertEquals(0, handler.doneLost)
275
276
277    def test_disconnected(self):
278        """
279        Protocol handlers have connectionLost called on stream disconnect.
280        """
281        sm = self.streamManager
282        handler = DummyXMPPHandler()
283        handler.setHandlerParent(sm)
284        sm._disconnected(None)
285        self.assertEquals(0, handler.doneMade)
286        self.assertEquals(0, handler.doneInitialized)
287        self.assertEquals(1, handler.doneLost)
288
289
290    def test_disconnectedReason(self):
291        """
292        A L{STREAM_END_EVENT} results in L{StreamManager} firing the handlers
293        L{connectionLost} methods, passing a L{failure.Failure} reason.
294        """
295        sm = self.streamManager
296        handler = FailureReasonXMPPHandler()
297        handler.setHandlerParent(sm)
298        xs = xmlstream.XmlStream(xmlstream.Authenticator())
299        sm._disconnected(failure.Failure(Exception("no reason")))
300        self.assertEquals(True, handler.gotFailureReason)
301
302
303    def test_addHandler(self):
304        """
305        Test the addition of a protocol handler while not connected.
306        """
307        sm = self.streamManager
308        handler = DummyXMPPHandler()
309        handler.setHandlerParent(sm)
310
311        self.assertEquals(0, handler.doneMade)
312        self.assertEquals(0, handler.doneInitialized)
313        self.assertEquals(0, handler.doneLost)
314
315
316    def test_addHandlerConnected(self):
317        """
318        Adding a handler when connected doesn't call connectionInitialized.
319        """
320        sm = self.streamManager
321        xs = xmlstream.XmlStream(xmlstream.Authenticator())
322        sm._connected(xs)
323        handler = DummyXMPPHandler()
324        handler.setHandlerParent(sm)
325
326        self.assertEquals(1, handler.doneMade)
327        self.assertEquals(0, handler.doneInitialized)
328        self.assertEquals(0, handler.doneLost)
329
330
331    def test_addHandlerConnectedNested(self):
332        """
333        Adding a handler in connectionMade doesn't cause 2nd call.
334        """
335        class NestingHandler(DummyXMPPHandler):
336            nestedHandler = None
337
338            def connectionMade(self):
339                DummyXMPPHandler.connectionMade(self)
340                self.nestedHandler = DummyXMPPHandler()
341                self.nestedHandler.setHandlerParent(self.parent)
342
343        sm = self.streamManager
344        xs = xmlstream.XmlStream(xmlstream.Authenticator())
345        handler = NestingHandler()
346        handler.setHandlerParent(sm)
347        sm._connected(xs)
348
349        self.assertEquals(1, handler.doneMade)
350        self.assertEquals(0, handler.doneInitialized)
351        self.assertEquals(0, handler.doneLost)
352
353        self.assertEquals(1, handler.nestedHandler.doneMade)
354        self.assertEquals(0, handler.nestedHandler.doneInitialized)
355        self.assertEquals(0, handler.nestedHandler.doneLost)
356
357
358
359    def test_addHandlerInitialized(self):
360        """
361        Test the addition of a protocol handler after the stream
362        have been initialized.
363
364        Make sure that the handler will have the connected stream
365        passed via C{makeConnection} and have C{connectionInitialized}
366        called.
367        """
368        sm = self.streamManager
369        xs = xmlstream.XmlStream(xmlstream.Authenticator())
370        sm._connected(xs)
371        sm._authd(xs)
372        handler = DummyXMPPHandler()
373        handler.setHandlerParent(sm)
374
375        self.assertEquals(1, handler.doneMade)
376        self.assertEquals(1, handler.doneInitialized)
377        self.assertEquals(0, handler.doneLost)
378
379
380    def test_addHandlerInitializedNested(self):
381        """
382        Adding a handler in connectionInitialized doesn't cause 2nd call.
383        """
384        class NestingHandler(DummyXMPPHandler):
385            nestedHandler = None
386
387            def connectionInitialized(self):
388                DummyXMPPHandler.connectionInitialized(self)
389                self.nestedHandler = DummyXMPPHandler()
390                self.nestedHandler.setHandlerParent(self.parent)
391
392        sm = self.streamManager
393        xs = xmlstream.XmlStream(xmlstream.Authenticator())
394        handler = NestingHandler()
395        handler.setHandlerParent(sm)
396        sm._connected(xs)
397        sm._authd(xs)
398
399        self.assertEquals(1, handler.doneMade)
400        self.assertEquals(1, handler.doneInitialized)
401        self.assertEquals(0, handler.doneLost)
402
403        self.assertEquals(1, handler.nestedHandler.doneMade)
404        self.assertEquals(1, handler.nestedHandler.doneInitialized)
405        self.assertEquals(0, handler.nestedHandler.doneLost)
406
407
408    def test_addHandlerConnectionLostNested(self):
409        """
410        Adding a handler in connectionLost doesn't call connectionLost there.
411        """
412        class NestingHandler(DummyXMPPHandler):
413            nestedHandler = None
414
415            def connectionLost(self, reason):
416                DummyXMPPHandler.connectionLost(self, reason)
417                self.nestedHandler = DummyXMPPHandler()
418                self.nestedHandler.setHandlerParent(self.parent)
419
420        sm = self.streamManager
421        xs = xmlstream.XmlStream(xmlstream.Authenticator())
422        handler = NestingHandler()
423        handler.setHandlerParent(sm)
424        sm._connected(xs)
425        sm._authd(xs)
426        sm._disconnected(xs)
427
428        self.assertEquals(1, handler.doneMade)
429        self.assertEquals(1, handler.doneInitialized)
430        self.assertEquals(1, handler.doneLost)
431
432        self.assertEquals(0, handler.nestedHandler.doneMade)
433        self.assertEquals(0, handler.nestedHandler.doneInitialized)
434        self.assertEquals(0, handler.nestedHandler.doneLost)
435
436
437
438    def test_removeHandler(self):
439        """
440        Test removal of protocol handler.
441        """
442        sm = self.streamManager
443        handler = DummyXMPPHandler()
444        handler.setHandlerParent(sm)
445        handler.disownHandlerParent(sm)
446        self.assertNotIn(handler, sm)
447        self.assertIdentical(None, handler.parent)
448
449
450    def test_sendInitialized(self):
451        """
452        Test send when the stream has been initialized.
453
454        The data should be sent directly over the XML stream.
455        """
456        factory = xmlstream.XmlStreamFactory(xmlstream.Authenticator())
457        sm = subprotocols.StreamManager(factory)
458        xs = factory.buildProtocol(None)
459        xs.transport = proto_helpers.StringTransport()
460        xs.connectionMade()
461        xs.dataReceived("<stream:stream xmlns='jabber:client' "
462                        "xmlns:stream='http://etherx.jabber.org/streams' "
463                        "from='example.com' id='12345'>")
464        xs.dispatch(xs, "//event/stream/authd")
465        sm.send("<presence/>")
466        self.assertEquals("<presence/>", xs.transport.value())
467
468
469    def test_sendNotConnected(self):
470        """
471        Test send when there is no established XML stream.
472
473        The data should be cached until an XML stream has been established and
474        initialized.
475        """
476        factory = xmlstream.XmlStreamFactory(xmlstream.Authenticator())
477        sm = subprotocols.StreamManager(factory)
478        handler = DummyXMPPHandler()
479        sm.addHandler(handler)
480
481        xs = factory.buildProtocol(None)
482        xs.transport = proto_helpers.StringTransport()
483        sm.send("<presence/>")
484        self.assertEquals("", xs.transport.value())
485        self.assertEquals("<presence/>", sm._packetQueue[0])
486
487        xs.connectionMade()
488        self.assertEquals("", xs.transport.value())
489        self.assertEquals("<presence/>", sm._packetQueue[0])
490
491        xs.dataReceived("<stream:stream xmlns='jabber:client' "
492                        "xmlns:stream='http://etherx.jabber.org/streams' "
493                        "from='example.com' id='12345'>")
494        xs.dispatch(xs, "//event/stream/authd")
495
496        self.assertEquals("<presence/>", xs.transport.value())
497        self.assertFalse(sm._packetQueue)
498
499
500    def test_sendNotInitialized(self):
501        """
502        Test send when the stream is connected but not yet initialized.
503
504        The data should be cached until the XML stream has been initialized.
505        """
506        factory = xmlstream.XmlStreamFactory(xmlstream.Authenticator())
507        sm = subprotocols.StreamManager(factory)
508        xs = factory.buildProtocol(None)
509        xs.transport = proto_helpers.StringTransport()
510        xs.connectionMade()
511        xs.dataReceived("<stream:stream xmlns='jabber:client' "
512                        "xmlns:stream='http://etherx.jabber.org/streams' "
513                        "from='example.com' id='12345'>")
514        sm.send("<presence/>")
515        self.assertEquals("", xs.transport.value())
516        self.assertEquals("<presence/>", sm._packetQueue[0])
517
518
519    def test_sendDisconnected(self):
520        """
521        Test send after XML stream disconnection.
522
523        The data should be cached until a new XML stream has been established
524        and initialized.
525        """
526        factory = xmlstream.XmlStreamFactory(xmlstream.Authenticator())
527        sm = subprotocols.StreamManager(factory)
528        handler = DummyXMPPHandler()
529        sm.addHandler(handler)
530
531        xs = factory.buildProtocol(None)
532        xs.connectionMade()
533        xs.transport = proto_helpers.StringTransport()
534        xs.connectionLost(None)
535
536        sm.send("<presence/>")
537        self.assertEquals("", xs.transport.value())
538        self.assertEquals("<presence/>", sm._packetQueue[0])
539
540
541    def test_requestSendInitialized(self):
542        """
543        A request is sent out over the wire when the stream is initialized.
544        """
545        self._streamStarted()
546
547        self.streamManager.request(self.request)
548        expected = u"<iq type='get' id='%s'/>" % self.request.stanzaID
549        self.assertEquals(expected, self.transport.value())
550
551
552    def test_requestSendInitializedFreshID(self):
553        """
554        A request without an ID gets a fresh one upon send.
555        """
556        self._streamStarted()
557
558        self.request.stanzaID = None
559        self.streamManager.request(self.request)
560        self.assertNotIdentical(None, self.request.stanzaID)
561        expected = u"<iq type='get' id='%s'/>" % self.request.stanzaID
562        self.assertEquals(expected, self.transport.value())
563
564
565    def test_requestSendNotConnected(self):
566        """
567        A request is queued until a stream is initialized.
568        """
569        handler = DummyXMPPHandler()
570        self.streamManager.addHandler(handler)
571
572        self.streamManager.request(self.request)
573        expected = u"<iq type='get' id='test'/>"
574
575        xs = self.xmlstream
576        self.assertEquals("", xs.transport.value())
577
578        xs.connectionMade()
579        self.assertEquals("", xs.transport.value())
580
581        xs.dataReceived("<stream:stream xmlns='jabber:client' "
582                        "xmlns:stream='http://etherx.jabber.org/streams' "
583                        "from='example.com' id='12345'>")
584        xs.dispatch(xs, "//event/stream/authd")
585
586        self.assertEquals(expected, xs.transport.value())
587        self.assertFalse(self.streamManager._packetQueue)
588
589
590    def test_requestResultResponse(self):
591        """
592        A result response gets the request deferred fired with the response.
593        """
594        def cb(result):
595            self.assertEquals(result['type'], 'result')
596
597        self._streamStarted()
598        d = self.streamManager.request(self.request)
599        d.addCallback(cb)
600
601        xs = self.xmlstream
602        xs.dataReceived("<iq type='result' id='test'/>")
603        return d
604
605
606    def test_requestErrorResponse(self):
607        """
608        An error response gets the request deferred fired with a failure.
609        """
610        self._streamStarted()
611        d = self.streamManager.request(self.request)
612        self.assertFailure(d, error.StanzaError)
613
614        xs = self.xmlstream
615        xs.dataReceived("<iq type='error' id='test'/>")
616        return d
617
618
619    def test_requestNonTrackedResponse(self):
620        """
621        Test that untracked iq responses don't trigger any action.
622
623        Untracked means that the id of the incoming response iq is not
624        in the stream's C{iqDeferreds} dictionary.
625        """
626        # Set up a fallback handler that checks the stanza's handled attribute.
627        # If that is set to True, the iq tracker claims to have handled the
628        # response.
629        dispatched = []
630        def cb(iq):
631            dispatched.append(iq)
632
633        self._streamStarted()
634        self.xmlstream.addObserver("/iq", cb, -1)
635
636        # Receive an untracked iq response
637        self.xmlstream.dataReceived("<iq type='result' id='other'/>")
638        self.assertEquals(1, len(dispatched))
639        self.assertFalse(getattr(dispatched[-1], 'handled', False))
640
641
642    def test_requestCleanup(self):
643        """
644        Test if the deferred associated with an iq request is removed
645        from the list kept in the L{XmlStream} object after it has
646        been fired.
647        """
648        self._streamStarted()
649        d = self.streamManager.request(self.request)
650        xs = self.xmlstream
651        xs.dataReceived("<iq type='result' id='test'/>")
652        self.assertNotIn('test', self.streamManager._iqDeferreds)
653        return d
654
655
656    def test_requestDisconnectCleanup(self):
657        """
658        Test if deferreds for iq's that haven't yet received a response
659        have their errback called on stream disconnect.
660        """
661        d = self.streamManager.request(self.request)
662        xs = self.xmlstream
663        xs.connectionLost(failure.Failure(ConnectionDone()))
664        self.assertFailure(d, ConnectionDone)
665        return d
666
667
668    def test_requestNoModifyingDict(self):
669        """
670        Test to make sure the errbacks cannot cause the iteration of the
671        iqDeferreds to blow up in our face.
672        """
673
674        def eb(failure):
675            d = xmlstream.IQ(self.xmlstream).send()
676            d.addErrback(eb)
677
678        d = self.streamManager.request(self.request)
679        d.addErrback(eb)
680        self.xmlstream.connectionLost(failure.Failure(ConnectionDone()))
681        return d
682
683
684    def test_requestTimingOut(self):
685        """
686        Test that an iq request with a defined timeout times out.
687        """
688        self.request.timeout = 60
689        d = self.streamManager.request(self.request)
690        self.assertFailure(d, xmlstream.TimeoutError)
691
692        self.clock.pump([1, 60])
693        self.assertFalse(self.clock.calls)
694        self.assertFalse(self.streamManager._iqDeferreds)
695        return d
696
697
698    def test_requestNotTimingOut(self):
699        """
700        Test that an iq request with a defined timeout does not time out
701        when a response was received before the timeout period elapsed.
702        """
703        self._streamStarted()
704        self.request.timeout = 60
705        d = self.streamManager.request(self.request)
706        self.clock.callLater(1, self.xmlstream.dataReceived,
707                             "<iq type='result' id='test'/>")
708        self.clock.pump([1, 1])
709        self.assertFalse(self.clock.calls)
710        return d
711
712
713    def test_requestDisconnectTimeoutCancellation(self):
714        """
715        Test if timeouts for iq's that haven't yet received a response
716        are cancelled on stream disconnect.
717        """
718
719        self.request.timeout = 60
720        d = self.streamManager.request(self.request)
721
722        self.xmlstream.connectionLost(failure.Failure(ConnectionDone()))
723        self.assertFailure(d, ConnectionDone)
724        self.assertFalse(self.clock.calls)
725        return d
726
727
728    def test_requestNotIQ(self):
729        """
730        The request stanza must be an iq.
731        """
732        stanza = generic.Stanza()
733        stanza.stanzaKind = 'message'
734
735        d = self.streamManager.request(stanza)
736        self.assertFailure(d, ValueError)
737
738
739    def test_requestNotResult(self):
740        """
741        The request stanza cannot be of type 'result'.
742        """
743        stanza = generic.Stanza()
744        stanza.stanzaKind = 'iq'
745        stanza.stanzaType = 'result'
746
747        d = self.streamManager.request(stanza)
748        self.assertFailure(d, ValueError)
749
750
751    def test_requestNotError(self):
752        """
753        The request stanza cannot be of type 'error'.
754        """
755        stanza = generic.Stanza()
756        stanza.stanzaKind = 'iq'
757        stanza.stanzaType = 'error'
758
759        d = self.streamManager.request(stanza)
760        self.assertFailure(d, ValueError)
761
762
763
764class DummyIQHandler(subprotocols.IQHandlerMixin):
765    iqHandlers = {'/iq[@type="get"]': 'onGet'}
766
767    def __init__(self):
768        self.output = []
769        self.xmlstream = xmlstream.XmlStream(xmlstream.Authenticator())
770        self.xmlstream.send = self.output.append
771
772    def send(self, obj):
773        self.xmlstream.send(obj)
774
775
776class IQHandlerTest(unittest.TestCase):
777
778    def test_match(self):
779        """
780        Test that the matching handler gets called.
781        """
782
783        class Handler(DummyIQHandler):
784            called = False
785
786            def onGet(self, iq):
787                self.called = True
788
789        iq = domish.Element((None, 'iq'))
790        iq['type'] = 'get'
791        iq['id'] = 'r1'
792        handler = Handler()
793        handler.handleRequest(iq)
794        self.assertTrue(handler.called)
795
796    def test_noMatch(self):
797        """
798        Test that the matching handler gets called.
799        """
800
801        class Handler(DummyIQHandler):
802            called = False
803
804            def onGet(self, iq):
805                self.called = True
806
807        iq = domish.Element((None, 'iq'))
808        iq['type'] = 'set'
809        iq['id'] = 'r1'
810        handler = Handler()
811        handler.handleRequest(iq)
812        self.assertFalse(handler.called)
813
814    def test_success(self):
815        """
816        Test response when the request is handled successfully.
817        """
818
819        class Handler(DummyIQHandler):
820            def onGet(self, iq):
821                return None
822
823        iq = domish.Element((None, 'iq'))
824        iq['type'] = 'get'
825        iq['id'] = 'r1'
826        handler = Handler()
827        handler.handleRequest(iq)
828        response = handler.output[-1]
829        self.assertEquals(None, response.uri)
830        self.assertEquals('iq', response.name)
831        self.assertEquals('result', response['type'])
832
833    def test_successPayload(self):
834        """
835        Test response when the request is handled successfully with payload.
836        """
837
838        class Handler(DummyIQHandler):
839            payload = domish.Element(('testns', 'foo'))
840
841            def onGet(self, iq):
842                return self.payload
843
844        iq = domish.Element((None, 'iq'))
845        iq['type'] = 'get'
846        iq['id'] = 'r1'
847        handler = Handler()
848        handler.handleRequest(iq)
849        response = handler.output[-1]
850        self.assertEquals(None, response.uri)
851        self.assertEquals('iq', response.name)
852        self.assertEquals('result', response['type'])
853        payload = response.elements().next()
854        self.assertEqual(handler.payload, payload)
855
856    def test_successDeferred(self):
857        """
858        Test response when where the handler was a deferred.
859        """
860
861        class Handler(DummyIQHandler):
862            def onGet(self, iq):
863                return defer.succeed(None)
864
865        iq = domish.Element((None, 'iq'))
866        iq['type'] = 'get'
867        iq['id'] = 'r1'
868        handler = Handler()
869        handler.handleRequest(iq)
870        response = handler.output[-1]
871        self.assertEquals(None, response.uri)
872        self.assertEquals('iq', response.name)
873        self.assertEquals('result', response['type'])
874
875    def test_failure(self):
876        """
877        Test response when the request is handled unsuccessfully.
878        """
879
880        class Handler(DummyIQHandler):
881            def onGet(self, iq):
882                raise error.StanzaError('forbidden')
883
884        iq = domish.Element((None, 'iq'))
885        iq['type'] = 'get'
886        iq['id'] = 'r1'
887        handler = Handler()
888        handler.handleRequest(iq)
889        response = handler.output[-1]
890        self.assertEquals(None, response.uri)
891        self.assertEquals('iq', response.name)
892        self.assertEquals('error', response['type'])
893        e = error.exceptionFromStanza(response)
894        self.assertEquals('forbidden', e.condition)
895
896    def test_failureUnknown(self):
897        """
898        Test response when the request handler raises a non-stanza-error.
899        """
900
901        class TestError(Exception):
902            pass
903
904        class Handler(DummyIQHandler):
905            def onGet(self, iq):
906                raise TestError()
907
908        iq = domish.Element((None, 'iq'))
909        iq['type'] = 'get'
910        iq['id'] = 'r1'
911        handler = Handler()
912        handler.handleRequest(iq)
913        response = handler.output[-1]
914        self.assertEquals(None, response.uri)
915        self.assertEquals('iq', response.name)
916        self.assertEquals('error', response['type'])
917        e = error.exceptionFromStanza(response)
918        self.assertEquals('internal-server-error', e.condition)
919        self.assertEquals(1, len(self.flushLoggedErrors(TestError)))
920
921    def test_notImplemented(self):
922        """
923        Test response when the request is recognised but not implemented.
924        """
925
926        class Handler(DummyIQHandler):
927            def onGet(self, iq):
928                raise NotImplementedError()
929
930        iq = domish.Element((None, 'iq'))
931        iq['type'] = 'get'
932        iq['id'] = 'r1'
933        handler = Handler()
934        handler.handleRequest(iq)
935        response = handler.output[-1]
936        self.assertEquals(None, response.uri)
937        self.assertEquals('iq', response.name)
938        self.assertEquals('error', response['type'])
939        e = error.exceptionFromStanza(response)
940        self.assertEquals('feature-not-implemented', e.condition)
941
942    def test_noHandler(self):
943        """
944        Test when the request is not recognised.
945        """
946
947        iq = domish.Element((None, 'iq'))
948        iq['type'] = 'set'
949        iq['id'] = 'r1'
950        handler = DummyIQHandler()
951        handler.handleRequest(iq)
952        response = handler.output[-1]
953        self.assertEquals(None, response.uri)
954        self.assertEquals('iq', response.name)
955        self.assertEquals('error', response['type'])
956        e = error.exceptionFromStanza(response)
957        self.assertEquals('feature-not-implemented', e.condition)
Note: See TracBrowser for help on using the repository browser.