Ausgabe der neuen DB Einträge

This commit is contained in:
hubobel 2022-01-02 21:50:48 +01:00
parent bad48e1627
commit cfbbb9ee3d
2399 changed files with 843193 additions and 43 deletions

View file

@ -0,0 +1 @@
"Words tests"

View file

@ -0,0 +1,68 @@
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""
Tests for L{twisted.words.im.basechat}.
"""
from twisted.trial import unittest
from twisted.words.im import basechat, basesupport
class ChatUITests(unittest.TestCase):
"""
Tests for the L{basechat.ChatUI} chat client.
"""
def setUp(self):
self.ui = basechat.ChatUI()
self.account = basesupport.AbstractAccount("fooAccount", False, "foo",
"password", "host", "port")
self.person = basesupport.AbstractPerson("foo", self.account)
def test_contactChangedNickNoKey(self):
"""
L{basechat.ChatUI.contactChangedNick} on an
L{twisted.words.im.interfaces.IPerson} who doesn't have an account
associated with the L{basechat.ChatUI} instance has no effect.
"""
self.assertEqual(self.person.name, "foo")
self.assertEqual(self.person.account, self.account)
self.ui.contactChangedNick(self.person, "bar")
self.assertEqual(self.person.name, "foo")
self.assertEqual(self.person.account, self.account)
def test_contactChangedNickNoConversation(self):
"""
L{basechat.ChatUI.contactChangedNick} changes the name for an
L{twisted.words.im.interfaces.IPerson}.
"""
self.ui.persons[self.person.name, self.person.account] = self.person
self.assertEqual(self.person.name, "foo")
self.assertEqual(self.person.account, self.account)
self.ui.contactChangedNick(self.person, "bar")
self.assertEqual(self.person.name, "bar")
self.assertEqual(self.person.account, self.account)
def test_contactChangedNickHasConversation(self):
"""
If an L{twisted.words.im.interfaces.IPerson} is in a
L{basechat.Conversation}, L{basechat.ChatUI.contactChangedNick} causes a
name change for that person in both the L{basechat.Conversation} and the
L{basechat.ChatUI}.
"""
self.ui.persons[self.person.name, self.person.account] = self.person
conversation = basechat.Conversation(self.person, self.ui)
self.ui.conversations[self.person] = conversation
self.assertEqual(self.person.name, "foo")
self.assertEqual(self.person.account, self.account)
self.ui.contactChangedNick(self.person, "bar")
self.assertEqual(self.person.name, "bar")
self.assertEqual(self.person.account, self.account)

View file

@ -0,0 +1,97 @@
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
from twisted.trial import unittest
from twisted.words.im import basesupport
from twisted.internet import error, defer
class DummyAccount(basesupport.AbstractAccount):
"""
An account object that will do nothing when asked to start to log on.
"""
loginHasFailed = False
loginCallbackCalled = False
def _startLogOn(self, *args):
"""
Set self.loginDeferred to the same as the deferred returned, allowing a
testcase to .callback or .errback.
@return: A deferred.
"""
self.loginDeferred = defer.Deferred()
return self.loginDeferred
def _loginFailed(self, result):
self.loginHasFailed = True
return basesupport.AbstractAccount._loginFailed(self, result)
def _cb_logOn(self, result):
self.loginCallbackCalled = True
return basesupport.AbstractAccount._cb_logOn(self, result)
class DummyUI(object):
"""
Provide just the interface required to be passed to AbstractAccount.logOn.
"""
clientRegistered = False
def registerAccountClient(self, result):
self.clientRegistered = True
class ClientMsgTests(unittest.TestCase):
def makeUI(self):
return DummyUI()
def makeAccount(self):
return DummyAccount('la', False, 'la', None, 'localhost', 6667)
def test_connect(self):
"""
Test that account.logOn works, and it calls the right callback when a
connection is established.
"""
account = self.makeAccount()
ui = self.makeUI()
d = account.logOn(ui)
account.loginDeferred.callback(None)
def check(result):
self.assertFalse(account.loginHasFailed,
"Login shouldn't have failed")
self.assertTrue(account.loginCallbackCalled,
"We should be logged in")
d.addCallback(check)
return d
def test_failedConnect(self):
"""
Test that account.logOn works, and it calls the right callback when a
connection is established.
"""
account = self.makeAccount()
ui = self.makeUI()
d = account.logOn(ui)
account.loginDeferred.errback(Exception())
def err(reason):
self.assertTrue(account.loginHasFailed, "Login should have failed")
self.assertFalse(account.loginCallbackCalled,
"We shouldn't be logged in")
self.assertTrue(not ui.clientRegistered,
"Client shouldn't be registered in the UI")
cb = lambda r: self.assertTrue(False, "Shouldn't get called back")
d.addCallbacks(cb, err)
return d
def test_alreadyConnecting(self):
"""
Test that it can fail sensibly when someone tried to connect before
we did.
"""
account = self.makeAccount()
ui = self.makeUI()
account.logOn(ui)
self.assertRaises(error.ConnectError, account.logOn, ui)

View file

@ -0,0 +1,587 @@
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""
Tests for L{twisted.words.xish.domish}, a DOM-like library for XMPP.
"""
from __future__ import absolute_import, division
from zope.interface.verify import verifyObject
from twisted.python.compat import _PY3, unicode
from twisted.python.reflect import requireModule
from twisted.trial import unittest
from twisted.words.xish import domish
class ElementTests(unittest.TestCase):
"""
Tests for L{domish.Element}.
"""
def test_interface(self):
"""
L{domish.Element} implements L{domish.IElement}.
"""
verifyObject(domish.IElement, domish.Element((None, u"foo")))
def test_escaping(self):
"""
The built-in entity references are properly encoded.
"""
s = "&<>'\""
self.assertEqual(domish.escapeToXml(s), "&amp;&lt;&gt;'\"")
self.assertEqual(domish.escapeToXml(s, 1), "&amp;&lt;&gt;&apos;&quot;")
def test_namespace(self):
"""
An attribute on L{domish.Namespace} yields a qualified name.
"""
ns = domish.Namespace("testns")
self.assertEqual(ns.foo, ("testns", "foo"))
def test_elementInit(self):
"""
Basic L{domish.Element} initialization tests.
"""
e = domish.Element((None, "foo"))
self.assertEqual(e.name, "foo")
self.assertEqual(e.uri, None)
self.assertEqual(e.defaultUri, None)
self.assertEqual(e.parent, None)
e = domish.Element(("", "foo"))
self.assertEqual(e.name, "foo")
self.assertEqual(e.uri, "")
self.assertEqual(e.defaultUri, "")
self.assertEqual(e.parent, None)
e = domish.Element(("testns", "foo"))
self.assertEqual(e.name, "foo")
self.assertEqual(e.uri, "testns")
self.assertEqual(e.defaultUri, "testns")
self.assertEqual(e.parent, None)
e = domish.Element(("testns", "foo"), "test2ns")
self.assertEqual(e.name, "foo")
self.assertEqual(e.uri, "testns")
self.assertEqual(e.defaultUri, "test2ns")
def test_childOps(self):
"""
Basic L{domish.Element} child tests.
"""
e = domish.Element(("testns", "foo"))
e.addContent(u"somecontent")
b2 = e.addElement(("testns2", "bar2"))
e["attrib1"] = "value1"
e[("testns2", "attrib2")] = "value2"
e.addElement("bar")
e.addElement("bar")
e.addContent(u"abc")
e.addContent(u"123")
# Check content merging
self.assertEqual(e.children[-1], "abc123")
# Check direct child accessor
self.assertEqual(e.bar2, b2)
e.bar2.addContent(u"subcontent")
e.bar2["bar2value"] = "somevalue"
# Check child ops
self.assertEqual(e.children[1], e.bar2)
self.assertEqual(e.children[2], e.bar)
# Check attribute ops
self.assertEqual(e["attrib1"], "value1")
del e["attrib1"]
self.assertEqual(e.hasAttribute("attrib1"), 0)
self.assertEqual(e.hasAttribute("attrib2"), 0)
self.assertEqual(e[("testns2", "attrib2")], "value2")
def test_characterData(self):
"""
Extract character data using L{unicode}.
"""
element = domish.Element((u"testns", u"foo"))
element.addContent(u"somecontent")
text = unicode(element)
self.assertEqual(u"somecontent", text)
self.assertIsInstance(text, unicode)
def test_characterDataNativeString(self):
"""
Extract ascii character data using L{str}.
"""
element = domish.Element((u"testns", u"foo"))
element.addContent(u"somecontent")
text = str(element)
self.assertEqual("somecontent", text)
self.assertIsInstance(text, str)
def test_characterDataUnicode(self):
"""
Extract character data using L{unicode}.
"""
element = domish.Element((u"testns", u"foo"))
element.addContent(u"\N{SNOWMAN}")
text = unicode(element)
self.assertEqual(u"\N{SNOWMAN}", text)
self.assertIsInstance(text, unicode)
def test_characterDataBytes(self):
"""
Extract character data as UTF-8 using L{bytes}.
"""
element = domish.Element((u"testns", u"foo"))
element.addContent(u"\N{SNOWMAN}")
text = bytes(element)
self.assertEqual(u"\N{SNOWMAN}".encode('utf-8'), text)
self.assertIsInstance(text, bytes)
def test_characterDataMixed(self):
"""
Mixing addChild with cdata and element, the first cdata is returned.
"""
element = domish.Element((u"testns", u"foo"))
element.addChild(u"abc")
element.addElement("bar")
element.addChild(u"def")
self.assertEqual(u"abc", unicode(element))
def test_addContent(self):
"""
Unicode strings passed to C{addContent} become the character data.
"""
element = domish.Element((u"testns", u"foo"))
element.addContent(u'unicode')
self.assertEqual(u"unicode", unicode(element))
def test_addContentNativeStringASCII(self):
"""
ASCII native strings passed to C{addContent} become the character data.
"""
element = domish.Element((u"testns", u"foo"))
element.addContent('native')
self.assertEqual(u"native", unicode(element))
def test_addContentBytes(self):
"""
Byte strings passed to C{addContent} are not acceptable on Python 3.
"""
element = domish.Element((u"testns", u"foo"))
self.assertRaises(TypeError, element.addContent, b'bytes')
if not _PY3:
test_addContentBytes.skip = (
"Bytes behavior of addContent only provided on Python 3.")
def test_addContentBytesNonASCII(self):
"""
Non-ASCII byte strings passed to C{addContent} yield L{UnicodeError}.
"""
element = domish.Element((u"testns", u"foo"))
self.assertRaises(UnicodeError, element.addContent, b'\xe2\x98\x83')
if _PY3:
test_addContentBytesNonASCII.skip = (
"Bytes behavior of addContent only provided on Python 2.")
def test_addElementContent(self):
"""
Content passed to addElement becomes character data on the new child.
"""
element = domish.Element((u"testns", u"foo"))
child = element.addElement("bar", content=u"abc")
self.assertEqual(u"abc", unicode(child))
def test_elements(self):
"""
Calling C{elements} without arguments on a L{domish.Element} returns
all child elements, whatever the qualified name.
"""
e = domish.Element((u"testns", u"foo"))
c1 = e.addElement(u"name")
c2 = e.addElement((u"testns2", u"baz"))
c3 = e.addElement(u"quux")
c4 = e.addElement((u"testns", u"name"))
elts = list(e.elements())
self.assertIn(c1, elts)
self.assertIn(c2, elts)
self.assertIn(c3, elts)
self.assertIn(c4, elts)
def test_elementsWithQN(self):
"""
Calling C{elements} with a namespace and local name on a
L{domish.Element} returns all child elements with that qualified name.
"""
e = domish.Element((u"testns", u"foo"))
c1 = e.addElement(u"name")
c2 = e.addElement((u"testns2", u"baz"))
c3 = e.addElement(u"quux")
c4 = e.addElement((u"testns", u"name"))
elts = list(e.elements(u"testns", u"name"))
self.assertIn(c1, elts)
self.assertNotIn(c2, elts)
self.assertNotIn(c3, elts)
self.assertIn(c4, elts)
class DomishStreamTestsMixin:
"""
Mixin defining tests for different stream implementations.
@ivar streamClass: A no-argument callable which will be used to create an
XML parser which can produce a stream of elements from incremental
input.
"""
def setUp(self):
self.doc_started = False
self.doc_ended = False
self.root = None
self.elements = []
self.stream = self.streamClass()
self.stream.DocumentStartEvent = self._docStarted
self.stream.ElementEvent = self.elements.append
self.stream.DocumentEndEvent = self._docEnded
def _docStarted(self, root):
self.root = root
self.doc_started = True
def _docEnded(self):
self.doc_ended = True
def doTest(self, xml):
self.stream.parse(xml)
def testHarness(self):
xml = b"<root><child/><child2/></root>"
self.stream.parse(xml)
self.assertEqual(self.doc_started, True)
self.assertEqual(self.root.name, 'root')
self.assertEqual(self.elements[0].name, 'child')
self.assertEqual(self.elements[1].name, 'child2')
self.assertEqual(self.doc_ended, True)
def testBasic(self):
xml = b"<stream:stream xmlns:stream='etherx' xmlns='jabber'>\n" + \
b" <message to='bar'>" + \
b" <x xmlns='xdelay'>some&amp;data&gt;</x>" + \
b" </message>" + \
b"</stream:stream>"
self.stream.parse(xml)
self.assertEqual(self.root.name, 'stream')
self.assertEqual(self.root.uri, 'etherx')
self.assertEqual(self.elements[0].name, 'message')
self.assertEqual(self.elements[0].uri, 'jabber')
self.assertEqual(self.elements[0]['to'], 'bar')
self.assertEqual(self.elements[0].x.uri, 'xdelay')
self.assertEqual(unicode(self.elements[0].x), 'some&data>')
def testNoRootNS(self):
xml = b"<stream><error xmlns='etherx'/></stream>"
self.stream.parse(xml)
self.assertEqual(self.root.uri, '')
self.assertEqual(self.elements[0].uri, 'etherx')
def testNoDefaultNS(self):
xml = b"<stream:stream xmlns:stream='etherx'><error/></stream:stream>"
self.stream.parse(xml)
self.assertEqual(self.root.uri, 'etherx')
self.assertEqual(self.root.defaultUri, '')
self.assertEqual(self.elements[0].uri, '')
self.assertEqual(self.elements[0].defaultUri, '')
def testChildDefaultNS(self):
xml = b"<root xmlns='testns'><child/></root>"
self.stream.parse(xml)
self.assertEqual(self.root.uri, 'testns')
self.assertEqual(self.elements[0].uri, 'testns')
def testEmptyChildNS(self):
xml = b"""<root xmlns='testns'>
<child1><child2 xmlns=''/></child1>
</root>"""
self.stream.parse(xml)
self.assertEqual(self.elements[0].child2.uri, '')
def test_namespaceWithWhitespace(self):
"""
Whitespace in an xmlns value is preserved in the resulting node's C{uri}
attribute.
"""
xml = b"<root xmlns:foo=' bar baz '><foo:bar foo:baz='quux'/></root>"
self.stream.parse(xml)
self.assertEqual(self.elements[0].uri, " bar baz ")
self.assertEqual(
self.elements[0].attributes, {(" bar baz ", "baz"): "quux"})
def test_attributesWithNamespaces(self):
"""
Attributes with namespace are parsed without Exception.
(https://twistedmatrix.com/trac/ticket/9730 regression test)
"""
xml = b"""<root xmlns:test='http://example.org' xml:lang='en'>
<test:test>test</test:test>
</root>"""
# with Python 3.8 and without #9730 fix, the following error would
# happen at next line:
# ``RuntimeError: dictionary keys changed during iteration``
self.stream.parse(xml)
self.assertEqual(self.elements[0].uri, "http://example.org")
def testChildPrefix(self):
xml = b"<root xmlns='testns' xmlns:foo='testns2'><foo:child/></root>"
self.stream.parse(xml)
self.assertEqual(self.root.localPrefixes['foo'], 'testns2')
self.assertEqual(self.elements[0].uri, 'testns2')
def testUnclosedElement(self):
self.assertRaises(domish.ParserError, self.stream.parse,
b"<root><error></root>")
def test_namespaceReuse(self):
"""
Test that reuse of namespaces does affect an element's serialization.
When one element uses a prefix for a certain namespace, this is
stored in the C{localPrefixes} attribute of the element. We want
to make sure that elements created after such use, won't have this
prefix end up in their C{localPrefixes} attribute, too.
"""
xml = b"""<root>
<foo:child1 xmlns:foo='testns'/>
<child2 xmlns='testns'/>
</root>"""
self.stream.parse(xml)
self.assertEqual('child1', self.elements[0].name)
self.assertEqual('testns', self.elements[0].uri)
self.assertEqual('', self.elements[0].defaultUri)
self.assertEqual({'foo': 'testns'}, self.elements[0].localPrefixes)
self.assertEqual('child2', self.elements[1].name)
self.assertEqual('testns', self.elements[1].uri)
self.assertEqual('testns', self.elements[1].defaultUri)
self.assertEqual({}, self.elements[1].localPrefixes)
class DomishExpatStreamTests(DomishStreamTestsMixin, unittest.TestCase):
"""
Tests for L{domish.ExpatElementStream}, the expat-based element stream
implementation.
"""
streamClass = domish.ExpatElementStream
if requireModule('pyexpat', default=None) is None:
skip = "pyexpat is required for ExpatElementStream tests."
else:
skip = None
class DomishSuxStreamTests(DomishStreamTestsMixin, unittest.TestCase):
"""
Tests for L{domish.SuxElementStream}, the L{twisted.web.sux}-based element
stream implementation.
"""
streamClass = domish.SuxElementStream
class SerializerTests(unittest.TestCase):
def testNoNamespace(self):
e = domish.Element((None, "foo"))
self.assertEqual(e.toXml(), "<foo/>")
self.assertEqual(e.toXml(closeElement = 0), "<foo>")
def testDefaultNamespace(self):
e = domish.Element(("testns", "foo"))
self.assertEqual(e.toXml(), "<foo xmlns='testns'/>")
def testOtherNamespace(self):
e = domish.Element(("testns", "foo"), "testns2")
self.assertEqual(e.toXml({'testns': 'bar'}),
"<bar:foo xmlns:bar='testns' xmlns='testns2'/>")
def testChildDefaultNamespace(self):
e = domish.Element(("testns", "foo"))
e.addElement("bar")
self.assertEqual(e.toXml(), "<foo xmlns='testns'><bar/></foo>")
def testChildSameNamespace(self):
e = domish.Element(("testns", "foo"))
e.addElement(("testns", "bar"))
self.assertEqual(e.toXml(), "<foo xmlns='testns'><bar/></foo>")
def testChildSameDefaultNamespace(self):
e = domish.Element(("testns", "foo"))
e.addElement("bar", "testns")
self.assertEqual(e.toXml(), "<foo xmlns='testns'><bar/></foo>")
def testChildOtherDefaultNamespace(self):
e = domish.Element(("testns", "foo"))
e.addElement(("testns2", "bar"), 'testns2')
self.assertEqual(e.toXml(), "<foo xmlns='testns'><bar xmlns='testns2'/></foo>")
def testOnlyChildDefaultNamespace(self):
e = domish.Element((None, "foo"))
e.addElement(("ns2", "bar"), 'ns2')
self.assertEqual(e.toXml(), "<foo><bar xmlns='ns2'/></foo>")
def testOnlyChildDefaultNamespace2(self):
e = domish.Element((None, "foo"))
e.addElement("bar")
self.assertEqual(e.toXml(), "<foo><bar/></foo>")
def testChildInDefaultNamespace(self):
e = domish.Element(("testns", "foo"), "testns2")
e.addElement(("testns2", "bar"))
self.assertEqual(e.toXml(), "<xn0:foo xmlns:xn0='testns' xmlns='testns2'><bar/></xn0:foo>")
def testQualifiedAttribute(self):
e = domish.Element((None, "foo"),
attribs = {("testns2", "bar"): "baz"})
self.assertEqual(e.toXml(), "<foo xmlns:xn0='testns2' xn0:bar='baz'/>")
def testQualifiedAttributeDefaultNS(self):
e = domish.Element(("testns", "foo"),
attribs = {("testns", "bar"): "baz"})
self.assertEqual(e.toXml(), "<foo xmlns='testns' xmlns:xn0='testns' xn0:bar='baz'/>")
def testTwoChilds(self):
e = domish.Element(('', "foo"))
child1 = e.addElement(("testns", "bar"), "testns2")
child1.addElement(('testns2', 'quux'))
child2 = e.addElement(("testns3", "baz"), "testns4")
child2.addElement(('testns', 'quux'))
self.assertEqual(e.toXml(), "<foo><xn0:bar xmlns:xn0='testns' xmlns='testns2'><quux/></xn0:bar><xn1:baz xmlns:xn1='testns3' xmlns='testns4'><xn0:quux xmlns:xn0='testns'/></xn1:baz></foo>")
def testXMLNamespace(self):
e = domish.Element((None, "foo"),
attribs = {("http://www.w3.org/XML/1998/namespace",
"lang"): "en_US"})
self.assertEqual(e.toXml(), "<foo xml:lang='en_US'/>")
def testQualifiedAttributeGivenListOfPrefixes(self):
e = domish.Element((None, "foo"),
attribs = {("testns2", "bar"): "baz"})
self.assertEqual(e.toXml({"testns2": "qux"}),
"<foo xmlns:qux='testns2' qux:bar='baz'/>")
def testNSPrefix(self):
e = domish.Element((None, "foo"),
attribs = {("testns2", "bar"): "baz"})
c = e.addElement(("testns2", "qux"))
c[("testns2", "bar")] = "quux"
self.assertEqual(e.toXml(), "<foo xmlns:xn0='testns2' xn0:bar='baz'><xn0:qux xn0:bar='quux'/></foo>")
def testDefaultNSPrefix(self):
e = domish.Element((None, "foo"),
attribs = {("testns2", "bar"): "baz"})
c = e.addElement(("testns2", "qux"))
c[("testns2", "bar")] = "quux"
c.addElement('foo')
self.assertEqual(e.toXml(), "<foo xmlns:xn0='testns2' xn0:bar='baz'><xn0:qux xn0:bar='quux'><xn0:foo/></xn0:qux></foo>")
def testPrefixScope(self):
e = domish.Element(('testns', 'foo'))
self.assertEqual(e.toXml(prefixes={'testns': 'bar'},
prefixesInScope=['bar']),
"<bar:foo/>")
def testLocalPrefixes(self):
e = domish.Element(('testns', 'foo'), localPrefixes={'bar': 'testns'})
self.assertEqual(e.toXml(), "<bar:foo xmlns:bar='testns'/>")
def testLocalPrefixesWithChild(self):
e = domish.Element(('testns', 'foo'), localPrefixes={'bar': 'testns'})
e.addElement('baz')
self.assertIdentical(e.baz.defaultUri, None)
self.assertEqual(e.toXml(), "<bar:foo xmlns:bar='testns'><baz/></bar:foo>")
def test_prefixesReuse(self):
"""
Test that prefixes passed to serialization are not modified.
This test makes sure that passing a dictionary of prefixes repeatedly
to C{toXml} of elements does not cause serialization errors. A
previous implementation changed the passed in dictionary internally,
causing havoc later on.
"""
prefixes = {'testns': 'foo'}
# test passing of dictionary
s = domish.SerializerClass(prefixes=prefixes)
self.assertNotIdentical(prefixes, s.prefixes)
# test proper serialization on prefixes reuse
e = domish.Element(('testns2', 'foo'),
localPrefixes={'quux': 'testns2'})
self.assertEqual("<quux:foo xmlns:quux='testns2'/>",
e.toXml(prefixes=prefixes))
e = domish.Element(('testns2', 'foo'))
self.assertEqual("<foo xmlns='testns2'/>",
e.toXml(prefixes=prefixes))
def testRawXMLSerialization(self):
e = domish.Element((None, "foo"))
e.addRawXml("<abc123>")
# The testcase below should NOT generate valid XML -- that's
# the whole point of using the raw XML call -- it's the callers
# responsibility to ensure that the data inserted is valid
self.assertEqual(e.toXml(), "<foo><abc123></foo>")
def testRawXMLWithUnicodeSerialization(self):
e = domish.Element((None, "foo"))
e.addRawXml(u"<degree>\u00B0</degree>")
self.assertEqual(e.toXml(), u"<foo><degree>\u00B0</degree></foo>")
def testUnicodeSerialization(self):
e = domish.Element((None, "foo"))
e["test"] = u"my value\u0221e"
e.addContent(u"A degree symbol...\u00B0")
self.assertEqual(e.toXml(),
u"<foo test='my value\u0221e'>A degree symbol...\u00B0</foo>")

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,291 @@
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""
Tests for IRC portions of L{twisted.words.service}.
"""
from twisted.cred import checkers, portal
from twisted.test import proto_helpers
from twisted.words.protocols import irc
from twisted.words.service import InMemoryWordsRealm, IRCFactory, IRCUser
from twisted.words.test.test_irc import IRCTestCase
class IRCUserTests(IRCTestCase):
"""
Isolated tests for L{IRCUser}
"""
def setUp(self):
"""
Sets up a Realm, Portal, Factory, IRCUser, Transport, and Connection
for our tests.
"""
self.realm = InMemoryWordsRealm("example.com")
self.checker = checkers.InMemoryUsernamePasswordDatabaseDontUse()
self.portal = portal.Portal(self.realm, [self.checker])
self.checker.addUser(u"john", u"pass")
self.factory = IRCFactory(self.realm, self.portal)
self.ircUser = self.factory.buildProtocol(None)
self.stringTransport = proto_helpers.StringTransport()
self.ircUser.makeConnection(self.stringTransport)
def test_sendMessage(self):
"""
Sending a message to a user after they have sent NICK, but before they
have authenticated, results in a message from "example.com".
"""
self.ircUser.irc_NICK("", ["mynick"])
self.stringTransport.clear()
self.ircUser.sendMessage("foo")
self.assertEqualBufferValue(self.stringTransport.value(), ":example.com foo mynick\r\n")
def test_utf8Messages(self):
"""
When a UTF8 message is sent with sendMessage and the current IRCUser
has a UTF8 nick and is set to UTF8 encoding, the message will be
written to the transport.
"""
expectedResult = (u":example.com \u0442\u0435\u0441\u0442 "
u"\u043d\u0438\u043a\r\n").encode('utf-8')
self.ircUser.irc_NICK("", [u"\u043d\u0438\u043a".encode('utf-8')])
self.stringTransport.clear()
self.ircUser.sendMessage(u"\u0442\u0435\u0441\u0442".encode('utf-8'))
self.assertEqualBufferValue(self.stringTransport.value(), expectedResult)
def test_invalidEncodingNick(self):
"""
A NICK command sent with a nickname that cannot be decoded with the
current IRCUser's encoding results in a PRIVMSG from NickServ
indicating that the nickname could not be decoded.
"""
self.ircUser.irc_NICK("", [b"\xd4\xc5\xd3\xd4"])
self.assertRaises(UnicodeError)
def response(self):
"""
Grabs our responses and then clears the transport
"""
response = self.ircUser.transport.value()
self.ircUser.transport.clear()
if bytes != str and isinstance(response, bytes):
response = response.decode("utf-8")
response = response.splitlines()
return [irc.parsemsg(r) for r in response]
def scanResponse(self, response, messageType):
"""
Gets messages out of a response
@param response: The parsed IRC messages of the response, as returned
by L{IRCUserTests.response}
@param messageType: The string type of the desired messages.
@return: An iterator which yields 2-tuples of C{(index, ircMessage)}
"""
for n, message in enumerate(response):
if (message[1] == messageType):
yield n, message
def test_sendNickSendsGreeting(self):
"""
Receiving NICK without authenticating sends the MOTD Start and MOTD End
messages, which is required by certain popular IRC clients (such as
Pidgin) before a connection is considered to be fully established.
"""
self.ircUser.irc_NICK("", ["mynick"])
response = self.response()
start = list(self.scanResponse(response, irc.RPL_MOTDSTART))
end = list(self.scanResponse(response, irc.RPL_ENDOFMOTD))
self.assertEqual(start,
[(0, ('example.com', '375', ['mynick', '- example.com Message of the Day - ']))])
self.assertEqual(end,
[(1, ('example.com', '376', ['mynick', 'End of /MOTD command.']))])
def test_fullLogin(self):
"""
Receiving USER, PASS, NICK will log in the user, and transmit the
appropriate response messages.
"""
self.ircUser.irc_USER("", ["john doe"])
self.ircUser.irc_PASS("", ["pass"])
self.ircUser.irc_NICK("", ["john"])
version = ('Your host is example.com, running version %s' %
(self.factory._serverInfo["serviceVersion"],))
creation = ('This server was created on %s' %
(self.factory._serverInfo["creationDate"],))
self.assertEqual(self.response(),
[('example.com', '375',
['john', '- example.com Message of the Day - ']),
('example.com', '376', ['john', 'End of /MOTD command.']),
('example.com', '001', ['john', 'connected to Twisted IRC']),
('example.com', '002', ['john', version]),
('example.com', '003', ['john', creation]),
('example.com', '004',
['john', 'example.com', self.factory._serverInfo["serviceVersion"],
'w', 'n'])])
def test_PART(self):
"""
irc_PART
"""
self.ircUser.irc_NICK("testuser", ["mynick"])
response = self.response()
self.ircUser.transport.clear()
self.assertEqual(response[0][1], irc.RPL_MOTDSTART)
self.ircUser.irc_JOIN("testuser", ["somechannel"])
response = self.response()
self.ircUser.transport.clear()
self.assertEqual(response[0][1], irc.ERR_NOSUCHCHANNEL)
self.ircUser.irc_PART("testuser", [b"somechannel", b"booga"])
response = self.response()
self.ircUser.transport.clear()
self.assertEqual(response[0][1], irc.ERR_NOTONCHANNEL)
self.ircUser.irc_PART("testuser", [u"somechannel", u"booga"])
response = self.response()
self.ircUser.transport.clear()
self.assertEqual(response[0][1], irc.ERR_NOTONCHANNEL)
def test_NAMES(self):
"""
irc_NAMES
"""
self.ircUser.irc_NICK("", ["testuser"])
self.ircUser.irc_JOIN("", ["somechannel"])
self.ircUser.transport.clear()
self.ircUser.irc_NAMES("", ["somechannel"])
response = self.response()
self.assertEqual(response[0][1], irc.RPL_ENDOFNAMES)
class MocksyIRCUser(IRCUser):
def __init__(self):
self.realm = InMemoryWordsRealm("example.com")
self.mockedCodes = []
def sendMessage(self, code, *_, **__):
self.mockedCodes.append(code)
BADTEXT = b'\xff'
class IRCUserBadEncodingTests(IRCTestCase):
"""
Verifies that L{IRCUser} sends the correct error messages back to clients
when given indecipherable bytes
"""
# TODO: irc_NICK -- but NICKSERV is used for that, so it isn't as easy.
def setUp(self):
self.ircUser = MocksyIRCUser()
def assertChokesOnBadBytes(self, irc_x, error):
"""
Asserts that IRCUser sends the relevant error code when a given irc_x
dispatch method is given undecodable bytes.
@param irc_x: the name of the irc_FOO method to test.
For example, irc_x = 'PRIVMSG' will check irc_PRIVMSG
@param error: the error code irc_x should send. For example,
irc.ERR_NOTONCHANNEL
"""
getattr(self.ircUser, 'irc_%s' % irc_x)(None, [BADTEXT])
self.assertEqual(self.ircUser.mockedCodes, [error])
# No such channel
def test_JOIN(self):
"""
Tests that irc_JOIN sends ERR_NOSUCHCHANNEL if the channel name can't
be decoded.
"""
self.assertChokesOnBadBytes('JOIN', irc.ERR_NOSUCHCHANNEL)
def test_NAMES(self):
"""
Tests that irc_NAMES sends ERR_NOSUCHCHANNEL if the channel name can't
be decoded.
"""
self.assertChokesOnBadBytes('NAMES', irc.ERR_NOSUCHCHANNEL)
def test_TOPIC(self):
"""
Tests that irc_TOPIC sends ERR_NOSUCHCHANNEL if the channel name can't
be decoded.
"""
self.assertChokesOnBadBytes('TOPIC', irc.ERR_NOSUCHCHANNEL)
def test_LIST(self):
"""
Tests that irc_LIST sends ERR_NOSUCHCHANNEL if the channel name can't
be decoded.
"""
self.assertChokesOnBadBytes('LIST', irc.ERR_NOSUCHCHANNEL)
# No such nick
def test_MODE(self):
"""
Tests that irc_MODE sends ERR_NOSUCHNICK if the target name can't
be decoded.
"""
self.assertChokesOnBadBytes('MODE', irc.ERR_NOSUCHNICK)
def test_PRIVMSG(self):
"""
Tests that irc_PRIVMSG sends ERR_NOSUCHNICK if the target name can't
be decoded.
"""
self.assertChokesOnBadBytes('PRIVMSG', irc.ERR_NOSUCHNICK)
def test_WHOIS(self):
"""
Tests that irc_WHOIS sends ERR_NOSUCHNICK if the target name can't
be decoded.
"""
self.assertChokesOnBadBytes('WHOIS', irc.ERR_NOSUCHNICK)
# Not on channel
def test_PART(self):
"""
Tests that irc_PART sends ERR_NOTONCHANNEL if the target name can't
be decoded.
"""
self.assertChokesOnBadBytes('PART', irc.ERR_NOTONCHANNEL)
# Probably nothing
def test_WHO(self):
"""
Tests that irc_WHO immediately ends the WHO list if the target name
can't be decoded.
"""
self.assertChokesOnBadBytes('WHO', irc.RPL_ENDOFWHO)

View file

@ -0,0 +1,292 @@
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""
Tests for L{twisted.words.im.ircsupport}.
"""
from twisted.test.proto_helpers import StringTransport
from twisted.words.im.basechat import ChatUI, Conversation, GroupConversation
from twisted.words.im.ircsupport import IRCAccount, IRCProto
from twisted.words.im.locals import OfflineError
from twisted.words.test.test_irc import IRCTestCase
class StubConversation(Conversation):
def show(self):
pass
def showMessage(self, message, metadata):
self.message = message
self.metadata = metadata
class StubGroupConversation(GroupConversation):
def setTopic(self, topic, nickname):
self.topic = topic
self.topicSetBy = nickname
def show(self):
pass
def showGroupMessage(self, sender, text, metadata=None):
self.metadata = metadata
self.text = text
self.metadata = metadata
class StubChatUI(ChatUI):
def getConversation(self, group, Class=StubConversation, stayHidden=0):
return ChatUI.getGroupConversation(self, group, Class, stayHidden)
def getGroupConversation(self, group, Class=StubGroupConversation, stayHidden=0):
return ChatUI.getGroupConversation(self, group, Class, stayHidden)
class IRCProtoTests(IRCTestCase):
"""
Tests for L{IRCProto}.
"""
def setUp(self):
self.account = IRCAccount(
"Some account", False, "alice", None, "example.com", 6667)
self.proto = IRCProto(self.account, StubChatUI(), None)
self.transport = StringTransport()
def test_login(self):
"""
When L{IRCProto} is connected to a transport, it sends I{NICK} and
I{USER} commands with the username from the account object.
"""
self.proto.makeConnection(self.transport)
self.assertEqualBufferValue(
self.transport.value(),
"NICK alice\r\n"
"USER alice foo bar :Twisted-IM user\r\n")
def test_authenticate(self):
"""
If created with an account with a password, L{IRCProto} sends a
I{PASS} command before the I{NICK} and I{USER} commands.
"""
self.account.password = "secret"
self.proto.makeConnection(self.transport)
self.assertEqualBufferValue(
self.transport.value(),
"PASS secret\r\n"
"NICK alice\r\n"
"USER alice foo bar :Twisted-IM user\r\n")
def test_channels(self):
"""
If created with an account with a list of channels, L{IRCProto}
joins each of those channels after registering.
"""
self.account.channels = ['#foo', '#bar']
self.proto.makeConnection(self.transport)
self.assertEqualBufferValue(
self.transport.value(),
"NICK alice\r\n"
"USER alice foo bar :Twisted-IM user\r\n"
"JOIN #foo\r\n"
"JOIN #bar\r\n")
def test_isupport(self):
"""
L{IRCProto} can interpret I{ISUPPORT} (I{005}) messages from the server
and reflect their information in its C{supported} attribute.
"""
self.proto.makeConnection(self.transport)
self.proto.dataReceived(
":irc.example.com 005 alice MODES=4 CHANLIMIT=#:20\r\n")
self.assertEqual(4, self.proto.supported.getFeature("MODES"))
def test_nick(self):
"""
IRC NICK command changes the nickname of a user.
"""
self.proto.makeConnection(self.transport)
self.proto.dataReceived(":alice JOIN #group1\r\n")
self.proto.dataReceived(":alice1 JOIN #group1\r\n")
self.proto.dataReceived(":alice1 NICK newnick\r\n")
self.proto.dataReceived(":alice3 NICK newnick3\r\n")
self.assertIn("newnick", self.proto._ingroups)
self.assertNotIn("alice1", self.proto._ingroups)
def test_part(self):
"""
IRC PART command removes a user from an IRC channel.
"""
self.proto.makeConnection(self.transport)
self.proto.dataReceived(":alice1 JOIN #group1\r\n")
self.assertIn("group1", self.proto._ingroups["alice1"])
self.assertNotIn("group2", self.proto._ingroups["alice1"])
self.proto.dataReceived(":alice PART #group1\r\n")
self.proto.dataReceived(":alice1 PART #group1\r\n")
self.proto.dataReceived(":alice1 PART #group2\r\n")
self.assertNotIn("group1", self.proto._ingroups["alice1"])
self.assertNotIn("group2", self.proto._ingroups["alice1"])
def test_quit(self):
"""
IRC QUIT command removes a user from all IRC channels.
"""
self.proto.makeConnection(self.transport)
self.proto.dataReceived(":alice1 JOIN #group1\r\n")
self.assertIn("group1", self.proto._ingroups["alice1"])
self.assertNotIn("group2", self.proto._ingroups["alice1"])
self.proto.dataReceived(":alice1 JOIN #group3\r\n")
self.assertIn("group3", self.proto._ingroups["alice1"])
self.proto.dataReceived(":alice1 QUIT\r\n")
self.assertTrue(len(self.proto._ingroups["alice1"]) == 0)
self.proto.dataReceived(":alice3 QUIT\r\n")
def test_topic(self):
"""
IRC TOPIC command changes the topic of an IRC channel.
"""
self.proto.makeConnection(self.transport)
self.proto.dataReceived(":alice1 JOIN #group1\r\n")
self.proto.dataReceived(":alice1 TOPIC #group1 newtopic\r\n")
groupConversation = self.proto.getGroupConversation("group1")
self.assertEqual(groupConversation.topic, "newtopic")
self.assertEqual(groupConversation.topicSetBy, "alice1")
def test_privmsg(self):
"""
PRIVMSG sends a private message to a user or channel.
"""
self.proto.makeConnection(self.transport)
self.proto.dataReceived(":alice1 PRIVMSG t2 test_message_1\r\n")
conversation = self.proto.chat.getConversation(
self.proto.getPerson("alice1"))
self.assertEqual(conversation.message, "test_message_1")
self.proto.dataReceived(":alice1 PRIVMSG #group1 test_message_2\r\n")
groupConversation = self.proto.getGroupConversation("group1")
self.assertEqual(groupConversation.text, "test_message_2")
self.proto.setNick("alice")
self.proto.dataReceived(":alice PRIVMSG #foo test_message_3\r\n")
groupConversation = self.proto.getGroupConversation("foo")
self.assertFalse(hasattr(groupConversation, "text"))
conversation = self.proto.chat.getConversation(
self.proto.getPerson("alice"))
self.assertFalse(hasattr(conversation, "message"))
def test_action(self):
"""
CTCP ACTION to a user or channel.
"""
self.proto.makeConnection(self.transport)
self.proto.dataReceived(":alice1 PRIVMSG alice1 :\01ACTION smiles\01\r\n")
conversation = self.proto.chat.getConversation(
self.proto.getPerson("alice1"))
self.assertEqual(conversation.message, "smiles")
self.proto.dataReceived(":alice1 PRIVMSG #group1 :\01ACTION laughs\01\r\n")
groupConversation = self.proto.getGroupConversation("group1")
self.assertEqual(groupConversation.text, "laughs")
self.proto.setNick("alice")
self.proto.dataReceived(":alice PRIVMSG #group1 :\01ACTION cries\01\r\n")
groupConversation = self.proto.getGroupConversation("foo")
self.assertFalse(hasattr(groupConversation, "text"))
conversation = self.proto.chat.getConversation(
self.proto.getPerson("alice"))
self.assertFalse(hasattr(conversation, "message"))
def test_rplNamreply(self):
"""
RPL_NAMREPLY server response (353) lists all the users in a channel.
RPL_ENDOFNAMES server response (363) is sent at the end of RPL_NAMREPLY
to indicate that there are no more names.
"""
self.proto.makeConnection(self.transport)
self.proto.dataReceived(
":example.com 353 z3p = #bnl :pSwede Dan- SkOyg @MrOp +MrPlus\r\n")
expectedInGroups = {'Dan-': ['bnl'],
'pSwede': ['bnl'],
'SkOyg': ['bnl'],
'MrOp': ['bnl'],
'MrPlus': ['bnl']}
expectedNamReplies = {
'bnl': ['pSwede', 'Dan-', 'SkOyg', 'MrOp', 'MrPlus']}
self.assertEqual(expectedInGroups, self.proto._ingroups)
self.assertEqual(expectedNamReplies, self.proto._namreplies)
self.proto.dataReceived(
":example.com 366 alice #bnl :End of /NAMES list\r\n")
self.assertEqual({}, self.proto._namreplies)
groupConversation = self.proto.getGroupConversation("bnl")
self.assertEqual(expectedNamReplies['bnl'], groupConversation.members)
def test_rplTopic(self):
"""
RPL_TOPIC server response (332) is sent when a channel's topic is changed
"""
self.proto.makeConnection(self.transport)
self.proto.dataReceived(
":example.com 332 alice, #foo :Some random topic\r\n")
self.assertEqual("Some random topic", self.proto._topics["foo"])
def test_sendMessage(self):
"""
L{IRCPerson.sendMessage}
"""
self.proto.makeConnection(self.transport)
person = self.proto.getPerson("alice")
self.assertRaises(OfflineError, person.sendMessage, "Some message")
person.account.client = self.proto
self.transport.clear()
person.sendMessage("Some message 2")
self.assertEqual(self.transport.io.getvalue(),
b'PRIVMSG alice :Some message 2\r\n')
self.transport.clear()
person.sendMessage("smiles", {"style": "emote"})
self.assertEqual(self.transport.io.getvalue(),
b'PRIVMSG alice :\x01ACTION smiles\x01\r\n')
def test_sendGroupMessage(self):
"""
L{IRCGroup.sendGroupMessage}
"""
self.proto.makeConnection(self.transport)
group = self.proto.chat.getGroup("#foo", self.proto)
self.assertRaises(OfflineError, group.sendGroupMessage, "Some message")
group.account.client = self.proto
self.transport.clear()
group.sendGroupMessage("Some message 2")
self.assertEqual(self.transport.io.getvalue(),
b'PRIVMSG #foo :Some message 2\r\n')
self.transport.clear()
group.sendGroupMessage("smiles", {"style": "emote"})
self.assertEqual(self.transport.io.getvalue(),
b'PRIVMSG #foo :\x01ACTION smiles\x01\r\n')

