source: wokkel/test/test_subprotocols.py @ 98:c3b20ad2bb70

Last change on this file since 98:c3b20ad2bb70 was 98:c3b20ad2bb70, checked in by Ralph Meijer <ralphm@…>, 10 years ago

Track change in Twisted Words, to pass a reason Failure with STREAM_END_EVENT.

  • Property exe set to *
File size: 18.3 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
13from twisted.python import failure
14from twisted.words.xish import domish
15from twisted.words.protocols.jabber import error, xmlstream
16
17from wokkel import iwokkel, subprotocols
18
19class DummyFactory(object):
20    """
21    Dummy XmlStream factory that only registers bootstrap observers.
22    """
23    def __init__(self):
24        self.callbacks = {}
25
26
27    def addBootstrap(self, event, callback):
28        self.callbacks[event] = callback
29
30
31
32class DummyXMPPHandler(subprotocols.XMPPHandler):
33    """
34    Dummy XMPP subprotocol handler to count the methods are called on it.
35    """
36    def __init__(self):
37        self.doneMade = 0
38        self.doneInitialized = 0
39        self.doneLost = 0
40
41
42    def makeConnection(self, xs):
43        self.connectionMade()
44
45
46    def connectionMade(self):
47        self.doneMade += 1
48
49
50    def connectionInitialized(self):
51        self.doneInitialized += 1
52
53
54    def connectionLost(self, reason):
55        self.doneLost += 1
56
57
58
59class FailureReasonXMPPHandler(subprotocols.XMPPHandler):
60    """
61    Dummy handler specifically for failure Reason tests.
62    """
63    def __init__(self):
64        self.gotFailureReason = False
65
66
67    def connectionLost(self, reason):
68        if isinstance(reason, failure.Failure):
69            self.gotFailureReason = True
70
71
72
73class XMPPHandlerTest(unittest.TestCase):
74    """
75    Tests for L{subprotocols.XMPPHandler}.
76    """
77
78    def test_interface(self):
79        """
80        L{xmlstream.XMPPHandler} implements L{iwokkel.IXMPPHandler}.
81        """
82        verifyObject(iwokkel.IXMPPHandler, subprotocols.XMPPHandler())
83
84
85    def test_send(self):
86        """
87        Test that data is passed on for sending by the stream manager.
88        """
89        class DummyStreamManager(object):
90            def __init__(self):
91                self.outlist = []
92
93            def send(self, data):
94                self.outlist.append(data)
95
96        handler = subprotocols.XMPPHandler()
97        handler.parent = DummyStreamManager()
98        handler.send('<presence/>')
99        self.assertEquals(['<presence/>'], handler.parent.outlist)
100
101
102    def test_makeConnection(self):
103        """
104        Test that makeConnection saves the XML stream and calls connectionMade.
105        """
106        class TestXMPPHandler(subprotocols.XMPPHandler):
107            def connectionMade(self):
108                self.doneMade = True
109
110        handler = TestXMPPHandler()
111        xs = xmlstream.XmlStream(xmlstream.Authenticator())
112        handler.makeConnection(xs)
113        self.assertTrue(handler.doneMade)
114        self.assertIdentical(xs, handler.xmlstream)
115
116
117    def test_connectionLost(self):
118        """
119        Test that connectionLost forgets the XML stream.
120        """
121        handler = subprotocols.XMPPHandler()
122        xs = xmlstream.XmlStream(xmlstream.Authenticator())
123        handler.makeConnection(xs)
124        handler.connectionLost(Exception())
125        self.assertIdentical(None, handler.xmlstream)
126
127
128
129class XMPPHandlerCollectionTest(unittest.TestCase):
130    """
131    Tests for L{subprotocols.XMPPHandlerCollection}.
132    """
133
134    def setUp(self):
135        self.collection = subprotocols.XMPPHandlerCollection()
136
137
138    def test_interface(self):
139        """
140        L{subprotocols.StreamManager} implements L{iwokkel.IXMPPHandlerCollection}.
141        """
142        verifyObject(iwokkel.IXMPPHandlerCollection, self.collection)
143
144
145    def test_addHandler(self):
146        """
147        Test the addition of a protocol handler.
148        """
149        handler = DummyXMPPHandler()
150        handler.setHandlerParent(self.collection)
151        self.assertIn(handler, self.collection)
152        self.assertIdentical(self.collection, handler.parent)
153
154
155    def test_removeHandler(self):
156        """
157        Test removal of a protocol handler.
158        """
159        handler = DummyXMPPHandler()
160        handler.setHandlerParent(self.collection)
161        handler.disownHandlerParent(self.collection)
162        self.assertNotIn(handler, self.collection)
163        self.assertIdentical(None, handler.parent)
164
165
166
167class StreamManagerTest(unittest.TestCase):
168    """
169    Tests for L{subprotocols.StreamManager}.
170    """
171
172    def setUp(self):
173        factory = DummyFactory()
174        self.streamManager = subprotocols.StreamManager(factory)
175
176    def test_basic(self):
177        """
178        Test correct initialization and setup of factory observers.
179        """
180        sm = self.streamManager
181        self.assertIdentical(None, sm.xmlstream)
182        self.assertEquals([], sm.handlers)
183        self.assertEquals(sm._connected,
184                          sm.factory.callbacks['//event/stream/connected'])
185        self.assertEquals(sm._authd,
186                          sm.factory.callbacks['//event/stream/authd'])
187        self.assertEquals(sm._disconnected,
188                          sm.factory.callbacks['//event/stream/end'])
189        self.assertEquals(sm.initializationFailed,
190                          sm.factory.callbacks['//event/xmpp/initfailed'])
191
192
193    def test_connected(self):
194        """
195        Test that protocol handlers have their connectionMade method called
196        when the XML stream is connected.
197        """
198        sm = self.streamManager
199        handler = DummyXMPPHandler()
200        handler.setHandlerParent(sm)
201        xs = xmlstream.XmlStream(xmlstream.Authenticator())
202        sm._connected(xs)
203        self.assertEquals(1, handler.doneMade)
204        self.assertEquals(0, handler.doneInitialized)
205        self.assertEquals(0, handler.doneLost)
206
207
208    def test_connectedLogTrafficFalse(self):
209        """
210        Test raw data functions unset when logTraffic is set to False.
211        """
212        sm = self.streamManager
213        handler = DummyXMPPHandler()
214        handler.setHandlerParent(sm)
215        xs = xmlstream.XmlStream(xmlstream.Authenticator())
216        sm._connected(xs)
217        self.assertIdentical(None, xs.rawDataInFn)
218        self.assertIdentical(None, xs.rawDataOutFn)
219
220
221    def test_connectedLogTrafficTrue(self):
222        """
223        Test raw data functions set when logTraffic is set to True.
224        """
225        sm = self.streamManager
226        sm.logTraffic = True
227        handler = DummyXMPPHandler()
228        handler.setHandlerParent(sm)
229        xs = xmlstream.XmlStream(xmlstream.Authenticator())
230        sm._connected(xs)
231        self.assertNotIdentical(None, xs.rawDataInFn)
232        self.assertNotIdentical(None, xs.rawDataOutFn)
233
234
235    def test_authd(self):
236        """
237        Test that protocol handlers have their connectionInitialized method
238        called when the XML stream is initialized.
239        """
240        sm = self.streamManager
241        handler = DummyXMPPHandler()
242        handler.setHandlerParent(sm)
243        xs = xmlstream.XmlStream(xmlstream.Authenticator())
244        sm._authd(xs)
245        self.assertEquals(0, handler.doneMade)
246        self.assertEquals(1, handler.doneInitialized)
247        self.assertEquals(0, handler.doneLost)
248
249
250    def test_disconnected(self):
251        """
252        Protocol handlers have connectionLost called on stream disconnect.
253        """
254        sm = self.streamManager
255        handler = DummyXMPPHandler()
256        handler.setHandlerParent(sm)
257        sm._disconnected(None)
258        self.assertEquals(0, handler.doneMade)
259        self.assertEquals(0, handler.doneInitialized)
260        self.assertEquals(1, handler.doneLost)
261
262
263    def test_disconnectedReason(self):
264        """
265        A L{STREAM_END_EVENT} results in L{StreamManager} firing the handlers
266        L{connectionLost} methods, passing a L{failure.Failure} reason.
267        """
268        sm = self.streamManager
269        handler = FailureReasonXMPPHandler()
270        handler.setHandlerParent(sm)
271        xs = xmlstream.XmlStream(xmlstream.Authenticator())
272        sm._disconnected(failure.Failure(Exception("no reason")))
273        self.assertEquals(True, handler.gotFailureReason)
274
275
276    def test_addHandler(self):
277        """
278        Test the addition of a protocol handler while not connected.
279        """
280        sm = self.streamManager
281        handler = DummyXMPPHandler()
282        handler.setHandlerParent(sm)
283
284        self.assertEquals(0, handler.doneMade)
285        self.assertEquals(0, handler.doneInitialized)
286        self.assertEquals(0, handler.doneLost)
287
288
289    def test_addHandlerInitialized(self):
290        """
291        Test the addition of a protocol handler after the stream
292        have been initialized.
293
294        Make sure that the handler will have the connected stream
295        passed via C{makeConnection} and have C{connectionInitialized}
296        called.
297        """
298        sm = self.streamManager
299        xs = xmlstream.XmlStream(xmlstream.Authenticator())
300        sm._connected(xs)
301        sm._authd(xs)
302        handler = DummyXMPPHandler()
303        handler.setHandlerParent(sm)
304
305        self.assertEquals(1, handler.doneMade)
306        self.assertEquals(1, handler.doneInitialized)
307        self.assertEquals(0, handler.doneLost)
308
309    def test_removeHandler(self):
310        """
311        Test removal of protocol handler.
312        """
313        sm = self.streamManager
314        handler = DummyXMPPHandler()
315        handler.setHandlerParent(sm)
316        handler.disownHandlerParent(sm)
317        self.assertNotIn(handler, sm)
318        self.assertIdentical(None, handler.parent)
319
320    def test_sendInitialized(self):
321        """
322        Test send when the stream has been initialized.
323
324        The data should be sent directly over the XML stream.
325        """
326        factory = xmlstream.XmlStreamFactory(xmlstream.Authenticator())
327        sm = subprotocols.StreamManager(factory)
328        xs = factory.buildProtocol(None)
329        xs.transport = proto_helpers.StringTransport()
330        xs.connectionMade()
331        xs.dataReceived("<stream:stream xmlns='jabber:client' "
332                        "xmlns:stream='http://etherx.jabber.org/streams' "
333                        "from='example.com' id='12345'>")
334        xs.dispatch(xs, "//event/stream/authd")
335        sm.send("<presence/>")
336        self.assertEquals("<presence/>", xs.transport.value())
337
338
339    def test_sendNotConnected(self):
340        """
341        Test send when there is no established XML stream.
342
343        The data should be cached until an XML stream has been established and
344        initialized.
345        """
346        factory = xmlstream.XmlStreamFactory(xmlstream.Authenticator())
347        sm = subprotocols.StreamManager(factory)
348        handler = DummyXMPPHandler()
349        sm.addHandler(handler)
350
351        xs = factory.buildProtocol(None)
352        xs.transport = proto_helpers.StringTransport()
353        sm.send("<presence/>")
354        self.assertEquals("", xs.transport.value())
355        self.assertEquals("<presence/>", sm._packetQueue[0])
356
357        xs.connectionMade()
358        self.assertEquals("", xs.transport.value())
359        self.assertEquals("<presence/>", sm._packetQueue[0])
360
361        xs.dataReceived("<stream:stream xmlns='jabber:client' "
362                        "xmlns:stream='http://etherx.jabber.org/streams' "
363                        "from='example.com' id='12345'>")
364        xs.dispatch(xs, "//event/stream/authd")
365
366        self.assertEquals("<presence/>", xs.transport.value())
367        self.assertFalse(sm._packetQueue)
368
369
370    def test_sendNotInitialized(self):
371        """
372        Test send when the stream is connected but not yet initialized.
373
374        The data should be cached until the XML stream has been initialized.
375        """
376        factory = xmlstream.XmlStreamFactory(xmlstream.Authenticator())
377        sm = subprotocols.StreamManager(factory)
378        xs = factory.buildProtocol(None)
379        xs.transport = proto_helpers.StringTransport()
380        xs.connectionMade()
381        xs.dataReceived("<stream:stream xmlns='jabber:client' "
382                        "xmlns:stream='http://etherx.jabber.org/streams' "
383                        "from='example.com' id='12345'>")
384        sm.send("<presence/>")
385        self.assertEquals("", xs.transport.value())
386        self.assertEquals("<presence/>", sm._packetQueue[0])
387
388
389    def test_sendDisconnected(self):
390        """
391        Test send after XML stream disconnection.
392
393        The data should be cached until a new XML stream has been established
394        and initialized.
395        """
396        factory = xmlstream.XmlStreamFactory(xmlstream.Authenticator())
397        sm = subprotocols.StreamManager(factory)
398        handler = DummyXMPPHandler()
399        sm.addHandler(handler)
400
401        xs = factory.buildProtocol(None)
402        xs.connectionMade()
403        xs.transport = proto_helpers.StringTransport()
404        xs.connectionLost(None)
405
406        sm.send("<presence/>")
407        self.assertEquals("", xs.transport.value())
408        self.assertEquals("<presence/>", sm._packetQueue[0])
409
410
411
412class DummyIQHandler(subprotocols.IQHandlerMixin):
413    iqHandlers = {'/iq[@type="get"]': 'onGet'}
414
415    def __init__(self):
416        self.output = []
417        self.xmlstream = xmlstream.XmlStream(xmlstream.Authenticator())
418        self.xmlstream.send = self.output.append
419
420    def send(self, obj):
421        self.xmlstream.send(obj)
422
423
424class IQHandlerTest(unittest.TestCase):
425
426    def test_match(self):
427        """
428        Test that the matching handler gets called.
429        """
430
431        class Handler(DummyIQHandler):
432            called = False
433
434            def onGet(self, iq):
435                self.called = True
436
437        iq = domish.Element((None, 'iq'))
438        iq['type'] = 'get'
439        iq['id'] = 'r1'
440        handler = Handler()
441        handler.handleRequest(iq)
442        self.assertTrue(handler.called)
443
444    def test_noMatch(self):
445        """
446        Test that the matching handler gets called.
447        """
448
449        class Handler(DummyIQHandler):
450            called = False
451
452            def onGet(self, iq):
453                self.called = True
454
455        iq = domish.Element((None, 'iq'))
456        iq['type'] = 'set'
457        iq['id'] = 'r1'
458        handler = Handler()
459        handler.handleRequest(iq)
460        self.assertFalse(handler.called)
461
462    def test_success(self):
463        """
464        Test response when the request is handled successfully.
465        """
466
467        class Handler(DummyIQHandler):
468            def onGet(self, iq):
469                return None
470
471        iq = domish.Element((None, 'iq'))
472        iq['type'] = 'get'
473        iq['id'] = 'r1'
474        handler = Handler()
475        handler.handleRequest(iq)
476        response = handler.output[-1]
477        self.assertEquals(None, response.uri)
478        self.assertEquals('iq', response.name)
479        self.assertEquals('result', response['type'])
480
481    def test_successPayload(self):
482        """
483        Test response when the request is handled successfully with payload.
484        """
485
486        class Handler(DummyIQHandler):
487            payload = domish.Element(('testns', 'foo'))
488
489            def onGet(self, iq):
490                return self.payload
491
492        iq = domish.Element((None, 'iq'))
493        iq['type'] = 'get'
494        iq['id'] = 'r1'
495        handler = Handler()
496        handler.handleRequest(iq)
497        response = handler.output[-1]
498        self.assertEquals(None, response.uri)
499        self.assertEquals('iq', response.name)
500        self.assertEquals('result', response['type'])
501        payload = response.elements().next()
502        self.assertEqual(handler.payload, payload)
503
504    def test_successDeferred(self):
505        """
506        Test response when where the handler was a deferred.
507        """
508
509        class Handler(DummyIQHandler):
510            def onGet(self, iq):
511                return defer.succeed(None)
512
513        iq = domish.Element((None, 'iq'))
514        iq['type'] = 'get'
515        iq['id'] = 'r1'
516        handler = Handler()
517        handler.handleRequest(iq)
518        response = handler.output[-1]
519        self.assertEquals(None, response.uri)
520        self.assertEquals('iq', response.name)
521        self.assertEquals('result', response['type'])
522
523    def test_failure(self):
524        """
525        Test response when the request is handled unsuccessfully.
526        """
527
528        class Handler(DummyIQHandler):
529            def onGet(self, iq):
530                raise error.StanzaError('forbidden')
531
532        iq = domish.Element((None, 'iq'))
533        iq['type'] = 'get'
534        iq['id'] = 'r1'
535        handler = Handler()
536        handler.handleRequest(iq)
537        response = handler.output[-1]
538        self.assertEquals(None, response.uri)
539        self.assertEquals('iq', response.name)
540        self.assertEquals('error', response['type'])
541        e = error.exceptionFromStanza(response)
542        self.assertEquals('forbidden', e.condition)
543
544    def test_failureUnknown(self):
545        """
546        Test response when the request handler raises a non-stanza-error.
547        """
548
549        class TestError(Exception):
550            pass
551
552        class Handler(DummyIQHandler):
553            def onGet(self, iq):
554                raise TestError()
555
556        iq = domish.Element((None, 'iq'))
557        iq['type'] = 'get'
558        iq['id'] = 'r1'
559        handler = Handler()
560        handler.handleRequest(iq)
561        response = handler.output[-1]
562        self.assertEquals(None, response.uri)
563        self.assertEquals('iq', response.name)
564        self.assertEquals('error', response['type'])
565        e = error.exceptionFromStanza(response)
566        self.assertEquals('internal-server-error', e.condition)
567        self.assertEquals(1, len(self.flushLoggedErrors(TestError)))
568
569    def test_notImplemented(self):
570        """
571        Test response when the request is recognised but not implemented.
572        """
573
574        class Handler(DummyIQHandler):
575            def onGet(self, iq):
576                raise NotImplementedError()
577
578        iq = domish.Element((None, 'iq'))
579        iq['type'] = 'get'
580        iq['id'] = 'r1'
581        handler = Handler()
582        handler.handleRequest(iq)
583        response = handler.output[-1]
584        self.assertEquals(None, response.uri)
585        self.assertEquals('iq', response.name)
586        self.assertEquals('error', response['type'])
587        e = error.exceptionFromStanza(response)
588        self.assertEquals('feature-not-implemented', e.condition)
589
590    def test_noHandler(self):
591        """
592        Test when the request is not recognised.
593        """
594
595        iq = domish.Element((None, 'iq'))
596        iq['type'] = 'set'
597        iq['id'] = 'r1'
598        handler = DummyIQHandler()
599        handler.handleRequest(iq)
600        response = handler.output[-1]
601        self.assertEquals(None, response.uri)
602        self.assertEquals('iq', response.name)
603        self.assertEquals('error', response['type'])
604        e = error.exceptionFromStanza(response)
605        self.assertEquals('feature-not-implemented', e.condition)
Note: See TracBrowser for help on using the repository browser.