17.12
This commit is contained in:
@@ -0,0 +1 @@
|
||||
"Words tests"
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,440 @@
|
||||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
|
||||
"""
|
||||
Tests for L{twisted.words.protocols.jabber.component}
|
||||
"""
|
||||
from hashlib import sha1
|
||||
|
||||
from zope.interface.verify import verifyObject
|
||||
|
||||
from twisted.python import failure
|
||||
from twisted.python.compat import unicode
|
||||
from twisted.trial import unittest
|
||||
from twisted.words.protocols.jabber import component, ijabber, xmlstream
|
||||
from twisted.words.protocols.jabber.jid import JID
|
||||
from twisted.words.xish import domish
|
||||
from twisted.words.xish.utility import XmlPipe
|
||||
|
||||
class DummyTransport:
|
||||
def __init__(self, list):
|
||||
self.list = list
|
||||
|
||||
def write(self, bytes):
|
||||
self.list.append(bytes)
|
||||
|
||||
class ComponentInitiatingInitializerTests(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.output = []
|
||||
|
||||
self.authenticator = xmlstream.Authenticator()
|
||||
self.authenticator.password = u'secret'
|
||||
self.xmlstream = xmlstream.XmlStream(self.authenticator)
|
||||
self.xmlstream.namespace = 'test:component'
|
||||
self.xmlstream.send = self.output.append
|
||||
self.xmlstream.connectionMade()
|
||||
self.xmlstream.dataReceived(
|
||||
"<stream:stream xmlns='test:component' "
|
||||
"xmlns:stream='http://etherx.jabber.org/streams' "
|
||||
"from='example.com' id='12345' version='1.0'>")
|
||||
self.xmlstream.sid = u'12345'
|
||||
self.init = component.ComponentInitiatingInitializer(self.xmlstream)
|
||||
|
||||
def testHandshake(self):
|
||||
"""
|
||||
Test basic operations of component handshake.
|
||||
"""
|
||||
|
||||
d = self.init.initialize()
|
||||
|
||||
# the initializer should have sent the handshake request
|
||||
|
||||
handshake = self.output[-1]
|
||||
self.assertEqual('handshake', handshake.name)
|
||||
self.assertEqual('test:component', handshake.uri)
|
||||
self.assertEqual(sha1(b'12345' + b'secret').hexdigest(),
|
||||
unicode(handshake))
|
||||
|
||||
# successful authentication
|
||||
|
||||
handshake.children = []
|
||||
self.xmlstream.dataReceived(handshake.toXml())
|
||||
|
||||
return d
|
||||
|
||||
class ComponentAuthTests(unittest.TestCase):
|
||||
def authPassed(self, stream):
|
||||
self.authComplete = True
|
||||
|
||||
def testAuth(self):
|
||||
self.authComplete = False
|
||||
outlist = []
|
||||
|
||||
ca = component.ConnectComponentAuthenticator(u"cjid", u"secret")
|
||||
xs = xmlstream.XmlStream(ca)
|
||||
xs.transport = DummyTransport(outlist)
|
||||
|
||||
xs.addObserver(xmlstream.STREAM_AUTHD_EVENT,
|
||||
self.authPassed)
|
||||
|
||||
# Go...
|
||||
xs.connectionMade()
|
||||
xs.dataReceived(b"<stream:stream xmlns='jabber:component:accept' xmlns:stream='http://etherx.jabber.org/streams' from='cjid' id='12345'>")
|
||||
|
||||
# Calculate what we expect the handshake value to be
|
||||
hv = sha1(b"12345" + b"secret").hexdigest().encode('ascii')
|
||||
|
||||
self.assertEqual(outlist[1], b"<handshake>" + hv + b"</handshake>")
|
||||
|
||||
xs.dataReceived("<handshake/>")
|
||||
|
||||
self.assertEqual(self.authComplete, True)
|
||||
|
||||
|
||||
|
||||
class ServiceTests(unittest.TestCase):
|
||||
"""
|
||||
Tests for L{component.Service}.
|
||||
"""
|
||||
|
||||
def test_interface(self):
|
||||
"""
|
||||
L{component.Service} implements L{ijabber.IService}.
|
||||
"""
|
||||
service = component.Service()
|
||||
verifyObject(ijabber.IService, service)
|
||||
|
||||
|
||||
|
||||
class JabberServiceHarness(component.Service):
|
||||
def __init__(self):
|
||||
self.componentConnectedFlag = False
|
||||
self.componentDisconnectedFlag = False
|
||||
self.transportConnectedFlag = False
|
||||
|
||||
def componentConnected(self, xmlstream):
|
||||
self.componentConnectedFlag = True
|
||||
|
||||
def componentDisconnected(self):
|
||||
self.componentDisconnectedFlag = True
|
||||
|
||||
def transportConnected(self, xmlstream):
|
||||
self.transportConnectedFlag = True
|
||||
|
||||
|
||||
class JabberServiceManagerTests(unittest.TestCase):
|
||||
def testSM(self):
|
||||
# Setup service manager and test harnes
|
||||
sm = component.ServiceManager("foo", "password")
|
||||
svc = JabberServiceHarness()
|
||||
svc.setServiceParent(sm)
|
||||
|
||||
# Create a write list
|
||||
wlist = []
|
||||
|
||||
# Setup a XmlStream
|
||||
xs = sm.getFactory().buildProtocol(None)
|
||||
xs.transport = self
|
||||
xs.transport.write = wlist.append
|
||||
|
||||
# Indicate that it's connected
|
||||
xs.connectionMade()
|
||||
|
||||
# Ensure the test service harness got notified
|
||||
self.assertEqual(True, svc.transportConnectedFlag)
|
||||
|
||||
# Jump ahead and pretend like the stream got auth'd
|
||||
xs.dispatch(xs, xmlstream.STREAM_AUTHD_EVENT)
|
||||
|
||||
# Ensure the test service harness got notified
|
||||
self.assertEqual(True, svc.componentConnectedFlag)
|
||||
|
||||
# Pretend to drop the connection
|
||||
xs.connectionLost(None)
|
||||
|
||||
# Ensure the test service harness got notified
|
||||
self.assertEqual(True, svc.componentDisconnectedFlag)
|
||||
|
||||
|
||||
|
||||
class RouterTests(unittest.TestCase):
|
||||
"""
|
||||
Tests for L{component.Router}.
|
||||
"""
|
||||
|
||||
def test_addRoute(self):
|
||||
"""
|
||||
Test route registration and routing on incoming stanzas.
|
||||
"""
|
||||
router = component.Router()
|
||||
routed = []
|
||||
router.route = lambda element: routed.append(element)
|
||||
|
||||
pipe = XmlPipe()
|
||||
router.addRoute('example.org', pipe.sink)
|
||||
self.assertEqual(1, len(router.routes))
|
||||
self.assertEqual(pipe.sink, router.routes['example.org'])
|
||||
|
||||
element = domish.Element(('testns', 'test'))
|
||||
pipe.source.send(element)
|
||||
self.assertEqual([element], routed)
|
||||
|
||||
|
||||
def test_route(self):
|
||||
"""
|
||||
Test routing of a message.
|
||||
"""
|
||||
component1 = XmlPipe()
|
||||
component2 = XmlPipe()
|
||||
router = component.Router()
|
||||
router.addRoute('component1.example.org', component1.sink)
|
||||
router.addRoute('component2.example.org', component2.sink)
|
||||
|
||||
outgoing = []
|
||||
component2.source.addObserver('/*',
|
||||
lambda element: outgoing.append(element))
|
||||
stanza = domish.Element((None, 'presence'))
|
||||
stanza['from'] = 'component1.example.org'
|
||||
stanza['to'] = 'component2.example.org'
|
||||
component1.source.send(stanza)
|
||||
self.assertEqual([stanza], outgoing)
|
||||
|
||||
|
||||
def test_routeDefault(self):
|
||||
"""
|
||||
Test routing of a message using the default route.
|
||||
|
||||
The default route is the one with L{None} as its key in the
|
||||
routing table. It is taken when there is no more specific route
|
||||
in the routing table that matches the stanza's destination.
|
||||
"""
|
||||
component1 = XmlPipe()
|
||||
s2s = XmlPipe()
|
||||
router = component.Router()
|
||||
router.addRoute('component1.example.org', component1.sink)
|
||||
router.addRoute(None, s2s.sink)
|
||||
|
||||
outgoing = []
|
||||
s2s.source.addObserver('/*', lambda element: outgoing.append(element))
|
||||
stanza = domish.Element((None, 'presence'))
|
||||
stanza['from'] = 'component1.example.org'
|
||||
stanza['to'] = 'example.com'
|
||||
component1.source.send(stanza)
|
||||
self.assertEqual([stanza], outgoing)
|
||||
|
||||
|
||||
|
||||
class ListenComponentAuthenticatorTests(unittest.TestCase):
|
||||
"""
|
||||
Tests for L{component.ListenComponentAuthenticator}.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.output = []
|
||||
authenticator = component.ListenComponentAuthenticator('secret')
|
||||
self.xmlstream = xmlstream.XmlStream(authenticator)
|
||||
self.xmlstream.send = self.output.append
|
||||
|
||||
|
||||
def loseConnection(self):
|
||||
"""
|
||||
Stub loseConnection because we are a transport.
|
||||
"""
|
||||
self.xmlstream.connectionLost("no reason")
|
||||
|
||||
|
||||
def test_streamStarted(self):
|
||||
"""
|
||||
The received stream header should set several attributes.
|
||||
"""
|
||||
observers = []
|
||||
|
||||
def addOnetimeObserver(event, observerfn):
|
||||
observers.append((event, observerfn))
|
||||
|
||||
xs = self.xmlstream
|
||||
xs.addOnetimeObserver = addOnetimeObserver
|
||||
|
||||
xs.makeConnection(self)
|
||||
self.assertIdentical(None, xs.sid)
|
||||
self.assertFalse(xs._headerSent)
|
||||
|
||||
xs.dataReceived("<stream:stream xmlns='jabber:component:accept' "
|
||||
"xmlns:stream='http://etherx.jabber.org/streams' "
|
||||
"to='component.example.org'>")
|
||||
self.assertEqual((0, 0), xs.version)
|
||||
self.assertNotIdentical(None, xs.sid)
|
||||
self.assertTrue(xs._headerSent)
|
||||
self.assertEqual(('/*', xs.authenticator.onElement), observers[-1])
|
||||
|
||||
|
||||
def test_streamStartedWrongNamespace(self):
|
||||
"""
|
||||
The received stream header should have a correct namespace.
|
||||
"""
|
||||
streamErrors = []
|
||||
|
||||
xs = self.xmlstream
|
||||
xs.sendStreamError = streamErrors.append
|
||||
xs.makeConnection(self)
|
||||
xs.dataReceived("<stream:stream xmlns='jabber:client' "
|
||||
"xmlns:stream='http://etherx.jabber.org/streams' "
|
||||
"to='component.example.org'>")
|
||||
self.assertEqual(1, len(streamErrors))
|
||||
self.assertEqual('invalid-namespace', streamErrors[-1].condition)
|
||||
|
||||
|
||||
def test_streamStartedNoTo(self):
|
||||
"""
|
||||
The received stream header should have a 'to' attribute.
|
||||
"""
|
||||
streamErrors = []
|
||||
|
||||
xs = self.xmlstream
|
||||
xs.sendStreamError = streamErrors.append
|
||||
xs.makeConnection(self)
|
||||
xs.dataReceived("<stream:stream xmlns='jabber:component:accept' "
|
||||
"xmlns:stream='http://etherx.jabber.org/streams'>")
|
||||
self.assertEqual(1, len(streamErrors))
|
||||
self.assertEqual('improper-addressing', streamErrors[-1].condition)
|
||||
|
||||
|
||||
def test_onElement(self):
|
||||
"""
|
||||
We expect a handshake element with a hash.
|
||||
"""
|
||||
handshakes = []
|
||||
|
||||
xs = self.xmlstream
|
||||
xs.authenticator.onHandshake = handshakes.append
|
||||
|
||||
handshake = domish.Element(('jabber:component:accept', 'handshake'))
|
||||
handshake.addContent(u'1234')
|
||||
xs.authenticator.onElement(handshake)
|
||||
self.assertEqual('1234', handshakes[-1])
|
||||
|
||||
def test_onElementNotHandshake(self):
|
||||
"""
|
||||
Reject elements that are not handshakes
|
||||
"""
|
||||
handshakes = []
|
||||
streamErrors = []
|
||||
|
||||
xs = self.xmlstream
|
||||
xs.authenticator.onHandshake = handshakes.append
|
||||
xs.sendStreamError = streamErrors.append
|
||||
|
||||
element = domish.Element(('jabber:component:accept', 'message'))
|
||||
xs.authenticator.onElement(element)
|
||||
self.assertFalse(handshakes)
|
||||
self.assertEqual('not-authorized', streamErrors[-1].condition)
|
||||
|
||||
|
||||
def test_onHandshake(self):
|
||||
"""
|
||||
Receiving a handshake matching the secret authenticates the stream.
|
||||
"""
|
||||
authd = []
|
||||
|
||||
def authenticated(xs):
|
||||
authd.append(xs)
|
||||
|
||||
xs = self.xmlstream
|
||||
xs.addOnetimeObserver(xmlstream.STREAM_AUTHD_EVENT, authenticated)
|
||||
xs.sid = u'1234'
|
||||
theHash = '32532c0f7dbf1253c095b18b18e36d38d94c1256'
|
||||
xs.authenticator.onHandshake(theHash)
|
||||
self.assertEqual('<handshake/>', self.output[-1])
|
||||
self.assertEqual(1, len(authd))
|
||||
|
||||
|
||||
def test_onHandshakeWrongHash(self):
|
||||
"""
|
||||
Receiving a bad handshake should yield a stream error.
|
||||
"""
|
||||
streamErrors = []
|
||||
authd = []
|
||||
|
||||
def authenticated(xs):
|
||||
authd.append(xs)
|
||||
|
||||
xs = self.xmlstream
|
||||
xs.addOnetimeObserver(xmlstream.STREAM_AUTHD_EVENT, authenticated)
|
||||
xs.sendStreamError = streamErrors.append
|
||||
|
||||
xs.sid = u'1234'
|
||||
theHash = '1234'
|
||||
xs.authenticator.onHandshake(theHash)
|
||||
self.assertEqual('not-authorized', streamErrors[-1].condition)
|
||||
self.assertEqual(0, len(authd))
|
||||
|
||||
|
||||
|
||||
class XMPPComponentServerFactoryTests(unittest.TestCase):
|
||||
"""
|
||||
Tests for L{component.XMPPComponentServerFactory}.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.router = component.Router()
|
||||
self.factory = component.XMPPComponentServerFactory(self.router,
|
||||
'secret')
|
||||
self.xmlstream = self.factory.buildProtocol(None)
|
||||
self.xmlstream.thisEntity = JID('component.example.org')
|
||||
|
||||
|
||||
def test_makeConnection(self):
|
||||
"""
|
||||
A new connection increases the stream serial count. No logs by default.
|
||||
"""
|
||||
self.xmlstream.dispatch(self.xmlstream,
|
||||
xmlstream.STREAM_CONNECTED_EVENT)
|
||||
self.assertEqual(0, self.xmlstream.serial)
|
||||
self.assertEqual(1, self.factory.serial)
|
||||
self.assertIdentical(None, self.xmlstream.rawDataInFn)
|
||||
self.assertIdentical(None, self.xmlstream.rawDataOutFn)
|
||||
|
||||
|
||||
def test_makeConnectionLogTraffic(self):
|
||||
"""
|
||||
Setting logTraffic should set up raw data loggers.
|
||||
"""
|
||||
self.factory.logTraffic = True
|
||||
self.xmlstream.dispatch(self.xmlstream,
|
||||
xmlstream.STREAM_CONNECTED_EVENT)
|
||||
self.assertNotIdentical(None, self.xmlstream.rawDataInFn)
|
||||
self.assertNotIdentical(None, self.xmlstream.rawDataOutFn)
|
||||
|
||||
|
||||
def test_onError(self):
|
||||
"""
|
||||
An observer for stream errors should trigger onError to log it.
|
||||
"""
|
||||
self.xmlstream.dispatch(self.xmlstream,
|
||||
xmlstream.STREAM_CONNECTED_EVENT)
|
||||
|
||||
class TestError(Exception):
|
||||
pass
|
||||
|
||||
reason = failure.Failure(TestError())
|
||||
self.xmlstream.dispatch(reason, xmlstream.STREAM_ERROR_EVENT)
|
||||
self.assertEqual(1, len(self.flushLoggedErrors(TestError)))
|
||||
|
||||
|
||||
def test_connectionInitialized(self):
|
||||
"""
|
||||
Make sure a new stream is added to the routing table.
|
||||
"""
|
||||
self.xmlstream.dispatch(self.xmlstream, xmlstream.STREAM_AUTHD_EVENT)
|
||||
self.assertIn('component.example.org', self.router.routes)
|
||||
self.assertIdentical(self.xmlstream,
|
||||
self.router.routes['component.example.org'])
|
||||
|
||||
|
||||
def test_connectionLost(self):
|
||||
"""
|
||||
Make sure a stream is removed from the routing table on disconnect.
|
||||
"""
|
||||
self.xmlstream.dispatch(self.xmlstream, xmlstream.STREAM_AUTHD_EVENT)
|
||||
self.xmlstream.dispatch(None, xmlstream.STREAM_END_EVENT)
|
||||
self.assertNotIn('component.example.org', self.router.routes)
|
||||
@@ -0,0 +1,36 @@
|
||||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
|
||||
"""
|
||||
Tests for L{twisted.words.protocols.jabber.jstrports}.
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, division
|
||||
|
||||
from twisted.trial import unittest
|
||||
|
||||
from twisted.words.protocols.jabber import jstrports
|
||||
from twisted.application.internet import TCPClient
|
||||
|
||||
|
||||
class JabberStrPortsPlaceHolderTests(unittest.TestCase):
|
||||
"""
|
||||
Tests for L{jstrports}
|
||||
"""
|
||||
|
||||
def test_parse(self):
|
||||
"""
|
||||
L{jstrports.parse} accepts an endpoint description string and returns a
|
||||
tuple and dict of parsed endpoint arguments.
|
||||
"""
|
||||
expected = ('TCP', ('DOMAIN', 65535, 'Factory'), {})
|
||||
got = jstrports.parse("tcp:DOMAIN:65535", "Factory")
|
||||
self.assertEqual(expected, got)
|
||||
|
||||
|
||||
def test_client(self):
|
||||
"""
|
||||
L{jstrports.client} returns a L{TCPClient} service.
|
||||
"""
|
||||
got = jstrports.client("tcp:DOMAIN:65535", "Factory")
|
||||
self.assertIsInstance(got, TCPClient)
|
||||
@@ -0,0 +1,115 @@
|
||||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
|
||||
from twisted.trial import unittest
|
||||
|
||||
from twisted.words.protocols.jabber.xmpp_stringprep import (
|
||||
nodeprep, resourceprep, nameprep)
|
||||
|
||||
|
||||
|
||||
class DeprecationTests(unittest.TestCase):
|
||||
"""
|
||||
Deprecations in L{twisted.words.protocols.jabber.xmpp_stringprep}.
|
||||
"""
|
||||
def test_crippled(self):
|
||||
"""
|
||||
L{xmpp_stringprep.crippled} is deprecated and always returns C{False}.
|
||||
"""
|
||||
from twisted.words.protocols.jabber.xmpp_stringprep import crippled
|
||||
warnings = self.flushWarnings(
|
||||
offendingFunctions=[self.test_crippled])
|
||||
self.assertEqual(DeprecationWarning, warnings[0]['category'])
|
||||
self.assertEqual(
|
||||
"twisted.words.protocols.jabber.xmpp_stringprep.crippled was "
|
||||
"deprecated in Twisted 13.1.0: crippled is always False",
|
||||
warnings[0]['message'])
|
||||
self.assertEqual(1, len(warnings))
|
||||
self.assertEqual(crippled, False)
|
||||
|
||||
|
||||
|
||||
class XMPPStringPrepTests(unittest.TestCase):
|
||||
"""
|
||||
The nodeprep stringprep profile is similar to the resourceprep profile,
|
||||
but does an extra mapping of characters (table B.2) and disallows
|
||||
more characters (table C.1.1 and eight extra punctuation characters).
|
||||
Due to this similarity, the resourceprep tests are more extensive, and
|
||||
the nodeprep tests only address the mappings additional restrictions.
|
||||
|
||||
The nameprep profile is nearly identical to the nameprep implementation in
|
||||
L{encodings.idna}, but that implementation assumes the C{UseSTD4ASCIIRules}
|
||||
flag to be false. This implementation assumes it to be true, and restricts
|
||||
the allowed set of characters. The tests here only check for the
|
||||
differences.
|
||||
"""
|
||||
|
||||
def testResourcePrep(self):
|
||||
self.assertEqual(resourceprep.prepare(u'resource'), u'resource')
|
||||
self.assertNotEqual(resourceprep.prepare(u'Resource'), u'resource')
|
||||
self.assertEqual(resourceprep.prepare(u' '), u' ')
|
||||
|
||||
self.assertEqual(resourceprep.prepare(u'Henry \u2163'), u'Henry IV')
|
||||
self.assertEqual(resourceprep.prepare(u'foo\xad\u034f\u1806\u180b'
|
||||
u'bar\u200b\u2060'
|
||||
u'baz\ufe00\ufe08\ufe0f\ufeff'),
|
||||
u'foobarbaz')
|
||||
self.assertEqual(resourceprep.prepare(u'\u00a0'), u' ')
|
||||
self.assertRaises(UnicodeError, resourceprep.prepare, u'\u1680')
|
||||
self.assertEqual(resourceprep.prepare(u'\u2000'), u' ')
|
||||
self.assertEqual(resourceprep.prepare(u'\u200b'), u'')
|
||||
self.assertRaises(UnicodeError, resourceprep.prepare, u'\u0010\u007f')
|
||||
self.assertRaises(UnicodeError, resourceprep.prepare, u'\u0085')
|
||||
self.assertRaises(UnicodeError, resourceprep.prepare, u'\u180e')
|
||||
self.assertEqual(resourceprep.prepare(u'\ufeff'), u'')
|
||||
self.assertRaises(UnicodeError, resourceprep.prepare, u'\uf123')
|
||||
self.assertRaises(UnicodeError, resourceprep.prepare, u'\U000f1234')
|
||||
self.assertRaises(UnicodeError, resourceprep.prepare, u'\U0010f234')
|
||||
self.assertRaises(UnicodeError, resourceprep.prepare, u'\U0008fffe')
|
||||
self.assertRaises(UnicodeError, resourceprep.prepare, u'\U0010ffff')
|
||||
self.assertRaises(UnicodeError, resourceprep.prepare, u'\udf42')
|
||||
self.assertRaises(UnicodeError, resourceprep.prepare, u'\ufffd')
|
||||
self.assertRaises(UnicodeError, resourceprep.prepare, u'\u2ff5')
|
||||
self.assertEqual(resourceprep.prepare(u'\u0341'), u'\u0301')
|
||||
self.assertRaises(UnicodeError, resourceprep.prepare, u'\u200e')
|
||||
self.assertRaises(UnicodeError, resourceprep.prepare, u'\u202a')
|
||||
self.assertRaises(UnicodeError, resourceprep.prepare, u'\U000e0001')
|
||||
self.assertRaises(UnicodeError, resourceprep.prepare, u'\U000e0042')
|
||||
self.assertRaises(UnicodeError, resourceprep.prepare, u'foo\u05bebar')
|
||||
self.assertRaises(UnicodeError, resourceprep.prepare, u'foo\ufd50bar')
|
||||
#self.assertEqual(resourceprep.prepare(u'foo\ufb38bar'),
|
||||
# u'foo\u064ebar')
|
||||
self.assertRaises(UnicodeError, resourceprep.prepare, u'\u06271')
|
||||
self.assertEqual(resourceprep.prepare(u'\u06271\u0628'),
|
||||
u'\u06271\u0628')
|
||||
self.assertRaises(UnicodeError, resourceprep.prepare, u'\U000e0002')
|
||||
|
||||
|
||||
def testNodePrep(self):
|
||||
self.assertEqual(nodeprep.prepare(u'user'), u'user')
|
||||
self.assertEqual(nodeprep.prepare(u'User'), u'user')
|
||||
self.assertRaises(UnicodeError, nodeprep.prepare, u'us&er')
|
||||
|
||||
|
||||
def test_nodeprepUnassignedInUnicode32(self):
|
||||
"""
|
||||
Make sure unassigned code points from Unicode 3.2 are rejected.
|
||||
"""
|
||||
self.assertRaises(UnicodeError, nodeprep.prepare, u'\u1d39')
|
||||
|
||||
|
||||
def testNamePrep(self):
|
||||
self.assertEqual(nameprep.prepare(u'example.com'), u'example.com')
|
||||
self.assertEqual(nameprep.prepare(u'Example.com'), u'example.com')
|
||||
self.assertRaises(UnicodeError, nameprep.prepare, u'ex@mple.com')
|
||||
self.assertRaises(UnicodeError, nameprep.prepare, u'-example.com')
|
||||
self.assertRaises(UnicodeError, nameprep.prepare, u'example-.com')
|
||||
|
||||
self.assertEqual(nameprep.prepare(u'stra\u00dfe.example.com'),
|
||||
u'strasse.example.com')
|
||||
|
||||
def test_nameprepTrailingDot(self):
|
||||
"""
|
||||
A trailing dot in domain names is preserved.
|
||||
"""
|
||||
self.assertEqual(nameprep.prepare(u'example.com.'), u'example.com.')
|
||||
@@ -0,0 +1,78 @@
|
||||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
|
||||
from twisted.cred import credentials, error
|
||||
from twisted.words import tap
|
||||
from twisted.trial import unittest
|
||||
|
||||
|
||||
|
||||
class WordsTapTests(unittest.TestCase):
|
||||
"""
|
||||
Ensures that the twisted.words.tap API works.
|
||||
"""
|
||||
|
||||
PASSWD_TEXT = b"admin:admin\njoe:foo\n"
|
||||
admin = credentials.UsernamePassword(b'admin', b'admin')
|
||||
joeWrong = credentials.UsernamePassword(b'joe', b'bar')
|
||||
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Create a file with two users.
|
||||
"""
|
||||
self.filename = self.mktemp()
|
||||
self.file = open(self.filename, 'wb')
|
||||
self.file.write(self.PASSWD_TEXT)
|
||||
self.file.flush()
|
||||
|
||||
|
||||
def tearDown(self):
|
||||
"""
|
||||
Close the dummy user database.
|
||||
"""
|
||||
self.file.close()
|
||||
|
||||
|
||||
def test_hostname(self):
|
||||
"""
|
||||
Tests that the --hostname parameter gets passed to Options.
|
||||
"""
|
||||
opt = tap.Options()
|
||||
opt.parseOptions(['--hostname', 'myhost'])
|
||||
self.assertEqual(opt['hostname'], 'myhost')
|
||||
|
||||
|
||||
def test_passwd(self):
|
||||
"""
|
||||
Tests the --passwd command for backwards-compatibility.
|
||||
"""
|
||||
opt = tap.Options()
|
||||
opt.parseOptions(['--passwd', self.file.name])
|
||||
self._loginTest(opt)
|
||||
|
||||
|
||||
def test_auth(self):
|
||||
"""
|
||||
Tests that the --auth command generates a checker.
|
||||
"""
|
||||
opt = tap.Options()
|
||||
opt.parseOptions(['--auth', 'file:'+self.file.name])
|
||||
self._loginTest(opt)
|
||||
|
||||
|
||||
def _loginTest(self, opt):
|
||||
"""
|
||||
This method executes both positive and negative authentication
|
||||
tests against whatever credentials checker has been stored in
|
||||
the Options class.
|
||||
|
||||
@param opt: An instance of L{tap.Options}.
|
||||
"""
|
||||
self.assertEqual(len(opt['credCheckers']), 1)
|
||||
checker = opt['credCheckers'][0]
|
||||
self.assertFailure(checker.requestAvatarId(self.joeWrong),
|
||||
error.UnauthorizedLogin)
|
||||
def _gotAvatar(username):
|
||||
self.assertEqual(username, self.admin.username)
|
||||
return checker.requestAvatarId(self.admin).addCallback(_gotAvatar)
|
||||
@@ -0,0 +1,348 @@
|
||||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
|
||||
"""
|
||||
Test cases for twisted.words.xish.utility
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import, division
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
from twisted.trial import unittest
|
||||
|
||||
from twisted.words.xish import utility
|
||||
from twisted.words.xish.domish import Element
|
||||
from twisted.words.xish.utility import EventDispatcher
|
||||
|
||||
class CallbackTracker:
|
||||
"""
|
||||
Test helper for tracking callbacks.
|
||||
|
||||
Increases a counter on each call to L{call} and stores the object
|
||||
passed in the call.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.called = 0
|
||||
self.obj = None
|
||||
|
||||
|
||||
def call(self, obj):
|
||||
self.called = self.called + 1
|
||||
self.obj = obj
|
||||
|
||||
|
||||
|
||||
class OrderedCallbackTracker:
|
||||
"""
|
||||
Test helper for tracking callbacks and their order.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.callList = []
|
||||
|
||||
|
||||
def call1(self, object):
|
||||
self.callList.append(self.call1)
|
||||
|
||||
|
||||
def call2(self, object):
|
||||
self.callList.append(self.call2)
|
||||
|
||||
|
||||
def call3(self, object):
|
||||
self.callList.append(self.call3)
|
||||
|
||||
|
||||
|
||||
class EventDispatcherTests(unittest.TestCase):
|
||||
"""
|
||||
Tests for L{EventDispatcher}.
|
||||
"""
|
||||
|
||||
def testStuff(self):
|
||||
d = EventDispatcher()
|
||||
cb1 = CallbackTracker()
|
||||
cb2 = CallbackTracker()
|
||||
cb3 = CallbackTracker()
|
||||
|
||||
d.addObserver("/message/body", cb1.call)
|
||||
d.addObserver("/message", cb1.call)
|
||||
d.addObserver("/presence", cb2.call)
|
||||
d.addObserver("//event/testevent", cb3.call)
|
||||
|
||||
msg = Element(("ns", "message"))
|
||||
msg.addElement("body")
|
||||
|
||||
pres = Element(("ns", "presence"))
|
||||
pres.addElement("presence")
|
||||
|
||||
d.dispatch(msg)
|
||||
self.assertEqual(cb1.called, 2)
|
||||
self.assertEqual(cb1.obj, msg)
|
||||
self.assertEqual(cb2.called, 0)
|
||||
|
||||
d.dispatch(pres)
|
||||
self.assertEqual(cb1.called, 2)
|
||||
self.assertEqual(cb2.called, 1)
|
||||
self.assertEqual(cb2.obj, pres)
|
||||
self.assertEqual(cb3.called, 0)
|
||||
|
||||
d.dispatch(d, "//event/testevent")
|
||||
self.assertEqual(cb3.called, 1)
|
||||
self.assertEqual(cb3.obj, d)
|
||||
|
||||
d.removeObserver("/presence", cb2.call)
|
||||
d.dispatch(pres)
|
||||
self.assertEqual(cb2.called, 1)
|
||||
|
||||
|
||||
def test_addObserverTwice(self):
|
||||
"""
|
||||
Test adding two observers for the same query.
|
||||
|
||||
When the event is dispatched both of the observers need to be called.
|
||||
"""
|
||||
d = EventDispatcher()
|
||||
cb1 = CallbackTracker()
|
||||
cb2 = CallbackTracker()
|
||||
|
||||
d.addObserver("//event/testevent", cb1.call)
|
||||
d.addObserver("//event/testevent", cb2.call)
|
||||
d.dispatch(d, "//event/testevent")
|
||||
|
||||
self.assertEqual(cb1.called, 1)
|
||||
self.assertEqual(cb1.obj, d)
|
||||
self.assertEqual(cb2.called, 1)
|
||||
self.assertEqual(cb2.obj, d)
|
||||
|
||||
|
||||
def test_addObserverInDispatch(self):
|
||||
"""
|
||||
Test for registration of an observer during dispatch.
|
||||
"""
|
||||
d = EventDispatcher()
|
||||
msg = Element(("ns", "message"))
|
||||
cb = CallbackTracker()
|
||||
|
||||
def onMessage(_):
|
||||
d.addObserver("/message", cb.call)
|
||||
|
||||
d.addOnetimeObserver("/message", onMessage)
|
||||
|
||||
d.dispatch(msg)
|
||||
self.assertEqual(cb.called, 0)
|
||||
|
||||
d.dispatch(msg)
|
||||
self.assertEqual(cb.called, 1)
|
||||
|
||||
d.dispatch(msg)
|
||||
self.assertEqual(cb.called, 2)
|
||||
|
||||
|
||||
def test_addOnetimeObserverInDispatch(self):
|
||||
"""
|
||||
Test for registration of a onetime observer during dispatch.
|
||||
"""
|
||||
d = EventDispatcher()
|
||||
msg = Element(("ns", "message"))
|
||||
cb = CallbackTracker()
|
||||
|
||||
def onMessage(msg):
|
||||
d.addOnetimeObserver("/message", cb.call)
|
||||
|
||||
d.addOnetimeObserver("/message", onMessage)
|
||||
|
||||
d.dispatch(msg)
|
||||
self.assertEqual(cb.called, 0)
|
||||
|
||||
d.dispatch(msg)
|
||||
self.assertEqual(cb.called, 1)
|
||||
|
||||
d.dispatch(msg)
|
||||
self.assertEqual(cb.called, 1)
|
||||
|
||||
|
||||
def testOnetimeDispatch(self):
|
||||
d = EventDispatcher()
|
||||
msg = Element(("ns", "message"))
|
||||
cb = CallbackTracker()
|
||||
|
||||
d.addOnetimeObserver("/message", cb.call)
|
||||
d.dispatch(msg)
|
||||
self.assertEqual(cb.called, 1)
|
||||
d.dispatch(msg)
|
||||
self.assertEqual(cb.called, 1)
|
||||
|
||||
|
||||
def testDispatcherResult(self):
|
||||
d = EventDispatcher()
|
||||
msg = Element(("ns", "message"))
|
||||
pres = Element(("ns", "presence"))
|
||||
cb = CallbackTracker()
|
||||
|
||||
d.addObserver("/presence", cb.call)
|
||||
result = d.dispatch(msg)
|
||||
self.assertEqual(False, result)
|
||||
|
||||
result = d.dispatch(pres)
|
||||
self.assertEqual(True, result)
|
||||
|
||||
|
||||
def testOrderedXPathDispatch(self):
|
||||
d = EventDispatcher()
|
||||
cb = OrderedCallbackTracker()
|
||||
d.addObserver("/message/body", cb.call2)
|
||||
d.addObserver("/message", cb.call3, -1)
|
||||
d.addObserver("/message/body", cb.call1, 1)
|
||||
|
||||
msg = Element(("ns", "message"))
|
||||
msg.addElement("body")
|
||||
d.dispatch(msg)
|
||||
self.assertEqual(cb.callList, [cb.call1, cb.call2, cb.call3],
|
||||
"Calls out of order: %s" %
|
||||
repr([c.__name__ for c in cb.callList]))
|
||||
|
||||
|
||||
# Observers are put into CallbackLists that are then put into dictionaries
|
||||
# keyed by the event trigger. Upon removal of the last observer for a
|
||||
# particular event trigger, the (now empty) CallbackList and corresponding
|
||||
# event trigger should be removed from those dictionaries to prevent
|
||||
# slowdown and memory leakage.
|
||||
|
||||
def test_cleanUpRemoveEventObserver(self):
|
||||
"""
|
||||
Test observer clean-up after removeObserver for named events.
|
||||
"""
|
||||
|
||||
d = EventDispatcher()
|
||||
cb = CallbackTracker()
|
||||
|
||||
d.addObserver('//event/test', cb.call)
|
||||
d.dispatch(None, '//event/test')
|
||||
self.assertEqual(1, cb.called)
|
||||
d.removeObserver('//event/test', cb.call)
|
||||
self.assertEqual(0, len(d._eventObservers.pop(0)))
|
||||
|
||||
|
||||
def test_cleanUpRemoveXPathObserver(self):
|
||||
"""
|
||||
Test observer clean-up after removeObserver for XPath events.
|
||||
"""
|
||||
|
||||
d = EventDispatcher()
|
||||
cb = CallbackTracker()
|
||||
msg = Element((None, "message"))
|
||||
|
||||
d.addObserver('/message', cb.call)
|
||||
d.dispatch(msg)
|
||||
self.assertEqual(1, cb.called)
|
||||
d.removeObserver('/message', cb.call)
|
||||
self.assertEqual(0, len(d._xpathObservers.pop(0)))
|
||||
|
||||
|
||||
def test_cleanUpOnetimeEventObserver(self):
|
||||
"""
|
||||
Test observer clean-up after onetime named events.
|
||||
"""
|
||||
|
||||
d = EventDispatcher()
|
||||
cb = CallbackTracker()
|
||||
|
||||
d.addOnetimeObserver('//event/test', cb.call)
|
||||
d.dispatch(None, '//event/test')
|
||||
self.assertEqual(1, cb.called)
|
||||
self.assertEqual(0, len(d._eventObservers.pop(0)))
|
||||
|
||||
|
||||
def test_cleanUpOnetimeXPathObserver(self):
|
||||
"""
|
||||
Test observer clean-up after onetime XPath events.
|
||||
"""
|
||||
|
||||
d = EventDispatcher()
|
||||
cb = CallbackTracker()
|
||||
msg = Element((None, "message"))
|
||||
|
||||
d.addOnetimeObserver('/message', cb.call)
|
||||
d.dispatch(msg)
|
||||
self.assertEqual(1, cb.called)
|
||||
self.assertEqual(0, len(d._xpathObservers.pop(0)))
|
||||
|
||||
|
||||
def test_observerRaisingException(self):
|
||||
"""
|
||||
Test that exceptions in observers do not bubble up to dispatch.
|
||||
|
||||
The exceptions raised in observers should be logged and other
|
||||
observers should be called as if nothing happened.
|
||||
"""
|
||||
|
||||
class OrderedCallbackList(utility.CallbackList):
|
||||
def __init__(self):
|
||||
self.callbacks = OrderedDict()
|
||||
|
||||
class TestError(Exception):
|
||||
pass
|
||||
|
||||
def raiseError(_):
|
||||
raise TestError()
|
||||
|
||||
d = EventDispatcher()
|
||||
cb = CallbackTracker()
|
||||
|
||||
originalCallbackList = utility.CallbackList
|
||||
|
||||
try:
|
||||
utility.CallbackList = OrderedCallbackList
|
||||
|
||||
d.addObserver('//event/test', raiseError)
|
||||
d.addObserver('//event/test', cb.call)
|
||||
try:
|
||||
d.dispatch(None, '//event/test')
|
||||
except TestError:
|
||||
self.fail("TestError raised. Should have been logged instead.")
|
||||
|
||||
self.assertEqual(1, len(self.flushLoggedErrors(TestError)))
|
||||
self.assertEqual(1, cb.called)
|
||||
finally:
|
||||
utility.CallbackList = originalCallbackList
|
||||
|
||||
|
||||
|
||||
class XmlPipeTests(unittest.TestCase):
|
||||
"""
|
||||
Tests for L{twisted.words.xish.utility.XmlPipe}.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.pipe = utility.XmlPipe()
|
||||
|
||||
|
||||
def test_sendFromSource(self):
|
||||
"""
|
||||
Send an element from the source and observe it from the sink.
|
||||
"""
|
||||
def cb(obj):
|
||||
called.append(obj)
|
||||
|
||||
called = []
|
||||
self.pipe.sink.addObserver('/test[@xmlns="testns"]', cb)
|
||||
element = Element(('testns', 'test'))
|
||||
self.pipe.source.send(element)
|
||||
self.assertEqual([element], called)
|
||||
|
||||
|
||||
def test_sendFromSink(self):
|
||||
"""
|
||||
Send an element from the sink and observe it from the source.
|
||||
"""
|
||||
def cb(obj):
|
||||
called.append(obj)
|
||||
|
||||
called = []
|
||||
self.pipe.source.addObserver('/test[@xmlns="testns"]', cb)
|
||||
element = Element(('testns', 'test'))
|
||||
self.pipe.sink.send(element)
|
||||
self.assertEqual([element], called)
|
||||
@@ -0,0 +1,84 @@
|
||||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
|
||||
"""
|
||||
Tests for L{twisted.words.xmpproutertap}.
|
||||
"""
|
||||
|
||||
from twisted.application import internet
|
||||
from twisted.trial import unittest
|
||||
from twisted.words import xmpproutertap as tap
|
||||
from twisted.words.protocols.jabber import component
|
||||
|
||||
class XMPPRouterTapTests(unittest.TestCase):
|
||||
|
||||
def test_port(self):
|
||||
"""
|
||||
The port option is recognised as a parameter.
|
||||
"""
|
||||
opt = tap.Options()
|
||||
opt.parseOptions(['--port', '7001'])
|
||||
self.assertEqual(opt['port'], '7001')
|
||||
|
||||
|
||||
def test_portDefault(self):
|
||||
"""
|
||||
The port option has '5347' as default value
|
||||
"""
|
||||
opt = tap.Options()
|
||||
opt.parseOptions([])
|
||||
self.assertEqual(opt['port'], 'tcp:5347:interface=127.0.0.1')
|
||||
|
||||
|
||||
def test_secret(self):
|
||||
"""
|
||||
The secret option is recognised as a parameter.
|
||||
"""
|
||||
opt = tap.Options()
|
||||
opt.parseOptions(['--secret', 'hushhush'])
|
||||
self.assertEqual(opt['secret'], 'hushhush')
|
||||
|
||||
|
||||
def test_secretDefault(self):
|
||||
"""
|
||||
The secret option has 'secret' as default value
|
||||
"""
|
||||
opt = tap.Options()
|
||||
opt.parseOptions([])
|
||||
self.assertEqual(opt['secret'], 'secret')
|
||||
|
||||
|
||||
def test_verbose(self):
|
||||
"""
|
||||
The verbose option is recognised as a flag.
|
||||
"""
|
||||
opt = tap.Options()
|
||||
opt.parseOptions(['--verbose'])
|
||||
self.assertTrue(opt['verbose'])
|
||||
|
||||
|
||||
def test_makeService(self):
|
||||
"""
|
||||
The service gets set up with a router and factory.
|
||||
"""
|
||||
opt = tap.Options()
|
||||
opt.parseOptions([])
|
||||
s = tap.makeService(opt)
|
||||
self.assertIsInstance(s, internet.StreamServerEndpointService)
|
||||
self.assertEqual('127.0.0.1', s.endpoint._interface)
|
||||
self.assertEqual(5347, s.endpoint._port)
|
||||
factory = s.factory
|
||||
self.assertIsInstance(factory, component.XMPPComponentServerFactory)
|
||||
self.assertIsInstance(factory.router, component.Router)
|
||||
self.assertEqual('secret', factory.secret)
|
||||
self.assertFalse(factory.logTraffic)
|
||||
|
||||
|
||||
def test_makeServiceVerbose(self):
|
||||
"""
|
||||
The verbose flag enables traffic logging.
|
||||
"""
|
||||
opt = tap.Options()
|
||||
opt.parseOptions(['--verbose'])
|
||||
s = tap.makeService(opt)
|
||||
self.assertTrue(s.factory.logTraffic)
|
||||
Reference in New Issue
Block a user