View file

@ -0,0 +1,497 @@
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""
Tests for L{twisted.words.protocols.jabber.client}
"""
from __future__ import absolute_import, division
from hashlib import sha1
from twisted.internet import defer
from twisted.python.compat import unicode
from twisted.trial import unittest
from twisted.words.protocols.jabber import client, error, jid, xmlstream
from twisted.words.protocols.jabber.sasl import SASLInitiatingInitializer
from twisted.words.xish import utility
try:
from twisted.internet import ssl
except ImportError:
ssl = None
skipWhenNoSSL = "SSL not available"
else:
skipWhenNoSSL = None
IQ_AUTH_GET = '/iq[@type="get"]/query[@xmlns="jabber:iq:auth"]'
IQ_AUTH_SET = '/iq[@type="set"]/query[@xmlns="jabber:iq:auth"]'
NS_BIND = 'urn:ietf:params:xml:ns:xmpp-bind'
IQ_BIND_SET = '/iq[@type="set"]/bind[@xmlns="%s"]' % NS_BIND
NS_SESSION = 'urn:ietf:params:xml:ns:xmpp-session'
IQ_SESSION_SET = '/iq[@type="set"]/session[@xmlns="%s"]' % NS_SESSION
class CheckVersionInitializerTests(unittest.TestCase):
def setUp(self):
a = xmlstream.Authenticator()
xs = xmlstream.XmlStream(a)
self.init = client.CheckVersionInitializer(xs)
def testSupported(self):
"""
Test supported version number 1.0
"""
self.init.xmlstream.version = (1, 0)
self.init.initialize()
def testNotSupported(self):
"""
Test unsupported version number 0.0, and check exception.
"""
self.init.xmlstream.version = (0, 0)
exc = self.assertRaises(error.StreamError, self.init.initialize)
self.assertEqual('unsupported-version', exc.condition)
class InitiatingInitializerHarness(object):
"""
Testing harness for interacting with XML stream initializers.
This sets up an L{utility.XmlPipe} to create a communication channel between
the initializer and the stubbed receiving entity. It features a sink and
source side that both act similarly to a real L{xmlstream.XmlStream}. The
sink is augmented with an authenticator to which initializers can be added.
The harness also provides some utility methods to work with event observers
and deferreds.
"""
def setUp(self):
self.output = []
self.pipe = utility.XmlPipe()
self.xmlstream = self.pipe.sink
self.authenticator = xmlstream.ConnectAuthenticator('example.org')
self.xmlstream.authenticator = self.authenticator
def waitFor(self, event, handler):
"""
Observe an output event, returning a deferred.
The returned deferred will be fired when the given event has been
observed on the source end of the L{XmlPipe} tied to the protocol
under test. The handler is added as the first callback.
@param event: The event to be observed. See
L{utility.EventDispatcher.addOnetimeObserver}.
@param handler: The handler to be called with the observed event object.
@rtype: L{defer.Deferred}.
"""
d = defer.Deferred()
d.addCallback(handler)
self.pipe.source.addOnetimeObserver(event, d.callback)
return d
class IQAuthInitializerTests(InitiatingInitializerHarness, unittest.TestCase):
"""
Tests for L{client.IQAuthInitializer}.
"""
def setUp(self):
super(IQAuthInitializerTests, self).setUp()
self.init = client.IQAuthInitializer(self.xmlstream)
self.authenticator.jid = jid.JID('user@example.com/resource')
self.authenticator.password = u'secret'
def testPlainText(self):
"""
Test plain-text authentication.
Act as a server supporting plain-text authentication and expect the
C{password} field to be filled with the password. Then act as if
authentication succeeds.
"""
def onAuthGet(iq):
"""
Called when the initializer sent a query for authentication methods.
The response informs the client that plain-text authentication
is supported.
"""
# Create server response
response = xmlstream.toResponse(iq, 'result')
response.addElement(('jabber:iq:auth', 'query'))
response.query.addElement('username')
response.query.addElement('password')
response.query.addElement('resource')
# Set up an observer for the next request we expect.
d = self.waitFor(IQ_AUTH_SET, onAuthSet)
# Send server response
self.pipe.source.send(response)
return d
def onAuthSet(iq):
"""
Called when the initializer sent the authentication request.
The server checks the credentials and responds with an empty result
signalling success.
"""
self.assertEqual('user', unicode(iq.query.username))
self.assertEqual('secret', unicode(iq.query.password))
self.assertEqual('resource', unicode(iq.query.resource))
# Send server response
response = xmlstream.toResponse(iq, 'result')
self.pipe.source.send(response)
# Set up an observer for the request for authentication fields
d1 = self.waitFor(IQ_AUTH_GET, onAuthGet)
# Start the initializer
d2 = self.init.initialize()
return defer.gatherResults([d1, d2])
def testDigest(self):
"""
Test digest authentication.
Act as a server supporting digest authentication and expect the
C{digest} field to be filled with a sha1 digest of the concatenated
stream session identifier and password. Then act as if authentication
succeeds.
"""
def onAuthGet(iq):
"""
Called when the initializer sent a query for authentication methods.
The response informs the client that digest authentication is
supported.
"""
# Create server response
response = xmlstream.toResponse(iq, 'result')
response.addElement(('jabber:iq:auth', 'query'))
response.query.addElement('username')
response.query.addElement('digest')
response.query.addElement('resource')
# Set up an observer for the next request we expect.
d = self.waitFor(IQ_AUTH_SET, onAuthSet)
# Send server response
self.pipe.source.send(response)
return d
def onAuthSet(iq):
"""
Called when the initializer sent the authentication request.
The server checks the credentials and responds with an empty result
signalling success.
"""
self.assertEqual('user', unicode(iq.query.username))
self.assertEqual(sha1(b'12345secret').hexdigest(),
unicode(iq.query.digest))
self.assertEqual('resource', unicode(iq.query.resource))
# Send server response
response = xmlstream.toResponse(iq, 'result')
self.pipe.source.send(response)
# Digest authentication relies on the stream session identifier. Set it.
self.xmlstream.sid = u'12345'
# Set up an observer for the request for authentication fields
d1 = self.waitFor(IQ_AUTH_GET, onAuthGet)
# Start the initializer
d2 = self.init.initialize()
return defer.gatherResults([d1, d2])
def testFailRequestFields(self):
"""
Test initializer failure of request for fields for authentication.
"""
def onAuthGet(iq):
"""
Called when the initializer sent a query for authentication methods.
The server responds that the client is not authorized to authenticate.
"""
response = error.StanzaError('not-authorized').toResponse(iq)
self.pipe.source.send(response)
# Set up an observer for the request for authentication fields
d1 = self.waitFor(IQ_AUTH_GET, onAuthGet)
# Start the initializer
d2 = self.init.initialize()
# The initialized should fail with a stanza error.
self.assertFailure(d2, error.StanzaError)
return defer.gatherResults([d1, d2])
def testFailAuth(self):
"""
Test initializer failure to authenticate.
"""
def onAuthGet(iq):
"""
Called when the initializer sent a query for authentication methods.
The response informs the client that plain-text authentication
is supported.
"""
# Send server response
response = xmlstream.toResponse(iq, 'result')
response.addElement(('jabber:iq:auth', 'query'))
response.query.addElement('username')
response.query.addElement('password')
response.query.addElement('resource')
# Set up an observer for the next request we expect.
d = self.waitFor(IQ_AUTH_SET, onAuthSet)
# Send server response
self.pipe.source.send(response)
return d
def onAuthSet(iq):
"""
Called when the initializer sent the authentication request.
The server checks the credentials and responds with a not-authorized
stanza error.
"""
response = error.StanzaError('not-authorized').toResponse(iq)
self.pipe.source.send(response)
# Set up an observer for the request for authentication fields
d1 = self.waitFor(IQ_AUTH_GET, onAuthGet)
# Start the initializer
d2 = self.init.initialize()
# The initializer should fail with a stanza error.
self.assertFailure(d2, error.StanzaError)
return defer.gatherResults([d1, d2])
class BindInitializerTests(InitiatingInitializerHarness, unittest.TestCase):
"""
Tests for L{client.BindInitializer}.
"""
def setUp(self):
super(BindInitializerTests, self).setUp()
self.init = client.BindInitializer(self.xmlstream)
self.authenticator.jid = jid.JID('user@example.com/resource')
def testBasic(self):
"""
Set up a stream, and act as if resource binding succeeds.
"""
def onBind(iq):
response = xmlstream.toResponse(iq, 'result')
response.addElement((NS_BIND, 'bind'))
response.bind.addElement('jid',
content=u'user@example.com/other resource')
self.pipe.source.send(response)
def cb(result):
self.assertEqual(jid.JID('user@example.com/other resource'),
self.authenticator.jid)
d1 = self.waitFor(IQ_BIND_SET, onBind)
d2 = self.init.start()
d2.addCallback(cb)
return defer.gatherResults([d1, d2])
def testFailure(self):
"""
Set up a stream, and act as if resource binding fails.
"""
def onBind(iq):
response = error.StanzaError('conflict').toResponse(iq)
self.pipe.source.send(response)
d1 = self.waitFor(IQ_BIND_SET, onBind)
d2 = self.init.start()
self.assertFailure(d2, error.StanzaError)
return defer.gatherResults([d1, d2])
class SessionInitializerTests(InitiatingInitializerHarness, unittest.TestCase):
"""
Tests for L{client.SessionInitializer}.
"""
def setUp(self):
super(SessionInitializerTests, self).setUp()
self.init = client.SessionInitializer(self.xmlstream)
def testSuccess(self):
"""
Set up a stream, and act as if session establishment succeeds.
"""
def onSession(iq):
response = xmlstream.toResponse(iq, 'result')
self.pipe.source.send(response)
d1 = self.waitFor(IQ_SESSION_SET, onSession)
d2 = self.init.start()
return defer.gatherResults([d1, d2])
def testFailure(self):
"""
Set up a stream, and act as if session establishment fails.
"""
def onSession(iq):
response = error.StanzaError('forbidden').toResponse(iq)
self.pipe.source.send(response)
d1 = self.waitFor(IQ_SESSION_SET, onSession)
d2 = self.init.start()
self.assertFailure(d2, error.StanzaError)
return defer.gatherResults([d1, d2])
class BasicAuthenticatorTests(unittest.TestCase):
"""
Test for both BasicAuthenticator and basicClientFactory.
"""
def test_basic(self):
"""
Authenticator and stream are properly constructed by the factory.
The L{xmlstream.XmlStream} protocol created by the factory has the new
L{client.BasicAuthenticator} instance in its C{authenticator}
attribute. It is set up with C{jid} and C{password} as passed to the
factory, C{otherHost} taken from the client JID. The stream futher has
two initializers, for TLS and authentication, of which the first has
its C{required} attribute set to C{True}.
"""
self.client_jid = jid.JID('user@example.com/resource')
# Get an XmlStream instance. Note that it gets initialized with the
# XMPPAuthenticator (that has its associateWithXmlStream called) that
# is in turn initialized with the arguments to the factory.
xs = client.basicClientFactory(self.client_jid,
'secret').buildProtocol(None)
# test authenticator's instance variables
self.assertEqual('example.com', xs.authenticator.otherHost)
self.assertEqual(self.client_jid, xs.authenticator.jid)
self.assertEqual('secret', xs.authenticator.password)
# test list of initializers
tls, auth = xs.initializers
self.assertIsInstance(tls, xmlstream.TLSInitiatingInitializer)
self.assertIsInstance(auth, client.IQAuthInitializer)
self.assertFalse(tls.required)
class XMPPAuthenticatorTests(unittest.TestCase):
"""
Test for both XMPPAuthenticator and XMPPClientFactory.
"""
def test_basic(self):
"""
Test basic operations.
Setup an XMPPClientFactory, which sets up an XMPPAuthenticator, and let
it produce a protocol instance. Then inspect the instance variables of
the authenticator and XML stream objects.
"""
self.client_jid = jid.JID('user@example.com/resource')
# Get an XmlStream instance. Note that it gets initialized with the
# XMPPAuthenticator (that has its associateWithXmlStream called) that
# is in turn initialized with the arguments to the factory.
xs = client.XMPPClientFactory(self.client_jid,
'secret').buildProtocol(None)
# test authenticator's instance variables
self.assertEqual('example.com', xs.authenticator.otherHost)
self.assertEqual(self.client_jid, xs.authenticator.jid)
self.assertEqual('secret', xs.authenticator.password)
# test list of initializers
version, tls, sasl, bind, session = xs.initializers
self.assertIsInstance(tls, xmlstream.TLSInitiatingInitializer)
self.assertIsInstance(sasl, SASLInitiatingInitializer)
self.assertIsInstance(bind, client.BindInitializer)
self.assertIsInstance(session, client.SessionInitializer)
self.assertTrue(tls.required)
self.assertTrue(sasl.required)
self.assertTrue(bind.required)
self.assertFalse(session.required)
def test_tlsConfiguration(self):
"""
A TLS configuration is passed to the TLS initializer.
"""
configs = []
def init(self, xs, required=True, configurationForTLS=None):
configs.append(configurationForTLS)
self.client_jid = jid.JID('user@example.com/resource')
# Get an XmlStream instance. Note that it gets initialized with the
# XMPPAuthenticator (that has its associateWithXmlStream called) that
# is in turn initialized with the arguments to the factory.
configurationForTLS = ssl.CertificateOptions()
factory = client.XMPPClientFactory(
self.client_jid, 'secret',
configurationForTLS=configurationForTLS)
self.patch(xmlstream.TLSInitiatingInitializer, "__init__", init)
xs = factory.buildProtocol(None)
# test list of initializers
version, tls, sasl, bind, session = xs.initializers
self.assertIsInstance(tls, xmlstream.TLSInitiatingInitializer)
self.assertIs(configurationForTLS, configs[0])
test_tlsConfiguration.skip = skipWhenNoSSL

View file

@ -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)

View file

@ -0,0 +1,333 @@
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""
Tests for L{twisted.words.protocols.jabber.error}.
"""
from __future__ import absolute_import, division
from twisted.python.compat import unicode
from twisted.trial import unittest
from twisted.words.protocols.jabber import error
from twisted.words.xish import domish
NS_XML = 'http://www.w3.org/XML/1998/namespace'
NS_STREAMS = 'http://etherx.jabber.org/streams'
NS_XMPP_STREAMS = 'urn:ietf:params:xml:ns:xmpp-streams'
NS_XMPP_STANZAS = 'urn:ietf:params:xml:ns:xmpp-stanzas'
class BaseErrorTests(unittest.TestCase):
def test_getElementPlain(self):
"""
Test getting an element for a plain error.
"""
e = error.BaseError('feature-not-implemented')
element = e.getElement()
self.assertIdentical(element.uri, None)
self.assertEqual(len(element.children), 1)
def test_getElementText(self):
"""
Test getting an element for an error with a text.
"""
e = error.BaseError('feature-not-implemented', u'text')
element = e.getElement()
self.assertEqual(len(element.children), 2)
self.assertEqual(unicode(element.text), 'text')
self.assertEqual(element.text.getAttribute((NS_XML, 'lang')), None)
def test_getElementTextLang(self):
"""
Test getting an element for an error with a text and language.
"""
e = error.BaseError('feature-not-implemented', u'text', 'en_US')
element = e.getElement()
self.assertEqual(len(element.children), 2)
self.assertEqual(unicode(element.text), 'text')
self.assertEqual(element.text[(NS_XML, 'lang')], 'en_US')
def test_getElementAppCondition(self):
"""
Test getting an element for an error with an app specific condition.
"""
ac = domish.Element(('testns', 'myerror'))
e = error.BaseError('feature-not-implemented', appCondition=ac)
element = e.getElement()
self.assertEqual(len(element.children), 2)
self.assertEqual(element.myerror, ac)
class StreamErrorTests(unittest.TestCase):
def test_getElementPlain(self):
"""
Test namespace of the element representation of an error.
"""
e = error.StreamError('feature-not-implemented')
element = e.getElement()
self.assertEqual(element.uri, NS_STREAMS)
def test_getElementConditionNamespace(self):
"""
Test that the error condition element has the correct namespace.
"""
e = error.StreamError('feature-not-implemented')
element = e.getElement()
self.assertEqual(NS_XMPP_STREAMS, getattr(element, 'feature-not-implemented').uri)
def test_getElementTextNamespace(self):
"""
Test that the error text element has the correct namespace.
"""
e = error.StreamError('feature-not-implemented', u'text')
element = e.getElement()
self.assertEqual(NS_XMPP_STREAMS, element.text.uri)
class StanzaErrorTests(unittest.TestCase):
"""
Tests for L{error.StreamError}.
"""
def test_typeRemoteServerTimeout(self):
"""
Remote Server Timeout should yield type wait, code 504.
"""
e = error.StanzaError('remote-server-timeout')
self.assertEqual('wait', e.type)
self.assertEqual('504', e.code)
def test_getElementPlain(self):
"""
Test getting an element for a plain stanza error.
"""
e = error.StanzaError('feature-not-implemented')
element = e.getElement()
self.assertEqual(element.uri, None)
self.assertEqual(element['type'], 'cancel')
self.assertEqual(element['code'], '501')
def test_getElementType(self):
"""
Test getting an element for a stanza error with a given type.
"""
e = error.StanzaError('feature-not-implemented', 'auth')
element = e.getElement()
self.assertEqual(element.uri, None)
self.assertEqual(element['type'], 'auth')
self.assertEqual(element['code'], '501')
def test_getElementConditionNamespace(self):
"""
Test that the error condition element has the correct namespace.
"""
e = error.StanzaError('feature-not-implemented')
element = e.getElement()
self.assertEqual(NS_XMPP_STANZAS, getattr(element, 'feature-not-implemented').uri)
def test_getElementTextNamespace(self):
"""
Test that the error text element has the correct namespace.
"""
e = error.StanzaError('feature-not-implemented', text=u'text')
element = e.getElement()
self.assertEqual(NS_XMPP_STANZAS, element.text.uri)
def test_toResponse(self):
"""
Test an error response is generated from a stanza.
The addressing on the (new) response stanza should be reversed, an
error child (with proper properties) added and the type set to
C{'error'}.
"""
stanza = domish.Element(('jabber:client', 'message'))
stanza['type'] = 'chat'
stanza['to'] = 'user1@example.com'
stanza['from'] = 'user2@example.com/resource'
e = error.StanzaError('service-unavailable')
response = e.toResponse(stanza)
self.assertNotIdentical(response, stanza)
self.assertEqual(response['from'], 'user1@example.com')
self.assertEqual(response['to'], 'user2@example.com/resource')
self.assertEqual(response['type'], 'error')
self.assertEqual(response.error.children[0].name,
'service-unavailable')
self.assertEqual(response.error['type'], 'cancel')
self.assertNotEqual(stanza.children, response.children)
class ParseErrorTests(unittest.TestCase):
"""
Tests for L{error._parseError}.
"""
def setUp(self):
self.error = domish.Element((None, 'error'))
def test_empty(self):
"""
Test parsing of the empty error element.
"""
result = error._parseError(self.error, 'errorns')
self.assertEqual({'condition': None,
'text': None,
'textLang': None,
'appCondition': None}, result)
def test_condition(self):
"""
Test parsing of an error element with a condition.
"""
self.error.addElement(('errorns', 'bad-request'))
result = error._parseError(self.error, 'errorns')
self.assertEqual('bad-request', result['condition'])
def test_text(self):
"""
Test parsing of an error element with a text.
"""
text = self.error.addElement(('errorns', 'text'))
text.addContent(u'test')
result = error._parseError(self.error, 'errorns')
self.assertEqual('test', result['text'])
self.assertEqual(None, result['textLang'])
def test_textLang(self):
"""
Test parsing of an error element with a text with a defined language.
"""
text = self.error.addElement(('errorns', 'text'))
text[NS_XML, 'lang'] = 'en_US'
text.addContent(u'test')
result = error._parseError(self.error, 'errorns')
self.assertEqual('en_US', result['textLang'])
def test_appCondition(self):
"""
Test parsing of an error element with an app specific condition.
"""
condition = self.error.addElement(('testns', 'condition'))
result = error._parseError(self.error, 'errorns')
self.assertEqual(condition, result['appCondition'])
def test_appConditionMultiple(self):
"""
Test parsing of an error element with multiple app specific conditions.
"""
self.error.addElement(('testns', 'condition'))
condition = self.error.addElement(('testns', 'condition2'))
result = error._parseError(self.error, 'errorns')
self.assertEqual(condition, result['appCondition'])
class ExceptionFromStanzaTests(unittest.TestCase):
def test_basic(self):
"""
Test basic operations of exceptionFromStanza.
Given a realistic stanza, check if a sane exception is returned.
Using this stanza::
<iq type='error'
from='pubsub.shakespeare.lit'
to='francisco@denmark.lit/barracks'
id='subscriptions1'>
<pubsub xmlns='http://jabber.org/protocol/pubsub'>
<subscriptions/>
</pubsub>
<error type='cancel'>
<feature-not-implemented
xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
<unsupported xmlns='http://jabber.org/protocol/pubsub#errors'
feature='retrieve-subscriptions'/>
</error>
</iq>
"""
stanza = domish.Element((None, 'stanza'))
p = stanza.addElement(('http://jabber.org/protocol/pubsub', 'pubsub'))
p.addElement('subscriptions')
e = stanza.addElement('error')
e['type'] = 'cancel'
e.addElement((NS_XMPP_STANZAS, 'feature-not-implemented'))
uc = e.addElement(('http://jabber.org/protocol/pubsub#errors',
'unsupported'))
uc['feature'] = 'retrieve-subscriptions'
result = error.exceptionFromStanza(stanza)
self.assertIsInstance(result, error.StanzaError)
self.assertEqual('feature-not-implemented', result.condition)
self.assertEqual('cancel', result.type)
self.assertEqual(uc, result.appCondition)
self.assertEqual([p], result.children)
def test_legacy(self):
"""
Test legacy operations of exceptionFromStanza.
Given a realistic stanza with only legacy (pre-XMPP) error information,
check if a sane exception is returned.
Using this stanza::
<message type='error'
to='piers@pipetree.com/Home'
from='qmacro@jaber.org'>
<body>Are you there?</body>
<error code='502'>Unable to resolve hostname.</error>
</message>
"""
stanza = domish.Element((None, 'stanza'))
p = stanza.addElement('body', content=u'Are you there?')
e = stanza.addElement('error', content=u'Unable to resolve hostname.')
e['code'] = '502'
result = error.exceptionFromStanza(stanza)
self.assertIsInstance(result, error.StanzaError)
self.assertEqual('service-unavailable', result.condition)
self.assertEqual('wait', result.type)
self.assertEqual('Unable to resolve hostname.', result.text)
self.assertEqual([p], result.children)
class ExceptionFromStreamErrorTests(unittest.TestCase):
def test_basic(self):
"""
Test basic operations of exceptionFromStreamError.
Given a realistic stream error, check if a sane exception is returned.
Using this error::
<stream:error xmlns:stream='http://etherx.jabber.org/streams'>
<xml-not-well-formed xmlns='urn:ietf:params:xml:ns:xmpp-streams'/>
</stream:error>
"""
e = domish.Element(('http://etherx.jabber.org/streams', 'error'))
e.addElement((NS_XMPP_STREAMS, 'xml-not-well-formed'))
result = error.exceptionFromStreamError(e)
self.assertIsInstance(result, error.StreamError)
self.assertEqual('xml-not-well-formed', result.condition)

