OLD | NEW |
| (Empty) |
1 # Copyright (c) 2001-2008 Twisted Matrix Laboratories. | |
2 # See LICENSE for details. | |
3 | |
4 """ | |
5 Tests for L{twisted.words.protocols.jabber.xmlstream}. | |
6 """ | |
7 | |
8 from twisted.trial import unittest | |
9 | |
10 from zope.interface.verify import verifyObject | |
11 | |
12 from twisted.internet import defer, task | |
13 from twisted.internet.error import ConnectionLost | |
14 from twisted.test import proto_helpers | |
15 from twisted.words.xish import domish | |
16 from twisted.words.protocols.jabber import error, ijabber, jid, xmlstream | |
17 | |
18 | |
19 | |
20 NS_XMPP_TLS = 'urn:ietf:params:xml:ns:xmpp-tls' | |
21 | |
22 | |
23 | |
24 class IQTest(unittest.TestCase): | |
25 """ | |
26 Tests both IQ and the associated IIQResponseTracker callback. | |
27 """ | |
28 | |
29 def setUp(self): | |
30 authenticator = xmlstream.ConnectAuthenticator('otherhost') | |
31 authenticator.namespace = 'testns' | |
32 self.xmlstream = xmlstream.XmlStream(authenticator) | |
33 self.clock = task.Clock() | |
34 self.xmlstream._callLater = self.clock.callLater | |
35 self.xmlstream.makeConnection(proto_helpers.StringTransport()) | |
36 self.xmlstream.dataReceived( | |
37 "<stream:stream xmlns:stream='http://etherx.jabber.org/streams' " | |
38 "xmlns='testns' from='otherhost' version='1.0'>") | |
39 self.iq = xmlstream.IQ(self.xmlstream, 'get') | |
40 | |
41 | |
42 def testBasic(self): | |
43 self.assertEquals(self.iq['type'], 'get') | |
44 self.assertTrue(self.iq['id']) | |
45 | |
46 | |
47 def testSend(self): | |
48 self.xmlstream.transport.clear() | |
49 self.iq.send() | |
50 self.assertEquals("<iq type='get' id='%s'/>" % self.iq['id'], | |
51 self.xmlstream.transport.value()) | |
52 | |
53 | |
54 def testResultResponse(self): | |
55 def cb(result): | |
56 self.assertEquals(result['type'], 'result') | |
57 | |
58 d = self.iq.send() | |
59 d.addCallback(cb) | |
60 | |
61 xs = self.xmlstream | |
62 xs.dataReceived("<iq type='result' id='%s'/>" % self.iq['id']) | |
63 return d | |
64 | |
65 | |
66 def testErrorResponse(self): | |
67 d = self.iq.send() | |
68 self.assertFailure(d, error.StanzaError) | |
69 | |
70 xs = self.xmlstream | |
71 xs.dataReceived("<iq type='error' id='%s'/>" % self.iq['id']) | |
72 return d | |
73 | |
74 | |
75 def testNonTrackedResponse(self): | |
76 """ | |
77 Test that untracked iq responses don't trigger any action. | |
78 | |
79 Untracked means that the id of the incoming response iq is not | |
80 in the stream's C{iqDeferreds} dictionary. | |
81 """ | |
82 xs = self.xmlstream | |
83 xmlstream.upgradeWithIQResponseTracker(xs) | |
84 | |
85 # Make sure we aren't tracking any iq's. | |
86 self.failIf(xs.iqDeferreds) | |
87 | |
88 # Set up a fallback handler that checks the stanza's handled attribute. | |
89 # If that is set to True, the iq tracker claims to have handled the | |
90 # response. | |
91 def cb(iq): | |
92 self.failIf(getattr(iq, 'handled', False)) | |
93 | |
94 xs.addObserver("/iq", cb, -1) | |
95 | |
96 # Receive an untracked iq response | |
97 xs.dataReceived("<iq type='result' id='test'/>") | |
98 | |
99 | |
100 def testCleanup(self): | |
101 """ | |
102 Test if the deferred associated with an iq request is removed | |
103 from the list kept in the L{XmlStream} object after it has | |
104 been fired. | |
105 """ | |
106 | |
107 d = self.iq.send() | |
108 xs = self.xmlstream | |
109 xs.dataReceived("<iq type='result' id='%s'/>" % self.iq['id']) | |
110 self.assertNotIn(self.iq['id'], xs.iqDeferreds) | |
111 return d | |
112 | |
113 | |
114 def testDisconnectCleanup(self): | |
115 """ | |
116 Test if deferreds for iq's that haven't yet received a response | |
117 have their errback called on stream disconnect. | |
118 """ | |
119 | |
120 d = self.iq.send() | |
121 xs = self.xmlstream | |
122 xs.connectionLost("Closed by peer") | |
123 self.assertFailure(d, ConnectionLost) | |
124 return d | |
125 | |
126 | |
127 def testNoModifyingDict(self): | |
128 """ | |
129 Test to make sure the errbacks cannot cause the iteration of the | |
130 iqDeferreds to blow up in our face. | |
131 """ | |
132 | |
133 def eb(failure): | |
134 d = xmlstream.IQ(self.xmlstream).send() | |
135 d.addErrback(eb) | |
136 | |
137 d = self.iq.send() | |
138 d.addErrback(eb) | |
139 self.xmlstream.connectionLost("Closed by peer") | |
140 return d | |
141 | |
142 | |
143 def testRequestTimingOut(self): | |
144 """ | |
145 Test that an iq request with a defined timeout times out. | |
146 """ | |
147 self.iq.timeout = 60 | |
148 d = self.iq.send() | |
149 self.assertFailure(d, xmlstream.TimeoutError) | |
150 | |
151 self.clock.pump([1, 60]) | |
152 self.failIf(self.clock.calls) | |
153 self.failIf(self.xmlstream.iqDeferreds) | |
154 return d | |
155 | |
156 | |
157 def testRequestNotTimingOut(self): | |
158 """ | |
159 Test that an iq request with a defined timeout does not time out | |
160 when a response was received before the timeout period elapsed. | |
161 """ | |
162 self.iq.timeout = 60 | |
163 d = self.iq.send() | |
164 self.clock.callLater(1, self.xmlstream.dataReceived, | |
165 "<iq type='result' id='%s'/>" % self.iq['id']) | |
166 self.clock.pump([1, 1]) | |
167 self.failIf(self.clock.calls) | |
168 return d | |
169 | |
170 | |
171 def testDisconnectTimeoutCancellation(self): | |
172 """ | |
173 Test if timeouts for iq's that haven't yet received a response | |
174 are cancelled on stream disconnect. | |
175 """ | |
176 | |
177 self.iq.timeout = 60 | |
178 d = self.iq.send() | |
179 | |
180 xs = self.xmlstream | |
181 xs.connectionLost("Closed by peer") | |
182 self.assertFailure(d, ConnectionLost) | |
183 self.failIf(self.clock.calls) | |
184 return d | |
185 | |
186 | |
187 | |
188 class XmlStreamTest(unittest.TestCase): | |
189 | |
190 def onStreamStart(self, obj): | |
191 self.gotStreamStart = True | |
192 | |
193 | |
194 def onStreamEnd(self, obj): | |
195 self.gotStreamEnd = True | |
196 | |
197 | |
198 def onStreamError(self, obj): | |
199 self.gotStreamError = True | |
200 | |
201 | |
202 def setUp(self): | |
203 """ | |
204 Set up XmlStream and several observers. | |
205 """ | |
206 self.gotStreamStart = False | |
207 self.gotStreamEnd = False | |
208 self.gotStreamError = False | |
209 xs = xmlstream.XmlStream(xmlstream.Authenticator()) | |
210 xs.addObserver('//event/stream/start', self.onStreamStart) | |
211 xs.addObserver('//event/stream/end', self.onStreamEnd) | |
212 xs.addObserver('//event/stream/error', self.onStreamError) | |
213 xs.makeConnection(proto_helpers.StringTransportWithDisconnection()) | |
214 xs.transport.protocol = xs | |
215 xs.namespace = 'testns' | |
216 xs.version = (1, 0) | |
217 self.xmlstream = xs | |
218 | |
219 | |
220 def test_sendHeaderBasic(self): | |
221 """ | |
222 Basic test on the header sent by sendHeader. | |
223 """ | |
224 xs = self.xmlstream | |
225 xs.sendHeader() | |
226 splitHeader = self.xmlstream.transport.value()[0:-1].split(' ') | |
227 self.assertIn("<stream:stream", splitHeader) | |
228 self.assertIn("xmlns:stream='http://etherx.jabber.org/streams'", | |
229 splitHeader) | |
230 self.assertIn("xmlns='testns'", splitHeader) | |
231 self.assertIn("version='1.0'", splitHeader) | |
232 self.assertTrue(xs._headerSent) | |
233 | |
234 | |
235 def test_sendHeaderAdditionalNamespaces(self): | |
236 """ | |
237 Test for additional namespace declarations. | |
238 """ | |
239 xs = self.xmlstream | |
240 xs.prefixes['jabber:server:dialback'] = 'db' | |
241 xs.sendHeader() | |
242 splitHeader = self.xmlstream.transport.value()[0:-1].split(' ') | |
243 self.assertIn("<stream:stream", splitHeader) | |
244 self.assertIn("xmlns:stream='http://etherx.jabber.org/streams'", | |
245 splitHeader) | |
246 self.assertIn("xmlns:db='jabber:server:dialback'", splitHeader) | |
247 self.assertIn("xmlns='testns'", splitHeader) | |
248 self.assertIn("version='1.0'", splitHeader) | |
249 self.assertTrue(xs._headerSent) | |
250 | |
251 | |
252 def test_sendHeaderInitiating(self): | |
253 """ | |
254 Test addressing when initiating a stream. | |
255 """ | |
256 xs = self.xmlstream | |
257 xs.thisEntity = jid.JID('thisHost') | |
258 xs.otherEntity = jid.JID('otherHost') | |
259 xs.initiating = True | |
260 xs.sendHeader() | |
261 splitHeader = xs.transport.value()[0:-1].split(' ') | |
262 self.assertIn("to='otherhost'", splitHeader) | |
263 self.assertIn("from='thishost'", splitHeader) | |
264 | |
265 | |
266 def test_sendHeaderReceiving(self): | |
267 """ | |
268 Test addressing when receiving a stream. | |
269 """ | |
270 xs = self.xmlstream | |
271 xs.thisEntity = jid.JID('thisHost') | |
272 xs.otherEntity = jid.JID('otherHost') | |
273 xs.initiating = False | |
274 xs.sid = 'session01' | |
275 xs.sendHeader() | |
276 splitHeader = xs.transport.value()[0:-1].split(' ') | |
277 self.assertIn("to='otherhost'", splitHeader) | |
278 self.assertIn("from='thishost'", splitHeader) | |
279 self.assertIn("id='session01'", splitHeader) | |
280 | |
281 | |
282 def test_receiveStreamError(self): | |
283 """ | |
284 Test events when a stream error is received. | |
285 """ | |
286 xs = self.xmlstream | |
287 xs.dataReceived("<stream:stream xmlns='jabber:client' " | |
288 "xmlns:stream='http://etherx.jabber.org/streams' " | |
289 "from='example.com' id='12345' version='1.0'>") | |
290 xs.dataReceived("<stream:error/>") | |
291 self.assertTrue(self.gotStreamError) | |
292 self.assertTrue(self.gotStreamEnd) | |
293 | |
294 | |
295 def test_sendStreamErrorInitiating(self): | |
296 """ | |
297 Test sendStreamError on an initiating xmlstream with a header sent. | |
298 | |
299 An error should be sent out and the connection lost. | |
300 """ | |
301 xs = self.xmlstream | |
302 xs.initiating = True | |
303 xs.sendHeader() | |
304 xs.transport.clear() | |
305 xs.sendStreamError(error.StreamError('version-unsupported')) | |
306 self.assertNotEqual('', xs.transport.value()) | |
307 self.assertTrue(self.gotStreamEnd) | |
308 | |
309 | |
310 def test_sendStreamErrorInitiatingNoHeader(self): | |
311 """ | |
312 Test sendStreamError on an initiating xmlstream without having sent a | |
313 header. | |
314 | |
315 In this case, no header should be generated. Also, the error should | |
316 not be sent out on the stream. Just closing the connection. | |
317 """ | |
318 xs = self.xmlstream | |
319 xs.initiating = True | |
320 xs.transport.clear() | |
321 xs.sendStreamError(error.StreamError('version-unsupported')) | |
322 self.assertNot(xs._headerSent) | |
323 self.assertEqual('', xs.transport.value()) | |
324 self.assertTrue(self.gotStreamEnd) | |
325 | |
326 | |
327 def test_sendStreamErrorReceiving(self): | |
328 """ | |
329 Test sendStreamError on a receiving xmlstream with a header sent. | |
330 | |
331 An error should be sent out and the connection lost. | |
332 """ | |
333 xs = self.xmlstream | |
334 xs.initiating = False | |
335 xs.sendHeader() | |
336 xs.transport.clear() | |
337 xs.sendStreamError(error.StreamError('version-unsupported')) | |
338 self.assertNotEqual('', xs.transport.value()) | |
339 self.assertTrue(self.gotStreamEnd) | |
340 | |
341 | |
342 def test_sendStreamErrorReceivingNoHeader(self): | |
343 """ | |
344 Test sendStreamError on a receiving xmlstream without having sent a | |
345 header. | |
346 | |
347 In this case, a header should be generated. Then, the error should | |
348 be sent out on the stream followed by closing the connection. | |
349 """ | |
350 xs = self.xmlstream | |
351 xs.initiating = False | |
352 xs.transport.clear() | |
353 xs.sendStreamError(error.StreamError('version-unsupported')) | |
354 self.assertTrue(xs._headerSent) | |
355 self.assertNotEqual('', xs.transport.value()) | |
356 self.assertTrue(self.gotStreamEnd) | |
357 | |
358 | |
359 def test_reset(self): | |
360 """ | |
361 Test resetting the XML stream to start a new layer. | |
362 """ | |
363 xs = self.xmlstream | |
364 xs.sendHeader() | |
365 stream = xs.stream | |
366 xs.reset() | |
367 self.assertNotEqual(stream, xs.stream) | |
368 self.assertNot(xs._headerSent) | |
369 | |
370 | |
371 def test_send(self): | |
372 """ | |
373 Test send with various types of objects. | |
374 """ | |
375 xs = self.xmlstream | |
376 xs.send('<presence/>') | |
377 self.assertEqual(xs.transport.value(), '<presence/>') | |
378 | |
379 xs.transport.clear() | |
380 el = domish.Element(('testns', 'presence')) | |
381 xs.send(el) | |
382 self.assertEqual(xs.transport.value(), '<presence/>') | |
383 | |
384 xs.transport.clear() | |
385 el = domish.Element(('http://etherx.jabber.org/streams', 'features')) | |
386 xs.send(el) | |
387 self.assertEqual(xs.transport.value(), '<stream:features/>') | |
388 | |
389 | |
390 def test_authenticator(self): | |
391 """ | |
392 Test that the associated authenticator is correctly called. | |
393 """ | |
394 connectionMadeCalls = [] | |
395 streamStartedCalls = [] | |
396 associateWithStreamCalls = [] | |
397 | |
398 class TestAuthenticator: | |
399 def connectionMade(self): | |
400 connectionMadeCalls.append(None) | |
401 | |
402 def streamStarted(self, rootElement): | |
403 streamStartedCalls.append(rootElement) | |
404 | |
405 def associateWithStream(self, xs): | |
406 associateWithStreamCalls.append(xs) | |
407 | |
408 a = TestAuthenticator() | |
409 xs = xmlstream.XmlStream(a) | |
410 self.assertEqual([xs], associateWithStreamCalls) | |
411 xs.connectionMade() | |
412 self.assertEqual([None], connectionMadeCalls) | |
413 xs.dataReceived("<stream:stream xmlns='jabber:client' " | |
414 "xmlns:stream='http://etherx.jabber.org/streams' " | |
415 "from='example.com' id='12345'>") | |
416 self.assertEqual(1, len(streamStartedCalls)) | |
417 xs.reset() | |
418 self.assertEqual([None], connectionMadeCalls) | |
419 | |
420 | |
421 | |
422 class TestError(Exception): | |
423 pass | |
424 | |
425 | |
426 | |
427 class AuthenticatorTest(unittest.TestCase): | |
428 def setUp(self): | |
429 self.authenticator = xmlstream.ListenAuthenticator() | |
430 self.xmlstream = xmlstream.XmlStream(self.authenticator) | |
431 | |
432 | |
433 def test_streamStart(self): | |
434 """ | |
435 Test streamStart to fill the appropriate attributes from the | |
436 stream header. | |
437 """ | |
438 xs = self.xmlstream | |
439 xs.makeConnection(proto_helpers.StringTransport()) | |
440 xs.dataReceived("<stream:stream xmlns='jabber:client' " | |
441 "xmlns:stream='http://etherx.jabber.org/streams' " | |
442 "from='example.org' to='example.com' id='12345' " | |
443 "version='1.0'>") | |
444 self.assertEqual((1, 0), xs.version) | |
445 self.assertIdentical(None, xs.sid) | |
446 self.assertEqual('jabber:client', xs.namespace) | |
447 self.assertIdentical(None, xs.otherEntity) | |
448 self.assertEqual('example.com', xs.thisEntity.host) | |
449 | |
450 | |
451 def test_streamStartLegacy(self): | |
452 """ | |
453 Test streamStart to fill the appropriate attributes from the | |
454 stream header for a pre-XMPP-1.0 header. | |
455 """ | |
456 xs = self.xmlstream | |
457 xs.makeConnection(proto_helpers.StringTransport()) | |
458 xs.dataReceived("<stream:stream xmlns='jabber:client' " | |
459 "xmlns:stream='http://etherx.jabber.org/streams' " | |
460 "from='example.com' id='12345'>") | |
461 self.assertEqual((0, 0), xs.version) | |
462 | |
463 | |
464 def test_streamBadVersionOneDigit(self): | |
465 """ | |
466 Test streamStart to fill the appropriate attributes from the | |
467 stream header for a version with only one digit. | |
468 """ | |
469 xs = self.xmlstream | |
470 xs.makeConnection(proto_helpers.StringTransport()) | |
471 xs.dataReceived("<stream:stream xmlns='jabber:client' " | |
472 "xmlns:stream='http://etherx.jabber.org/streams' " | |
473 "from='example.com' id='12345' version='1'>") | |
474 self.assertEqual((0, 0), xs.version) | |
475 | |
476 | |
477 def test_streamBadVersionNoNumber(self): | |
478 """ | |
479 Test streamStart to fill the appropriate attributes from the | |
480 stream header for a malformed version. | |
481 """ | |
482 xs = self.xmlstream | |
483 xs.makeConnection(proto_helpers.StringTransport()) | |
484 xs.dataReceived("<stream:stream xmlns='jabber:client' " | |
485 "xmlns:stream='http://etherx.jabber.org/streams' " | |
486 "from='example.com' id='12345' version='blah'>") | |
487 self.assertEqual((0, 0), xs.version) | |
488 | |
489 | |
490 | |
491 class ConnectAuthenticatorTest(unittest.TestCase): | |
492 | |
493 def setUp(self): | |
494 self.gotAuthenticated = False | |
495 self.initFailure = None | |
496 self.authenticator = xmlstream.ConnectAuthenticator('otherHost') | |
497 self.xmlstream = xmlstream.XmlStream(self.authenticator) | |
498 self.xmlstream.addObserver('//event/stream/authd', self.onAuthenticated) | |
499 self.xmlstream.addObserver('//event/xmpp/initfailed', self.onInitFailed) | |
500 | |
501 | |
502 def onAuthenticated(self, obj): | |
503 self.gotAuthenticated = True | |
504 | |
505 | |
506 def onInitFailed(self, failure): | |
507 self.initFailure = failure | |
508 | |
509 | |
510 def testSucces(self): | |
511 """ | |
512 Test successful completion of an initialization step. | |
513 """ | |
514 class Initializer: | |
515 def initialize(self): | |
516 pass | |
517 | |
518 init = Initializer() | |
519 self.xmlstream.initializers = [init] | |
520 | |
521 self.authenticator.initializeStream() | |
522 self.assertEqual([], self.xmlstream.initializers) | |
523 self.assertTrue(self.gotAuthenticated) | |
524 | |
525 | |
526 def testFailure(self): | |
527 """ | |
528 Test failure of an initialization step. | |
529 """ | |
530 class Initializer: | |
531 def initialize(self): | |
532 raise TestError | |
533 | |
534 init = Initializer() | |
535 self.xmlstream.initializers = [init] | |
536 | |
537 self.authenticator.initializeStream() | |
538 self.assertEqual([init], self.xmlstream.initializers) | |
539 self.assertFalse(self.gotAuthenticated) | |
540 self.assertNotIdentical(None, self.initFailure) | |
541 self.assertTrue(self.initFailure.check(TestError)) | |
542 | |
543 | |
544 def test_streamStart(self): | |
545 """ | |
546 Test streamStart to fill the appropriate attributes from the | |
547 stream header. | |
548 """ | |
549 self.authenticator.namespace = 'testns' | |
550 xs = self.xmlstream | |
551 xs.makeConnection(proto_helpers.StringTransport()) | |
552 xs.dataReceived("<stream:stream xmlns='jabber:client' " | |
553 "xmlns:stream='http://etherx.jabber.org/streams' " | |
554 "from='example.com' to='example.org' id='12345' " | |
555 "version='1.0'>") | |
556 self.assertEqual((1, 0), xs.version) | |
557 self.assertEqual('12345', xs.sid) | |
558 self.assertEqual('testns', xs.namespace) | |
559 self.assertEqual('example.com', xs.otherEntity.host) | |
560 self.assertIdentical(None, xs.thisEntity) | |
561 self.assertNot(self.gotAuthenticated) | |
562 xs.dataReceived("<stream:features>" | |
563 "<test xmlns='testns'/>" | |
564 "</stream:features>") | |
565 self.assertIn(('testns', 'test'), xs.features) | |
566 self.assertTrue(self.gotAuthenticated) | |
567 | |
568 | |
569 | |
570 class ListenAuthenticatorTest(unittest.TestCase): | |
571 def setUp(self): | |
572 self.authenticator = xmlstream.ListenAuthenticator() | |
573 self.xmlstream = xmlstream.XmlStream(self.authenticator) | |
574 | |
575 | |
576 def test_streamStart(self): | |
577 """ | |
578 Test streamStart to fill the appropriate attributes from the | |
579 stream header. | |
580 """ | |
581 xs = self.xmlstream | |
582 xs.makeConnection(proto_helpers.StringTransport()) | |
583 xs.dataReceived("<stream:stream xmlns='jabber:client' " | |
584 "xmlns:stream='http://etherx.jabber.org/streams' " | |
585 "from='example.org' to='example.com' id='12345' " | |
586 "version='1.0'>") | |
587 self.assertEqual((1, 0), xs.version) | |
588 self.assertIdentical(None, xs.sid) | |
589 self.assertEqual('jabber:client', xs.namespace) | |
590 self.assertIdentical(None, xs.otherEntity) | |
591 self.assertEqual('example.com', xs.thisEntity.host) | |
592 | |
593 | |
594 | |
595 class TLSInitiatingInitializerTest(unittest.TestCase): | |
596 def setUp(self): | |
597 self.output = [] | |
598 self.done = [] | |
599 | |
600 self.savedSSL = xmlstream.ssl | |
601 | |
602 self.authenticator = xmlstream.Authenticator() | |
603 self.xmlstream = xmlstream.XmlStream(self.authenticator) | |
604 self.xmlstream.send = self.output.append | |
605 self.xmlstream.connectionMade() | |
606 self.xmlstream.dataReceived("<stream:stream xmlns='jabber:client' " | |
607 "xmlns:stream='http://etherx.jabber.org/streams' " | |
608 "from='example.com' id='12345' version='1.0'>") | |
609 self.init = xmlstream.TLSInitiatingInitializer(self.xmlstream) | |
610 | |
611 | |
612 def tearDown(self): | |
613 xmlstream.ssl = self.savedSSL | |
614 | |
615 | |
616 def testWantedSupported(self): | |
617 """ | |
618 Test start when TLS is wanted and the SSL library available. | |
619 """ | |
620 self.xmlstream.transport = proto_helpers.StringTransport() | |
621 self.xmlstream.transport.startTLS = lambda ctx: self.done.append('TLS') | |
622 self.xmlstream.reset = lambda: self.done.append('reset') | |
623 self.xmlstream.sendHeader = lambda: self.done.append('header') | |
624 | |
625 d = self.init.start() | |
626 d.addCallback(self.assertEquals, xmlstream.Reset) | |
627 starttls = self.output[0] | |
628 self.assertEquals('starttls', starttls.name) | |
629 self.assertEquals(NS_XMPP_TLS, starttls.uri) | |
630 self.xmlstream.dataReceived("<proceed xmlns='%s'/>" % NS_XMPP_TLS) | |
631 self.assertEquals(['TLS', 'reset', 'header'], self.done) | |
632 | |
633 return d | |
634 | |
635 if not xmlstream.ssl: | |
636 testWantedSupported.skip = "SSL not available" | |
637 | |
638 | |
639 def testWantedNotSupportedNotRequired(self): | |
640 """ | |
641 Test start when TLS is wanted and the SSL library available. | |
642 """ | |
643 xmlstream.ssl = None | |
644 | |
645 d = self.init.start() | |
646 d.addCallback(self.assertEquals, None) | |
647 self.assertEquals([], self.output) | |
648 | |
649 return d | |
650 | |
651 | |
652 def testWantedNotSupportedRequired(self): | |
653 """ | |
654 Test start when TLS is wanted and the SSL library available. | |
655 """ | |
656 xmlstream.ssl = None | |
657 self.init.required = True | |
658 | |
659 d = self.init.start() | |
660 self.assertFailure(d, xmlstream.TLSNotSupported) | |
661 self.assertEquals([], self.output) | |
662 | |
663 return d | |
664 | |
665 | |
666 def testNotWantedRequired(self): | |
667 """ | |
668 Test start when TLS is not wanted, but required by the server. | |
669 """ | |
670 tls = domish.Element(('urn:ietf:params:xml:ns:xmpp-tls', 'starttls')) | |
671 tls.addElement('required') | |
672 self.xmlstream.features = {(tls.uri, tls.name): tls} | |
673 self.init.wanted = False | |
674 | |
675 d = self.init.start() | |
676 self.assertEquals([], self.output) | |
677 self.assertFailure(d, xmlstream.TLSRequired) | |
678 | |
679 return d | |
680 | |
681 | |
682 def testNotWantedNotRequired(self): | |
683 """ | |
684 Test start when TLS is not wanted, but required by the server. | |
685 """ | |
686 tls = domish.Element(('urn:ietf:params:xml:ns:xmpp-tls', 'starttls')) | |
687 self.xmlstream.features = {(tls.uri, tls.name): tls} | |
688 self.init.wanted = False | |
689 | |
690 d = self.init.start() | |
691 d.addCallback(self.assertEqual, None) | |
692 self.assertEquals([], self.output) | |
693 return d | |
694 | |
695 | |
696 def testFailed(self): | |
697 """ | |
698 Test failed TLS negotiation. | |
699 """ | |
700 # Pretend that ssl is supported, it isn't actually used when the | |
701 # server starts out with a failure in response to our initial | |
702 # C{starttls} stanza. | |
703 xmlstream.ssl = 1 | |
704 | |
705 d = self.init.start() | |
706 self.assertFailure(d, xmlstream.TLSFailed) | |
707 self.xmlstream.dataReceived("<failure xmlns='%s'/>" % NS_XMPP_TLS) | |
708 return d | |
709 | |
710 | |
711 | |
712 class TestFeatureInitializer(xmlstream.BaseFeatureInitiatingInitializer): | |
713 feature = ('testns', 'test') | |
714 | |
715 def start(self): | |
716 return defer.succeed(None) | |
717 | |
718 | |
719 | |
720 class BaseFeatureInitiatingInitializerTest(unittest.TestCase): | |
721 | |
722 def setUp(self): | |
723 self.xmlstream = xmlstream.XmlStream(xmlstream.Authenticator()) | |
724 self.init = TestFeatureInitializer(self.xmlstream) | |
725 | |
726 | |
727 def testAdvertized(self): | |
728 """ | |
729 Test that an advertized feature results in successful initialization. | |
730 """ | |
731 self.xmlstream.features = {self.init.feature: | |
732 domish.Element(self.init.feature)} | |
733 return self.init.initialize() | |
734 | |
735 | |
736 def testNotAdvertizedRequired(self): | |
737 """ | |
738 Test that when the feature is not advertized, but required by the | |
739 initializer, an exception is raised. | |
740 """ | |
741 self.init.required = True | |
742 self.assertRaises(xmlstream.FeatureNotAdvertized, self.init.initialize) | |
743 | |
744 | |
745 def testNotAdvertizedNotRequired(self): | |
746 """ | |
747 Test that when the feature is not advertized, and not required by the | |
748 initializer, the initializer silently succeeds. | |
749 """ | |
750 self.init.required = False | |
751 self.assertIdentical(None, self.init.initialize()) | |
752 | |
753 | |
754 | |
755 class ToResponseTest(unittest.TestCase): | |
756 | |
757 def test_toResponse(self): | |
758 """ | |
759 Test that a response stanza is generated with addressing swapped. | |
760 """ | |
761 stanza = domish.Element(('jabber:client', 'iq')) | |
762 stanza['type'] = 'get' | |
763 stanza['to'] = 'user1@example.com' | |
764 stanza['from'] = 'user2@example.com/resource' | |
765 stanza['id'] = 'stanza1' | |
766 response = xmlstream.toResponse(stanza, 'result') | |
767 self.assertNotIdentical(stanza, response) | |
768 self.assertEqual(response['from'], 'user1@example.com') | |
769 self.assertEqual(response['to'], 'user2@example.com/resource') | |
770 self.assertEqual(response['type'], 'result') | |
771 self.assertEqual(response['id'], 'stanza1') | |
772 | |
773 | |
774 def test_toResponseNoFrom(self): | |
775 """ | |
776 Test that a response is generated from a stanza without a from address. | |
777 """ | |
778 stanza = domish.Element(('jabber:client', 'iq')) | |
779 stanza['type'] = 'get' | |
780 stanza['to'] = 'user1@example.com' | |
781 response = xmlstream.toResponse(stanza) | |
782 self.assertEqual(response['from'], 'user1@example.com') | |
783 self.failIf(response.hasAttribute('to')) | |
784 | |
785 | |
786 def test_toResponseNoTo(self): | |
787 """ | |
788 Test that a response is generated from a stanza without a to address. | |
789 """ | |
790 stanza = domish.Element(('jabber:client', 'iq')) | |
791 stanza['type'] = 'get' | |
792 stanza['from'] = 'user2@example.com/resource' | |
793 response = xmlstream.toResponse(stanza) | |
794 self.failIf(response.hasAttribute('from')) | |
795 self.assertEqual(response['to'], 'user2@example.com/resource') | |
796 | |
797 | |
798 def test_toResponseNoAddressing(self): | |
799 """ | |
800 Test that a response is generated from a stanza without any addressing. | |
801 """ | |
802 stanza = domish.Element(('jabber:client', 'message')) | |
803 stanza['type'] = 'chat' | |
804 response = xmlstream.toResponse(stanza) | |
805 self.failIf(response.hasAttribute('to')) | |
806 self.failIf(response.hasAttribute('from')) | |
807 | |
808 | |
809 def test_noID(self): | |
810 """ | |
811 Test that a proper response is generated without id attribute. | |
812 """ | |
813 stanza = domish.Element(('jabber:client', 'message')) | |
814 response = xmlstream.toResponse(stanza) | |
815 self.failIf(response.hasAttribute('id')) | |
816 | |
817 | |
818 | |
819 class DummyFactory(object): | |
820 """ | |
821 Dummy XmlStream factory that only registers bootstrap observers. | |
822 """ | |
823 def __init__(self): | |
824 self.callbacks = {} | |
825 | |
826 | |
827 def addBootstrap(self, event, callback): | |
828 self.callbacks[event] = callback | |
829 | |
830 | |
831 | |
832 class DummyXMPPHandler(xmlstream.XMPPHandler): | |
833 """ | |
834 Dummy XMPP subprotocol handler to count the methods are called on it. | |
835 """ | |
836 def __init__(self): | |
837 self.doneMade = 0 | |
838 self.doneInitialized = 0 | |
839 self.doneLost = 0 | |
840 | |
841 | |
842 def makeConnection(self, xs): | |
843 self.connectionMade() | |
844 | |
845 | |
846 def connectionMade(self): | |
847 self.doneMade += 1 | |
848 | |
849 | |
850 def connectionInitialized(self): | |
851 self.doneInitialized += 1 | |
852 | |
853 | |
854 def connectionLost(self, reason): | |
855 self.doneLost += 1 | |
856 | |
857 | |
858 | |
859 class XMPPHandlerTest(unittest.TestCase): | |
860 """ | |
861 Tests for L{xmlstream.XMPPHandler}. | |
862 """ | |
863 | |
864 def test_interface(self): | |
865 """ | |
866 L{xmlstream.XMPPHandler} implements L{ijabber.IXMPPHandler}. | |
867 """ | |
868 verifyObject(ijabber.IXMPPHandler, xmlstream.XMPPHandler()) | |
869 | |
870 | |
871 def test_send(self): | |
872 """ | |
873 Test that data is passed on for sending by the stream manager. | |
874 """ | |
875 class DummyStreamManager(object): | |
876 def __init__(self): | |
877 self.outlist = [] | |
878 | |
879 def send(self, data): | |
880 self.outlist.append(data) | |
881 | |
882 handler = xmlstream.XMPPHandler() | |
883 handler.parent = DummyStreamManager() | |
884 handler.send('<presence/>') | |
885 self.assertEquals(['<presence/>'], handler.parent.outlist) | |
886 | |
887 | |
888 def test_makeConnection(self): | |
889 """ | |
890 Test that makeConnection saves the XML stream and calls connectionMade. | |
891 """ | |
892 class TestXMPPHandler(xmlstream.XMPPHandler): | |
893 def connectionMade(self): | |
894 self.doneMade = True | |
895 | |
896 handler = TestXMPPHandler() | |
897 xs = xmlstream.XmlStream(xmlstream.Authenticator()) | |
898 handler.makeConnection(xs) | |
899 self.assertTrue(handler.doneMade) | |
900 self.assertIdentical(xs, handler.xmlstream) | |
901 | |
902 | |
903 def test_connectionLost(self): | |
904 """ | |
905 Test that connectionLost forgets the XML stream. | |
906 """ | |
907 handler = xmlstream.XMPPHandler() | |
908 xs = xmlstream.XmlStream(xmlstream.Authenticator()) | |
909 handler.makeConnection(xs) | |
910 handler.connectionLost(Exception()) | |
911 self.assertIdentical(None, handler.xmlstream) | |
912 | |
913 | |
914 | |
915 class XMPPHandlerCollectionTest(unittest.TestCase): | |
916 """ | |
917 Tests for L{xmlstream.XMPPHandlerCollection}. | |
918 """ | |
919 | |
920 def setUp(self): | |
921 self.collection = xmlstream.XMPPHandlerCollection() | |
922 | |
923 | |
924 def test_interface(self): | |
925 """ | |
926 L{xmlstream.StreamManager} implements L{ijabber.IXMPPHandlerCollection}. | |
927 """ | |
928 verifyObject(ijabber.IXMPPHandlerCollection, self.collection) | |
929 | |
930 | |
931 def test_addHandler(self): | |
932 """ | |
933 Test the addition of a protocol handler. | |
934 """ | |
935 handler = DummyXMPPHandler() | |
936 handler.setHandlerParent(self.collection) | |
937 self.assertIn(handler, self.collection) | |
938 self.assertIdentical(self.collection, handler.parent) | |
939 | |
940 | |
941 def test_removeHandler(self): | |
942 """ | |
943 Test removal of a protocol handler. | |
944 """ | |
945 handler = DummyXMPPHandler() | |
946 handler.setHandlerParent(self.collection) | |
947 handler.disownHandlerParent(self.collection) | |
948 self.assertNotIn(handler, self.collection) | |
949 self.assertIdentical(None, handler.parent) | |
950 | |
951 | |
952 | |
953 class StreamManagerTest(unittest.TestCase): | |
954 """ | |
955 Tests for L{xmlstream.StreamManager}. | |
956 """ | |
957 | |
958 def setUp(self): | |
959 factory = DummyFactory() | |
960 self.streamManager = xmlstream.StreamManager(factory) | |
961 | |
962 | |
963 def test_basic(self): | |
964 """ | |
965 Test correct initialization and setup of factory observers. | |
966 """ | |
967 sm = self.streamManager | |
968 self.assertIdentical(None, sm.xmlstream) | |
969 self.assertEquals([], sm.handlers) | |
970 self.assertEquals(sm._connected, | |
971 sm.factory.callbacks['//event/stream/connected']) | |
972 self.assertEquals(sm._authd, | |
973 sm.factory.callbacks['//event/stream/authd']) | |
974 self.assertEquals(sm._disconnected, | |
975 sm.factory.callbacks['//event/stream/end']) | |
976 self.assertEquals(sm.initializationFailed, | |
977 sm.factory.callbacks['//event/xmpp/initfailed']) | |
978 | |
979 | |
980 def test_connected(self): | |
981 """ | |
982 Test that protocol handlers have their connectionMade method called | |
983 when the XML stream is connected. | |
984 """ | |
985 sm = self.streamManager | |
986 handler = DummyXMPPHandler() | |
987 handler.setHandlerParent(sm) | |
988 xs = xmlstream.XmlStream(xmlstream.Authenticator()) | |
989 sm._connected(xs) | |
990 self.assertEquals(1, handler.doneMade) | |
991 self.assertEquals(0, handler.doneInitialized) | |
992 self.assertEquals(0, handler.doneLost) | |
993 | |
994 | |
995 def test_connectedLogTrafficFalse(self): | |
996 """ | |
997 Test raw data functions unset when logTraffic is set to False. | |
998 """ | |
999 sm = self.streamManager | |
1000 handler = DummyXMPPHandler() | |
1001 handler.setHandlerParent(sm) | |
1002 xs = xmlstream.XmlStream(xmlstream.Authenticator()) | |
1003 sm._connected(xs) | |
1004 self.assertIdentical(None, xs.rawDataInFn) | |
1005 self.assertIdentical(None, xs.rawDataOutFn) | |
1006 | |
1007 | |
1008 def test_connectedLogTrafficTrue(self): | |
1009 """ | |
1010 Test raw data functions set when logTraffic is set to True. | |
1011 """ | |
1012 sm = self.streamManager | |
1013 sm.logTraffic = True | |
1014 handler = DummyXMPPHandler() | |
1015 handler.setHandlerParent(sm) | |
1016 xs = xmlstream.XmlStream(xmlstream.Authenticator()) | |
1017 sm._connected(xs) | |
1018 self.assertNotIdentical(None, xs.rawDataInFn) | |
1019 self.assertNotIdentical(None, xs.rawDataOutFn) | |
1020 | |
1021 | |
1022 def test_authd(self): | |
1023 """ | |
1024 Test that protocol handlers have their connectionInitialized method | |
1025 called when the XML stream is initialized. | |
1026 """ | |
1027 sm = self.streamManager | |
1028 handler = DummyXMPPHandler() | |
1029 handler.setHandlerParent(sm) | |
1030 xs = xmlstream.XmlStream(xmlstream.Authenticator()) | |
1031 sm._authd(xs) | |
1032 self.assertEquals(0, handler.doneMade) | |
1033 self.assertEquals(1, handler.doneInitialized) | |
1034 self.assertEquals(0, handler.doneLost) | |
1035 | |
1036 | |
1037 def test_disconnected(self): | |
1038 """ | |
1039 Test that protocol handlers have their connectionLost method | |
1040 called when the XML stream is disconnected. | |
1041 """ | |
1042 sm = self.streamManager | |
1043 handler = DummyXMPPHandler() | |
1044 handler.setHandlerParent(sm) | |
1045 xs = xmlstream.XmlStream(xmlstream.Authenticator()) | |
1046 sm._disconnected(xs) | |
1047 self.assertEquals(0, handler.doneMade) | |
1048 self.assertEquals(0, handler.doneInitialized) | |
1049 self.assertEquals(1, handler.doneLost) | |
1050 | |
1051 | |
1052 def test_addHandler(self): | |
1053 """ | |
1054 Test the addition of a protocol handler while not connected. | |
1055 """ | |
1056 sm = self.streamManager | |
1057 handler = DummyXMPPHandler() | |
1058 handler.setHandlerParent(sm) | |
1059 | |
1060 self.assertEquals(0, handler.doneMade) | |
1061 self.assertEquals(0, handler.doneInitialized) | |
1062 self.assertEquals(0, handler.doneLost) | |
1063 | |
1064 | |
1065 def test_addHandlerInitialized(self): | |
1066 """ | |
1067 Test the addition of a protocol handler after the stream | |
1068 have been initialized. | |
1069 | |
1070 Make sure that the handler will have the connected stream | |
1071 passed via C{makeConnection} and have C{connectionInitialized} | |
1072 called. | |
1073 """ | |
1074 sm = self.streamManager | |
1075 xs = xmlstream.XmlStream(xmlstream.Authenticator()) | |
1076 sm._connected(xs) | |
1077 sm._authd(xs) | |
1078 handler = DummyXMPPHandler() | |
1079 handler.setHandlerParent(sm) | |
1080 | |
1081 self.assertEquals(1, handler.doneMade) | |
1082 self.assertEquals(1, handler.doneInitialized) | |
1083 self.assertEquals(0, handler.doneLost) | |
1084 | |
1085 | |
1086 def test_sendInitialized(self): | |
1087 """ | |
1088 Test send when the stream has been initialized. | |
1089 | |
1090 The data should be sent directly over the XML stream. | |
1091 """ | |
1092 factory = xmlstream.XmlStreamFactory(xmlstream.Authenticator()) | |
1093 sm = xmlstream.StreamManager(factory) | |
1094 xs = factory.buildProtocol(None) | |
1095 xs.transport = proto_helpers.StringTransport() | |
1096 xs.connectionMade() | |
1097 xs.dataReceived("<stream:stream xmlns='jabber:client' " | |
1098 "xmlns:stream='http://etherx.jabber.org/streams' " | |
1099 "from='example.com' id='12345'>") | |
1100 xs.dispatch(xs, "//event/stream/authd") | |
1101 sm.send("<presence/>") | |
1102 self.assertEquals("<presence/>", xs.transport.value()) | |
1103 | |
1104 | |
1105 def test_sendNotConnected(self): | |
1106 """ | |
1107 Test send when there is no established XML stream. | |
1108 | |
1109 The data should be cached until an XML stream has been established and | |
1110 initialized. | |
1111 """ | |
1112 factory = xmlstream.XmlStreamFactory(xmlstream.Authenticator()) | |
1113 sm = xmlstream.StreamManager(factory) | |
1114 handler = DummyXMPPHandler() | |
1115 sm.addHandler(handler) | |
1116 | |
1117 xs = factory.buildProtocol(None) | |
1118 xs.transport = proto_helpers.StringTransport() | |
1119 sm.send("<presence/>") | |
1120 self.assertEquals("", xs.transport.value()) | |
1121 self.assertEquals("<presence/>", sm._packetQueue[0]) | |
1122 | |
1123 xs.connectionMade() | |
1124 self.assertEquals("", xs.transport.value()) | |
1125 self.assertEquals("<presence/>", sm._packetQueue[0]) | |
1126 | |
1127 xs.dataReceived("<stream:stream xmlns='jabber:client' " | |
1128 "xmlns:stream='http://etherx.jabber.org/streams' " | |
1129 "from='example.com' id='12345'>") | |
1130 xs.dispatch(xs, "//event/stream/authd") | |
1131 | |
1132 self.assertEquals("<presence/>", xs.transport.value()) | |
1133 self.failIf(sm._packetQueue) | |
1134 | |
1135 | |
1136 def test_sendNotInitialized(self): | |
1137 """ | |
1138 Test send when the stream is connected but not yet initialized. | |
1139 | |
1140 The data should be cached until the XML stream has been initialized. | |
1141 """ | |
1142 factory = xmlstream.XmlStreamFactory(xmlstream.Authenticator()) | |
1143 sm = xmlstream.StreamManager(factory) | |
1144 xs = factory.buildProtocol(None) | |
1145 xs.transport = proto_helpers.StringTransport() | |
1146 xs.connectionMade() | |
1147 xs.dataReceived("<stream:stream xmlns='jabber:client' " | |
1148 "xmlns:stream='http://etherx.jabber.org/streams' " | |
1149 "from='example.com' id='12345'>") | |
1150 sm.send("<presence/>") | |
1151 self.assertEquals("", xs.transport.value()) | |
1152 self.assertEquals("<presence/>", sm._packetQueue[0]) | |
1153 | |
1154 | |
1155 def test_sendDisconnected(self): | |
1156 """ | |
1157 Test send after XML stream disconnection. | |
1158 | |
1159 The data should be cached until a new XML stream has been established | |
1160 and initialized. | |
1161 """ | |
1162 factory = xmlstream.XmlStreamFactory(xmlstream.Authenticator()) | |
1163 sm = xmlstream.StreamManager(factory) | |
1164 handler = DummyXMPPHandler() | |
1165 sm.addHandler(handler) | |
1166 | |
1167 xs = factory.buildProtocol(None) | |
1168 xs.connectionMade() | |
1169 xs.transport = proto_helpers.StringTransport() | |
1170 xs.connectionLost(None) | |
1171 | |
1172 sm.send("<presence/>") | |
1173 self.assertEquals("", xs.transport.value()) | |
1174 self.assertEquals("<presence/>", sm._packetQueue[0]) | |
OLD | NEW |