mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2024-12-26 00:41:25 -06:00
0a64e9bd70
Web UI tests were marked as tier1 tests. The tier system is intended to be used together with CI system to make sure the more complicated tests are being run only when all of the basic functionality is working. The system is using pytest's marker system. E.g. an invocation of all tier1 tests with listing will look like: $ py.test -v -m tier1 ipatests or in case of out of tree tests: $ ipa-run-tests -m tier1 Reviewed-By: Ales 'alich' Marecek <amarecek@redhat.com>
1199 lines
39 KiB
Python
1199 lines
39 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.
|
|
"""
|
|
|
|
# FIXME: Pylint errors
|
|
# pylint: disable=no-member
|
|
import pytest
|
|
|
|
import six
|
|
|
|
from ipatests.util import raises, read_only
|
|
from ipatests.util import ClassChecker, create_test_api
|
|
from ipatests.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, messages
|
|
from ipalib.parameters import Str
|
|
from ipapython.version import API_VERSION
|
|
|
|
if six.PY3:
|
|
unicode = str
|
|
|
|
|
|
pytestmark = pytest.mark.tier0
|
|
|
|
|
|
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.
|
|
"""
|
|
api = 'the api instance'
|
|
class WithTuple(self.cls):
|
|
takes_stuff = ('one', 'two')
|
|
o = WithTuple(api)
|
|
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(api)
|
|
assert o._get_param_iterable('stuff') is junk
|
|
|
|
class WithParam(self.cls):
|
|
takes_stuff = parameters.Str('five')
|
|
o = WithParam(api)
|
|
assert o._get_param_iterable('stuff') == (WithParam.takes_stuff,)
|
|
|
|
class WithStr(self.cls):
|
|
takes_stuff = 'six'
|
|
o = WithStr(api)
|
|
assert o._get_param_iterable('stuff') == ('six',)
|
|
|
|
class Wrong(self.cls):
|
|
takes_stuff = ['seven', 'eight']
|
|
o = Wrong(api)
|
|
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.
|
|
"""
|
|
api = 'the api instance'
|
|
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(api)
|
|
|
|
# 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(api)
|
|
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(api)
|
|
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 api(object):
|
|
@staticmethod
|
|
def is_production_mode():
|
|
return False
|
|
class example(self.cls):
|
|
takes_args = args
|
|
takes_options = options
|
|
o = example(api)
|
|
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.
|
|
"""
|
|
api = 'the api instance'
|
|
assert list(self.cls(api).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.
|
|
"""
|
|
api = 'the api instance'
|
|
options = list(self.cls(api).get_options())
|
|
assert len(options) == 1
|
|
assert options[0].name == 'version'
|
|
options = ('verbose', 'debug')
|
|
o = self.get_instance(options=options)
|
|
assert len(tuple(o.get_options())) == 3
|
|
assert 'verbose' in tuple(o.get_options())
|
|
assert 'debug' in tuple(o.get_options())
|
|
|
|
def test_args(self):
|
|
"""
|
|
Test the ``ipalib.frontend.Command.args`` instance attribute.
|
|
"""
|
|
class api(object):
|
|
@staticmethod
|
|
def is_production_mode():
|
|
return False
|
|
o = self.cls(api)
|
|
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:
|
|
if six.PY2:
|
|
e = raises(TypeError, self.get_instance, args=(u'whatever',))
|
|
assert str(e) == TYPE_ERROR % (
|
|
'spec', (str, parameters.Param), u'whatever', unicode)
|
|
else:
|
|
e = raises(TypeError, self.get_instance, args=(b'whatever',))
|
|
assert str(e) == TYPE_ERROR % (
|
|
'spec', (str, parameters.Param), b'whatever', bytes)
|
|
|
|
# Test ValueError, required after optional:
|
|
e = raises(ValueError, self.get_instance, args=('arg1?', 'arg2'))
|
|
assert str(e) == "arg2: required argument after optional in %s arguments ['arg1?', 'arg2']" % (self.get_instance().name)
|
|
|
|
# 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.
|
|
"""
|
|
class api(object):
|
|
@staticmethod
|
|
def is_production_mode():
|
|
return False
|
|
o = self.cls(api)
|
|
o.finalize()
|
|
assert type(o.options) is plugable.NameSpace
|
|
assert len(o.options) == 1
|
|
options = ('target', 'files*')
|
|
ns = self.get_instance(options=options).options
|
|
assert type(ns) is plugable.NameSpace
|
|
assert len(ns) == len(options) + 1
|
|
assert list(ns) == ['target', 'files', 'version']
|
|
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.
|
|
"""
|
|
class api(object):
|
|
@staticmethod
|
|
def is_production_mode():
|
|
return False
|
|
inst = self.cls(api)
|
|
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.
|
|
"""
|
|
api = 'the api instance'
|
|
class Example(self.cls):
|
|
pass
|
|
inst = Example(api)
|
|
|
|
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 api(object):
|
|
env = config.Env(context='cli')
|
|
@staticmethod
|
|
def is_production_mode():
|
|
return False
|
|
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(api)
|
|
cmd.finalize()
|
|
assert list(cmd.params) == ['givenname', 'sn', 'uid', 'version']
|
|
ret = cmd.soft_validate({})
|
|
assert sorted(ret['values']) == ['version']
|
|
assert sorted(ret['errors']) == ['givenname', 'sn', 'uid']
|
|
assert cmd.soft_validate(dict(givenname=u'First', sn=u'Last')) == dict(
|
|
values=dict(givenname=u'First', sn=u'Last', uid=u'flast',
|
|
version=None),
|
|
errors=dict(),
|
|
)
|
|
|
|
def test_convert(self):
|
|
"""
|
|
Test the `ipalib.frontend.Command.convert` method.
|
|
"""
|
|
class api(object):
|
|
@staticmethod
|
|
def is_production_mode():
|
|
return False
|
|
kw = dict(
|
|
option0=u'1.5',
|
|
option1=u'7',
|
|
)
|
|
o = self.subcls(api)
|
|
o.finalize()
|
|
for (key, value) in o.convert(**kw).items():
|
|
assert_equal(unicode(kw[key]), value)
|
|
|
|
def test_normalize(self):
|
|
"""
|
|
Test the `ipalib.frontend.Command.normalize` method.
|
|
"""
|
|
class api(object):
|
|
@staticmethod
|
|
def is_production_mode():
|
|
return False
|
|
kw = dict(
|
|
option0=u'OPTION0',
|
|
option1=u'OPTION1',
|
|
)
|
|
norm = dict((k, v.lower()) for (k, v) in kw.items())
|
|
sub = self.subcls(api)
|
|
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(api)
|
|
o.finalize()
|
|
e = o(**kw) # pylint: disable=not-callable
|
|
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.
|
|
"""
|
|
class api(object):
|
|
env = config.Env(context='cli')
|
|
@staticmethod
|
|
def is_production_mode():
|
|
return False
|
|
|
|
sub = self.subcls(api)
|
|
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.
|
|
"""
|
|
api = 'the api instance'
|
|
o = self.cls(api)
|
|
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 OptionError is raised when an extra option is given:
|
|
o = self.get_instance()
|
|
e = raises(errors.OptionError, o.args_options_2_params, bad_option=True)
|
|
assert e.option == 'bad_option'
|
|
|
|
# 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(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(api)
|
|
if six.PY2:
|
|
assert o.run.__func__ is self.cls.run.__func__
|
|
else:
|
|
assert o.run.__func__ is self.cls.run
|
|
out = o.run(*args, **kw)
|
|
assert ('execute', args, kw) == out
|
|
|
|
# Test in non-server context
|
|
(api, home) = create_test_api(in_server=False)
|
|
api.finalize()
|
|
o = my_cmd(api)
|
|
if six.PY2:
|
|
assert o.run.__func__ is self.cls.run.__func__
|
|
else:
|
|
assert o.run.__func__ is self.cls.run
|
|
assert ('forward', args, kw) == o.run(*args, **kw)
|
|
|
|
def test_messages(self):
|
|
"""
|
|
Test correct handling of messages
|
|
"""
|
|
class TestMessage(messages.PublicMessage):
|
|
type = 'info'
|
|
format = 'This is a message.'
|
|
errno = 1234
|
|
|
|
class my_cmd(self.cls):
|
|
def execute(self, *args, **kw):
|
|
result = {'name': 'execute'}
|
|
messages.add_message(kw['version'], result, TestMessage())
|
|
return result
|
|
|
|
def forward(self, *args, **kw):
|
|
result = {'name': 'forward'}
|
|
messages.add_message(kw['version'], result, TestMessage())
|
|
return result
|
|
|
|
args = ('Hello,', 'world,')
|
|
kw = dict(how_are='you', on_this='fine day?', version=API_VERSION)
|
|
|
|
expected = [TestMessage().to_dict()]
|
|
|
|
# Test in server context:
|
|
(api, home) = create_test_api(in_server=True)
|
|
api.finalize()
|
|
o = my_cmd(api)
|
|
if six.PY2:
|
|
assert o.run.__func__ is self.cls.run.__func__
|
|
else:
|
|
assert o.run.__func__ is self.cls.run
|
|
assert {'name': 'execute', 'messages': expected} == o.run(*args, **kw)
|
|
|
|
# Test in non-server context
|
|
(api, home) = create_test_api(in_server=False)
|
|
api.finalize()
|
|
o = my_cmd(api)
|
|
if six.PY2:
|
|
assert o.run.__func__ is self.cls.run.__func__
|
|
else:
|
|
assert o.run.__func__ is self.cls.run
|
|
assert {'name': 'forward', 'messages': expected} == o.run(*args, **kw)
|
|
|
|
def test_validate_output_basic(self):
|
|
"""
|
|
Test the `ipalib.frontend.Command.validate_output` method.
|
|
"""
|
|
class api(object):
|
|
@staticmethod
|
|
def is_production_mode():
|
|
return False
|
|
class Example(self.cls):
|
|
has_output = ('foo', 'bar', 'baz')
|
|
|
|
inst = Example(api)
|
|
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 different keys:
|
|
wrong = dict(baz=1, xyzzy=2, quux=3)
|
|
e = raises(ValueError, inst.validate_output, wrong)
|
|
assert str(e) == '%s.validate_output(): missing keys %r in %r' % (
|
|
'Example', ['bar', 'foo'], wrong
|
|
), str(e)
|
|
|
|
def test_validate_output_per_type(self):
|
|
"""
|
|
Test `ipalib.frontend.Command.validate_output` per-type validation.
|
|
"""
|
|
class api(object):
|
|
@staticmethod
|
|
def is_production_mode():
|
|
return False
|
|
|
|
class Complex(self.cls):
|
|
has_output = (
|
|
output.Output('foo', int),
|
|
output.Output('bar', list),
|
|
)
|
|
inst = Complex(api)
|
|
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
|
|
)
|
|
|
|
def test_validate_output_nested(self):
|
|
"""
|
|
Test `ipalib.frontend.Command.validate_output` nested validation.
|
|
"""
|
|
class api(object):
|
|
@staticmethod
|
|
def is_production_mode():
|
|
return False
|
|
|
|
class Subclass(output.ListOfEntries):
|
|
pass
|
|
|
|
# Test nested validation:
|
|
class nested(self.cls):
|
|
has_output = (
|
|
output.Output('hello', int),
|
|
Subclass('world'),
|
|
)
|
|
inst = nested(api)
|
|
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 api(object):
|
|
@staticmethod
|
|
def is_production_mode():
|
|
return False
|
|
class example(self.cls):
|
|
has_output_params = (
|
|
'one',
|
|
'two',
|
|
'three',
|
|
)
|
|
takes_args = (
|
|
'foo',
|
|
)
|
|
takes_options = (
|
|
Str('bar', flags='no_output'),
|
|
'baz',
|
|
)
|
|
|
|
inst = example(api)
|
|
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.
|
|
"""
|
|
class api(object):
|
|
@staticmethod
|
|
def is_production_mode():
|
|
return False
|
|
o = self.cls(api)
|
|
o.finalize()
|
|
assert list(o.args) == []
|
|
assert list(o.options) == ['server', 'version']
|
|
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.add_plugin(example)
|
|
api.finalize()
|
|
cmd = api.Command.example
|
|
assert cmd(version=u'2.47') == dict(
|
|
result=('execute', (None,), dict(version=u'2.47', server=False))
|
|
)
|
|
assert cmd(u'var', version=u'2.47') == dict(
|
|
result=('execute', (u'var',), dict(version=u'2.47', server=False))
|
|
)
|
|
assert cmd(server=True, version=u'2.47') == dict(
|
|
result=('forward', (None,), dict(version=u'2.47', server=True))
|
|
)
|
|
assert cmd(u'var', server=True, version=u'2.47') == dict(
|
|
result=('forward', (u'var',), dict(version=u'2.47', server=True))
|
|
)
|
|
|
|
# Test when in_server=True (should always call execute):
|
|
(api, home) = create_test_api(in_server=True)
|
|
api.add_plugin(example)
|
|
api.finalize()
|
|
cmd = api.Command.example
|
|
assert cmd(version=u'2.47') == dict(
|
|
result=('execute', (None,), dict(version=u'2.47', server=False))
|
|
)
|
|
assert cmd(u'var', version=u'2.47') == dict(
|
|
result=('execute', (u'var',), dict(version=u'2.47', server=False))
|
|
)
|
|
assert cmd(server=True, version=u'2.47') == dict(
|
|
result=('execute', (None,), dict(version=u'2.47', server=True))
|
|
)
|
|
assert cmd(u'var', server=True, version=u'2.47') == dict(
|
|
result=('execute', (u'var',), dict(version=u'2.47', 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.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.
|
|
"""
|
|
# 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 range(cnt):
|
|
yield DummyAttribute(name, format % i)
|
|
|
|
cnt = 10
|
|
methods_format = 'method_%d'
|
|
|
|
class FakeAPI(object):
|
|
Method = plugable.NameSpace(
|
|
get_attributes(cnt, methods_format)
|
|
)
|
|
def __contains__(self, key):
|
|
return hasattr(self, key)
|
|
def __getitem__(self, key):
|
|
return getattr(self, key)
|
|
def is_production_mode(self):
|
|
return False
|
|
api = FakeAPI()
|
|
assert len(api.Method) == cnt * 3
|
|
|
|
class user(self.cls):
|
|
pass
|
|
|
|
# Actually perform test:
|
|
o = user(api)
|
|
assert read_only(o, 'api') is api
|
|
|
|
namespace = o.methods
|
|
assert isinstance(namespace, plugable.NameSpace)
|
|
assert len(namespace) == cnt
|
|
f = methods_format
|
|
for i in range(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(api)
|
|
ns = o.params
|
|
assert type(ns) is plugable.NameSpace
|
|
assert len(ns) == 0
|
|
class example(self.cls):
|
|
takes_params = ('banana', 'apple')
|
|
o = example(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(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(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(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.add_plugin(ldap)
|
|
class user(frontend.Object):
|
|
backend_name = 'ldap'
|
|
api.add_plugin(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.
|
|
"""
|
|
api = 'the api instance'
|
|
o = self.cls(api)
|
|
e = raises(NotImplementedError, o.get_dn, 'primary key')
|
|
assert str(e) == 'Object.get_dn()'
|
|
class user(self.cls):
|
|
pass
|
|
o = user(api)
|
|
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')
|
|
(api, home) = create_test_api()
|
|
api.finalize()
|
|
o = example(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.
|
|
"""
|
|
user_obj = 'The user frontend.Object instance'
|
|
class api(object):
|
|
Object = dict(user=user_obj)
|
|
@staticmethod
|
|
def is_production_mode():
|
|
return False
|
|
class user_add(self.cls):
|
|
pass
|
|
o = user_add(api)
|
|
assert read_only(o, 'api') is api
|
|
assert read_only(o, 'obj') is user_obj
|
|
assert read_only(o, 'obj_name') == 'user'
|
|
assert read_only(o, 'attr_name') == 'add'
|
|
|
|
|
|
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.add_plugin(user)
|
|
api.add_plugin(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.
|
|
"""
|
|
api = 'the api instance'
|
|
class user_add(self.cls):
|
|
pass
|
|
o = user_add(api)
|
|
assert o.name == 'user_add'
|
|
assert o.obj_name == 'user'
|
|
assert o.attr_name == 'add'
|