View file

@ -0,0 +1,226 @@
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""
Tests for L{twisted.words.protocols.jabber.jid}.
"""
from twisted.python.compat import unicode
from twisted.trial import unittest
from twisted.words.protocols.jabber import jid
class JIDParsingTests(unittest.TestCase):
def test_parse(self):
"""
Test different forms of JIDs.
"""
# Basic forms
self.assertEqual(jid.parse("user@host/resource"),
("user", "host", "resource"))
self.assertEqual(jid.parse("user@host"),
("user", "host", None))
self.assertEqual(jid.parse("host"),
(None, "host", None))
self.assertEqual(jid.parse("host/resource"),
(None, "host", "resource"))
# More interesting forms
self.assertEqual(jid.parse("foo/bar@baz"),
(None, "foo", "bar@baz"))
self.assertEqual(jid.parse("boo@foo/bar@baz"),
("boo", "foo", "bar@baz"))
self.assertEqual(jid.parse("boo@foo/bar/baz"),
("boo", "foo", "bar/baz"))
self.assertEqual(jid.parse("boo/foo@bar@baz"),
(None, "boo", "foo@bar@baz"))
self.assertEqual(jid.parse("boo/foo/bar"),
(None, "boo", "foo/bar"))
self.assertEqual(jid.parse("boo//foo"),
(None, "boo", "/foo"))
def test_noHost(self):
"""
Test for failure on no host part.
"""
self.assertRaises(jid.InvalidFormat, jid.parse, "user@")
def test_doubleAt(self):
"""
Test for failure on double @ signs.
This should fail because @ is not a valid character for the host
part of the JID.
"""
self.assertRaises(jid.InvalidFormat, jid.parse, "user@@host")
def test_multipleAt(self):
"""
Test for failure on two @ signs.
This should fail because @ is not a valid character for the host
part of the JID.
"""
self.assertRaises(jid.InvalidFormat, jid.parse, "user@host@host")
# Basic tests for case mapping. These are fallback tests for the
# prepping done in twisted.words.protocols.jabber.xmpp_stringprep
def test_prepCaseMapUser(self):
"""
Test case mapping of the user part of the JID.
"""
self.assertEqual(jid.prep("UsEr", "host", "resource"),
("user", "host", "resource"))
def test_prepCaseMapHost(self):
"""
Test case mapping of the host part of the JID.
"""
self.assertEqual(jid.prep("user", "hoST", "resource"),
("user", "host", "resource"))
def test_prepNoCaseMapResource(self):
"""
Test no case mapping of the resourcce part of the JID.
"""
self.assertEqual(jid.prep("user", "hoST", "resource"),
("user", "host", "resource"))
self.assertNotEqual(jid.prep("user", "host", "Resource"),
("user", "host", "resource"))
class JIDTests(unittest.TestCase):
def test_noneArguments(self):
"""
Test that using no arguments raises an exception.
"""
self.assertRaises(RuntimeError, jid.JID)
def test_attributes(self):
"""
Test that the attributes correspond with the JID parts.
"""
j = jid.JID("user@host/resource")
self.assertEqual(j.user, "user")
self.assertEqual(j.host, "host")
self.assertEqual(j.resource, "resource")
def test_userhost(self):
"""
Test the extraction of the bare JID.
"""
j = jid.JID("user@host/resource")
self.assertEqual("user@host", j.userhost())
def test_userhostOnlyHost(self):
"""
Test the extraction of the bare JID of the full form host/resource.
"""
j = jid.JID("host/resource")
self.assertEqual("host", j.userhost())
def test_userhostJID(self):
"""
Test getting a JID object of the bare JID.
"""
j1 = jid.JID("user@host/resource")
j2 = jid.internJID("user@host")
self.assertIdentical(j2, j1.userhostJID())
def test_userhostJIDNoResource(self):
"""
Test getting a JID object of the bare JID when there was no resource.
"""
j = jid.JID("user@host")
self.assertIdentical(j, j.userhostJID())
def test_fullHost(self):
"""
Test giving a string representation of the JID with only a host part.
"""
j = jid.JID(tuple=(None, 'host', None))
self.assertEqual('host', j.full())
def test_fullHostResource(self):
"""
Test giving a string representation of the JID with host, resource.
"""
j = jid.JID(tuple=(None, 'host', 'resource'))
self.assertEqual('host/resource', j.full())
def test_fullUserHost(self):
"""
Test giving a string representation of the JID with user, host.
"""
j = jid.JID(tuple=('user', 'host', None))
self.assertEqual('user@host', j.full())
def test_fullAll(self):
"""
Test giving a string representation of the JID.
"""
j = jid.JID(tuple=('user', 'host', 'resource'))
self.assertEqual('user@host/resource', j.full())
def test_equality(self):
"""
Test JID equality.
"""
j1 = jid.JID("user@host/resource")
j2 = jid.JID("user@host/resource")
self.assertNotIdentical(j1, j2)
self.assertEqual(j1, j2)
def test_equalityWithNonJIDs(self):
"""
Test JID equality.
"""
j = jid.JID("user@host/resource")
self.assertFalse(j == 'user@host/resource')
def test_inequality(self):
"""
Test JID inequality.
"""
j1 = jid.JID("user1@host/resource")
j2 = jid.JID("user2@host/resource")
self.assertNotEqual(j1, j2)
def test_inequalityWithNonJIDs(self):
"""
Test JID equality.
"""
j = jid.JID("user@host/resource")
self.assertNotEqual(j, 'user@host/resource')
def test_hashable(self):
"""
Test JID hashability.
"""
j1 = jid.JID("user@host/resource")
j2 = jid.JID("user@host/resource")
self.assertEqual(hash(j1), hash(j2))
def test_unicode(self):
"""
Test unicode representation of JIDs.
"""
j = jid.JID(tuple=('user', 'host', 'resource'))
self.assertEqual(u"user@host/resource", unicode(j))
def test_repr(self):
"""
Test representation of JID objects.
"""
j = jid.JID(tuple=('user', 'host', 'resource'))
self.assertEqual("JID(%s)" % repr(u'user@host/resource'), repr(j))
class InternJIDTests(unittest.TestCase):
def test_identity(self):
"""
Test that two interned JIDs yield the same object.
"""
j1 = jid.internJID("user@host")
j2 = jid.internJID("user@host")
self.assertIdentical(j1, j2)

View file

@ -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)

View file

@ -0,0 +1,292 @@
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
from __future__ import absolute_import, division
from zope.interface import implementer
from twisted.internet import defer
from twisted.python.compat import unicode
from twisted.trial import unittest
from twisted.words.protocols.jabber import sasl, sasl_mechanisms, xmlstream, jid
from twisted.words.xish import domish
NS_XMPP_SASL = 'urn:ietf:params:xml:ns:xmpp-sasl'
@implementer(sasl_mechanisms.ISASLMechanism)
class DummySASLMechanism(object):
"""
Dummy SASL mechanism.
This just returns the initialResponse passed on creation, stores any
challenges and replies with the value of C{response}.
@ivar challenge: Last received challenge.
@type challenge: C{unicode}.
@ivar initialResponse: Initial response to be returned when requested
via C{getInitialResponse} or L{None}.
@type initialResponse: C{unicode}
"""
challenge = None
name = u"DUMMY"
response = b""
def __init__(self, initialResponse):
self.initialResponse = initialResponse
def getInitialResponse(self):
return self.initialResponse
def getResponse(self, challenge):
self.challenge = challenge
return self.response
class DummySASLInitiatingInitializer(sasl.SASLInitiatingInitializer):
"""
Dummy SASL Initializer for initiating entities.
This hardwires the SASL mechanism to L{DummySASLMechanism}, that is
instantiated with the value of C{initialResponse}.
@ivar initialResponse: The initial response to be returned by the
dummy SASL mechanism or L{None}.
@type initialResponse: C{unicode}.
"""
initialResponse = None
def setMechanism(self):
self.mechanism = DummySASLMechanism(self.initialResponse)
class SASLInitiatingInitializerTests(unittest.TestCase):
"""
Tests for L{sasl.SASLInitiatingInitializer}
"""
def setUp(self):
self.output = []
self.authenticator = xmlstream.Authenticator()
self.xmlstream = xmlstream.XmlStream(self.authenticator)
self.xmlstream.send = self.output.append
self.xmlstream.connectionMade()
self.xmlstream.dataReceived(b"<stream:stream xmlns='jabber:client' "
b"xmlns:stream='http://etherx.jabber.org/streams' "
b"from='example.com' id='12345' version='1.0'>")
self.init = DummySASLInitiatingInitializer(self.xmlstream)
def test_onFailure(self):
"""
Test that the SASL error condition is correctly extracted.
"""
failure = domish.Element(('urn:ietf:params:xml:ns:xmpp-sasl',
'failure'))
failure.addElement('not-authorized')
self.init._deferred = defer.Deferred()
self.init.onFailure(failure)
self.assertFailure(self.init._deferred, sasl.SASLAuthError)
self.init._deferred.addCallback(lambda e:
self.assertEqual('not-authorized',
e.condition))
return self.init._deferred
def test_sendAuthInitialResponse(self):
"""
Test starting authentication with an initial response.
"""
self.init.initialResponse = b"dummy"
self.init.start()
auth = self.output[0]
self.assertEqual(NS_XMPP_SASL, auth.uri)
self.assertEqual(u'auth', auth.name)
self.assertEqual(u'DUMMY', auth['mechanism'])
self.assertEqual(u'ZHVtbXk=', unicode(auth))
def test_sendAuthNoInitialResponse(self):
"""
Test starting authentication without an initial response.
"""
self.init.initialResponse = None
self.init.start()
auth = self.output[0]
self.assertEqual(u'', str(auth))
def test_sendAuthEmptyInitialResponse(self):
"""
Test starting authentication where the initial response is empty.
"""
self.init.initialResponse = b""
self.init.start()
auth = self.output[0]
self.assertEqual('=', unicode(auth))
def test_onChallenge(self):
"""
Test receiving a challenge message.
"""
d = self.init.start()
challenge = domish.Element((NS_XMPP_SASL, 'challenge'))
challenge.addContent(u'bXkgY2hhbGxlbmdl')
self.init.onChallenge(challenge)
self.assertEqual(b'my challenge', self.init.mechanism.challenge)
self.init.onSuccess(None)
return d
def test_onChallengeResponse(self):
"""
A non-empty response gets encoded and included as character data.
"""
d = self.init.start()
challenge = domish.Element((NS_XMPP_SASL, 'challenge'))
challenge.addContent(u'bXkgY2hhbGxlbmdl')
self.init.mechanism.response = b"response"
self.init.onChallenge(challenge)
response = self.output[1]
self.assertEqual(u'cmVzcG9uc2U=', unicode(response))
self.init.onSuccess(None)
return d
def test_onChallengeEmpty(self):
"""
Test receiving an empty challenge message.
"""
d = self.init.start()
challenge = domish.Element((NS_XMPP_SASL, 'challenge'))
self.init.onChallenge(challenge)
self.assertEqual(b'', self.init.mechanism.challenge)
self.init.onSuccess(None)
return d
def test_onChallengeIllegalPadding(self):
"""
Test receiving a challenge message with illegal padding.
"""
d = self.init.start()
challenge = domish.Element((NS_XMPP_SASL, 'challenge'))
challenge.addContent(u'bXkg=Y2hhbGxlbmdl')
self.init.onChallenge(challenge)
self.assertFailure(d, sasl.SASLIncorrectEncodingError)
return d
def test_onChallengeIllegalCharacters(self):
"""
Test receiving a challenge message with illegal characters.
"""
d = self.init.start()
challenge = domish.Element((NS_XMPP_SASL, 'challenge'))
challenge.addContent(u'bXkg*Y2hhbGxlbmdl')
self.init.onChallenge(challenge)
self.assertFailure(d, sasl.SASLIncorrectEncodingError)
return d
def test_onChallengeMalformed(self):
"""
Test receiving a malformed challenge message.
"""
d = self.init.start()
challenge = domish.Element((NS_XMPP_SASL, 'challenge'))
challenge.addContent(u'a')
self.init.onChallenge(challenge)
self.assertFailure(d, sasl.SASLIncorrectEncodingError)
return d
class SASLInitiatingInitializerSetMechanismTests(unittest.TestCase):
"""
Test for L{sasl.SASLInitiatingInitializer.setMechanism}.
"""
def setUp(self):
self.output = []
self.authenticator = xmlstream.Authenticator()
self.xmlstream = xmlstream.XmlStream(self.authenticator)
self.xmlstream.send = self.output.append
self.xmlstream.connectionMade()
self.xmlstream.dataReceived("<stream:stream xmlns='jabber:client' "
"xmlns:stream='http://etherx.jabber.org/streams' "
"from='example.com' id='12345' version='1.0'>")
self.init = sasl.SASLInitiatingInitializer(self.xmlstream)
def _setMechanism(self, name):
"""
Set up the XML Stream to have a SASL feature with the given mechanism.
"""
feature = domish.Element((NS_XMPP_SASL, 'mechanisms'))
feature.addElement('mechanism', content=name)
self.xmlstream.features[(feature.uri, feature.name)] = feature
self.init.setMechanism()
return self.init.mechanism.name
def test_anonymous(self):
"""
Test setting ANONYMOUS as the authentication mechanism.
"""
self.authenticator.jid = jid.JID('example.com')
self.authenticator.password = None
name = u"ANONYMOUS"
self.assertEqual(name, self._setMechanism(name))
def test_plain(self):
"""
Test setting PLAIN as the authentication mechanism.
"""
self.authenticator.jid = jid.JID('test@example.com')
self.authenticator.password = 'secret'
name = u"PLAIN"
self.assertEqual(name, self._setMechanism(name))
def test_digest(self):
"""
Test setting DIGEST-MD5 as the authentication mechanism.
"""
self.authenticator.jid = jid.JID('test@example.com')
self.authenticator.password = 'secret'
name = u"DIGEST-MD5"
self.assertEqual(name, self._setMechanism(name))
def test_notAcceptable(self):
"""
Test using an unacceptable SASL authentication mechanism.
"""
self.authenticator.jid = jid.JID('test@example.com')
self.authenticator.password = u'secret'
self.assertRaises(sasl.SASLNoAcceptableMechanism,
self._setMechanism, u'SOMETHING_UNACCEPTABLE')
def test_notAcceptableWithoutUser(self):
"""
Test using an unacceptable SASL authentication mechanism with no JID.
"""
self.authenticator.jid = jid.JID('example.com')
self.authenticator.password = u'secret'
self.assertRaises(sasl.SASLNoAcceptableMechanism,
self._setMechanism, u'SOMETHING_UNACCEPTABLE')

View file

@ -0,0 +1,163 @@
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""
Tests for L{twisted.words.protocols.jabber.sasl_mechanisms}.
"""
from __future__ import absolute_import, division
from twisted.trial import unittest
from twisted.python.compat import networkString
from twisted.words.protocols.jabber import sasl_mechanisms
class PlainTests(unittest.TestCase):
"""
Tests for L{twisted.words.protocols.jabber.sasl_mechanisms.Plain}.
"""
def test_getInitialResponse(self):
"""
Test the initial response.
"""
m = sasl_mechanisms.Plain(None, u'test', u'secret')
self.assertEqual(m.getInitialResponse(), b'\x00test\x00secret')
class AnonymousTests(unittest.TestCase):
"""
Tests for L{twisted.words.protocols.jabber.sasl_mechanisms.Anonymous}.
"""
def test_getInitialResponse(self):
"""
Test the initial response to be empty.
"""
m = sasl_mechanisms.Anonymous()
self.assertEqual(m.getInitialResponse(), None)
class DigestMD5Tests(unittest.TestCase):
"""
Tests for L{twisted.words.protocols.jabber.sasl_mechanisms.DigestMD5}.
"""
def setUp(self):
self.mechanism = sasl_mechanisms.DigestMD5(
u'xmpp', u'example.org', None, u'test', u'secret')
def test_getInitialResponse(self):
"""
Test that no initial response is generated.
"""
self.assertIdentical(self.mechanism.getInitialResponse(), None)
def test_getResponse(self):
"""
The response to a Digest-MD5 challenge includes the parameters from the
challenge.
"""
challenge = (
b'realm="localhost",nonce="1234",qop="auth",charset=utf-8,'
b'algorithm=md5-sess')
directives = self.mechanism._parse(
self.mechanism.getResponse(challenge))
del directives[b"cnonce"], directives[b"response"]
self.assertEqual({
b'username': b'test', b'nonce': b'1234', b'nc': b'00000001',
b'qop': [b'auth'], b'charset': b'utf-8',
b'realm': b'localhost', b'digest-uri': b'xmpp/example.org'
}, directives)
def test_getResponseNonAsciiRealm(self):
"""
Bytes outside the ASCII range in the challenge are nevertheless
included in the response.
"""
challenge = (b'realm="\xc3\xa9chec.example.org",nonce="1234",'
b'qop="auth",charset=utf-8,algorithm=md5-sess')
directives = self.mechanism._parse(
self.mechanism.getResponse(challenge))
del directives[b"cnonce"], directives[b"response"]
self.assertEqual({
b'username': b'test', b'nonce': b'1234', b'nc': b'00000001',
b'qop': [b'auth'], b'charset': b'utf-8',
b'realm': b'\xc3\xa9chec.example.org',
b'digest-uri': b'xmpp/example.org'}, directives)
def test_getResponseNoRealm(self):
"""
The response to a challenge without a realm uses the host part of the
JID as the realm.
"""
challenge = b'nonce="1234",qop="auth",charset=utf-8,algorithm=md5-sess'
directives = self.mechanism._parse(
self.mechanism.getResponse(challenge))
self.assertEqual(directives[b'realm'], b'example.org')
def test_getResponseNoRealmIDN(self):
"""
If the challenge does not include a realm and the host part of the JID
includes bytes outside of the ASCII range, the response still includes
the host part of the JID as the realm.
"""
self.mechanism = sasl_mechanisms.DigestMD5(
u'xmpp', u'\u00e9chec.example.org', None, u'test', u'secret')
challenge = b'nonce="1234",qop="auth",charset=utf-8,algorithm=md5-sess'
directives = self.mechanism._parse(
self.mechanism.getResponse(challenge))
self.assertEqual(directives[b'realm'], b'\xc3\xa9chec.example.org')
def test_getResponseRspauth(self):
"""
If the challenge just has a rspauth directive, the response is empty.
"""
challenge = \
b'rspauth=cnNwYXV0aD1lYTQwZjYwMzM1YzQyN2I1NTI3Yjg0ZGJhYmNkZmZmZA=='
response = self.mechanism.getResponse(challenge)
self.assertEqual(b"", response)
def test_calculateResponse(self):
"""
The response to a Digest-MD5 challenge is computed according to RFC
2831.
"""
charset = 'utf-8'
nonce = b'OA6MG9tEQGm2hh'
nc = networkString('%08x' % (1,))
cnonce = b'OA6MHXh6VqTrRk'
username = u'\u0418chris'
password = u'\u0418secret'
host = u'\u0418elwood.innosoft.com'
digestURI = u'imap/\u0418elwood.innosoft.com'.encode(charset)
mechanism = sasl_mechanisms.DigestMD5(
b'imap', host, None, username, password)
response = mechanism._calculateResponse(
cnonce, nc, nonce, username.encode(charset),
password.encode(charset), host.encode(charset), digestURI)
self.assertEqual(response, b'7928f233258be88392424d094453c5e3')
def test_parse(self):
"""
A challenge can be parsed into a L{dict} with L{bytes} or L{list}
values.
"""
challenge = (
b'nonce="1234",qop="auth,auth-conf",charset=utf-8,'
b'algorithm=md5-sess,cipher="des,3des"')
directives = self.mechanism._parse(challenge)
self.assertEqual({
b"algorithm": b"md5-sess", b"nonce": b"1234",
b"charset": b"utf-8", b"qop": [b'auth', b'auth-conf'],
b"cipher": [b'des', b'3des']
}, directives)

