mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-01-02 12:16:56 -06:00
e7a6d10555
This patch changes the way plugins are initialized. Instead of finalizing all the plugins at once, plugins are finalized only after they are accessed (currently applies to Command, Object and Attribute subclasses, only in CLI by default). This change provides significant performance boost, as only the plugins that are actually used are finalized. ticket 1336
1123 lines
36 KiB
Python
1123 lines
36 KiB
Python
# Authors:
|
|
# Jason Gerard DeRose <jderose@redhat.com>
|
|
#
|
|
# Copyright (C) 2008 Red Hat
|
|
# see file 'COPYING' for use and warranty information
|
|
#
|
|
# 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.
|
|
#
|
|
# 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
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
"""
|
|
Test the `ipalib.frontend` module.
|
|
"""
|
|
|
|
from tests.util import raises, getitem, no_set, no_del, read_only
|
|
from tests.util import check_TypeError, ClassChecker, create_test_api
|
|
from tests.util import assert_equal
|
|
from ipalib.constants import TYPE_ERROR
|
|
from ipalib.base import NameSpace
|
|
from ipalib import frontend, backend, plugable, errors, parameters, config
|
|
from ipalib import output
|
|
from ipalib.parameters import Str
|
|
from ipapython.version import API_VERSION
|
|
|
|
def test_RULE_FLAG():
|
|
assert frontend.RULE_FLAG == 'validation_rule'
|
|
|
|
|
|
def test_rule():
|
|
"""
|
|
Test the `ipalib.frontend.rule` function.
|
|
"""
|
|
flag = frontend.RULE_FLAG
|
|
rule = frontend.rule
|
|
def my_func():
|
|
pass
|
|
assert not hasattr(my_func, flag)
|
|
rule(my_func)
|
|
assert getattr(my_func, flag) is True
|
|
@rule
|
|
def my_func2():
|
|
pass
|
|
assert getattr(my_func2, flag) is True
|
|
|
|
|
|
def test_is_rule():
|
|
"""
|
|
Test the `ipalib.frontend.is_rule` function.
|
|
"""
|
|
is_rule = frontend.is_rule
|
|
flag = frontend.RULE_FLAG
|
|
|
|
class no_call(object):
|
|
def __init__(self, value):
|
|
if value is not None:
|
|
assert value in (True, False)
|
|
setattr(self, flag, value)
|
|
|
|
class call(no_call):
|
|
def __call__(self):
|
|
pass
|
|
|
|
assert is_rule(call(True))
|
|
assert not is_rule(no_call(True))
|
|
assert not is_rule(call(False))
|
|
assert not is_rule(call(None))
|
|
|
|
|
|
class test_HasParam(ClassChecker):
|
|
"""
|
|
Test the `ipalib.frontend.Command` class.
|
|
"""
|
|
|
|
_cls = frontend.HasParam
|
|
|
|
def test_get_param_iterable(self):
|
|
"""
|
|
Test the `ipalib.frontend.HasParam._get_param_iterable` method.
|
|
"""
|
|
class WithTuple(self.cls):
|
|
takes_stuff = ('one', 'two')
|
|
o = WithTuple()
|
|
assert o._get_param_iterable('stuff') is WithTuple.takes_stuff
|
|
|
|
junk = ('three', 'four')
|
|
class WithCallable(self.cls):
|
|
def takes_stuff(self):
|
|
return junk
|
|
o = WithCallable()
|
|
assert o._get_param_iterable('stuff') is junk
|
|
|
|
class WithParam(self.cls):
|
|
takes_stuff = parameters.Str('five')
|
|
o = WithParam()
|
|
assert o._get_param_iterable('stuff') == (WithParam.takes_stuff,)
|
|
|
|
class WithStr(self.cls):
|
|
takes_stuff = 'six'
|
|
o = WithStr()
|
|
assert o._get_param_iterable('stuff') == ('six',)
|
|
|
|
class Wrong(self.cls):
|
|
takes_stuff = ['seven', 'eight']
|
|
o = Wrong()
|
|
e = raises(TypeError, o._get_param_iterable, 'stuff')
|
|
assert str(e) == '%s.%s must be a tuple, callable, or spec; got %r' % (
|
|
'Wrong', 'takes_stuff', Wrong.takes_stuff
|
|
)
|
|
|
|
def test_filter_param_by_context(self):
|
|
"""
|
|
Test the `ipalib.frontend.HasParam._filter_param_by_context` method.
|
|
"""
|
|
class Example(self.cls):
|
|
def get_stuff(self):
|
|
return (
|
|
'one', # Make sure create_param() is called for each spec
|
|
'two',
|
|
parameters.Str('three', include='cli'),
|
|
parameters.Str('four', exclude='server'),
|
|
parameters.Str('five', exclude=['whatever', 'cli']),
|
|
)
|
|
o = Example()
|
|
|
|
# Test when env is None:
|
|
params = list(o._filter_param_by_context('stuff'))
|
|
assert list(p.name for p in params) == [
|
|
'one', 'two', 'three', 'four', 'five'
|
|
]
|
|
for p in params:
|
|
assert type(p) is parameters.Str
|
|
|
|
# Test when env.context == 'cli':
|
|
cli = config.Env(context='cli')
|
|
assert cli.context == 'cli'
|
|
params = list(o._filter_param_by_context('stuff', cli))
|
|
assert list(p.name for p in params) == ['one', 'two', 'three', 'four']
|
|
for p in params:
|
|
assert type(p) is parameters.Str
|
|
|
|
# Test when env.context == 'server'
|
|
server = config.Env(context='server')
|
|
assert server.context == 'server'
|
|
params = list(o._filter_param_by_context('stuff', server))
|
|
assert list(p.name for p in params) == ['one', 'two', 'five']
|
|
for p in params:
|
|
assert type(p) is parameters.Str
|
|
|
|
# Test with no get_stuff:
|
|
class Missing(self.cls):
|
|
pass
|
|
o = Missing()
|
|
gen = o._filter_param_by_context('stuff')
|
|
e = raises(NotImplementedError, list, gen)
|
|
assert str(e) == 'Missing.get_stuff()'
|
|
|
|
# Test when get_stuff is not callable:
|
|
class NotCallable(self.cls):
|
|
get_stuff = ('one', 'two')
|
|
o = NotCallable()
|
|
gen = o._filter_param_by_context('stuff')
|
|
e = raises(TypeError, list, gen)
|
|
assert str(e) == '%s.%s must be a callable; got %r' % (
|
|
'NotCallable', 'get_stuff', NotCallable.get_stuff
|
|
)
|
|
|
|
|
|
class test_Command(ClassChecker):
|
|
"""
|
|
Test the `ipalib.frontend.Command` class.
|
|
"""
|
|
|
|
_cls = frontend.Command
|
|
|
|
def get_subcls(self):
|
|
"""
|
|
Return a standard subclass of `ipalib.frontend.Command`.
|
|
"""
|
|
class Rule(object):
|
|
def __init__(self, name):
|
|
self.name = name
|
|
|
|
def __call__(self, _, value):
|
|
if value != self.name:
|
|
return _('must equal %r') % self.name
|
|
|
|
default_from = parameters.DefaultFrom(
|
|
lambda arg: arg,
|
|
'default_from'
|
|
)
|
|
normalizer = lambda value: value.lower()
|
|
|
|
class example(self.cls):
|
|
takes_options = (
|
|
parameters.Str('option0', Rule('option0'),
|
|
normalizer=normalizer,
|
|
default_from=default_from,
|
|
),
|
|
parameters.Str('option1', Rule('option1'),
|
|
normalizer=normalizer,
|
|
default_from=default_from,
|
|
),
|
|
)
|
|
return example
|
|
|
|
def get_instance(self, args=tuple(), options=tuple()):
|
|
"""
|
|
Helper method used to test args and options.
|
|
"""
|
|
class example(self.cls):
|
|
takes_args = args
|
|
takes_options = options
|
|
o = example()
|
|
o.finalize()
|
|
return o
|
|
|
|
def test_class(self):
|
|
"""
|
|
Test the `ipalib.frontend.Command` class.
|
|
"""
|
|
assert self.cls.takes_options == tuple()
|
|
assert self.cls.takes_args == tuple()
|
|
|
|
def test_get_args(self):
|
|
"""
|
|
Test the `ipalib.frontend.Command.get_args` method.
|
|
"""
|
|
assert list(self.cls().get_args()) == []
|
|
args = ('login', 'stuff')
|
|
o = self.get_instance(args=args)
|
|
assert tuple(o.get_args()) == args
|
|
|
|
def test_get_options(self):
|
|
"""
|
|
Test the `ipalib.frontend.Command.get_options` method.
|
|
"""
|
|
assert list(self.cls().get_options()) == []
|
|
options = ('verbose', 'debug')
|
|
o = self.get_instance(options=options)
|
|
assert tuple(o.get_options()) == options
|
|
|
|
def test_args(self):
|
|
"""
|
|
Test the ``ipalib.frontend.Command.args`` instance attribute.
|
|
"""
|
|
assert self.cls().args is None
|
|
o = self.cls()
|
|
o.finalize()
|
|
assert type(o.args) is plugable.NameSpace
|
|
assert len(o.args) == 0
|
|
args = ('destination', 'source?')
|
|
ns = self.get_instance(args=args).args
|
|
assert type(ns) is plugable.NameSpace
|
|
assert len(ns) == len(args)
|
|
assert list(ns) == ['destination', 'source']
|
|
assert type(ns.destination) is parameters.Str
|
|
assert type(ns.source) is parameters.Str
|
|
assert ns.destination.required is True
|
|
assert ns.destination.multivalue is False
|
|
assert ns.source.required is False
|
|
assert ns.source.multivalue is False
|
|
|
|
# Test TypeError:
|
|
e = raises(TypeError, self.get_instance, args=(u'whatever',))
|
|
assert str(e) == TYPE_ERROR % (
|
|
'spec', (str, parameters.Param), u'whatever', unicode)
|
|
|
|
# Test ValueError, required after optional:
|
|
e = raises(ValueError, self.get_instance, args=('arg1?', 'arg2'))
|
|
assert str(e) == 'arg2: required argument after optional'
|
|
|
|
# Test ValueError, scalar after multivalue:
|
|
e = raises(ValueError, self.get_instance, args=('arg1+', 'arg2'))
|
|
assert str(e) == 'arg2: only final argument can be multivalue'
|
|
|
|
def test_max_args(self):
|
|
"""
|
|
Test the ``ipalib.frontend.Command.max_args`` instance attribute.
|
|
"""
|
|
o = self.get_instance()
|
|
assert o.max_args == 0
|
|
o = self.get_instance(args=('one?',))
|
|
assert o.max_args == 1
|
|
o = self.get_instance(args=('one', 'two?'))
|
|
assert o.max_args == 2
|
|
o = self.get_instance(args=('one', 'multi+',))
|
|
assert o.max_args is None
|
|
o = self.get_instance(args=('one', 'multi*',))
|
|
assert o.max_args is None
|
|
|
|
def test_options(self):
|
|
"""
|
|
Test the ``ipalib.frontend.Command.options`` instance attribute.
|
|
"""
|
|
assert self.cls().options is None
|
|
o = self.cls()
|
|
o.finalize()
|
|
assert type(o.options) is plugable.NameSpace
|
|
assert len(o.options) == 0
|
|
options = ('target', 'files*')
|
|
ns = self.get_instance(options=options).options
|
|
assert type(ns) is plugable.NameSpace
|
|
assert len(ns) == len(options)
|
|
assert list(ns) == ['target', 'files']
|
|
assert type(ns.target) is parameters.Str
|
|
assert type(ns.files) is parameters.Str
|
|
assert ns.target.required is True
|
|
assert ns.target.multivalue is False
|
|
assert ns.files.required is False
|
|
assert ns.files.multivalue is True
|
|
|
|
def test_output(self):
|
|
"""
|
|
Test the ``ipalib.frontend.Command.output`` instance attribute.
|
|
"""
|
|
inst = self.cls()
|
|
assert inst.output is None
|
|
inst.finalize()
|
|
assert type(inst.output) is plugable.NameSpace
|
|
assert list(inst.output) == ['result']
|
|
assert type(inst.output.result) is output.Output
|
|
|
|
def test_iter_output(self):
|
|
"""
|
|
Test the ``ipalib.frontend.Command._iter_output`` instance attribute.
|
|
"""
|
|
class Example(self.cls):
|
|
pass
|
|
inst = Example()
|
|
|
|
inst.has_output = tuple()
|
|
assert list(inst._iter_output()) == []
|
|
|
|
wrong = ['hello', 'world']
|
|
inst.has_output = wrong
|
|
e = raises(TypeError, list, inst._iter_output())
|
|
assert str(e) == 'Example.has_output: need a %r; got a %r: %r' % (
|
|
tuple, list, wrong
|
|
)
|
|
|
|
wrong = ('hello', 17)
|
|
inst.has_output = wrong
|
|
e = raises(TypeError, list, inst._iter_output())
|
|
assert str(e) == 'Example.has_output[1]: need a %r; got a %r: %r' % (
|
|
(str, output.Output), int, 17
|
|
)
|
|
|
|
okay = ('foo', output.Output('bar'), 'baz')
|
|
inst.has_output = okay
|
|
items = list(inst._iter_output())
|
|
assert len(items) == 3
|
|
assert list(o.name for o in items) == ['foo', 'bar', 'baz']
|
|
for o in items:
|
|
assert type(o) is output.Output
|
|
|
|
def test_soft_validate(self):
|
|
"""
|
|
Test the `ipalib.frontend.Command.soft_validate` method.
|
|
"""
|
|
class user_add(frontend.Command):
|
|
takes_args = parameters.Str('uid',
|
|
normalizer=lambda value: value.lower(),
|
|
default_from=lambda givenname, sn: givenname[0] + sn,
|
|
)
|
|
|
|
takes_options = ('givenname', 'sn')
|
|
|
|
cmd = user_add()
|
|
cmd.env = config.Env(context='cli')
|
|
cmd.finalize()
|
|
assert list(cmd.params) == ['givenname', 'sn', 'uid']
|
|
ret = cmd.soft_validate({})
|
|
assert len(ret['values']) == 0
|
|
assert len(ret['errors']) == 3
|
|
assert cmd.soft_validate(dict(givenname=u'First', sn=u'Last')) == dict(
|
|
values=dict(givenname=u'First', sn=u'Last', uid=u'flast'),
|
|
errors=dict(),
|
|
)
|
|
|
|
def test_convert(self):
|
|
"""
|
|
Test the `ipalib.frontend.Command.convert` method.
|
|
"""
|
|
kw = dict(
|
|
option0=u'1.5',
|
|
option1=u'7',
|
|
)
|
|
o = self.subcls()
|
|
o.finalize()
|
|
for (key, value) in o.convert(**kw).iteritems():
|
|
assert_equal(unicode(kw[key]), value)
|
|
|
|
def test_normalize(self):
|
|
"""
|
|
Test the `ipalib.frontend.Command.normalize` method.
|
|
"""
|
|
kw = dict(
|
|
option0=u'OPTION0',
|
|
option1=u'OPTION1',
|
|
)
|
|
norm = dict((k, v.lower()) for (k, v) in kw.items())
|
|
sub = self.subcls()
|
|
sub.finalize()
|
|
assert sub.normalize(**kw) == norm
|
|
|
|
def test_get_default(self):
|
|
"""
|
|
Test the `ipalib.frontend.Command.get_default` method.
|
|
"""
|
|
# FIXME: Add an updated unit tests for get_default()
|
|
|
|
def test_default_from_chaining(self):
|
|
"""
|
|
Test chaining of parameters through default_from.
|
|
"""
|
|
class my_cmd(self.cls):
|
|
takes_options = (
|
|
Str('option0'),
|
|
Str('option1', default_from=lambda option0: option0),
|
|
Str('option2', default_from=lambda option1: option1),
|
|
)
|
|
|
|
def run(self, *args, **options):
|
|
return dict(result=options)
|
|
|
|
kw = dict(option0=u'some value')
|
|
|
|
(api, home) = create_test_api()
|
|
api.finalize()
|
|
o = my_cmd()
|
|
o.set_api(api)
|
|
o.finalize()
|
|
e = o(**kw)
|
|
assert type(e) is dict
|
|
assert 'result' in e
|
|
assert 'option2' in e['result']
|
|
assert e['result']['option2'] == u'some value'
|
|
|
|
def test_validate(self):
|
|
"""
|
|
Test the `ipalib.frontend.Command.validate` method.
|
|
"""
|
|
|
|
sub = self.subcls()
|
|
sub.env = config.Env(context='cli')
|
|
sub.finalize()
|
|
|
|
# Check with valid values
|
|
okay = dict(
|
|
option0=u'option0',
|
|
option1=u'option1',
|
|
another_option='some value',
|
|
version=API_VERSION,
|
|
)
|
|
sub.validate(**okay)
|
|
|
|
# Check with an invalid value
|
|
fail = dict(okay)
|
|
fail['option0'] = u'whatever'
|
|
e = raises(errors.ValidationError, sub.validate, **fail)
|
|
assert_equal(e.name, 'option0')
|
|
assert_equal(e.value, u'whatever')
|
|
assert_equal(e.error, u"must equal 'option0'")
|
|
assert e.rule.__class__.__name__ == 'Rule'
|
|
assert e.index is None
|
|
|
|
# Check with a missing required arg
|
|
fail = dict(okay)
|
|
fail.pop('option1')
|
|
e = raises(errors.RequirementError, sub.validate, **fail)
|
|
assert e.name == 'option1'
|
|
|
|
def test_execute(self):
|
|
"""
|
|
Test the `ipalib.frontend.Command.execute` method.
|
|
"""
|
|
o = self.cls()
|
|
e = raises(NotImplementedError, o.execute)
|
|
assert str(e) == 'Command.execute()'
|
|
|
|
def test_args_options_2_params(self):
|
|
"""
|
|
Test the `ipalib.frontend.Command.args_options_2_params` method.
|
|
"""
|
|
|
|
# Test that ZeroArgumentError is raised:
|
|
o = self.get_instance()
|
|
e = raises(errors.ZeroArgumentError, o.args_options_2_params, 1)
|
|
assert e.name == 'example'
|
|
|
|
# Test that MaxArgumentError is raised (count=1)
|
|
o = self.get_instance(args=('one?',))
|
|
e = raises(errors.MaxArgumentError, o.args_options_2_params, 1, 2)
|
|
assert e.name == 'example'
|
|
assert e.count == 1
|
|
assert str(e) == "command 'example' takes at most 1 argument"
|
|
|
|
# Test that MaxArgumentError is raised (count=2)
|
|
o = self.get_instance(args=('one', 'two?'))
|
|
e = raises(errors.MaxArgumentError, o.args_options_2_params, 1, 2, 3)
|
|
assert e.name == 'example'
|
|
assert e.count == 2
|
|
assert str(e) == "command 'example' takes at most 2 arguments"
|
|
|
|
# Test that OverlapError is raised:
|
|
o = self.get_instance(args=('one', 'two'), options=('three', 'four'))
|
|
e = raises(errors.OverlapError, o.args_options_2_params,
|
|
1, 2, three=3, two=2, four=4, one=1)
|
|
assert e.names == ['one', 'two']
|
|
|
|
# Test the permutations:
|
|
o = self.get_instance(args=('one', 'two*'), options=('three', 'four'))
|
|
mthd = o.args_options_2_params
|
|
assert mthd() == dict()
|
|
assert mthd(1) == dict(one=1)
|
|
assert mthd(1, 2) == dict(one=1, two=(2,))
|
|
assert mthd(1, 21, 22, 23) == dict(one=1, two=(21, 22, 23))
|
|
assert mthd(1, (21, 22, 23)) == dict(one=1, two=(21, 22, 23))
|
|
assert mthd(three=3, four=4) == dict(three=3, four=4)
|
|
assert mthd(three=3, four=4, one=1, two=2) == \
|
|
dict(one=1, two=2, three=3, four=4)
|
|
assert mthd(1, 21, 22, 23, three=3, four=4) == \
|
|
dict(one=1, two=(21, 22, 23), three=3, four=4)
|
|
assert mthd(1, (21, 22, 23), three=3, four=4) == \
|
|
dict(one=1, two=(21, 22, 23), three=3, four=4)
|
|
|
|
def test_args_options_2_entry(self):
|
|
"""
|
|
Test `ipalib.frontend.Command.args_options_2_entry` method.
|
|
"""
|
|
class my_cmd(self.cls):
|
|
takes_args = (
|
|
parameters.Str('one', attribute=True),
|
|
parameters.Str('two', attribute=False),
|
|
)
|
|
takes_options = (
|
|
parameters.Str('three', attribute=True, multivalue=True),
|
|
parameters.Str('four', attribute=True, multivalue=False),
|
|
)
|
|
|
|
def run(self, *args, **kw):
|
|
return self.args_options_2_entry(*args, **kw)
|
|
|
|
args = ('one', 'two')
|
|
kw = dict(three=('three1', 'three2'), four='four')
|
|
|
|
(api, home) = create_test_api()
|
|
api.finalize()
|
|
o = my_cmd()
|
|
o.set_api(api)
|
|
o.finalize()
|
|
e = o.run(*args, **kw)
|
|
assert type(e) is dict
|
|
assert 'one' in e
|
|
assert 'two' not in e
|
|
assert 'three' in e
|
|
assert 'four' in e
|
|
assert e['one'] == 'one'
|
|
assert e['three'] == ['three1', 'three2']
|
|
assert e['four'] == 'four'
|
|
|
|
def test_params_2_args_options(self):
|
|
"""
|
|
Test the `ipalib.frontend.Command.params_2_args_options` method.
|
|
"""
|
|
o = self.get_instance(args='one', options='two')
|
|
assert o.params_2_args_options() == ((None,), {})
|
|
assert o.params_2_args_options(one=1) == ((1,), {})
|
|
assert o.params_2_args_options(two=2) == ((None,), dict(two=2))
|
|
assert o.params_2_args_options(two=2, one=1) == ((1,), dict(two=2))
|
|
|
|
def test_run(self):
|
|
"""
|
|
Test the `ipalib.frontend.Command.run` method.
|
|
"""
|
|
class my_cmd(self.cls):
|
|
def execute(self, *args, **kw):
|
|
return ('execute', args, kw)
|
|
|
|
def forward(self, *args, **kw):
|
|
return ('forward', args, kw)
|
|
|
|
args = ('Hello,', 'world,')
|
|
kw = dict(how_are='you', on_this='fine day?', version=API_VERSION)
|
|
|
|
# Test in server context:
|
|
(api, home) = create_test_api(in_server=True)
|
|
api.finalize()
|
|
o = my_cmd()
|
|
o.set_api(api)
|
|
assert o.run.im_func is self.cls.run.im_func
|
|
out = o.run(*args, **kw)
|
|
del kw['version']
|
|
assert ('execute', args, kw) == out
|
|
|
|
# Test in non-server context
|
|
(api, home) = create_test_api(in_server=False)
|
|
api.finalize()
|
|
o = my_cmd()
|
|
o.set_api(api)
|
|
assert o.run.im_func is self.cls.run.im_func
|
|
assert ('forward', args, kw) == o.run(*args, **kw)
|
|
|
|
def test_validate_output(self):
|
|
"""
|
|
Test the `ipalib.frontend.Command.validate_output` method.
|
|
"""
|
|
class Example(self.cls):
|
|
has_output = ('foo', 'bar', 'baz')
|
|
|
|
inst = Example()
|
|
inst.finalize()
|
|
|
|
# Test with wrong type:
|
|
wrong = ('foo', 'bar', 'baz')
|
|
e = raises(TypeError, inst.validate_output, wrong)
|
|
assert str(e) == '%s.validate_output(): need a %r; got a %r: %r' % (
|
|
'Example', dict, tuple, wrong
|
|
)
|
|
|
|
# Test with a missing keys:
|
|
wrong = dict(bar='hello')
|
|
e = raises(ValueError, inst.validate_output, wrong)
|
|
assert str(e) == '%s.validate_output(): missing keys %r in %r' % (
|
|
'Example', ['baz', 'foo'], wrong
|
|
)
|
|
|
|
# Test with extra keys:
|
|
wrong = dict(foo=1, bar=2, baz=3, fee=4, azz=5)
|
|
e = raises(ValueError, inst.validate_output, wrong)
|
|
assert str(e) == '%s.validate_output(): unexpected keys %r in %r' % (
|
|
'Example', ['azz', 'fee'], wrong
|
|
)
|
|
|
|
# Test with per item type validation:
|
|
class Complex(self.cls):
|
|
has_output = (
|
|
output.Output('foo', int),
|
|
output.Output('bar', list),
|
|
)
|
|
inst = Complex()
|
|
inst.finalize()
|
|
|
|
wrong = dict(foo=17.9, bar=[18])
|
|
e = raises(TypeError, inst.validate_output, wrong)
|
|
assert str(e) == '%s:\n output[%r]: need %r; got %r: %r' % (
|
|
'Complex.validate_output()', 'foo', int, float, 17.9
|
|
)
|
|
|
|
wrong = dict(foo=18, bar=17)
|
|
e = raises(TypeError, inst.validate_output, wrong)
|
|
assert str(e) == '%s:\n output[%r]: need %r; got %r: %r' % (
|
|
'Complex.validate_output()', 'bar', list, int, 17
|
|
)
|
|
|
|
class Subclass(output.ListOfEntries):
|
|
pass
|
|
|
|
# Test nested validation:
|
|
class nested(self.cls):
|
|
has_output = (
|
|
output.Output('hello', int),
|
|
Subclass('world'),
|
|
)
|
|
inst = nested()
|
|
inst.finalize()
|
|
okay = dict(foo='bar')
|
|
nope = ('aye', 'bee')
|
|
|
|
wrong = dict(hello=18, world=[okay, nope, okay])
|
|
e = raises(TypeError, inst.validate_output, wrong)
|
|
assert str(e) == output.emsg % (
|
|
'nested', 'Subclass', 'world', 1, dict, tuple, nope
|
|
)
|
|
|
|
wrong = dict(hello=18, world=[okay, okay, okay, okay, nope])
|
|
e = raises(TypeError, inst.validate_output, wrong)
|
|
assert str(e) == output.emsg % (
|
|
'nested', 'Subclass', 'world', 4, dict, tuple, nope
|
|
)
|
|
|
|
def test_get_output_params(self):
|
|
"""
|
|
Test the `ipalib.frontend.Command.get_output_params` method.
|
|
"""
|
|
class example(self.cls):
|
|
has_output_params = (
|
|
'one',
|
|
'two',
|
|
'three',
|
|
)
|
|
takes_args = (
|
|
'foo',
|
|
)
|
|
takes_options = (
|
|
Str('bar', flags='no_output'),
|
|
'baz',
|
|
)
|
|
|
|
inst = example()
|
|
assert list(inst.get_output_params()) == ['one', 'two', 'three']
|
|
inst.finalize()
|
|
assert list(inst.get_output_params()) == [
|
|
'one', 'two', 'three', inst.params.foo, inst.params.baz
|
|
]
|
|
assert list(inst.output_params) == ['one', 'two', 'three', 'foo', 'baz']
|
|
|
|
|
|
class test_LocalOrRemote(ClassChecker):
|
|
"""
|
|
Test the `ipalib.frontend.LocalOrRemote` class.
|
|
"""
|
|
_cls = frontend.LocalOrRemote
|
|
|
|
def test_init(self):
|
|
"""
|
|
Test the `ipalib.frontend.LocalOrRemote.__init__` method.
|
|
"""
|
|
o = self.cls()
|
|
o.finalize()
|
|
assert list(o.args) == []
|
|
assert list(o.options) == ['server']
|
|
op = o.options.server
|
|
assert op.required is False
|
|
assert op.default is False
|
|
|
|
def test_run(self):
|
|
"""
|
|
Test the `ipalib.frontend.LocalOrRemote.run` method.
|
|
"""
|
|
class example(self.cls):
|
|
takes_args = 'key?'
|
|
|
|
def forward(self, *args, **options):
|
|
return dict(result=('forward', args, options))
|
|
|
|
def execute(self, *args, **options):
|
|
return dict(result=('execute', args, options))
|
|
|
|
# Test when in_server=False:
|
|
(api, home) = create_test_api(in_server=False)
|
|
api.register(example)
|
|
api.finalize()
|
|
cmd = api.Command.example
|
|
assert cmd() == dict(
|
|
result=('execute', (None,), dict(server=False))
|
|
)
|
|
assert cmd(u'var') == dict(
|
|
result=('execute', (u'var',), dict(server=False))
|
|
)
|
|
assert cmd(server=True) == dict(
|
|
result=('forward', (None,), dict(server=True))
|
|
)
|
|
assert cmd(u'var', server=True) == dict(
|
|
result=('forward', (u'var',), dict(server=True))
|
|
)
|
|
|
|
# Test when in_server=True (should always call execute):
|
|
(api, home) = create_test_api(in_server=True)
|
|
api.register(example)
|
|
api.finalize()
|
|
cmd = api.Command.example
|
|
assert cmd() == dict(
|
|
result=('execute', (None,), dict(server=False))
|
|
)
|
|
assert cmd(u'var') == dict(
|
|
result=('execute', (u'var',), dict(server=False))
|
|
)
|
|
assert cmd(server=True) == dict(
|
|
result=('execute', (None,), dict(server=True))
|
|
)
|
|
assert cmd(u'var', server=True) == dict(
|
|
result=('execute', (u'var',), dict(server=True))
|
|
)
|
|
|
|
|
|
class test_Object(ClassChecker):
|
|
"""
|
|
Test the `ipalib.frontend.Object` class.
|
|
"""
|
|
_cls = frontend.Object
|
|
|
|
def test_class(self):
|
|
"""
|
|
Test the `ipalib.frontend.Object` class.
|
|
"""
|
|
assert self.cls.backend is None
|
|
assert self.cls.methods is None
|
|
assert self.cls.properties is None
|
|
assert self.cls.params is None
|
|
assert self.cls.params_minus_pk is None
|
|
assert self.cls.takes_params == tuple()
|
|
|
|
def test_init(self):
|
|
"""
|
|
Test the `ipalib.frontend.Object.__init__` method.
|
|
"""
|
|
o = self.cls()
|
|
assert o.backend is None
|
|
assert o.methods is None
|
|
assert o.properties is None
|
|
assert o.params is None
|
|
assert o.params_minus_pk is None
|
|
assert o.properties is None
|
|
|
|
def test_set_api(self):
|
|
"""
|
|
Test the `ipalib.frontend.Object.set_api` method.
|
|
"""
|
|
# Setup for test:
|
|
class DummyAttribute(object):
|
|
def __init__(self, obj_name, attr_name, name=None):
|
|
self.obj_name = obj_name
|
|
self.attr_name = attr_name
|
|
if name is None:
|
|
self.name = '%s_%s' % (obj_name, attr_name)
|
|
else:
|
|
self.name = name
|
|
self.param = frontend.create_param(attr_name)
|
|
|
|
def __clone__(self, attr_name):
|
|
return self.__class__(
|
|
self.obj_name,
|
|
self.attr_name,
|
|
getattr(self, attr_name)
|
|
)
|
|
|
|
def get_attributes(cnt, format):
|
|
for name in ['other', 'user', 'another']:
|
|
for i in xrange(cnt):
|
|
yield DummyAttribute(name, format % i)
|
|
|
|
cnt = 10
|
|
formats = dict(
|
|
methods='method_%d',
|
|
properties='property_%d',
|
|
)
|
|
|
|
|
|
_d = dict(
|
|
Method=plugable.NameSpace(
|
|
get_attributes(cnt, formats['methods'])
|
|
),
|
|
Property=plugable.NameSpace(
|
|
get_attributes(cnt, formats['properties'])
|
|
),
|
|
)
|
|
api = plugable.MagicDict(_d)
|
|
assert len(api.Method) == cnt * 3
|
|
assert len(api.Property) == cnt * 3
|
|
|
|
class user(self.cls):
|
|
pass
|
|
|
|
# Actually perform test:
|
|
o = user()
|
|
o.set_api(api)
|
|
assert read_only(o, 'api') is api
|
|
for name in ['methods', 'properties']:
|
|
namespace = getattr(o, name)
|
|
assert isinstance(namespace, plugable.NameSpace)
|
|
assert len(namespace) == cnt
|
|
f = formats[name]
|
|
for i in xrange(cnt):
|
|
attr_name = f % i
|
|
attr = namespace[attr_name]
|
|
assert isinstance(attr, DummyAttribute)
|
|
assert attr is getattr(namespace, attr_name)
|
|
assert attr.obj_name == 'user'
|
|
assert attr.attr_name == attr_name
|
|
assert attr.name == '%s_%s' % ('user', attr_name)
|
|
|
|
# Test params instance attribute
|
|
o = self.cls()
|
|
o.set_api(api)
|
|
ns = o.params
|
|
assert type(ns) is plugable.NameSpace
|
|
assert len(ns) == 0
|
|
class example(self.cls):
|
|
takes_params = ('banana', 'apple')
|
|
o = example()
|
|
o.set_api(api)
|
|
ns = o.params
|
|
assert type(ns) is plugable.NameSpace
|
|
assert len(ns) == 2, repr(ns)
|
|
assert list(ns) == ['banana', 'apple']
|
|
for p in ns():
|
|
assert type(p) is parameters.Str
|
|
assert p.required is True
|
|
assert p.multivalue is False
|
|
|
|
def test_primary_key(self):
|
|
"""
|
|
Test the `ipalib.frontend.Object.primary_key` attribute.
|
|
"""
|
|
(api, home) = create_test_api()
|
|
api.finalize()
|
|
|
|
# Test with no primary keys:
|
|
class example1(self.cls):
|
|
takes_params = (
|
|
'one',
|
|
'two',
|
|
)
|
|
o = example1()
|
|
o.set_api(api)
|
|
assert o.primary_key is None
|
|
|
|
# Test with 1 primary key:
|
|
class example2(self.cls):
|
|
takes_params = (
|
|
'one',
|
|
'two',
|
|
parameters.Str('three', primary_key=True),
|
|
'four',
|
|
)
|
|
o = example2()
|
|
o.set_api(api)
|
|
pk = o.primary_key
|
|
assert type(pk) is parameters.Str
|
|
assert pk.name == 'three'
|
|
assert pk.primary_key is True
|
|
assert o.params[2] is o.primary_key
|
|
assert isinstance(o.params_minus_pk, plugable.NameSpace)
|
|
assert list(o.params_minus_pk) == ['one', 'two', 'four']
|
|
|
|
# Test with multiple primary_key:
|
|
class example3(self.cls):
|
|
takes_params = (
|
|
parameters.Str('one', primary_key=True),
|
|
parameters.Str('two', primary_key=True),
|
|
'three',
|
|
parameters.Str('four', primary_key=True),
|
|
)
|
|
o = example3()
|
|
o.set_api(api)
|
|
e = raises(ValueError, o.finalize)
|
|
assert str(e) == \
|
|
'example3 (Object) has multiple primary keys: one, two, four'
|
|
|
|
def test_backend(self):
|
|
"""
|
|
Test the `ipalib.frontend.Object.backend` attribute.
|
|
"""
|
|
(api, home) = create_test_api()
|
|
class ldap(backend.Backend):
|
|
whatever = 'It worked!'
|
|
api.register(ldap)
|
|
class user(frontend.Object):
|
|
backend_name = 'ldap'
|
|
api.register(user)
|
|
api.finalize()
|
|
b = api.Object.user.backend
|
|
assert isinstance(b, ldap)
|
|
assert b.whatever == 'It worked!'
|
|
|
|
def test_get_dn(self):
|
|
"""
|
|
Test the `ipalib.frontend.Object.get_dn` method.
|
|
"""
|
|
o = self.cls()
|
|
e = raises(NotImplementedError, o.get_dn, 'primary key')
|
|
assert str(e) == 'Object.get_dn()'
|
|
class user(self.cls):
|
|
pass
|
|
o = user()
|
|
e = raises(NotImplementedError, o.get_dn, 'primary key')
|
|
assert str(e) == 'user.get_dn()'
|
|
|
|
def test_params_minus(self):
|
|
"""
|
|
Test the `ipalib.frontend.Object.params_minus` method.
|
|
"""
|
|
class example(self.cls):
|
|
takes_params = ('one', 'two', 'three', 'four')
|
|
o = example()
|
|
(api, home) = create_test_api()
|
|
o.set_api(api)
|
|
p = o.params
|
|
assert tuple(o.params_minus()) == tuple(p())
|
|
assert tuple(o.params_minus([])) == tuple(p())
|
|
assert tuple(o.params_minus('two', 'three')) == (p.one, p.four)
|
|
assert tuple(o.params_minus(['two', 'three'])) == (p.one, p.four)
|
|
assert tuple(o.params_minus(p.two, p.three)) == (p.one, p.four)
|
|
assert tuple(o.params_minus([p.two, p.three])) == (p.one, p.four)
|
|
ns = NameSpace([p.two, p.three])
|
|
assert tuple(o.params_minus(ns)) == (p.one, p.four)
|
|
|
|
|
|
class test_Attribute(ClassChecker):
|
|
"""
|
|
Test the `ipalib.frontend.Attribute` class.
|
|
"""
|
|
_cls = frontend.Attribute
|
|
|
|
def test_class(self):
|
|
"""
|
|
Test the `ipalib.frontend.Attribute` class.
|
|
"""
|
|
assert self.cls.__bases__ == (plugable.Plugin,)
|
|
assert type(self.cls.obj) is property
|
|
assert type(self.cls.obj_name) is property
|
|
assert type(self.cls.attr_name) is property
|
|
|
|
def test_init(self):
|
|
"""
|
|
Test the `ipalib.frontend.Attribute.__init__` method.
|
|
"""
|
|
class user_add(self.cls):
|
|
pass
|
|
o = user_add()
|
|
assert read_only(o, 'obj') is None
|
|
assert read_only(o, 'obj_name') == 'user'
|
|
assert read_only(o, 'attr_name') == 'add'
|
|
|
|
def test_set_api(self):
|
|
"""
|
|
Test the `ipalib.frontend.Attribute.set_api` method.
|
|
"""
|
|
user_obj = 'The user frontend.Object instance'
|
|
class api(object):
|
|
Object = dict(user=user_obj)
|
|
class user_add(self.cls):
|
|
pass
|
|
o = user_add()
|
|
assert read_only(o, 'api') is None
|
|
assert read_only(o, 'obj') is None
|
|
o.set_api(api)
|
|
assert read_only(o, 'api') is api
|
|
assert read_only(o, 'obj') is user_obj
|
|
|
|
|
|
class test_Method(ClassChecker):
|
|
"""
|
|
Test the `ipalib.frontend.Method` class.
|
|
"""
|
|
_cls = frontend.Method
|
|
|
|
def get_api(self, args=tuple(), options=tuple()):
|
|
"""
|
|
Return a finalized `ipalib.plugable.API` instance.
|
|
"""
|
|
(api, home) = create_test_api()
|
|
class user(frontend.Object):
|
|
takes_params = (
|
|
'givenname',
|
|
'sn',
|
|
frontend.Param('uid', primary_key=True),
|
|
'initials',
|
|
)
|
|
class user_verb(self.cls):
|
|
takes_args = args
|
|
takes_options = options
|
|
api.register(user)
|
|
api.register(user_verb)
|
|
api.finalize()
|
|
return api
|
|
|
|
def test_class(self):
|
|
"""
|
|
Test the `ipalib.frontend.Method` class.
|
|
"""
|
|
assert self.cls.__bases__ == (frontend.Attribute, frontend.Command)
|
|
|
|
def test_init(self):
|
|
"""
|
|
Test the `ipalib.frontend.Method.__init__` method.
|
|
"""
|
|
class user_add(self.cls):
|
|
pass
|
|
o = user_add()
|
|
assert o.name == 'user_add'
|
|
assert o.obj_name == 'user'
|
|
assert o.attr_name == 'add'
|
|
|
|
|
|
class test_Property(ClassChecker):
|
|
"""
|
|
Test the `ipalib.frontend.Property` class.
|
|
"""
|
|
_cls = frontend.Property
|
|
|
|
def get_subcls(self):
|
|
"""
|
|
Return a standard subclass of `ipalib.frontend.Property`.
|
|
"""
|
|
class user_givenname(self.cls):
|
|
'User first name'
|
|
|
|
@frontend.rule
|
|
def rule0_lowercase(self, value):
|
|
if not value.islower():
|
|
return 'Must be lowercase'
|
|
return user_givenname
|
|
|
|
def test_class(self):
|
|
"""
|
|
Test the `ipalib.frontend.Property` class.
|
|
"""
|
|
assert self.cls.__bases__ == (frontend.Attribute,)
|
|
assert self.cls.klass is parameters.Str
|
|
|
|
def test_init(self):
|
|
"""
|
|
Test the `ipalib.frontend.Property.__init__` method.
|
|
"""
|
|
o = self.subcls()
|
|
assert len(o.rules) == 1
|
|
assert o.rules[0].__name__ == 'rule0_lowercase'
|
|
param = o.param
|
|
assert isinstance(param, parameters.Str)
|
|
assert param.name == 'givenname'
|
|
assert unicode(param.doc) == u'User first name'
|