freeipa/ipalib/tests/test_public.py

671 lines
21 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; version 2 only
#
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
"""
Unit tests for `ipalib.public` module.
"""
from tstutil import raises, getitem, no_set, no_del, read_only, ClassChecker
from ipalib import public, plugable, errors, ipa_types
def test_RULE_FLAG():
assert public.RULE_FLAG == 'validation_rule'
def test_rule():
flag = public.RULE_FLAG
rule = public.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():
is_rule = public.is_rule
flag = public.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_DefaltFrom(ClassChecker):
"""
Tests the `public.DefaltFrom` class.
"""
_cls = public.DefaultFrom
def test_class(self):
assert self.cls.__bases__ == (plugable.ReadOnly,)
def test_init(self):
"""
Tests the `public.DefaultFrom.__init__` method.
"""
def callback(*args):
return args
keys = ('givenname', 'sn')
o = self.cls(callback, *keys)
assert read_only(o, 'callback') is callback
assert read_only(o, 'keys') == keys
def test_call(self):
"""
Tests the `public.DefaultFrom.__call__` method.
"""
def callback(givenname, sn):
return givenname[0] + sn[0]
keys = ('givenname', 'sn')
o = self.cls(callback, *keys)
kw = dict(
givenname='John',
sn='Public',
hello='world',
)
assert o(**kw) == 'JP'
assert o() is None
for key in ('givenname', 'sn'):
kw_copy = dict(kw)
del kw_copy[key]
assert o(**kw_copy) is None
class test_Option2(ClassChecker):
"""
Tests the `public.Option2` class.
"""
_cls = public.Option2
def test_class(self):
assert self.cls.__bases__ == (plugable.ReadOnly,)
def test_init(self):
"""
Tests the `public.Option2.__init__` method.
"""
name = 'sn'
doc = 'Last Name'
type_ = ipa_types.Unicode()
o = self.cls(name, doc, type_)
assert o.__islocked__() is True
assert read_only(o, 'name') is name
assert read_only(o, 'doc') is doc
assert read_only(o, 'type') is type_
assert read_only(o, 'required') is False
assert read_only(o, 'multivalue') is False
assert read_only(o, 'default') is None
assert read_only(o, 'default_from') is None
assert read_only(o, 'rules') == (type_.validate,)
def test_convert(self):
name = 'sn'
doc = 'User last name'
type_ = ipa_types.Unicode()
class Hello(object):
def __unicode__(self):
return u'hello'
hello = Hello()
values = (u'hello', 'hello', hello)
# Test when multivalue=False:
o = self.cls(name, doc, type_)
for value in values:
new = o.convert(value)
assert new == u'hello'
assert type(new) is unicode
# Test when multivalue=True:
o = self.cls(name, doc, type_, multivalue=True)
for value in values:
for v in (value, (value,)):
new = o.convert(hello)
assert new == (u'hello',)
assert type(new) is tuple
def test_normalize(self):
"""
Tests the `public.Option2.validate` method.
"""
name = 'sn'
doc = 'User last name'
t = ipa_types.Unicode()
callback = lambda value: value.lower()
orig = u'Hello World'
orig_str = str(orig)
norm = u'hello world'
tup_orig = (orig, norm, u'WONDERFUL!')
tup_norm = (norm, norm, u'wonderful!')
tup_str = (orig_str, orig)
all_values = (None, orig, orig_str, norm, tup_orig, tup_norm, tup_str)
## Scenario 1: multivalue=False, normalize=None
o = self.cls(name, doc, t)
for v in all_values:
# When normalize=None, value is returned, no type checking:
assert o.normalize(v) is v
## Scenario 2: multivalue=False, normalize=callback
o = self.cls(name, doc, t, normalize=callback)
assert o.normalize(None) is None
for v in (orig, norm):
assert o.normalize(v) == norm
for v in (orig_str, tup_orig, tup_norm, tup_str): # Not unicode
e = raises(TypeError, o.normalize, v)
assert str(e) == 'need a %r; got %r' % (unicode, v)
## Scenario 3: multivalue=True, normalize=None
o = self.cls(name, doc, t, multivalue=True)
for v in all_values:
# When normalize=None, value is returned, no type checking:
assert o.normalize(v) is v
## Scenario 4: multivalue=True, normalize=callback
o = self.cls(name, doc, t, multivalue=True, normalize=callback)
assert o.normalize(None) is None
for v in (tup_orig, tup_norm):
assert o.normalize(v) == tup_norm
for v in (orig, orig_str, norm): # Not tuple
e = raises(TypeError, o.normalize, v)
assert str(e) == 'multivalue must be a tuple; got %r' % v
for v in [tup_str, (norm, orig, orig_str)]: # Not unicode
e = raises(TypeError, o.normalize, v)
assert str(e) == 'need a %r; got %r' % (unicode, orig_str)
def test_validate(self):
"""
Tests the `public.Option2.validate` method.
"""
name = 'sn'
doc = 'User last name'
type_ = ipa_types.Unicode()
def case_rule(value):
if not value.islower():
return 'Must be lower case'
my_rules = (case_rule,)
okay = u'whatever'
fail_case = u'Whatever'
fail_type = 'whatever'
## Scenario 1: multivalue=False
o = self.cls(name, doc, type_, rules=my_rules)
assert o.rules == (type_.validate, case_rule)
# Test a valid value:
o.validate(okay)
# Check that RuleError is raised with wrong case:
e = raises(errors.RuleError, o.validate, fail_case)
assert e.name is name
assert e.value is fail_case
assert e.error == 'Must be lower case'
# Test a RuleError is raise with wrong type:
e = raises(errors.RuleError, o.validate, fail_type)
assert e.name is name
assert e.value is fail_type
assert e.error == 'Must be a string'
## Scenario 2: multivalue=True
o = self.cls(name, doc, type_, multivalue=True, rules=my_rules)
def check_type_error(value):
e = raises(TypeError, o.validate, value)
assert str(e) == 'multivalue must be a tuple; got %r' % value
# Check a valid value:
check_type_error(okay)
o.validate((okay,))
# Check that RuleError is raised with wrong case:
check_type_error(fail_case)
for value in [(okay, fail_case), (fail_case, okay)]:
e = raises(errors.RuleError, o.validate, value)
assert e.name is name
assert e.value is fail_case
assert e.error == 'Must be lower case'
# Check that RuleError is raise with wrong type:
check_type_error(fail_type)
for value in [(okay, fail_type), (fail_type, okay)]:
e = raises(errors.RuleError, o.validate, value)
assert e.name is name
assert e.value is fail_type
assert e.error == 'Must be a string'
class test_Option(ClassChecker):
"""
Tests the `public.Option` class.
"""
_cls = public.Option
def get_subcls(self):
rule = public.rule
class int_opt(self.cls):
type = int
@rule
def rule_0(self, value):
if value == 0:
return 'cannot be 0'
@rule
def rule_1(self, value):
if value == 1:
return 'cannot be 1'
@rule
def rule_2(self, value):
if value == 2:
return 'cannot be 2'
return int_opt
def test_class(self):
"""
Perform some tests on the class (not an instance).
"""
assert self.cls.__bases__ == (plugable.Plugin,)
assert type(self.cls.rules) is property
def test_normalize(self):
"""
Tests the `public.Option.normalize` method.
"""
assert 'normalize' in self.cls.__public__
o = self.subcls()
# Test with values that can't be converted:
nope = (
'7.0'
'whatever',
object,
None,
)
for val in nope:
e = raises(errors.NormalizationError, o.normalize, val)
assert isinstance(e, errors.ValidationError)
assert e.name == 'int_opt'
assert e.value == val
assert e.error == "not <type 'int'>"
assert e.type is int
# Test with values that can be converted:
okay = (
7,
7.0,
7.2,
7L,
'7',
' 7 ',
)
for val in okay:
assert o.normalize(val) == 7
def test_validate(self):
"""
Tests the `public.Option.validate` method.
"""
assert 'validate' in self.cls.__public__
o = self.subcls()
o.validate(9)
for i in xrange(3):
e = raises(errors.RuleError, o.validate, i)
assert e.error == 'cannot be %d' % i
assert e.value == i
def test_rules(self):
"""
Tests the `public.Option.rules` property.
"""
o = self.subcls()
assert len(o.rules) == 3
def get_rule(i):
return getattr(o, 'rule_%d' % i)
rules = tuple(get_rule(i) for i in xrange(3))
assert o.rules == rules
def test_get_default(self):
"""
Tests the `public.Option.get_default` method.
"""
assert 'get_default' in self.cls.__public__
assert 'default' in self.cls.__public__
assert 'default_from' in self.cls.__public__
assert self.cls().get_default() is None
class subclass(self.cls):
default = 3
default_from = public.DefaultFrom(
lambda a,b: a * b,
'key0', 'key1'
)
o = subclass()
assert o.get_default() == 3
assert o.get_default(key0=2, key1=5) == 10
assert o.get_default(key0=7) == 3
class test_Command(ClassChecker):
"""
Tests the `public.Command` class.
"""
_cls = public.Command
def get_subcls(self):
class my_option(public.Option):
def normalize(self, value):
return super(my_option, self).normalize(value).lower()
@public.rule
def my_rule(self, value):
if value != self.name:
return 'must equal %r' % self.name
default_from = public.DefaultFrom(
lambda arg: arg, 'default_from'
)
class option0(my_option):
pass
class option1(my_option):
required = True
class example(self.cls):
option_classes = (option0, option1)
return example
def test_class(self):
assert self.cls.__bases__ == (plugable.Plugin,)
assert type(self.cls.options) == property
def test_get_options(self):
"""
Tests the `public.Command.get_options` method.
"""
assert list(self.cls().get_options()) == []
sub = self.subcls()
for (i, proxy) in enumerate(sub.get_options()):
assert isinstance(proxy, plugable.PluginProxy)
assert read_only(proxy, 'name') == 'option%d' % i
assert proxy.implements(public.Option)
assert i == 1
def test_options(self):
"""
Tests the `public.Command.options` property.
"""
assert 'options' in self.cls.__public__ # Public
sub = self.subcls()
options = sub.options
assert type(options) == plugable.NameSpace
assert len(options) == 2
for name in ('option0', 'option1'):
assert name in options
proxy = options[name]
assert getattr(options, name) is proxy
assert isinstance(proxy, plugable.PluginProxy)
assert proxy.name == name
def test_normalize(self):
"""
Tests the `public.Command.normalize` method.
"""
assert 'normalize' in self.cls.__public__ # Public
kw = dict(
option0='OPTION0',
option1='OPTION1',
option2='option2',
)
norm = dict((k, v.lower()) for (k, v) in kw.items())
sub = self.subcls()
assert sub.normalize(**kw) == norm
def test_get_default(self):
"""
Tests the `public.Command.get_default` method.
"""
assert 'get_default' in self.cls.__public__ # Public
no_fill = dict(
option0='value0',
option1='value1',
whatever='hello world',
)
fill = dict(
default_from='the default',
)
default = dict(
option0='the default',
option1='the default',
)
sub = self.subcls()
assert sub.get_default(**no_fill) == {}
assert sub.get_default(**fill) == default
def test_validate(self):
"""
Tests the `public.Command.validate` method.
"""
assert 'validate' in self.cls.__public__ # Public
sub = self.subcls()
# Check with valid args
okay = dict(
option0='option0',
option1='option1',
another_option='some value',
)
sub.validate(**okay)
# Check with an invalid arg
fail = dict(okay)
fail['option0'] = 'whatever'
raises(errors.RuleError, sub.validate, **fail)
# Check with a missing required arg
fail = dict(okay)
fail.pop('option1')
raises(errors.RequirementError, sub.validate, **fail)
# Check with missing *not* required arg
okay.pop('option0')
sub.validate(**okay)
def test_execute(self):
"""
Tests the `public.Command.execute` method.
"""
assert 'execute' in self.cls.__public__ # Public
class test_Object(ClassChecker):
"""
Tests the `public.Object` class.
"""
_cls = public.Object
def test_class(self):
assert self.cls.__bases__ == (plugable.Plugin,)
assert type(self.cls.Method) is property
assert type(self.cls.Property) is property
def test_init(self):
"""
Tests the `public.Object.__init__` method.
"""
o = self.cls()
assert read_only(o, 'Method') is None
assert read_only(o, 'Property') is None
def test_finalize(self):
"""
Tests the `public.Object.finalize` 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
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(
Method='method_%d',
Property='property_%d',
)
class api(object):
Method = plugable.NameSpace(
get_attributes(cnt, formats['Method'])
)
Property = plugable.NameSpace(
get_attributes(cnt, formats['Property'])
)
assert len(api.Method) == cnt * 3
assert len(api.Property) == cnt * 3
class user(self.cls):
pass
# Actually perform test:
o = user()
o.finalize(api)
assert read_only(o, 'api') is api
for name in ['Method', 'Property']:
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 == attr_name
class test_Attribute(ClassChecker):
"""
Tests the `public.Attribute` class.
"""
_cls = public.Attribute
def test_class(self):
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):
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_finalize(self):
user_obj = 'The user public.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.finalize(api)
assert read_only(o, 'api') is api
assert read_only(o, 'obj') is user_obj
class test_Method(ClassChecker):
"""
Tests the `public.Method` class.
"""
_cls = public.Method
def test_class(self):
assert self.cls.__bases__ == (public.Attribute, public.Command)
assert self.cls.implements(public.Command)
def get_subcls(self):
class option0(public.Option):
pass
class option1(public.Option):
pass
class example_prop0(public.Property):
pass
class example_prop1(public.Property):
pass
class example_obj(object):
__prop = None
def __get_prop(self):
if self.__prop is None:
self.__prop = plugable.NameSpace([
plugable.PluginProxy(
public.Property, example_prop0(), 'attr_name'
),
plugable.PluginProxy(
public.Property, example_prop1(), 'attr_name'
),
])
return self.__prop
Property = property(__get_prop)
class noun_verb(self.cls):
option_classes = (option0, option1)
obj = example_obj()
return noun_verb
def test_get_options(self):
"""
Tests the `public.Method.get_options` method.
"""
sub = self.subcls()
names = ('option0', 'option1', 'prop0', 'prop1')
proxies = tuple(sub.get_options())
assert len(proxies) == 4
for (i, proxy) in enumerate(proxies):
assert proxy.name == names[i]
assert isinstance(proxy, plugable.PluginProxy)
assert proxy.implements(public.Option)
class test_prop(ClassChecker):
_cls = public.Property
def test_class(self):
assert self.cls.__bases__ == (public.Attribute, public.Option)
assert self.cls.implements(public.Option)