1
# -*- test-case-name: twisted.web.test.test_xmlrpc -*-
3
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
4
# See LICENSE for details.
8
"""Test XML-RPC support."""
12
from twisted.web2 import xmlrpc
13
from twisted.web2.xmlrpc import XMLRPC, addIntrospection
14
from twisted.internet import defer
16
from twisted.web2.test.test_server import BaseCase
18
class TestRuntimeError(RuntimeError):
20
Fake RuntimeError for testing purposes.
23
class TestValueError(ValueError):
25
Fake ValueError for testing purposes.
28
class XMLRPCTestResource(XMLRPC):
30
This is the XML-RPC "server" against which the tests will be run.
36
addSlash = True # cause it's at the root
38
# the doc string is part of the test
39
def xmlrpc_add(self, request, a, b):
40
"""This function add two numbers."""
43
xmlrpc_add.signature = [['int', 'int', 'int'],
44
['double', 'double', 'double']]
46
# the doc string is part of the test
47
def xmlrpc_pair(self, request, string, num):
48
"""This function puts the two arguments in an array."""
51
xmlrpc_pair.signature = [['array', 'string', 'int']]
53
# the doc string is part of the test
54
def xmlrpc_defer(self, request, x):
56
return defer.succeed(x)
58
def xmlrpc_deferFail(self, request):
59
return defer.fail(TestValueError())
61
# don't add a doc string, it's part of the test
62
def xmlrpc_fail(self, request):
63
raise TestRuntimeError
65
def xmlrpc_fault(self, request):
66
return xmlrpc.Fault(12, "hello")
68
def xmlrpc_deferFault(self, request):
69
return defer.fail(xmlrpc.Fault(17, "hi"))
71
def xmlrpc_complex(self, request):
72
return {"a": ["b", "c", 12, []], "D": "foo"}
74
def xmlrpc_dict(self, request, map, key):
77
def getFunction(self, functionPath):
79
return XMLRPC.getFunction(self, functionPath)
80
except xmlrpc.NoSuchFunction:
81
if functionPath.startswith("SESSION"):
82
raise xmlrpc.Fault(self.SESSION_EXPIRED, "Session non-existant/expired.")
86
xmlrpc_dict.help = 'Help for dict.'
88
class XMLRPCServerBase(BaseCase):
90
The parent class of the XML-RPC test classes.
96
self.root = XMLRPCTestResource()
97
self.xml = ("<?xml version='1.0'?>\n<methodResponse>\n" +
98
"%s</methodResponse>\n")
100
class XMLRPCServerGETTest(XMLRPCServerBase):
102
Attempt access to the RPC resources as regular HTTP resource.
106
super(XMLRPCServerGETTest, self).setUp()
108
self.errorRPC = ('<html><head><title>XML-RPC responder</title>' +
109
'</head><body><h1>XML-RPC responder</h1>POST your XML-RPC ' +
110
'here.</body></html>')
111
self.errorHTTP = ('<html><head><title>404 Not Found</title>' +
112
'</head><body><h1>Not Found</h1>The resource http://host/add ' +
113
'cannot be found.</body></html>')
115
def test_rootGET(self):
117
Test a simple GET against the XML-RPC server.
119
return self.assertResponse(
120
(self.root, 'http://host/'),
121
(200, {}, self.errorRPC))
123
def test_childGET(self):
125
Try to access an XML-RPC method as a regular resource via GET.
127
return self.assertResponse(
128
(self.root, 'http://host/add'),
129
(404, {}, self.errorHTTP))
131
class XMLRPCServerPOSTTest(XMLRPCServerBase):
133
Tests for standard XML-RPC usage.
135
def test_RPCMethods(self):
137
Make RPC calls of the defined methods, checking for the expected
142
("defer", ("a",), "a"),
143
("dict", ({"a": 1}, "a"), 1),
144
("pair", ("a", 1), ["a", 1]),
145
("complex", (), {"a": ["b", "c", 12, []], "D": "foo"})]
147
for meth, args, outp in inputOutput:
148
postdata = xmlrpclib.dumps(args, meth)
149
respdata = xmlrpclib.dumps((outp,))
150
reqdata = (self.root, 'http://host/', {}, None, None, '', postdata)
151
d = self.assertResponse(reqdata, (200, {}, self.xml % respdata))
153
return defer.DeferredList(dl, fireOnOneErrback=True)
155
def test_RPCFaults(self):
157
Ensure that RPC faults are properly processed.
161
(12, "fault", 'hello'),
162
(23, "noSuchMethod", 'function noSuchMethod not found'),
163
(17, "deferFault", 'hi'),
164
(42, "SESSION_TEST", 'Session non-existant/expired.')]
165
for code, meth, fault in codeMethod:
166
postdata = xmlrpclib.dumps((), meth)
167
respdata = xmlrpclib.dumps(xmlrpc.Fault(code, fault))
168
reqdata = (self.root, 'http://host/', {}, None, None, '', postdata)
169
d = self.assertResponse(reqdata, (200, {}, respdata))
171
d = defer.DeferredList(dl, fireOnOneErrback=True)
174
def test_RPCFailures(self):
176
Ensure that failures behave as expected.
182
for code, meth in codeMethod:
183
postdata = xmlrpclib.dumps((), meth)
184
respdata = xmlrpclib.dumps(xmlrpc.Fault(code, 'error'))
185
reqdata = (self.root, 'http://host/', {}, None, None, '', postdata)
186
d = self.assertResponse(reqdata, (200, {}, respdata))
187
d.addCallback(self.flushLoggedErrors, TestRuntimeError, TestValueError)
189
d = defer.DeferredList(dl, fireOnOneErrback=True)
192
class XMLRPCTestIntrospection(XMLRPCServerBase):
196
Introspection requires additional setup, most importantly, adding
197
introspection to the root object.
199
super(XMLRPCTestIntrospection, self).setUp()
200
addIntrospection(self.root)
201
self.methodList = ['add', 'complex', 'defer', 'deferFail',
202
'deferFault', 'dict', 'fail', 'fault', 'pair',
203
'system.listMethods', 'system.methodHelp', 'system.methodSignature']
205
def test_listMethods(self):
207
Check that the introspection method "listMethods" returns all the
208
methods we defined in the XML-RPC server.
210
def cbMethods(meths):
212
self.failUnlessEqual(
215
postdata = xmlrpclib.dumps((), 'system.listMethods')
216
respdata = xmlrpclib.dumps((self.methodList,))
217
reqdata = (self.root, 'http://host/', {}, None, None, '', postdata)
218
return self.assertResponse(reqdata, (200, {}, self.xml % respdata))
220
def test_methodHelp(self):
222
Check the RPC methods for docstrings or .help attributes.
225
("defer", "Help for defer."),
227
("dict", "Help for dict.")]
230
for meth, outp in inputOutput:
231
postdata = xmlrpclib.dumps((meth,), 'system.methodHelp')
232
respdata = xmlrpclib.dumps((outp,))
233
reqdata = (self.root, 'http://host/', {}, None, None, '', postdata)
234
d = self.assertResponse(reqdata, (200, {}, self.xml % respdata))
236
return defer.DeferredList(dl, fireOnOneErrback=True)
238
def test_methodSignature(self):
240
Check that the RPC methods whose signatures have been set via the
241
.signature attribute (on the method) are returned as expected.
245
("add", [['int', 'int', 'int'],
246
['double', 'double', 'double']]),
247
("pair", [['array', 'string', 'int']])]
250
for meth, outp in inputOutput:
251
postdata = xmlrpclib.dumps((meth,), 'system.methodSignature')
252
respdata = xmlrpclib.dumps((outp,))
253
reqdata = (self.root, 'http://host/', {}, None, None, '', postdata)
254
d = self.assertResponse(reqdata, (200, {}, self.xml % respdata))
256
return defer.DeferredList(dl, fireOnOneErrback=True)