source: wokkel/test/test_component.py @ 35:eb020b49a77d

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

Add XMPP router and server side component authenticator.

Author: ralphm.
Fixed #30.

File size: 8.5 KB
Line 
1# Copyright (c) 2003-2008 Ralph Meijer
2# See LICENSE for details.
3
4"""
5Tests for L{wokkel.component}
6"""
7
8from twisted.python import failure
9from twisted.trial import unittest
10from twisted.words.protocols.jabber import xmlstream
11from twisted.words.protocols.jabber.jid import JID
12from twisted.words.xish import domish
13
14from wokkel import component
15from wokkel.generic import XmlPipe
16
17class RouterServiceTest(unittest.TestCase):
18    """
19    Tests for L{component.RouterService}.
20    """
21
22    def test_addRoute(self):
23        """
24        Test route registration and routing on incoming stanzas.
25        """
26        router = component.RouterService()
27        routed = []
28        router.route = lambda element: routed.append(element)
29
30        pipe = XmlPipe()
31        router.addRoute('example.org', pipe.sink)
32        self.assertEquals(1, len(router.routes))
33        self.assertEquals(pipe.sink, router.routes['example.org'])
34
35        element = domish.Element(('testns', 'test'))
36        pipe.source.send(element)
37        self.assertEquals([element], routed)
38
39
40    def test_route(self):
41        """
42        Test routing of a message.
43        """
44        component1 = XmlPipe()
45        component2 = XmlPipe()
46        router = component.RouterService()
47        router.addRoute('component1.example.org', component1.sink)
48        router.addRoute('component2.example.org', component2.sink)
49
50        outgoing = []
51        component2.source.addObserver('/*',
52                                      lambda element: outgoing.append(element))
53        stanza = domish.Element((None, 'route'))
54        stanza['from'] = 'component1.example.org'
55        stanza['to'] = 'component2.example.org'
56        stanza.addElement('presence')
57        component1.source.send(stanza)
58        self.assertEquals([stanza], outgoing)
59
60
61    def test_routeDefault(self):
62        """
63        Test routing of a message using the default route.
64
65        The default route is the one with C{None} as its key in the
66        routing table. It is taken when there is no more specific route
67        in the routing table that matches the stanza's destination.
68        """
69        component1 = XmlPipe()
70        s2s = XmlPipe()
71        router = component.RouterService()
72        router.addRoute('component1.example.org', component1.sink)
73        router.addRoute(None, s2s.sink)
74
75        outgoing = []
76        s2s.source.addObserver('/*', lambda element: outgoing.append(element))
77        stanza = domish.Element((None, 'route'))
78        stanza['from'] = 'component1.example.org'
79        stanza['to'] = 'example.com'
80        stanza.addElement('presence')
81        component1.source.send(stanza)
82        self.assertEquals([stanza], outgoing)
83
84
85
86class ListenComponentAuthenticatorTest(unittest.TestCase):
87    """
88    Tests for L{component.ListenComponentAuthenticator}.
89    """
90
91    def setUp(self):
92        self.output = []
93        authenticator = component.ListenComponentAuthenticator('secret')
94        self.xmlstream = xmlstream.XmlStream(authenticator)
95        self.xmlstream.send = self.output.append
96
97
98    def loseConnection(self):
99        """
100        Stub loseConnection because we are a transport.
101        """
102        self.xmlstream.connectionLost("no reason")
103
104
105    def test_streamStarted(self):
106        observers = []
107
108        def addOnetimeObserver(event, observerfn):
109            observers.append((event, observerfn))
110
111        xs = self.xmlstream
112        xs.addOnetimeObserver = addOnetimeObserver
113
114        xs.makeConnection(self)
115        self.assertIdentical(None, xs.sid)
116        self.assertFalse(xs._headerSent)
117
118        xs.dataReceived("<stream:stream xmlns='jabber:component:accept' "
119                         "xmlns:stream='http://etherx.jabber.org/streams' "
120                         "to='component.example.org'>")
121        self.assertEqual((0, 0), xs.version)
122        self.assertNotIdentical(None, xs.sid)
123        self.assertTrue(xs._headerSent)
124        self.assertEquals(('/*', xs.authenticator.onElement), observers[-1])
125
126
127    def test_streamStartedWrongNamespace(self):
128        """
129        The received stream header should have a correct namespace.
130        """
131        streamErrors = []
132
133        xs = self.xmlstream
134        xs.sendStreamError = streamErrors.append
135        xs.makeConnection(self)
136        xs.dataReceived("<stream:stream xmlns='jabber:client' "
137                         "xmlns:stream='http://etherx.jabber.org/streams' "
138                         "to='component.example.org'>")
139        self.assertEquals(1, len(streamErrors))
140        self.assertEquals('invalid-namespace', streamErrors[-1].condition)
141
142
143    def test_streamStartedNoTo(self):
144        streamErrors = []
145
146        xs = self.xmlstream
147        xs.sendStreamError = streamErrors.append
148        xs.makeConnection(self)
149        xs.dataReceived("<stream:stream xmlns='jabber:component:accept' "
150                         "xmlns:stream='http://etherx.jabber.org/streams'>")
151        self.assertEquals(1, len(streamErrors))
152        self.assertEquals('improper-addressing', streamErrors[-1].condition)
153
154
155    def test_onElement(self):
156        """
157        We expect a handshake element with a hash.
158        """
159        handshakes = []
160
161        xs = self.xmlstream
162        xs.authenticator.onHandshake = handshakes.append
163
164        handshake = domish.Element(('jabber:component:accept', 'handshake'))
165        handshake.addContent('1234')
166        xs.authenticator.onElement(handshake)
167        self.assertEqual('1234', handshakes[-1])
168
169    def test_onHandshake(self):
170        xs = self.xmlstream
171        xs.sid = '1234'
172        theHash = '32532c0f7dbf1253c095b18b18e36d38d94c1256'
173        xs.authenticator.onHandshake(theHash)
174        self.assertEqual('<handshake/>', self.output[-1])
175
176
177    def test_onHandshakeWrongHash(self):
178        streamErrors = []
179        authd = []
180
181        def authenticated(self, xs):
182            authd.append(xs)
183
184        xs = self.xmlstream
185        xs.addOnetimeObserver(xmlstream.STREAM_AUTHD_EVENT, authenticated)
186        xs.sendStreamError = streamErrors.append
187
188        xs.sid = '1234'
189        theHash = '1234'
190        xs.authenticator.onHandshake(theHash)
191        self.assertEquals('not-authorized', streamErrors[-1].condition)
192        self.assertEquals(0, len(authd))
193
194
195
196class ComponentServerTest(unittest.TestCase):
197    """
198    Tests for L{component.ComponentServer}.
199    """
200
201    def setUp(self):
202        self.router = component.RouterService()
203        self.server = component.ComponentServer(self.router)
204        self.xmlstream = self.server.factory.buildProtocol(None)
205        self.xmlstream.thisEntity = JID('component.example.org')
206
207
208    def test_makeConnection(self):
209        """
210        A new connection increases the stream serial count. No logs by default.
211        """
212        self.xmlstream.dispatch(self.xmlstream,
213                                xmlstream.STREAM_CONNECTED_EVENT)
214        self.assertEqual(0, self.xmlstream.serial)
215        self.assertEqual(1, self.server.serial)
216        self.assertIdentical(None, self.xmlstream.rawDataInFn)
217        self.assertIdentical(None, self.xmlstream.rawDataOutFn)
218
219
220    def test_makeConnectionLogTraffic(self):
221        """
222        Setting logTraffic should set up raw data loggers.
223        """
224        self.server.logTraffic = True
225        self.xmlstream.dispatch(self.xmlstream,
226                                xmlstream.STREAM_CONNECTED_EVENT)
227        self.assertNotIdentical(None, self.xmlstream.rawDataInFn)
228        self.assertNotIdentical(None, self.xmlstream.rawDataOutFn)
229
230
231    def test_onError(self):
232        """
233        An observer for stream errors should trigger onError to log it.
234        """
235        self.xmlstream.dispatch(self.xmlstream,
236                                xmlstream.STREAM_CONNECTED_EVENT)
237
238        class TestError(Exception):
239            pass
240
241        reason = failure.Failure(TestError())
242        self.xmlstream.dispatch(reason, xmlstream.STREAM_ERROR_EVENT)
243        self.assertEqual(1, len(self.flushLoggedErrors(TestError)))
244
245
246    def test_connectionInitialized(self):
247        """
248        Make sure a new stream is added to the routing table.
249        """
250        self.xmlstream.dispatch(self.xmlstream, xmlstream.STREAM_AUTHD_EVENT)
251        self.assertIn('component.example.org', self.router.routes)
252        self.assertIdentical(self.xmlstream,
253                             self.router.routes['component.example.org'])
254
255
256    def test_connectionLost(self):
257        """
258        Make sure a stream is removed from the routing table on disconnect.
259        """
260        self.xmlstream.dispatch(self.xmlstream, xmlstream.STREAM_AUTHD_EVENT)
261        self.xmlstream.dispatch(None, xmlstream.STREAM_END_EVENT)
262        self.assertNotIn('component.example.org', self.router.routes)
Note: See TracBrowser for help on using the repository browser.