2008-11-24 13:51:03 -06:00
|
|
|
# Authors:
|
|
|
|
# Jason Gerard DeRose <jderose@redhat.com>
|
|
|
|
#
|
|
|
|
# Copyright (C) 2008 Red Hat
|
|
|
|
# see file 'COPYING' for use and warranty information
|
|
|
|
#
|
2010-12-09 06:59:11 -06:00
|
|
|
# This program is free software; you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU General Public License as published by
|
|
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
|
|
# (at your option) any later version.
|
2008-11-24 13:51:03 -06:00
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License
|
2010-12-09 06:59:11 -06:00
|
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2008-11-24 13:51:03 -06:00
|
|
|
|
|
|
|
"""
|
|
|
|
Test the `ipalib.rpc` module.
|
|
|
|
"""
|
2015-08-12 06:44:11 -05:00
|
|
|
from __future__ import print_function
|
2008-11-24 13:51:03 -06:00
|
|
|
|
2014-01-14 06:41:19 -06:00
|
|
|
from xmlrpclib import Binary, Fault, dumps, loads
|
|
|
|
|
|
|
|
import nose
|
2015-09-11 06:43:28 -05:00
|
|
|
import six
|
|
|
|
|
2013-05-21 06:40:27 -05:00
|
|
|
from ipatests.util import raises, assert_equal, PluginTester, DummyClass
|
|
|
|
from ipatests.data import binary_bytes, utf8_bytes, unicode_str
|
2009-01-19 22:10:42 -06:00
|
|
|
from ipalib.frontend import Command
|
2009-01-23 19:02:32 -06:00
|
|
|
from ipalib.request import context, Connection
|
2014-01-14 06:41:19 -06:00
|
|
|
from ipalib import rpc, errors, api, request
|
2014-03-28 03:51:10 -05:00
|
|
|
from ipapython.version import API_VERSION
|
2009-01-19 22:10:42 -06:00
|
|
|
|
2015-09-11 06:43:28 -05:00
|
|
|
if six.PY3:
|
|
|
|
unicode = str
|
|
|
|
|
2009-01-19 22:10:42 -06:00
|
|
|
|
|
|
|
std_compound = (binary_bytes, utf8_bytes, unicode_str)
|
2008-11-24 13:51:03 -06:00
|
|
|
|
|
|
|
|
|
|
|
def dump_n_load(value):
|
|
|
|
(param, method) = loads(
|
2009-01-16 00:52:50 -06:00
|
|
|
dumps((value,), allow_none=True)
|
2008-11-24 13:51:03 -06:00
|
|
|
)
|
|
|
|
return param[0]
|
|
|
|
|
|
|
|
|
|
|
|
def round_trip(value):
|
2009-01-16 00:52:50 -06:00
|
|
|
return rpc.xml_unwrap(
|
2014-03-28 03:51:10 -05:00
|
|
|
dump_n_load(rpc.xml_wrap(value, API_VERSION))
|
2008-11-24 13:51:03 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
def test_round_trip():
|
|
|
|
"""
|
2009-01-16 00:52:50 -06:00
|
|
|
Test `ipalib.rpc.xml_wrap` and `ipalib.rpc.xml_unwrap`.
|
2008-11-24 13:51:03 -06:00
|
|
|
|
|
|
|
This tests the two functions together with ``xmlrpclib.dumps()`` and
|
2008-11-24 22:34:01 -06:00
|
|
|
``xmlrpclib.loads()`` in a full wrap/dumps/loads/unwrap round trip.
|
2008-11-24 13:51:03 -06:00
|
|
|
"""
|
|
|
|
# We first test that our assumptions about xmlrpclib module in the Python
|
|
|
|
# standard library are correct:
|
2009-01-16 00:52:50 -06:00
|
|
|
assert_equal(dump_n_load(utf8_bytes), unicode_str)
|
|
|
|
assert_equal(dump_n_load(unicode_str), unicode_str)
|
|
|
|
assert_equal(dump_n_load(Binary(binary_bytes)).data, binary_bytes)
|
2008-12-08 16:10:01 -06:00
|
|
|
assert isinstance(dump_n_load(Binary(binary_bytes)), Binary)
|
2008-11-24 13:51:03 -06:00
|
|
|
assert type(dump_n_load('hello')) is str
|
|
|
|
assert type(dump_n_load(u'hello')) is str
|
2009-01-16 00:52:50 -06:00
|
|
|
assert_equal(dump_n_load(''), '')
|
|
|
|
assert_equal(dump_n_load(u''), '')
|
|
|
|
assert dump_n_load(None) is None
|
2008-11-24 13:51:03 -06:00
|
|
|
|
|
|
|
# Now we test our wrap and unwrap methods in combination with dumps, loads:
|
|
|
|
# All str should come back str (because they get wrapped in
|
|
|
|
# xmlrpclib.Binary(). All unicode should come back unicode because str
|
2009-01-16 00:52:50 -06:00
|
|
|
# explicity get decoded by rpc.xml_unwrap() if they weren't already
|
2008-11-24 13:51:03 -06:00
|
|
|
# decoded by xmlrpclib.loads().
|
2009-01-16 00:52:50 -06:00
|
|
|
assert_equal(round_trip(utf8_bytes), utf8_bytes)
|
|
|
|
assert_equal(round_trip(unicode_str), unicode_str)
|
|
|
|
assert_equal(round_trip(binary_bytes), binary_bytes)
|
2008-11-24 13:51:03 -06:00
|
|
|
assert type(round_trip('hello')) is str
|
|
|
|
assert type(round_trip(u'hello')) is unicode
|
2009-01-16 00:52:50 -06:00
|
|
|
assert_equal(round_trip(''), '')
|
|
|
|
assert_equal(round_trip(u''), u'')
|
|
|
|
assert round_trip(None) is None
|
|
|
|
compound = [utf8_bytes, None, binary_bytes, (None, unicode_str),
|
2008-12-08 16:10:01 -06:00
|
|
|
dict(utf8=utf8_bytes, chars=unicode_str, data=binary_bytes)
|
2008-11-24 13:51:03 -06:00
|
|
|
]
|
|
|
|
assert round_trip(compound) == tuple(compound)
|
|
|
|
|
|
|
|
|
2009-01-16 00:52:50 -06:00
|
|
|
def test_xml_wrap():
|
2008-11-24 13:51:03 -06:00
|
|
|
"""
|
2009-01-16 00:52:50 -06:00
|
|
|
Test the `ipalib.rpc.xml_wrap` function.
|
2008-11-24 13:51:03 -06:00
|
|
|
"""
|
2009-01-16 00:52:50 -06:00
|
|
|
f = rpc.xml_wrap
|
2014-03-28 03:51:10 -05:00
|
|
|
assert f([], API_VERSION) == tuple()
|
|
|
|
assert f({}, API_VERSION) == dict()
|
|
|
|
b = f('hello', API_VERSION)
|
2008-11-24 13:51:03 -06:00
|
|
|
assert isinstance(b, Binary)
|
|
|
|
assert b.data == 'hello'
|
2014-03-28 03:51:10 -05:00
|
|
|
u = f(u'hello', API_VERSION)
|
2008-11-24 13:51:03 -06:00
|
|
|
assert type(u) is unicode
|
|
|
|
assert u == u'hello'
|
2014-03-28 03:51:10 -05:00
|
|
|
value = f([dict(one=False, two=u'hello'), None, 'hello'], API_VERSION)
|
2008-11-24 13:51:03 -06:00
|
|
|
|
|
|
|
|
2009-01-16 00:52:50 -06:00
|
|
|
def test_xml_unwrap():
|
2008-11-24 13:51:03 -06:00
|
|
|
"""
|
2009-01-16 00:52:50 -06:00
|
|
|
Test the `ipalib.rpc.xml_unwrap` function.
|
2008-11-24 13:51:03 -06:00
|
|
|
"""
|
2009-01-16 00:52:50 -06:00
|
|
|
f = rpc.xml_unwrap
|
2008-11-24 13:51:03 -06:00
|
|
|
assert f([]) == tuple()
|
|
|
|
assert f({}) == dict()
|
2008-12-08 16:10:01 -06:00
|
|
|
value = f(Binary(utf8_bytes))
|
2008-11-24 13:51:03 -06:00
|
|
|
assert type(value) is str
|
2008-12-08 16:10:01 -06:00
|
|
|
assert value == utf8_bytes
|
|
|
|
assert f(utf8_bytes) == unicode_str
|
|
|
|
assert f(unicode_str) == unicode_str
|
|
|
|
value = f([True, Binary('hello'), dict(one=1, two=utf8_bytes, three=None)])
|
|
|
|
assert value == (True, 'hello', dict(one=1, two=unicode_str, three=None))
|
2008-11-24 13:51:03 -06:00
|
|
|
assert type(value[1]) is str
|
|
|
|
assert type(value[2]['two']) is unicode
|
2009-01-16 00:52:50 -06:00
|
|
|
|
|
|
|
|
|
|
|
def test_xml_dumps():
|
|
|
|
"""
|
|
|
|
Test the `ipalib.rpc.xml_dumps` function.
|
|
|
|
"""
|
|
|
|
f = rpc.xml_dumps
|
|
|
|
params = (binary_bytes, utf8_bytes, unicode_str, None)
|
|
|
|
|
|
|
|
# Test serializing an RPC request:
|
2014-03-28 03:51:10 -05:00
|
|
|
data = f(params, API_VERSION, 'the_method')
|
2009-01-16 00:52:50 -06:00
|
|
|
(p, m) = loads(data)
|
2010-07-27 15:38:32 -05:00
|
|
|
assert_equal(m, u'the_method')
|
2009-01-16 00:52:50 -06:00
|
|
|
assert type(p) is tuple
|
|
|
|
assert rpc.xml_unwrap(p) == params
|
|
|
|
|
|
|
|
# Test serializing an RPC response:
|
2014-03-28 03:51:10 -05:00
|
|
|
data = f((params,), API_VERSION, methodresponse=True)
|
2009-01-16 00:52:50 -06:00
|
|
|
(tup, m) = loads(data)
|
|
|
|
assert m is None
|
|
|
|
assert len(tup) == 1
|
|
|
|
assert type(tup) is tuple
|
|
|
|
assert rpc.xml_unwrap(tup[0]) == params
|
|
|
|
|
|
|
|
# Test serializing an RPC response containing a Fault:
|
|
|
|
fault = Fault(69, unicode_str)
|
2014-03-28 03:51:10 -05:00
|
|
|
data = f(fault, API_VERSION, methodresponse=True)
|
2009-01-16 00:52:50 -06:00
|
|
|
e = raises(Fault, loads, data)
|
|
|
|
assert e.faultCode == 69
|
|
|
|
assert_equal(e.faultString, unicode_str)
|
|
|
|
|
|
|
|
|
|
|
|
def test_xml_loads():
|
|
|
|
"""
|
|
|
|
Test the `ipalib.rpc.xml_loads` function.
|
|
|
|
"""
|
|
|
|
f = rpc.xml_loads
|
|
|
|
params = (binary_bytes, utf8_bytes, unicode_str, None)
|
2014-03-28 03:51:10 -05:00
|
|
|
wrapped = rpc.xml_wrap(params, API_VERSION)
|
2009-01-16 00:52:50 -06:00
|
|
|
|
|
|
|
# Test un-serializing an RPC request:
|
|
|
|
data = dumps(wrapped, 'the_method', allow_none=True)
|
|
|
|
(p, m) = f(data)
|
2010-07-27 15:38:32 -05:00
|
|
|
assert_equal(m, u'the_method')
|
2009-01-16 00:52:50 -06:00
|
|
|
assert_equal(p, params)
|
|
|
|
|
|
|
|
# Test un-serializing an RPC response:
|
|
|
|
data = dumps((wrapped,), methodresponse=True, allow_none=True)
|
|
|
|
(tup, m) = f(data)
|
|
|
|
assert m is None
|
|
|
|
assert len(tup) == 1
|
|
|
|
assert type(tup) is tuple
|
|
|
|
assert_equal(tup[0], params)
|
|
|
|
|
|
|
|
# Test un-serializing an RPC response containing a Fault:
|
2009-01-22 16:41:54 -06:00
|
|
|
for error in (unicode_str, u'hello'):
|
|
|
|
fault = Fault(69, error)
|
|
|
|
data = dumps(fault, methodresponse=True, allow_none=True, encoding='UTF-8')
|
|
|
|
e = raises(Fault, f, data)
|
|
|
|
assert e.faultCode == 69
|
|
|
|
assert_equal(e.faultString, error)
|
|
|
|
assert type(e.faultString) is unicode
|
2009-01-19 22:10:42 -06:00
|
|
|
|
|
|
|
|
|
|
|
class test_xmlclient(PluginTester):
|
|
|
|
"""
|
|
|
|
Test the `ipalib.rpc.xmlclient` plugin.
|
|
|
|
"""
|
|
|
|
_plugin = rpc.xmlclient
|
|
|
|
|
|
|
|
def test_forward(self):
|
|
|
|
"""
|
|
|
|
Test the `ipalib.rpc.xmlclient.forward` method.
|
|
|
|
"""
|
|
|
|
class user_add(Command):
|
|
|
|
pass
|
|
|
|
|
|
|
|
# Test that ValueError is raised when forwarding a command that is not
|
|
|
|
# in api.Command:
|
|
|
|
(o, api, home) = self.instance('Backend', in_server=False)
|
|
|
|
e = raises(ValueError, o.forward, 'user_add')
|
|
|
|
assert str(e) == '%s.forward(): %r not in api.Command' % (
|
|
|
|
'xmlclient', 'user_add'
|
|
|
|
)
|
|
|
|
|
|
|
|
(o, api, home) = self.instance('Backend', user_add, in_server=False)
|
|
|
|
args = (binary_bytes, utf8_bytes, unicode_str)
|
|
|
|
kw = dict(one=binary_bytes, two=utf8_bytes, three=unicode_str)
|
2010-03-26 04:56:53 -05:00
|
|
|
params = [args, kw]
|
2009-01-19 22:10:42 -06:00
|
|
|
result = (unicode_str, binary_bytes, utf8_bytes)
|
2009-01-23 19:02:32 -06:00
|
|
|
conn = DummyClass(
|
2009-01-19 22:10:42 -06:00
|
|
|
(
|
|
|
|
'user_add',
|
2014-03-28 03:51:10 -05:00
|
|
|
rpc.xml_wrap(params, API_VERSION),
|
2009-01-19 22:10:42 -06:00
|
|
|
{},
|
2014-03-28 03:51:10 -05:00
|
|
|
rpc.xml_wrap(result, API_VERSION),
|
2009-01-19 22:10:42 -06:00
|
|
|
),
|
|
|
|
(
|
|
|
|
'user_add',
|
2014-03-28 03:51:10 -05:00
|
|
|
rpc.xml_wrap(params, API_VERSION),
|
2009-01-19 22:10:42 -06:00
|
|
|
{},
|
2009-01-22 00:39:17 -06:00
|
|
|
Fault(3007, u"'four' is required"), # RequirementError
|
2009-01-19 22:10:42 -06:00
|
|
|
),
|
|
|
|
(
|
|
|
|
'user_add',
|
2014-03-28 03:51:10 -05:00
|
|
|
rpc.xml_wrap(params, API_VERSION),
|
2009-01-19 22:10:42 -06:00
|
|
|
{},
|
|
|
|
Fault(700, u'no such error'), # There is no error 700
|
|
|
|
),
|
|
|
|
|
|
|
|
)
|
2015-07-21 10:38:06 -05:00
|
|
|
|
|
|
|
# Create connection for the current thread
|
|
|
|
setattr(context, o.id, Connection(conn, lambda: None))
|
2009-01-23 19:02:32 -06:00
|
|
|
context.xmlclient = Connection(conn, lambda: None)
|
2009-01-19 22:10:42 -06:00
|
|
|
|
|
|
|
# Test with a successful return value:
|
|
|
|
assert o.forward('user_add', *args, **kw) == result
|
|
|
|
|
|
|
|
# Test with an errno the client knows:
|
2009-04-23 07:51:59 -05:00
|
|
|
e = raises(errors.RequirementError, o.forward, 'user_add', *args, **kw)
|
2010-07-26 16:54:38 -05:00
|
|
|
assert_equal(e.args[0], u"'four' is required")
|
2009-01-19 22:10:42 -06:00
|
|
|
|
|
|
|
# Test with an errno the client doesn't know
|
2009-04-23 07:51:59 -05:00
|
|
|
e = raises(errors.UnknownError, o.forward, 'user_add', *args, **kw)
|
2009-01-19 22:10:42 -06:00
|
|
|
assert_equal(e.code, 700)
|
|
|
|
assert_equal(e.error, u'no such error')
|
|
|
|
|
2009-01-23 19:02:32 -06:00
|
|
|
assert context.xmlclient.conn._calledall() is True
|
2014-01-14 06:41:19 -06:00
|
|
|
|
|
|
|
|
|
|
|
class test_xml_introspection(object):
|
|
|
|
@classmethod
|
2014-10-07 05:48:22 -05:00
|
|
|
def setup_class(self):
|
2014-01-14 06:41:19 -06:00
|
|
|
try:
|
|
|
|
api.Backend.xmlclient.connect(fallback=False)
|
|
|
|
except (errors.NetworkError, IOError):
|
|
|
|
raise nose.SkipTest('%r: Server not available: %r' %
|
|
|
|
(__name__, api.env.xmlrpc_uri))
|
|
|
|
|
|
|
|
@classmethod
|
2014-10-07 05:48:22 -05:00
|
|
|
def teardown_class(self):
|
2014-01-14 06:41:19 -06:00
|
|
|
request.destroy_context()
|
|
|
|
|
|
|
|
def test_list_methods(self):
|
|
|
|
result = api.Backend.xmlclient.conn.system.listMethods()
|
|
|
|
assert len(result)
|
|
|
|
assert 'ping' in result
|
|
|
|
assert 'user_add' in result
|
|
|
|
assert 'system.listMethods' in result
|
|
|
|
assert 'system.methodSignature' in result
|
|
|
|
assert 'system.methodHelp' in result
|
|
|
|
|
|
|
|
def test_list_methods_many_params(self):
|
|
|
|
try:
|
|
|
|
result = api.Backend.xmlclient.conn.system.listMethods('foo')
|
2015-07-30 09:49:29 -05:00
|
|
|
except Fault as f:
|
2015-08-12 06:44:11 -05:00
|
|
|
print(f)
|
2014-01-14 06:41:19 -06:00
|
|
|
assert f.faultCode == 3003
|
|
|
|
assert f.faultString == (
|
|
|
|
"command 'system.listMethods' takes no arguments")
|
|
|
|
else:
|
|
|
|
raise AssertionError('did not raise')
|
|
|
|
|
|
|
|
def test_ping_signature(self):
|
|
|
|
result = api.Backend.xmlclient.conn.system.methodSignature('ping')
|
|
|
|
assert result == [['struct', 'array', 'struct']]
|
|
|
|
|
|
|
|
|
|
|
|
def test_ping_help(self):
|
|
|
|
result = api.Backend.xmlclient.conn.system.methodHelp('ping')
|
|
|
|
assert result == 'Ping a remote server.'
|
|
|
|
|
|
|
|
def test_signature_no_params(self):
|
|
|
|
try:
|
|
|
|
result = api.Backend.xmlclient.conn.system.methodSignature()
|
2015-07-30 09:49:29 -05:00
|
|
|
except Fault as f:
|
2015-08-12 06:44:11 -05:00
|
|
|
print(f)
|
2014-01-14 06:41:19 -06:00
|
|
|
assert f.faultCode == 3007
|
|
|
|
assert f.faultString == "'method name' is required"
|
|
|
|
else:
|
|
|
|
raise AssertionError('did not raise')
|
|
|
|
|
|
|
|
def test_signature_many_params(self):
|
|
|
|
try:
|
|
|
|
result = api.Backend.xmlclient.conn.system.methodSignature('a', 'b')
|
2015-07-30 09:49:29 -05:00
|
|
|
except Fault as f:
|
2015-08-12 06:44:11 -05:00
|
|
|
print(f)
|
2014-01-14 06:41:19 -06:00
|
|
|
assert f.faultCode == 3004
|
|
|
|
assert f.faultString == (
|
|
|
|
"command 'system.methodSignature' takes at most 1 argument")
|
|
|
|
else:
|
|
|
|
raise AssertionError('did not raise')
|
|
|
|
|
|
|
|
def test_help_no_params(self):
|
|
|
|
try:
|
|
|
|
result = api.Backend.xmlclient.conn.system.methodHelp()
|
2015-07-30 09:49:29 -05:00
|
|
|
except Fault as f:
|
2015-08-12 06:44:11 -05:00
|
|
|
print(f)
|
2014-01-14 06:41:19 -06:00
|
|
|
assert f.faultCode == 3007
|
|
|
|
assert f.faultString == "'method name' is required"
|
|
|
|
else:
|
|
|
|
raise AssertionError('did not raise')
|
|
|
|
|
|
|
|
def test_help_many_params(self):
|
|
|
|
try:
|
|
|
|
result = api.Backend.xmlclient.conn.system.methodHelp('a', 'b')
|
2015-07-30 09:49:29 -05:00
|
|
|
except Fault as f:
|
2015-08-12 06:44:11 -05:00
|
|
|
print(f)
|
2014-01-14 06:41:19 -06:00
|
|
|
assert f.faultCode == 3004
|
|
|
|
assert f.faultString == (
|
|
|
|
"command 'system.methodHelp' takes at most 1 argument")
|
|
|
|
else:
|
|
|
|
raise AssertionError('did not raise')
|