source: wokkel/test/test_subprotocols.py @ 4:e8e7d5543a6f

Last change on this file since 4:e8e7d5543a6f was 4:e8e7d5543a6f, checked in by Ralph Meijer <ralphm@…>, 14 years ago

Reverse how handlers are associated with the stream manager. Kick version.

File size: 14.4 KB
Line 
1# Copyright (c) 2003-2007 Ralph Meijer
2# See LICENSE for details.
3
4"""
5Tests for L{wokkel.subprotocols}
6"""
7
8from twisted.trial import unittest
9from twisted.test import proto_helpers
10from twisted.internet import defer
11from twisted.words.xish import domish
12from twisted.words.protocols.jabber import error, xmlstream
13
14from wokkel import subprotocols
15
16class DummyFactory(object):
17    def __init__(self):
18        self.callbacks = {}
19
20    def addBootstrap(self, event, callback):
21        self.callbacks[event] = callback
22
23
24class DummyXMPPHandler(subprotocols.XMPPHandler):
25    def __init__(self):
26        self.doneMade = 0
27        self.doneInitialized = 0
28        self.doneLost = 0
29
30    def makeConnection(self, xs):
31        self.connectionMade()
32
33    def connectionMade(self):
34        self.doneMade += 1
35
36    def connectionInitialized(self):
37        self.doneInitialized += 1
38
39    def connectionLost(self, reason):
40        self.doneLost += 1
41
42
43class XMPPHandlerTest(unittest.TestCase):
44
45    def test_send(self):
46        """
47        Test that data is passed on for sending by the stream manager.
48        """
49
50        class DummyStreamManager(object):
51            def __init__(self):
52                self.outlist = []
53
54            def send(self, data):
55                self.outlist.append(data)
56
57        handler = subprotocols.XMPPHandler()
58        handler.parent = DummyStreamManager()
59        handler.send('<presence/>')
60        self.assertEquals(['<presence/>'], handler.parent.outlist)
61
62
63class StreamManagerTest(unittest.TestCase):
64
65    def setUp(self):
66        factory = DummyFactory()
67        self.streamManager = subprotocols.StreamManager(factory)
68
69    def test_basic(self):
70        """
71        Test correct initialization and setup of factory observers.
72        """
73        sm = self.streamManager
74        self.assertIdentical(None, sm.xmlstream)
75        self.assertEquals([], sm.handlers)
76        self.assertEquals(sm._connected,
77                          sm.factory.callbacks['//event/stream/connected'])
78        self.assertEquals(sm._authd,
79                          sm.factory.callbacks['//event/stream/authd'])
80        self.assertEquals(sm._disconnected,
81                          sm.factory.callbacks['//event/stream/end'])
82        self.assertEquals(sm.initializationFailed,
83                          sm.factory.callbacks['//event/xmpp/initfailed'])
84
85    def test__connected(self):
86        """
87        Test that protocol handlers have their connectionMade method called
88        when the XML stream is connected.
89        """
90        sm = self.streamManager
91        handler = DummyXMPPHandler()
92        handler.setHandlerParent(sm)
93        xs = xmlstream.XmlStream(xmlstream.Authenticator())
94        sm._connected(xs)
95        self.assertEquals(1, handler.doneMade)
96        self.assertEquals(0, handler.doneInitialized)
97        self.assertEquals(0, handler.doneLost)
98
99    def test__authd(self):
100        """
101        Test that protocol handlers have their connectionInitialized method
102        called when the XML stream is initialized.
103        """
104        sm = self.streamManager
105        handler = DummyXMPPHandler()
106        handler.setHandlerParent(sm)
107        xs = xmlstream.XmlStream(xmlstream.Authenticator())
108        sm._authd(xs)
109        self.assertEquals(0, handler.doneMade)
110        self.assertEquals(1, handler.doneInitialized)
111        self.assertEquals(0, handler.doneLost)
112
113    def test__disconnected(self):
114        """
115        Test that protocol handlers have their connectionLost method
116        called when the XML stream is disconnected.
117        """
118        sm = self.streamManager
119        handler = DummyXMPPHandler()
120        handler.setHandlerParent(sm)
121        xs = xmlstream.XmlStream(xmlstream.Authenticator())
122        sm._disconnected(xs)
123        self.assertEquals(0, handler.doneMade)
124        self.assertEquals(0, handler.doneInitialized)
125        self.assertEquals(1, handler.doneLost)
126
127    def test_addHandler(self):
128        """
129        Test the addition of a protocol handler while not connected.
130        """
131        sm = self.streamManager
132        handler = DummyXMPPHandler()
133        handler.setHandlerParent(sm)
134        self.assertIn(handler, sm)
135        self.assertIdentical(sm, handler.parent)
136
137        self.assertEquals(0, handler.doneMade)
138        self.assertEquals(0, handler.doneInitialized)
139        self.assertEquals(0, handler.doneLost)
140
141    def test_addHandlerInitialized(self):
142        """
143        Test the addition of a protocol handler after the stream
144        have been initialized.
145
146        Make sure that the handler will have the connected stream
147        passed via C{makeConnection} and have C{connectionInitialized}
148        called.
149        """
150        sm = self.streamManager
151        xs = xmlstream.XmlStream(xmlstream.Authenticator())
152        sm._connected(xs)
153        sm._authd(xs)
154        handler = DummyXMPPHandler()
155        handler.setHandlerParent(sm)
156
157        self.assertEquals(1, handler.doneMade)
158        self.assertEquals(1, handler.doneInitialized)
159        self.assertEquals(0, handler.doneLost)
160
161    def test_removeHandler(self):
162        """
163        Test removal of protocol handler.
164        """
165        sm = self.streamManager
166        handler = DummyXMPPHandler()
167        handler.setHandlerParent(sm)
168        handler.disownHandlerParent(sm)
169        self.assertNotIn(handler, sm)
170        self.assertIdentical(None, handler.parent)
171
172    def test_sendInitialized(self):
173        """
174        Test send when the stream has been initialized.
175
176        The data should be sent directly over the XML stream.
177        """
178        factory = xmlstream.XmlStreamFactory(xmlstream.Authenticator())
179        sm = subprotocols.StreamManager(factory)
180        xs = factory.buildProtocol(None)
181        xs.transport = proto_helpers.StringTransport()
182        xs.connectionMade()
183        xs.dataReceived("<stream:stream xmlns='jabber:client' "
184                        "xmlns:stream='http://etherx.jabber.org/streams' "
185                        "from='example.com' id='12345'>")
186        xs.dispatch(xs, "//event/stream/authd")
187        sm.send("<presence/>")
188        self.assertEquals("<presence/>", xs.transport.value())
189
190    def test_sendNotConnected(self):
191        """
192        Test send when there is no established XML stream.
193
194        The data should be cached until an XML stream has been established and
195        initialized.
196        """
197        factory = xmlstream.XmlStreamFactory(xmlstream.Authenticator())
198        sm = subprotocols.StreamManager(factory)
199        handler = DummyXMPPHandler()
200        sm.addHandler(handler)
201
202        xs = factory.buildProtocol(None)
203        xs.transport = proto_helpers.StringTransport()
204        sm.send("<presence/>")
205        self.assertEquals("", xs.transport.value())
206        self.assertEquals("<presence/>", sm._packetQueue[0])
207
208        xs.connectionMade()
209        self.assertEquals("", xs.transport.value())
210        self.assertEquals("<presence/>", sm._packetQueue[0])
211
212        xs.dataReceived("<stream:stream xmlns='jabber:client' "
213                        "xmlns:stream='http://etherx.jabber.org/streams' "
214                        "from='example.com' id='12345'>")
215        xs.dispatch(xs, "//event/stream/authd")
216
217        self.assertEquals("<presence/>", xs.transport.value())
218        self.failIf(sm._packetQueue)
219
220    def test_sendNotInitialized(self):
221        """
222        Test send when the stream is connected but not yet initialized.
223
224        The data should be cached until the XML stream has been initialized.
225        """
226        factory = xmlstream.XmlStreamFactory(xmlstream.Authenticator())
227        sm = subprotocols.StreamManager(factory)
228        xs = factory.buildProtocol(None)
229        xs.transport = proto_helpers.StringTransport()
230        xs.connectionMade()
231        xs.dataReceived("<stream:stream xmlns='jabber:client' "
232                        "xmlns:stream='http://etherx.jabber.org/streams' "
233                        "from='example.com' id='12345'>")
234        sm.send("<presence/>")
235        self.assertEquals("", xs.transport.value())
236        self.assertEquals("<presence/>", sm._packetQueue[0])
237
238    def test_sendDisconnected(self):
239        """
240        Test send after XML stream disconnection.
241
242        The data should be cached until a new XML stream has been established
243        and initialized.
244        """
245        factory = xmlstream.XmlStreamFactory(xmlstream.Authenticator())
246        sm = subprotocols.StreamManager(factory)
247        handler = DummyXMPPHandler()
248        sm.addHandler(handler)
249
250        xs = factory.buildProtocol(None)
251        xs.connectionMade()
252        xs.transport = proto_helpers.StringTransport()
253        xs.connectionLost(None)
254
255        sm.send("<presence/>")
256        self.assertEquals("", xs.transport.value())
257        self.assertEquals("<presence/>", sm._packetQueue[0])
258
259
260class DummyIQHandler(subprotocols.IQHandlerMixin):
261    iqHandlers = {'/iq[@type="get"]': 'onGet'}
262
263    def __init__(self):
264        self.output = []
265        self.xmlstream = xmlstream.XmlStream(xmlstream.Authenticator())
266        self.xmlstream.send = self.output.append
267
268    def send(self, obj):
269        self.xmlstream.send(obj)
270
271
272class IQHandlerTest(unittest.TestCase):
273
274    def test_match(self):
275        """
276        Test that the matching handler gets called.
277        """
278
279        class Handler(DummyIQHandler):
280            called = False
281
282            def onGet(self, iq):
283                self.called = True
284
285        iq = domish.Element((None, 'iq'))
286        iq['type'] = 'get'
287        iq['id'] = 'r1'
288        handler = Handler()
289        handler.handleRequest(iq)
290        self.assertTrue(handler.called)
291
292    def test_noMatch(self):
293        """
294        Test that the matching handler gets called.
295        """
296
297        class Handler(DummyIQHandler):
298            called = False
299
300            def onGet(self, iq):
301                self.called = True
302
303        iq = domish.Element((None, 'iq'))
304        iq['type'] = 'set'
305        iq['id'] = 'r1'
306        handler = Handler()
307        handler.handleRequest(iq)
308        self.assertFalse(handler.called)
309
310    def test_success(self):
311        """
312        Test response when the request is handled successfully.
313        """
314
315        class Handler(DummyIQHandler):
316            def onGet(self, iq):
317                return None
318
319        iq = domish.Element((None, 'iq'))
320        iq['type'] = 'get'
321        iq['id'] = 'r1'
322        handler = Handler()
323        handler.handleRequest(iq)
324        response = handler.output[-1]
325        self.assertEquals(None, response.uri)
326        self.assertEquals('iq', response.name)
327        self.assertEquals('result', response['type'])
328
329    def test_successPayload(self):
330        """
331        Test response when the request is handled successfully with payload.
332        """
333
334        class Handler(DummyIQHandler):
335            payload = domish.Element(('testns', 'foo'))
336
337            def onGet(self, iq):
338                return self.payload
339
340        iq = domish.Element((None, 'iq'))
341        iq['type'] = 'get'
342        iq['id'] = 'r1'
343        handler = Handler()
344        handler.handleRequest(iq)
345        response = handler.output[-1]
346        self.assertEquals(None, response.uri)
347        self.assertEquals('iq', response.name)
348        self.assertEquals('result', response['type'])
349        payload = response.elements().next()
350        self.assertEqual(handler.payload, payload)
351
352    def test_successDeferred(self):
353        """
354        Test response when where the handler was a deferred.
355        """
356
357        class Handler(DummyIQHandler):
358            def onGet(self, iq):
359                return defer.succeed(None)
360
361        iq = domish.Element((None, 'iq'))
362        iq['type'] = 'get'
363        iq['id'] = 'r1'
364        handler = Handler()
365        handler.handleRequest(iq)
366        response = handler.output[-1]
367        self.assertEquals(None, response.uri)
368        self.assertEquals('iq', response.name)
369        self.assertEquals('result', response['type'])
370
371    def test_failure(self):
372        """
373        Test response when the request is handled unsuccessfully.
374        """
375
376        class Handler(DummyIQHandler):
377            def onGet(self, iq):
378                raise error.StanzaError('forbidden')
379
380        iq = domish.Element((None, 'iq'))
381        iq['type'] = 'get'
382        iq['id'] = 'r1'
383        handler = Handler()
384        handler.handleRequest(iq)
385        response = handler.output[-1]
386        self.assertEquals(None, response.uri)
387        self.assertEquals('iq', response.name)
388        self.assertEquals('error', response['type'])
389        e = error.exceptionFromStanza(response)
390        self.assertEquals('forbidden', e.condition)
391
392    def test_failureUnknown(self):
393        """
394        Test response when the request handler raises a non-stanza-error.
395        """
396
397        class TestError(Exception):
398            pass
399
400        class Handler(DummyIQHandler):
401            def onGet(self, iq):
402                raise TestError()
403
404        iq = domish.Element((None, 'iq'))
405        iq['type'] = 'get'
406        iq['id'] = 'r1'
407        handler = Handler()
408        handler.handleRequest(iq)
409        response = handler.output[-1]
410        self.assertEquals(None, response.uri)
411        self.assertEquals('iq', response.name)
412        self.assertEquals('error', response['type'])
413        e = error.exceptionFromStanza(response)
414        self.assertEquals('internal-server-error', e.condition)
415        self.assertEquals(1, len(self.flushLoggedErrors(TestError)))
416
417    def test_notImplemented(self):
418        """
419        Test response when the request is recognised but not implemented.
420        """
421
422        class Handler(DummyIQHandler):
423            def onGet(self, iq):
424                raise NotImplementedError()
425
426        iq = domish.Element((None, 'iq'))
427        iq['type'] = 'get'
428        iq['id'] = 'r1'
429        handler = Handler()
430        handler.handleRequest(iq)
431        response = handler.output[-1]
432        self.assertEquals(None, response.uri)
433        self.assertEquals('iq', response.name)
434        self.assertEquals('error', response['type'])
435        e = error.exceptionFromStanza(response)
436        self.assertEquals('feature-not-implemented', e.condition)
437
438    def test_noHandler(self):
439        """
440        Test when the request is not recognised.
441        """
442
443        iq = domish.Element((None, 'iq'))
444        iq['type'] = 'set'
445        iq['id'] = 'r1'
446        handler = DummyIQHandler()
447        handler.handleRequest(iq)
448        response = handler.output[-1]
449        self.assertEquals(None, response.uri)
450        self.assertEquals('iq', response.name)
451        self.assertEquals('error', response['type'])
452        e = error.exceptionFromStanza(response)
453        self.assertEquals('feature-not-implemented', e.condition)
Note: See TracBrowser for help on using the repository browser.