File diff suppressed because it is too large Load diff

View file

@ -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.')

View file

@ -0,0 +1,843 @@
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""
Tests for L{twisted.words.service}.
"""
import time
from twisted.cred import portal, credentials, checkers
from twisted.internet import address, defer, reactor
from twisted.internet.defer import Deferred, DeferredList, maybeDeferred, succeed
from twisted.python.compat import unicode
from twisted.spread import pb
from twisted.test import proto_helpers
from twisted.trial import unittest
from twisted.words import ewords, service
from twisted.words.protocols import irc
class RealmTests(unittest.TestCase):
def _entityCreationTest(self, kind):
# Kind is "user" or "group"
realm = service.InMemoryWordsRealm("realmname")
name = u'test' + kind.lower()
create = getattr(realm, 'create' + kind.title())
get = getattr(realm, 'get' + kind.title())
flag = 'create' + kind.title() + 'OnRequest'
dupExc = getattr(ewords, 'Duplicate' + kind.title())
noSuchExc = getattr(ewords, 'NoSuch' + kind.title())
# Creating should succeed
p = self.successResultOf(create(name))
self.assertEqual(name, p.name)
# Creating the same user again should not
self.failureResultOf(create(name)).trap(dupExc)
# Getting a non-existent user should succeed if createUserOnRequest is True
setattr(realm, flag, True)
p = self.successResultOf(get(u"new" + kind.lower()))
self.assertEqual("new" + kind.lower(), p.name)
# Getting that user again should return the same object
newp = self.successResultOf(get(u"new" + kind.lower()))
self.assertIdentical(p, newp)
# Getting a non-existent user should fail if createUserOnRequest is False
setattr(realm, flag, False)
self.failureResultOf(get(u"another" + kind.lower())).trap(noSuchExc)
def testUserCreation(self):
return self._entityCreationTest("User")
def testGroupCreation(self):
return self._entityCreationTest("Group")
def testUserRetrieval(self):
realm = service.InMemoryWordsRealm("realmname")
# Make a user to play around with
user = self.successResultOf(realm.createUser(u"testuser"))
# Make sure getting the user returns the same object
retrieved = self.successResultOf(realm.getUser(u"testuser"))
self.assertIdentical(user, retrieved)
# Make sure looking up the user also returns the same object
lookedUp = self.successResultOf(realm.lookupUser(u"testuser"))
self.assertIdentical(retrieved, lookedUp)
# Make sure looking up a user who does not exist fails
(self.failureResultOf(realm.lookupUser(u"nosuchuser"))
.trap(ewords.NoSuchUser))
def testUserAddition(self):
realm = service.InMemoryWordsRealm("realmname")
# Create and manually add a user to the realm
p = service.User("testuser")
user = self.successResultOf(realm.addUser(p))
self.assertIdentical(p, user)
# Make sure getting that user returns the same object
retrieved = self.successResultOf(realm.getUser(u"testuser"))
self.assertIdentical(user, retrieved)
# Make sure looking up that user returns the same object
lookedUp = self.successResultOf(realm.lookupUser(u"testuser"))
self.assertIdentical(retrieved, lookedUp)
def testGroupRetrieval(self):
realm = service.InMemoryWordsRealm("realmname")
group = self.successResultOf(realm.createGroup(u"testgroup"))
retrieved = self.successResultOf(realm.getGroup(u"testgroup"))
self.assertIdentical(group, retrieved)
(self.failureResultOf(realm.getGroup(u"nosuchgroup"))
.trap(ewords.NoSuchGroup))
def testGroupAddition(self):
realm = service.InMemoryWordsRealm("realmname")
p = service.Group("testgroup")
self.successResultOf(realm.addGroup(p))
group = self.successResultOf(realm.getGroup(u"testGroup"))
self.assertIdentical(p, group)
def testGroupUsernameCollision(self):
"""
Try creating a group with the same name as an existing user and
assert that it succeeds, since users and groups should not be in the
same namespace and collisions should be impossible.
"""
realm = service.InMemoryWordsRealm("realmname")
self.successResultOf(realm.createUser(u"test"))
self.successResultOf(realm.createGroup(u"test"))
def testEnumeration(self):
realm = service.InMemoryWordsRealm("realmname")
self.successResultOf(realm.createGroup(u"groupone"))
self.successResultOf(realm.createGroup(u"grouptwo"))
groups = self.successResultOf(realm.itergroups())
n = [g.name for g in groups]
n.sort()
self.assertEqual(n, ["groupone", "grouptwo"])
class TestCaseUserAgg(object):
def __init__(self, user, realm, factory, address=address.IPv4Address('TCP', '127.0.0.1', 54321)):
self.user = user
self.transport = proto_helpers.StringTransportWithDisconnection()
self.protocol = factory.buildProtocol(address)
self.transport.protocol = self.protocol
self.user.mind = self.protocol
self.protocol.makeConnection(self.transport)
def write(self, stuff):
self.protocol.dataReceived(stuff)
class IRCProtocolTests(unittest.TestCase):
STATIC_USERS = [
u'useruser', u'otheruser', u'someguy', u'firstuser', u'username',
u'userone', u'usertwo', u'userthree', 'userfour', b'userfive', u'someuser']
def setUp(self):
self.realm = service.InMemoryWordsRealm("realmname")
self.checker = checkers.InMemoryUsernamePasswordDatabaseDontUse()
self.portal = portal.Portal(self.realm, [self.checker])
self.factory = service.IRCFactory(self.realm, self.portal)
c = []
for nick in self.STATIC_USERS:
if isinstance(nick, bytes):
nick = nick.decode("utf-8")
c.append(self.realm.createUser(nick))
self.checker.addUser(nick, nick + u"_password")
return DeferredList(c)
def _assertGreeting(self, user):
"""
The user has been greeted with the four messages that are (usually)
considered to start an IRC session.
Asserts that the required responses were received.
"""
# Make sure we get 1-4 at least
response = self._response(user)
expected = [irc.RPL_WELCOME, irc.RPL_YOURHOST, irc.RPL_CREATED,
irc.RPL_MYINFO]
for (prefix, command, args) in response:
if command in expected:
expected.remove(command)
self.assertFalse(expected, "Missing responses for %r" % (expected,))
def _login(self, user, nick, password=None):
if password is None:
password = nick + "_password"
user.write(u'PASS %s\r\n' % (password,))
user.write(u'NICK %s extrainfo\r\n' % (nick,))
def _loggedInUser(self, name):
user = self.successResultOf(self.realm.lookupUser(name))
agg = TestCaseUserAgg(user, self.realm, self.factory)
self._login(agg, name)
return agg
def _response(self, user, messageType=None):
"""
Extracts the user's response, and returns a list of parsed lines.
If messageType is defined, only messages of that type will be returned.
"""
response = user.transport.value()
if bytes != str and isinstance(response, bytes):
response = response.decode("utf-8")
response = response.splitlines()
user.transport.clear()
result = []
for message in map(irc.parsemsg, response):
if messageType is None or message[1] == messageType:
result.append(message)
return result
def testPASSLogin(self):
user = self._loggedInUser(u'firstuser')
self._assertGreeting(user)
def test_nickServLogin(self):
"""
Sending NICK without PASS will prompt the user for their password.
When the user sends their password to NickServ, it will respond with a
Greeting.
"""
firstuser = self.successResultOf(self.realm.lookupUser(u'firstuser'))
user = TestCaseUserAgg(firstuser, self.realm, self.factory)
user.write('NICK firstuser extrainfo\r\n')
response = self._response(user, 'PRIVMSG')
self.assertEqual(len(response), 1)
self.assertEqual(response[0][0], service.NICKSERV)
self.assertEqual(response[0][1], 'PRIVMSG')
self.assertEqual(response[0][2], ['firstuser', 'Password?'])
user.transport.clear()
user.write('PRIVMSG nickserv firstuser_password\r\n')
self._assertGreeting(user)
def testFailedLogin(self):
firstuser = self.successResultOf(self.realm.lookupUser(u'firstuser'))
user = TestCaseUserAgg(firstuser, self.realm, self.factory)
self._login(user, u"firstuser", u"wrongpass")
response = self._response(user, "PRIVMSG")
self.assertEqual(len(response), 1)
self.assertEqual(response[0][2], ['firstuser', 'Login failed. Goodbye.'])
def testLogout(self):
logout = []
firstuser = self.successResultOf(self.realm.lookupUser(u'firstuser'))
user = TestCaseUserAgg(firstuser, self.realm, self.factory)
self._login(user, "firstuser")
user.protocol.logout = lambda: logout.append(True)
user.write('QUIT\r\n')
self.assertEqual(logout, [True])
def testJoin(self):
firstuser = self.successResultOf(self.realm.lookupUser(u'firstuser'))
somechannel = self.successResultOf(
self.realm.createGroup(u"somechannel"))
somechannel.meta['topic'] = 'some random topic'
# Bring in one user, make sure he gets into the channel sanely
user = TestCaseUserAgg(firstuser, self.realm, self.factory)
self._login(user, "firstuser")
user.transport.clear()
user.write('JOIN #somechannel\r\n')
response = self._response(user)
self.assertEqual(len(response), 5)
# Join message
self.assertEqual(response[0][0], 'firstuser!firstuser@realmname')
self.assertEqual(response[0][1], 'JOIN')
self.assertEqual(response[0][2], ['#somechannel'])
# User list
self.assertEqual(response[1][1], '353')
self.assertEqual(response[2][1], '366')
# Topic (or lack thereof, as the case may be)
self.assertEqual(response[3][1], '332')
self.assertEqual(response[4][1], '333')
# Hook up another client! It is a CHAT SYSTEM!!!!!!!
other = self._loggedInUser(u'otheruser')
other.transport.clear()
user.transport.clear()
other.write('JOIN #somechannel\r\n')
# At this point, both users should be in the channel
response = self._response(other)
event = self._response(user)
self.assertEqual(len(event), 1)
self.assertEqual(event[0][0], 'otheruser!otheruser@realmname')
self.assertEqual(event[0][1], 'JOIN')
self.assertEqual(event[0][2], ['#somechannel'])
self.assertEqual(response[1][0], 'realmname')
self.assertEqual(response[1][1], '353')
self.assertIn(response[1][2], [
['otheruser', '=', '#somechannel', 'firstuser otheruser'],
['otheruser', '=', '#somechannel', 'otheruser firstuser'],
])
def test_joinTopicless(self):
"""
When a user joins a group without a topic, no topic information is
sent to that user.
"""
firstuser = self.successResultOf(self.realm.lookupUser(u'firstuser'))
self.successResultOf(self.realm.createGroup(u"somechannel"))
# Bring in one user, make sure he gets into the channel sanely
user = TestCaseUserAgg(firstuser, self.realm, self.factory)
self._login(user, "firstuser")
user.transport.clear()
user.write('JOIN #somechannel\r\n')
response = self._response(user)
responseCodes = [r[1] for r in response]
self.assertNotIn('332', responseCodes)
self.assertNotIn('333', responseCodes)
def testLeave(self):
user = self._loggedInUser(u'useruser')
self.successResultOf(self.realm.createGroup(u"somechannel"))
user.write('JOIN #somechannel\r\n')
user.transport.clear()
other = self._loggedInUser(u'otheruser')
other.write('JOIN #somechannel\r\n')
user.transport.clear()
other.transport.clear()
user.write('PART #somechannel\r\n')
response = self._response(user)
event = self._response(other)
self.assertEqual(len(response), 1)
self.assertEqual(response[0][0], 'useruser!useruser@realmname')
self.assertEqual(response[0][1], 'PART')
self.assertEqual(response[0][2], ['#somechannel', 'leaving'])
self.assertEqual(response, event)
# Now again, with a part message
user.write('JOIN #somechannel\r\n')
user.transport.clear()
other.transport.clear()
user.write('PART #somechannel :goodbye stupidheads\r\n')
response = self._response(user)
event = self._response(other)
self.assertEqual(len(response), 1)
self.assertEqual(response[0][0], 'useruser!useruser@realmname')
self.assertEqual(response[0][1], 'PART')
self.assertEqual(response[0][2], ['#somechannel', 'goodbye stupidheads'])
self.assertEqual(response, event)
user.write(b'JOIN #somechannel\r\n')
user.transport.clear()
other.transport.clear()
user.write(b'PART #somechannel :goodbye stupidheads1\r\n')
response = self._response(user)
event = self._response(other)
self.assertEqual(len(response), 1)
self.assertEqual(response[0][0], 'useruser!useruser@realmname')
self.assertEqual(response[0][1], 'PART')
self.assertEqual(response[0][2], ['#somechannel', 'goodbye stupidheads1'])
self.assertEqual(response, event)
def testGetTopic(self):
user = self._loggedInUser(u'useruser')
group = service.Group("somechannel")
group.meta["topic"] = "This is a test topic."
group.meta["topic_author"] = "some_fellow"
group.meta["topic_date"] = 77777777
self.successResultOf(self.realm.addGroup(group))
user.transport.clear()
user.write("JOIN #somechannel\r\n")
response = self._response(user)
self.assertEqual(response[3][0], 'realmname')
self.assertEqual(response[3][1], '332')
# XXX Sigh. irc.parsemsg() is not as correct as one might hope.
self.assertEqual(response[3][2], ['useruser', '#somechannel', 'This is a test topic.'])
self.assertEqual(response[4][1], '333')
self.assertEqual(response[4][2], ['useruser', '#somechannel', 'some_fellow', '77777777'])
user.transport.clear()
user.write('TOPIC #somechannel\r\n')
response = self._response(user)
self.assertEqual(response[0][1], '332')
self.assertEqual(response[0][2], ['useruser', '#somechannel', 'This is a test topic.'])
self.assertEqual(response[1][1], '333')
self.assertEqual(response[1][2], ['useruser', '#somechannel', 'some_fellow', '77777777'])
def testSetTopic(self):
user = self._loggedInUser(u'useruser')
somechannel = self.successResultOf(
self.realm.createGroup(u"somechannel"))
user.write("JOIN #somechannel\r\n")
other = self._loggedInUser(u'otheruser')
other.write("JOIN #somechannel\r\n")
user.transport.clear()
other.transport.clear()
other.write('TOPIC #somechannel :This is the new topic.\r\n')
response = self._response(other)
event = self._response(user)
self.assertEqual(response, event)
self.assertEqual(response[0][0], 'otheruser!otheruser@realmname')
self.assertEqual(response[0][1], 'TOPIC')
self.assertEqual(response[0][2], ['#somechannel', 'This is the new topic.'])
other.transport.clear()
somechannel.meta['topic_date'] = 12345
other.write('TOPIC #somechannel\r\n')
response = self._response(other)
self.assertEqual(response[0][1], '332')
self.assertEqual(response[0][2], ['otheruser', '#somechannel', 'This is the new topic.'])
self.assertEqual(response[1][1], '333')
self.assertEqual(response[1][2], ['otheruser', '#somechannel', 'otheruser', '12345'])
other.transport.clear()
other.write('TOPIC #asdlkjasd\r\n')
response = self._response(other)
self.assertEqual(response[0][1], '403')
def testGroupMessage(self):
user = self._loggedInUser(u'useruser')
self.successResultOf(self.realm.createGroup(u"somechannel"))
user.write("JOIN #somechannel\r\n")
other = self._loggedInUser(u'otheruser')
other.write("JOIN #somechannel\r\n")
user.transport.clear()
other.transport.clear()
user.write('PRIVMSG #somechannel :Hello, world.\r\n')
response = self._response(user)
event = self._response(other)
self.assertFalse(response)
self.assertEqual(len(event), 1)
self.assertEqual(event[0][0], 'useruser!useruser@realmname')
self.assertEqual(event[0][1], 'PRIVMSG', -1)
self.assertEqual(event[0][2], ['#somechannel', 'Hello, world.'])
def testPrivateMessage(self):
user = self._loggedInUser(u'useruser')
other = self._loggedInUser(u'otheruser')
user.transport.clear()
other.transport.clear()
user.write('PRIVMSG otheruser :Hello, monkey.\r\n')
response = self._response(user)
event = self._response(other)
self.assertFalse(response)
self.assertEqual(len(event), 1)
self.assertEqual(event[0][0], 'useruser!useruser@realmname')
self.assertEqual(event[0][1], 'PRIVMSG')
self.assertEqual(event[0][2], ['otheruser', 'Hello, monkey.'])
user.write('PRIVMSG nousernamedthis :Hello, monkey.\r\n')
response = self._response(user)
self.assertEqual(len(response), 1)
self.assertEqual(response[0][0], 'realmname')
self.assertEqual(response[0][1], '401')
self.assertEqual(response[0][2], ['useruser', 'nousernamedthis', 'No such nick/channel.'])
def testOper(self):
user = self._loggedInUser(u'useruser')
user.transport.clear()
user.write('OPER user pass\r\n')
response = self._response(user)
self.assertEqual(len(response), 1)
self.assertEqual(response[0][1], '491')
def testGetUserMode(self):
user = self._loggedInUser(u'useruser')
user.transport.clear()
user.write('MODE useruser\r\n')
response = self._response(user)
self.assertEqual(len(response), 1)
self.assertEqual(response[0][0], 'realmname')
self.assertEqual(response[0][1], '221')
self.assertEqual(response[0][2], ['useruser', '+'])
def testSetUserMode(self):
user = self._loggedInUser(u'useruser')
user.transport.clear()
user.write('MODE useruser +abcd\r\n')
response = self._response(user)
self.assertEqual(len(response), 1)
self.assertEqual(response[0][1], '472')
def testGetGroupMode(self):
user = self._loggedInUser(u'useruser')
self.successResultOf(self.realm.createGroup(u"somechannel"))
user.write('JOIN #somechannel\r\n')
user.transport.clear()
user.write('MODE #somechannel\r\n')
response = self._response(user)
self.assertEqual(len(response), 1)
self.assertEqual(response[0][1], '324')
def testSetGroupMode(self):
user = self._loggedInUser(u'useruser')
self.successResultOf(self.realm.createGroup(u"groupname"))
user.write('JOIN #groupname\r\n')
user.transport.clear()
user.write('MODE #groupname +abcd\r\n')
response = self._response(user)
self.assertEqual(len(response), 1)
self.assertEqual(response[0][1], '472')
def testWho(self):
group = service.Group('groupname')
self.successResultOf(self.realm.addGroup(group))
users = []
for nick in u'userone', u'usertwo', u'userthree':
u = self._loggedInUser(nick)
users.append(u)
users[-1].write('JOIN #groupname\r\n')
for user in users:
user.transport.clear()
users[0].write('WHO #groupname\r\n')
r = self._response(users[0])
self.assertFalse(self._response(users[1]))
self.assertFalse(self._response(users[2]))
wantusers = ['userone', 'usertwo', 'userthree']
for (prefix, code, stuff) in r[:-1]:
self.assertEqual(prefix, 'realmname')
self.assertEqual(code, '352')
(myname, group, theirname, theirhost, theirserver, theirnick, flag, extra) = stuff
self.assertEqual(myname, 'userone')
self.assertEqual(group, '#groupname')
self.assertTrue(theirname in wantusers)
self.assertEqual(theirhost, 'realmname')
self.assertEqual(theirserver, 'realmname')
wantusers.remove(theirnick)
self.assertEqual(flag, 'H')
self.assertEqual(extra, '0 ' + theirnick)
self.assertFalse(wantusers)
prefix, code, stuff = r[-1]
self.assertEqual(prefix, 'realmname')
self.assertEqual(code, '315')
myname, channel, extra = stuff
self.assertEqual(myname, 'userone')
self.assertEqual(channel, '#groupname')
self.assertEqual(extra, 'End of /WHO list.')
def testList(self):
user = self._loggedInUser(u"someuser")
user.transport.clear()
somegroup = self.successResultOf(self.realm.createGroup(u"somegroup"))
somegroup.size = lambda: succeed(17)
somegroup.meta['topic'] = 'this is the topic woo'
# Test one group
user.write('LIST #somegroup\r\n')
r = self._response(user)
self.assertEqual(len(r), 2)
resp, end = r
self.assertEqual(resp[0], 'realmname')
self.assertEqual(resp[1], '322')
self.assertEqual(resp[2][0], 'someuser')
self.assertEqual(resp[2][1], 'somegroup')
self.assertEqual(resp[2][2], '17')
self.assertEqual(resp[2][3], 'this is the topic woo')
self.assertEqual(end[0], 'realmname')
self.assertEqual(end[1], '323')
self.assertEqual(end[2][0], 'someuser')
self.assertEqual(end[2][1], 'End of /LIST')
user.transport.clear()
# Test all groups
user.write('LIST\r\n')
r = self._response(user)
self.assertEqual(len(r), 2)
fg1, end = r
self.assertEqual(fg1[1], '322')
self.assertEqual(fg1[2][1], 'somegroup')
self.assertEqual(fg1[2][2], '17')
self.assertEqual(fg1[2][3], 'this is the topic woo')
self.assertEqual(end[1], '323')
def testWhois(self):
user = self._loggedInUser(u'someguy')
otherguy = service.User("otherguy")
otherguy.itergroups = lambda: iter([
service.Group('groupA'),
service.Group('groupB')])
otherguy.signOn = 10
otherguy.lastMessage = time.time() - 15
self.successResultOf(self.realm.addUser(otherguy))
user.transport.clear()
user.write('WHOIS otherguy\r\n')
r = self._response(user)
self.assertEqual(len(r), 5)
wuser, wserver, idle, channels, end = r
self.assertEqual(wuser[0], 'realmname')
self.assertEqual(wuser[1], '311')
self.assertEqual(wuser[2][0], 'someguy')
self.assertEqual(wuser[2][1], 'otherguy')
self.assertEqual(wuser[2][2], 'otherguy')
self.assertEqual(wuser[2][3], 'realmname')
self.assertEqual(wuser[2][4], '*')
self.assertEqual(wuser[2][5], 'otherguy')
self.assertEqual(wserver[0], 'realmname')
self.assertEqual(wserver[1], '312')
self.assertEqual(wserver[2][0], 'someguy')
self.assertEqual(wserver[2][1], 'otherguy')
self.assertEqual(wserver[2][2], 'realmname')
self.assertEqual(wserver[2][3], 'Hi mom!')
self.assertEqual(idle[0], 'realmname')
self.assertEqual(idle[1], '317')
self.assertEqual(idle[2][0], 'someguy')
self.assertEqual(idle[2][1], 'otherguy')
self.assertEqual(idle[2][2], '15')
self.assertEqual(idle[2][3], '10')
self.assertEqual(idle[2][4], "seconds idle, signon time")
self.assertEqual(channels[0], 'realmname')
self.assertEqual(channels[1], '319')
self.assertEqual(channels[2][0], 'someguy')
self.assertEqual(channels[2][1], 'otherguy')
self.assertEqual(channels[2][2], '#groupA #groupB')
self.assertEqual(end[0], 'realmname')
self.assertEqual(end[1], '318')
self.assertEqual(end[2][0], 'someguy')
self.assertEqual(end[2][1], 'otherguy')
self.assertEqual(end[2][2], 'End of WHOIS list.')
class TestMind(service.PBMind):
def __init__(self, *a, **kw):
self.joins = []
self.parts = []
self.messages = []
self.meta = []
def remote_userJoined(self, user, group):
self.joins.append((user, group))
def remote_userLeft(self, user, group, reason):
self.parts.append((user, group, reason))
def remote_receive(self, sender, recipient, message):
self.messages.append((sender, recipient, message))
def remote_groupMetaUpdate(self, group, meta):
self.meta.append((group, meta))
pb.setUnjellyableForClass(TestMind, service.PBMindReference)
class PBProtocolTests(unittest.TestCase):
def setUp(self):
self.realm = service.InMemoryWordsRealm("realmname")
self.checker = checkers.InMemoryUsernamePasswordDatabaseDontUse()
self.portal = portal.Portal(
self.realm, [self.checker])
self.serverFactory = pb.PBServerFactory(self.portal)
self.serverFactory.protocol = self._protocolFactory
self.serverFactory.unsafeTracebacks = True
self.clientFactory = pb.PBClientFactory()
self.clientFactory.unsafeTracebacks = True
self.serverPort = reactor.listenTCP(0, self.serverFactory)
self.clientConn = reactor.connectTCP(
'127.0.0.1',
self.serverPort.getHost().port,
self.clientFactory)
def _protocolFactory(self, *args, **kw):
self._serverProtocol = pb.Broker(0)
return self._serverProtocol
def tearDown(self):
d3 = Deferred()
self._serverProtocol.notifyOnDisconnect(lambda: d3.callback(None))
return DeferredList([
maybeDeferred(self.serverPort.stopListening),
maybeDeferred(self.clientConn.disconnect), d3])
def _loggedInAvatar(self, name, password, mind):
nameBytes = name
if isinstance(name, unicode):
nameBytes = name.encode("ascii")
creds = credentials.UsernamePassword(nameBytes, password)
self.checker.addUser(nameBytes, password)
d = self.realm.createUser(name)
d.addCallback(lambda ign: self.clientFactory.login(creds, mind))
return d
@defer.inlineCallbacks
def testGroups(self):
mindone = TestMind()
one = yield self._loggedInAvatar(u"one", b"p1", mindone)
mindtwo = TestMind()
two = yield self._loggedInAvatar(u"two", b"p2", mindtwo)
mindThree = TestMind()
three = yield self._loggedInAvatar(b"three", b"p3", mindThree)
yield self.realm.createGroup(u"foobar")
yield self.realm.createGroup(b"barfoo")
groupone = yield one.join(u"foobar")
grouptwo = yield two.join(b"barfoo")
yield two.join(u"foobar")
yield two.join(b"barfoo")
yield three.join(u"foobar")
yield groupone.send({b"text": b"hello, monkeys"})
yield groupone.leave()
yield grouptwo.leave()

View file

@ -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)

View file

@ -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)

View file

@ -0,0 +1,226 @@
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
"""
Tests for L{twisted.words.xish.xmlstream}.
"""
from __future__ import absolute_import, division
from twisted.internet import protocol
from twisted.python import failure
from twisted.trial import unittest
from twisted.words.xish import domish, utility, xmlstream
class XmlStreamTests(unittest.TestCase):
def setUp(self):
self.connectionLostMsg = "no reason"
self.outlist = []
self.xmlstream = xmlstream.XmlStream()
self.xmlstream.transport = self
self.xmlstream.transport.write = self.outlist.append
def loseConnection(self):
"""
Stub loseConnection because we are a transport.
"""
self.xmlstream.connectionLost(failure.Failure(
Exception(self.connectionLostMsg)))
def test_send(self):
"""
Calling L{xmlstream.XmlStream.send} results in the data being written
to the transport.
"""
self.xmlstream.connectionMade()
self.xmlstream.send(b"<root>")
self.assertEqual(self.outlist[0], b"<root>")
def test_receiveRoot(self):
"""
Receiving the starttag of the root element results in stream start.
"""
streamStarted = []
def streamStartEvent(rootelem):
streamStarted.append(None)
self.xmlstream.addObserver(xmlstream.STREAM_START_EVENT,
streamStartEvent)
self.xmlstream.connectionMade()
self.xmlstream.dataReceived("<root>")
self.assertEqual(1, len(streamStarted))
def test_receiveBadXML(self):
"""
Receiving malformed XML results in an L{STREAM_ERROR_EVENT}.
"""
streamError = []
streamEnd = []
def streamErrorEvent(reason):
streamError.append(reason)
def streamEndEvent(_):
streamEnd.append(None)
self.xmlstream.addObserver(xmlstream.STREAM_ERROR_EVENT,
streamErrorEvent)
self.xmlstream.addObserver(xmlstream.STREAM_END_EVENT,
streamEndEvent)
self.xmlstream.connectionMade()
self.xmlstream.dataReceived("<root>")
self.assertEqual(0, len(streamError))
self.assertEqual(0, len(streamEnd))
self.xmlstream.dataReceived("<child><unclosed></child>")
self.assertEqual(1, len(streamError))
self.assertTrue(streamError[0].check(domish.ParserError))
self.assertEqual(1, len(streamEnd))
def test_streamEnd(self):
"""
Ending the stream fires a L{STREAM_END_EVENT}.
"""
streamEnd = []
def streamEndEvent(reason):
streamEnd.append(reason)
self.xmlstream.addObserver(xmlstream.STREAM_END_EVENT,
streamEndEvent)
self.xmlstream.connectionMade()
self.loseConnection()
self.assertEqual(1, len(streamEnd))
self.assertIsInstance(streamEnd[0], failure.Failure)
self.assertEqual(streamEnd[0].getErrorMessage(),
self.connectionLostMsg)
class DummyProtocol(protocol.Protocol, utility.EventDispatcher):
"""
I am a protocol with an event dispatcher without further processing.
This protocol is only used for testing XmlStreamFactoryMixin to make
sure the bootstrap observers are added to the protocol instance.
"""
def __init__(self, *args, **kwargs):
self.args = args
self.kwargs = kwargs
self.observers = []
utility.EventDispatcher.__init__(self)
class BootstrapMixinTests(unittest.TestCase):
"""
Tests for L{xmlstream.BootstrapMixin}.
@ivar factory: Instance of the factory or mixin under test.
"""
def setUp(self):
self.factory = xmlstream.BootstrapMixin()
def test_installBootstraps(self):
"""
Dispatching an event fires registered bootstrap observers.
"""
called = []
def cb(data):
called.append(data)
dispatcher = DummyProtocol()
self.factory.addBootstrap('//event/myevent', cb)
self.factory.installBootstraps(dispatcher)
dispatcher.dispatch(None, '//event/myevent')
self.assertEqual(1, len(called))
def test_addAndRemoveBootstrap(self):
"""
Test addition and removal of a bootstrap event handler.
"""
called = []
def cb(data):
called.append(data)
self.factory.addBootstrap('//event/myevent', cb)
self.factory.removeBootstrap('//event/myevent', cb)
dispatcher = DummyProtocol()
self.factory.installBootstraps(dispatcher)
dispatcher.dispatch(None, '//event/myevent')
self.assertFalse(called)
class GenericXmlStreamFactoryTestsMixin(BootstrapMixinTests):
"""
Generic tests for L{XmlStream} factories.
"""
def setUp(self):
self.factory = xmlstream.XmlStreamFactory()
def test_buildProtocolInstallsBootstraps(self):
"""
The protocol factory installs bootstrap event handlers on the protocol.
"""
called = []
def cb(data):
called.append(data)
self.factory.addBootstrap('//event/myevent', cb)
xs = self.factory.buildProtocol(None)
xs.dispatch(None, '//event/myevent')
self.assertEqual(1, len(called))
def test_buildProtocolStoresFactory(self):
"""
The protocol factory is saved in the protocol.
"""
xs = self.factory.buildProtocol(None)
self.assertIdentical(self.factory, xs.factory)
class XmlStreamFactoryMixinTests(GenericXmlStreamFactoryTestsMixin):
"""
Tests for L{xmlstream.XmlStreamFactoryMixin}.
"""
def setUp(self):
self.factory = xmlstream.XmlStreamFactoryMixin(None, test=None)
self.factory.protocol = DummyProtocol
def test_buildProtocolFactoryArguments(self):
"""
Arguments passed to the factory are passed to protocol on
instantiation.
"""
xs = self.factory.buildProtocol(None)
self.assertEqual((None,), xs.args)
self.assertEqual({'test': None}, xs.kwargs)

View file

@ -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)

View file

@ -0,0 +1,298 @@
# -*- coding: utf-8 -*-
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.
from __future__ import absolute_import, division
from twisted.trial import unittest
from twisted.words.xish import xpath
from twisted.words.xish.domish import Element
from twisted.words.xish.xpath import XPathQuery
from twisted.words.xish.xpathparser import SyntaxError
class XPathTests(unittest.TestCase):
def setUp(self):
# Build element:
# <foo xmlns='testns' attrib1='value1' attrib3="user@host/resource">
# somecontent
# <bar>
# <foo>
# <gar>DEF</gar>
# </foo>
# </bar>
# somemorecontent
# <bar attrib2="value2">
# <bar>
# <foo/>
# <gar>ABC</gar>
# </bar>
# <bar/>
# <bar attrib4='value4' attrib5='value5'>
# <foo/>
# <gar>JKL</gar>
# </bar>
# <bar attrib4='value4' attrib5='value4'>
# <foo/>
# <gar>MNO</gar>
# </bar>
# <bar attrib4='value4' attrib5='value6' attrib6='á'>
# <quux>☃</quux>
# </bar>
# </foo>
self.e = Element(("testns", "foo"))
self.e["attrib1"] = "value1"
self.e["attrib3"] = "user@host/resource"
self.e.addContent(u"somecontent")
self.bar1 = self.e.addElement("bar")
self.subfoo = self.bar1.addElement("foo")
self.gar1 = self.subfoo.addElement("gar")
self.gar1.addContent(u"DEF")
self.e.addContent(u"somemorecontent")
self.bar2 = self.e.addElement("bar")
self.bar2["attrib2"] = "value2"
self.bar3 = self.bar2.addElement("bar")
self.subfoo2 = self.bar3.addElement("foo")
self.gar2 = self.bar3.addElement("gar")
self.gar2.addContent(u"ABC")
self.bar4 = self.e.addElement("bar")
self.bar5 = self.e.addElement("bar")
self.bar5["attrib4"] = "value4"
self.bar5["attrib5"] = "value5"
self.subfoo3 = self.bar5.addElement("foo")
self.gar3 = self.bar5.addElement("gar")
self.gar3.addContent(u"JKL")
self.bar6 = self.e.addElement("bar")
self.bar6["attrib4"] = "value4"
self.bar6["attrib5"] = "value4"
self.subfoo4 = self.bar6.addElement("foo")
self.gar4 = self.bar6.addElement("gar")
self.gar4.addContent(u"MNO")
self.bar7 = self.e.addElement("bar")
self.bar7["attrib4"] = "value4"
self.bar7["attrib5"] = "value6"
self.bar7["attrib6"] = u"á"
self.quux = self.bar7.addElement("quux")
self.quux.addContent(u"\N{SNOWMAN}")
def test_staticMethods(self):
"""
Test basic operation of the static methods.
"""
self.assertEqual(xpath.matches("/foo/bar", self.e),
True)
self.assertEqual(xpath.queryForNodes("/foo/bar", self.e),
[self.bar1, self.bar2, self.bar4,
self.bar5, self.bar6, self.bar7])
self.assertEqual(xpath.queryForString("/foo", self.e),
"somecontent")
self.assertEqual(xpath.queryForStringList("/foo", self.e),
["somecontent", "somemorecontent"])
def test_locationFooBar(self):
"""
Test matching foo with child bar.
"""
xp = XPathQuery("/foo/bar")
self.assertEqual(xp.matches(self.e), 1)
def test_locationFooBarFoo(self):
"""
Test finding foos at the second level.
"""
xp = XPathQuery("/foo/bar/foo")
self.assertEqual(xp.matches(self.e), 1)
self.assertEqual(xp.queryForNodes(self.e), [self.subfoo,
self.subfoo3,
self.subfoo4])
def test_locationNoBar3(self):
"""
Test not finding bar3.
"""
xp = XPathQuery("/foo/bar3")
self.assertEqual(xp.matches(self.e), 0)
def test_locationAllChilds(self):
"""
Test finding childs of foo.
"""
xp = XPathQuery("/foo/*")
self.assertEqual(xp.matches(self.e), True)
self.assertEqual(xp.queryForNodes(self.e), [self.bar1, self.bar2,
self.bar4, self.bar5,
self.bar6, self.bar7])
def test_attribute(self):
"""
Test matching foo with attribute.
"""
xp = XPathQuery("/foo[@attrib1]")
self.assertEqual(xp.matches(self.e), True)
def test_attributeWithValueAny(self):
"""
Test find nodes with attribute having value.
"""
xp = XPathQuery("/foo/*[@attrib2='value2']")
self.assertEqual(xp.matches(self.e), True)
self.assertEqual(xp.queryForNodes(self.e), [self.bar2])
def test_locationWithValueUnicode(self):
"""
Nodes' attributes can be matched with non-ASCII values.
"""
xp = XPathQuery(u"/foo/*[@attrib6='á']")
self.assertEqual(xp.matches(self.e), True)
self.assertEqual(xp.queryForNodes(self.e), [self.bar7])
def test_namespaceFound(self):
"""
Test matching node with namespace.
"""
xp = XPathQuery("/foo[@xmlns='testns']/bar")
self.assertEqual(xp.matches(self.e), 1)
def test_namespaceNotFound(self):
"""
Test not matching node with wrong namespace.
"""
xp = XPathQuery("/foo[@xmlns='badns']/bar2")
self.assertEqual(xp.matches(self.e), 0)
def test_attributeWithValue(self):
"""
Test matching node with attribute having value.
"""
xp = XPathQuery("/foo[@attrib1='value1']")
self.assertEqual(xp.matches(self.e), 1)
def test_queryForString(self):
"""
queryforString on absolute paths returns their first CDATA.
"""
xp = XPathQuery("/foo")
self.assertEqual(xp.queryForString(self.e), "somecontent")
def test_queryForStringList(self):
"""
queryforStringList on absolute paths returns all their CDATA.
"""
xp = XPathQuery("/foo")
self.assertEqual(xp.queryForStringList(self.e),
["somecontent", "somemorecontent"])
def test_queryForStringListAnyLocation(self):
"""
queryforStringList on relative paths returns all their CDATA.
"""
xp = XPathQuery("//foo")
self.assertEqual(xp.queryForStringList(self.e),
["somecontent", "somemorecontent"])
def test_queryForNodes(self):
"""
Test finding nodes.
"""
xp = XPathQuery("/foo/bar")
self.assertEqual(xp.queryForNodes(self.e), [self.bar1, self.bar2,
self.bar4, self.bar5,
self.bar6, self.bar7])
def test_textCondition(self):
"""
Test matching a node with given text.
"""
xp = XPathQuery("/foo[text() = 'somecontent']")
self.assertEqual(xp.matches(self.e), True)
def test_textConditionUnicode(self):
"""
A node can be matched by text with non-ascii code points.
"""
xp = XPathQuery(u"//*[text()='\N{SNOWMAN}']")
self.assertEqual(xp.matches(self.e), True)
self.assertEqual(xp.queryForNodes(self.e), [self.quux])
def test_textNotOperator(self):
"""
Test for not operator.
"""
xp = XPathQuery("/foo[not(@nosuchattrib)]")
self.assertEqual(xp.matches(self.e), True)
def test_anyLocationAndText(self):
"""
Test finding any nodes named gar and getting their text contents.
"""
xp = XPathQuery("//gar")
self.assertEqual(xp.matches(self.e), True)
self.assertEqual(xp.queryForNodes(self.e), [self.gar1, self.gar2,
self.gar3, self.gar4])
self.assertEqual(xp.queryForStringList(self.e), ["DEF", "ABC",
"JKL", "MNO"])
def test_anyLocation(self):
"""
Test finding any nodes named bar.
"""
xp = XPathQuery("//bar")
self.assertEqual(xp.matches(self.e), True)
self.assertEqual(xp.queryForNodes(self.e), [self.bar1, self.bar2,
self.bar3, self.bar4,
self.bar5, self.bar6,
self.bar7])
def test_anyLocationQueryForString(self):
"""
L{XPathQuery.queryForString} should raise a L{NotImplementedError}
for any location.
"""
xp = XPathQuery("//bar")
self.assertRaises(NotImplementedError, xp.queryForString, None)
def test_andOperator(self):
"""
Test boolean and operator in condition.
"""
xp = XPathQuery("//bar[@attrib4='value4' and @attrib5='value5']")
self.assertEqual(xp.matches(self.e), True)
self.assertEqual(xp.queryForNodes(self.e), [self.bar5])
def test_orOperator(self):
"""
Test boolean or operator in condition.
"""
xp = XPathQuery("//bar[@attrib5='value4' or @attrib5='value5']")
self.assertEqual(xp.matches(self.e), True)
self.assertEqual(xp.queryForNodes(self.e), [self.bar5, self.bar6])
def test_booleanOperatorsParens(self):
"""
Test multiple boolean operators in condition with parens.
"""
xp = XPathQuery("""//bar[@attrib4='value4' and
(@attrib5='value4' or @attrib5='value6')]""")
self.assertEqual(xp.matches(self.e), True)
self.assertEqual(xp.queryForNodes(self.e), [self.bar6, self.bar7])
def test_booleanOperatorsNoParens(self):
"""
Test multiple boolean operators in condition without parens.
"""
xp = XPathQuery("""//bar[@attrib5='value4' or
@attrib5='value5' or
@attrib5='value6']""")
self.assertEqual(xp.matches(self.e), True)
self.assertEqual(xp.queryForNodes(self.e), [self.bar5, self.bar6, self.bar7])
def test_badXPathNoClosingBracket(self):
"""
A missing closing bracket raises a SyntaxError.
This test excercises the most common failure mode.
"""
exc = self.assertRaises(SyntaxError, XPathQuery, """//bar[@attrib1""")
self.assertTrue(exc.msg.startswith("Trying to find one of"),
("SyntaxError message '%s' doesn't start with "
"'Trying to find one of'") % exc.msg)