freeipa/ipatests/test_ipalib/test_parameters.py
Jan Cholasta 0d370a959b pylint: enable the import-error check
Check for import errors with pylint to make sure new python package
dependencies are not overlooked.

https://fedorahosted.org/freeipa/ticket/6418

Reviewed-By: Petr Spacek <pspacek@redhat.com>
Reviewed-By: Martin Basti <mbasti@redhat.com>
2016-10-24 14:11:08 +02:00

1633 lines
55 KiB
Python

# -*- coding: utf-8 -*-
# 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.parameters` module.
"""
# FIXME: Pylint errors
# pylint: disable=no-member
import datetime
import re
import sys
from decimal import Decimal
from inspect import isclass
import pytest
import six
# pylint: disable=import-error
from six.moves.xmlrpc_client import MAXINT, MININT
# pylint: enable=import-error
from ipatests.util import raises, ClassChecker, read_only
from ipatests.util import dummy_ugettext, assert_equal
from ipatests.data import binary_bytes, utf8_bytes, unicode_str
from ipalib import parameters, text, errors, config
from ipalib.constants import TYPE_ERROR, CALLABLE_ERROR
from ipalib.errors import ValidationError, ConversionError
from ipalib import _
if six.PY3:
unicode = str
long = int
NULLS = (None, b'', u'', tuple(), [])
pytestmark = pytest.mark.tier0
class test_DefaultFrom(ClassChecker):
"""
Test the `ipalib.parameters.DefaultFrom` class.
"""
_cls = parameters.DefaultFrom
def test_init(self):
"""
Test the `ipalib.parameters.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
lam = lambda first, last: first[0] + last
o = self.cls(lam)
assert read_only(o, 'keys') == ('first', 'last')
# Test that TypeError is raised when callback isn't callable:
e = raises(TypeError, self.cls, 'whatever')
assert str(e) == CALLABLE_ERROR % ('callback', 'whatever', str)
# Test that TypeError is raised when a key isn't an str:
e = raises(TypeError, self.cls, callback, 'givenname', 17)
assert str(e) == TYPE_ERROR % ('keys', str, 17, int)
# Test that ValueError is raised when inferring keys from a callback
# which has *args:
e = raises(ValueError, self.cls, lambda foo, *args: None)
assert str(e) == "callback: variable-length argument list not allowed"
# Test that ValueError is raised when inferring keys from a callback
# which has **kwargs:
e = raises(ValueError, self.cls, lambda foo, **kwargs: None)
assert str(e) == "callback: variable-length argument list not allowed"
def test_repr(self):
"""
Test the `ipalib.parameters.DefaultFrom.__repr__` method.
"""
def stuff(one, two):
pass
o = self.cls(stuff)
assert repr(o) == "DefaultFrom('one', 'two')"
o = self.cls(stuff, 'aye', 'bee', 'see')
assert repr(o) == "DefaultFrom('aye', 'bee', 'see')"
cb = lambda first, last: first[0] + last
o = self.cls(cb)
assert repr(o) == "DefaultFrom('first', 'last')"
o = self.cls(cb, 'aye', 'bee', 'see')
assert repr(o) == "DefaultFrom('aye', 'bee', 'see')"
def test_call(self):
"""
Test the `ipalib.parameters.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
# Test using implied keys:
o = self.cls(lambda first, last: first[0] + last)
assert o(first='john', last='doe') == 'jdoe'
assert o(first='', last='doe') is None
assert o(one='john', two='doe') is None
# Test that co_varnames slice is used:
def callback2(first, last):
letter = first[0]
return letter + last
o = self.cls(callback2)
assert o.keys == ('first', 'last')
assert o(first='john', last='doe') == 'jdoe'
def test_parse_param_spec():
"""
Test the `ipalib.parameters.parse_param_spec` function.
"""
f = parameters.parse_param_spec
assert f('name') == ('name', dict(required=True, multivalue=False))
assert f('name?') == ('name', dict(required=False, multivalue=False))
assert f('name*') == ('name', dict(required=False, multivalue=True))
assert f('name+') == ('name', dict(required=True, multivalue=True))
# Make sure other "funny" endings are *not* treated special:
assert f('name^') == ('name^', dict(required=True, multivalue=False))
# Test that TypeError is raised if spec isn't an str:
if six.PY2:
bad_value = u'name?'
else:
bad_value = b'name?'
e = raises(TypeError, f, bad_value)
assert str(e) == TYPE_ERROR % ('spec', str, bad_value, type(bad_value))
class DummyRule(object):
def __init__(self, error=None):
assert error is None or type(error) is unicode
self.error = error
self.reset()
def __call__(self, *args):
self.calls.append(args)
return self.error
def reset(self):
self.calls = []
class test_Param(ClassChecker):
"""
Test the `ipalib.parameters.Param` class.
"""
_cls = parameters.Param
def test_init(self):
"""
Test the `ipalib.parameters.Param.__init__` method.
"""
name = 'my_param'
o = self.cls(name)
assert o.param_spec is name
assert o.name is name
assert o.nice == "Param('my_param')"
assert o.password is False
assert o.__islocked__() is True
# Test default rules:
assert o.rules == tuple()
assert o.class_rules == tuple()
assert o.all_rules == tuple()
# Test default kwarg values:
assert o.cli_name is name
assert o.label.msg == 'my_param'
assert o.doc.msg == 'my_param'
assert o.required is True
assert o.multivalue is False
assert o.primary_key is False
assert o.normalizer is None
assert o.default is None
assert o.default_from is None
assert o.autofill is False
assert o.query is False
assert o.attribute is False
assert o.include is None
assert o.exclude is None
assert o.flags == frozenset()
assert o.sortorder == 2
# Test that doc defaults from label:
o = self.cls('my_param', doc=_('Hello world'))
assert o.label.msg == 'my_param'
assert o.doc.msg == 'Hello world'
o = self.cls('my_param', label='My Param')
assert o.label == 'My Param'
assert o.doc == 'My Param'
# Test that ValueError is raised when a kwarg from a subclass
# conflicts with an attribute:
class Subclass1(self.cls):
kwargs = self.cls.kwargs + (
('convert', callable, None),
)
e = raises(ValueError, Subclass1, name)
assert str(e) == "kwarg 'convert' conflicts with attribute on Subclass1"
# Test type validation of keyword arguments:
class Subclass(self.cls):
kwargs = self.cls.kwargs + (
('extra1', bool, True),
('extra2', str, 'Hello'),
('extra3', (int, float), 42),
('extra4', callable, lambda whatever: whatever + 7),
)
o = Subclass('my_param') # Test with no **kw:
for key, kind, _default in o.kwargs:
# Test with a type invalid for all:
value = object()
kw = {key: value}
e = raises(TypeError, Subclass, 'my_param', **kw)
if kind is callable:
assert str(e) == CALLABLE_ERROR % (key, value, type(value))
else:
assert str(e) == TYPE_ERROR % (key, kind, value, type(value))
# Test with None:
kw = {key: None}
Subclass('my_param', **kw)
# Test when using unknown kwargs:
e = raises(TypeError, self.cls, 'my_param',
flags=['hello', 'world'],
whatever=u'Hooray!',
)
assert str(e) == \
"Param('my_param'): takes no such kwargs: 'whatever'"
e = raises(TypeError, self.cls, 'my_param', great='Yes', ape='he is!')
assert str(e) == \
"Param('my_param'): takes no such kwargs: 'ape', 'great'"
# Test that ValueError is raised if you provide both include and
# exclude:
e = raises(ValueError, self.cls, 'my_param',
include=['server', 'foo'],
exclude=['client', 'bar'],
)
assert str(e) == '%s: cannot have both %s=%r and %s=%r' % (
"Param('my_param')",
'include', frozenset(['server', 'foo']),
'exclude', frozenset(['client', 'bar']),
)
# Test that default_from gets set:
call = lambda first, last: first[0] + last
o = self.cls('my_param', default_from=call)
assert type(o.default_from) is parameters.DefaultFrom
assert o.default_from.callback is call
def test_repr(self):
"""
Test the `ipalib.parameters.Param.__repr__` method.
"""
for name in ['name', 'name?', 'name*', 'name+']:
o = self.cls(name)
assert repr(o) == 'Param(%r)' % name
o = self.cls('name', required=False)
assert repr(o) == "Param('name?')"
o = self.cls('name', multivalue=True)
assert repr(o) == "Param('name+')"
def test_use_in_context(self):
"""
Test the `ipalib.parameters.Param.use_in_context` method.
"""
set1 = ('one', 'two', 'three')
set2 = ('four', 'five', 'six')
param1 = self.cls('param1')
param2 = self.cls('param2', include=set1)
param3 = self.cls('param3', exclude=set2)
for context in set1:
env = config.Env()
env.context = context
assert param1.use_in_context(env) is True, context
assert param2.use_in_context(env) is True, context
assert param3.use_in_context(env) is True, context
for context in set2:
env = config.Env()
env.context = context
assert param1.use_in_context(env) is True, context
assert param2.use_in_context(env) is False, context
assert param3.use_in_context(env) is False, context
def test_safe_value(self):
"""
Test the `ipalib.parameters.Param.safe_value` method.
"""
values = (unicode_str, binary_bytes, utf8_bytes)
o = self.cls('my_param')
for value in values:
assert o.safe_value(value) is value
assert o.safe_value(None) is None
p = parameters.Password('my_passwd')
for value in values:
assert_equal(p.safe_value(value), u'********')
assert p.safe_value(None) is None
def test_clone(self):
"""
Test the `ipalib.parameters.Param.clone` method.
"""
# Test with the defaults
orig = self.cls('my_param')
clone = orig.clone()
assert clone is not orig
assert type(clone) is self.cls
assert clone.name is orig.name
for key, _kind, _default in self.cls.kwargs:
assert getattr(clone, key) == getattr(orig, key)
# Test with a param spec:
orig = self.cls('my_param*')
assert orig.param_spec == 'my_param*'
clone = orig.clone()
assert clone.param_spec == 'my_param*'
assert clone is not orig
assert type(clone) is self.cls
for key, _kind, _default in self.cls.kwargs:
assert getattr(clone, key) == getattr(orig, key)
# Test with overrides:
orig = self.cls('my_param*')
assert orig.required is False
assert orig.multivalue is True
clone = orig.clone(required=True)
assert clone is not orig
assert type(clone) is self.cls
assert clone.required is True
assert clone.multivalue is True
assert clone.param_spec == 'my_param+'
assert clone.name == 'my_param'
def test_clone_rename(self):
"""
Test the `ipalib.parameters.Param.clone` method.
"""
new_name = 'my_new_param'
# Test with the defaults
orig = self.cls('my_param')
clone = orig.clone_rename(new_name)
assert clone is not orig
assert type(clone) is self.cls
assert clone.name == new_name
for key, _kind, _default in self.cls.kwargs:
if key in ('cli_name', 'label', 'doc', 'cli_metavar'):
continue
assert getattr(clone, key) is getattr(orig, key)
# Test with overrides:
orig = self.cls('my_param*')
assert orig.required is False
assert orig.multivalue is True
clone = orig.clone_rename(new_name, required=True)
assert clone is not orig
assert type(clone) is self.cls
assert clone.required is True
assert clone.multivalue is True
assert clone.param_spec == "{0}+".format(new_name)
assert clone.name == new_name
def test_convert(self):
"""
Test the `ipalib.parameters.Param.convert` method.
"""
okay = ('Hello', u'Hello', 0, 4.2, True, False, unicode_str)
class Subclass(self.cls):
def _convert_scalar(self, value, index=None):
return value
# Test when multivalue=False:
o = Subclass('my_param')
for value in NULLS:
assert o.convert(value) is None
assert o.convert(None) is None
for value in okay:
assert o.convert(value) is value
# Test when multivalue=True:
o = Subclass('my_param', multivalue=True)
for value in NULLS:
assert o.convert(value) is None
assert o.convert(okay) == okay
assert o.convert(NULLS) is None
assert o.convert(okay + NULLS) == okay
assert o.convert(NULLS + okay) == okay
for value in okay:
assert o.convert(value) == (value,)
assert o.convert([None, value]) == (value,)
assert o.convert([value, None]) == (value,)
def test_convert_scalar(self):
"""
Test the `ipalib.parameters.Param._convert_scalar` method.
"""
dummy = dummy_ugettext()
# Test with correct type:
o = self.cls('my_param')
assert o._convert_scalar(None) is None
assert dummy.called() is False
# Test with incorrect type
raises(errors.ConversionError, o._convert_scalar, 'hello')
def test_validate(self):
"""
Test the `ipalib.parameters.Param.validate` method.
"""
# Test in default state (with no rules, no kwarg):
o = self.cls('my_param')
e = raises(errors.RequirementError, o.validate, None)
assert e.name == 'my_param'
# Test with required=False
o = self.cls('my_param', required=False)
assert o.required is False
assert o.validate(None) is None
# Test with query=True:
o = self.cls('my_param', query=True)
assert o.query is True
e = raises(errors.RequirementError, o.validate, None)
assert_equal(e.name, u'my_param')
# Test with multivalue=True:
o = self.cls('my_param', multivalue=True)
e = raises(TypeError, o.validate, [])
assert str(e) == TYPE_ERROR % ('value', tuple, [], list)
e = raises(ValueError, o.validate, tuple())
assert str(e) == 'value: empty tuple must be converted to None'
# Test with wrong (scalar) type:
e = raises(TypeError, o.validate, (None, None, 42, None))
assert str(e) == TYPE_ERROR % ('my_param', type(None), 42, int)
o = self.cls('my_param')
e = raises(TypeError, o.validate, 'Hello')
assert str(e) == TYPE_ERROR % ('my_param', type(None), 'Hello', str)
class Example(self.cls):
type = int
# Test with some rules and multivalue=False
pass1 = DummyRule()
pass2 = DummyRule()
fail = DummyRule(u'no good')
o = Example('example', pass1, pass2)
assert o.multivalue is False
assert o.validate(11) is None
assert pass1.calls == [(text.ugettext, 11)]
assert pass2.calls == [(text.ugettext, 11)]
pass1.reset()
pass2.reset()
o = Example('example', pass1, pass2, fail)
e = raises(errors.ValidationError, o.validate, 42)
assert e.name == 'example'
assert e.error == u'no good'
assert pass1.calls == [(text.ugettext, 42)]
assert pass2.calls == [(text.ugettext, 42)]
assert fail.calls == [(text.ugettext, 42)]
# Test with some rules and multivalue=True
pass1 = DummyRule()
pass2 = DummyRule()
fail = DummyRule(u'this one is not good')
o = Example('example', pass1, pass2, multivalue=True)
assert o.multivalue is True
assert o.validate((3, 9)) is None
assert pass1.calls == [
(text.ugettext, 3),
(text.ugettext, 9),
]
assert pass2.calls == [
(text.ugettext, 3),
(text.ugettext, 9),
]
pass1.reset()
pass2.reset()
o = Example('multi_example', pass1, pass2, fail, multivalue=True)
assert o.multivalue is True
e = raises(errors.ValidationError, o.validate, (3, 9))
assert e.name == 'multi_example'
assert e.error == u'this one is not good'
assert pass1.calls == [(text.ugettext, 3)]
assert pass2.calls == [(text.ugettext, 3)]
assert fail.calls == [(text.ugettext, 3)]
def test_validate_scalar(self):
"""
Test the `ipalib.parameters.Param._validate_scalar` method.
"""
class MyParam(self.cls):
type = bool
okay = DummyRule()
o = MyParam('my_param', okay)
# Test that TypeError is appropriately raised:
e = raises(TypeError, o._validate_scalar, 0)
assert str(e) == TYPE_ERROR % ('my_param', bool, 0, int)
# Test with passing rule:
assert o._validate_scalar(True) is None
assert o._validate_scalar(False) is None
assert okay.calls == [
(text.ugettext, True),
(text.ugettext, False),
]
# Test with a failing rule:
okay = DummyRule()
fail = DummyRule(u'this describes the error')
o = MyParam('my_param', okay, fail)
e = raises(errors.ValidationError, o._validate_scalar, True)
assert e.name == 'my_param'
assert e.error == u'this describes the error'
e = raises(errors.ValidationError, o._validate_scalar, False)
assert e.name == 'my_param'
assert e.error == u'this describes the error'
assert okay.calls == [
(text.ugettext, True),
(text.ugettext, False),
]
assert fail.calls == [
(text.ugettext, True),
(text.ugettext, False),
]
def test_get_default(self):
"""
Test the `ipalib.parameters.Param.get_default` method.
"""
class PassThrough(object):
value = None
def __call__(self, value):
assert self.value is None
assert value is not None
self.value = value
return value
def reset(self):
assert self.value is not None
self.value = None
class Str(self.cls):
type = unicode
def __init__(self, name, **kw):
# (Pylint complains because the superclass is unknowm)
# pylint: disable=bad-super-call, super-on-old-class
self._convert_scalar = PassThrough()
super(Str, self).__init__(name, **kw)
# Test with only a static default:
o = Str('my_str',
normalizer=PassThrough(),
default=u'Static Default',
)
assert_equal(o.get_default(), u'Static Default')
assert o._convert_scalar.value is None
assert o.normalizer.value is None
# Test with default_from:
o = Str('my_str',
normalizer=PassThrough(),
default=u'Static Default',
default_from=lambda first, last: first[0] + last,
)
assert_equal(o.get_default(), u'Static Default')
assert o._convert_scalar.value is None
assert o.normalizer.value is None
default = o.get_default(first=u'john', last='doe')
assert_equal(default, u'jdoe')
assert o._convert_scalar.value is default
assert o.normalizer.value is default
class test_Flag(ClassChecker):
"""
Test the `ipalib.parameters.Flag` class.
"""
_cls = parameters.Flag
def test_init(self):
"""
Test the `ipalib.parameters.Flag.__init__` method.
"""
# Test with no kwargs:
o = self.cls('my_flag')
assert o.type is bool
assert isinstance(o, parameters.Bool)
assert o.autofill is True
assert o.default is False
# Test that TypeError is raise if default is not a bool:
e = raises(TypeError, self.cls, 'my_flag', default=None)
assert str(e) == TYPE_ERROR % ('default', bool, None, type(None))
# Test with autofill=False, default=True
o = self.cls('my_flag', autofill=False, default=True)
assert o.autofill is True
assert o.default is True
# Test when cloning:
orig = self.cls('my_flag')
for clone in [orig.clone(), orig.clone(autofill=False)]:
assert clone.autofill is True
assert clone.default is False
assert clone is not orig
assert type(clone) is self.cls
# Test when cloning with default=True/False
orig = self.cls('my_flag')
assert orig.clone().default is False
assert orig.clone(default=True).default is True
orig = self.cls('my_flag', default=True)
assert orig.clone().default is True
assert orig.clone(default=False).default is False
class test_Data(ClassChecker):
"""
Test the `ipalib.parameters.Data` class.
"""
_cls = parameters.Data
def test_init(self):
"""
Test the `ipalib.parameters.Data.__init__` method.
"""
o = self.cls('my_data')
assert o.type is type(None)
assert o.password is False
assert o.rules == tuple()
assert o.class_rules == tuple()
assert o.all_rules == tuple()
assert o.minlength is None
assert o.maxlength is None
assert o.length is None
# Test mixing length with minlength or maxlength:
o = self.cls('my_data', length=5)
assert o.length == 5
permutations = [
dict(minlength=3),
dict(maxlength=7),
dict(minlength=3, maxlength=7),
]
for kw in permutations:
o = self.cls('my_data', **kw)
for (key, value) in kw.items():
assert getattr(o, key) == value
e = raises(ValueError, self.cls, 'my_data', length=5, **kw)
assert str(e) == \
"Data('my_data'): cannot mix length with minlength or maxlength"
# Test when minlength or maxlength are less than 1:
e = raises(ValueError, self.cls, 'my_data', minlength=0)
assert str(e) == "Data('my_data'): minlength must be >= 1; got 0"
e = raises(ValueError, self.cls, 'my_data', maxlength=0)
assert str(e) == "Data('my_data'): maxlength must be >= 1; got 0"
# Test when minlength > maxlength:
e = raises(ValueError, self.cls, 'my_data', minlength=22, maxlength=15)
assert str(e) == \
"Data('my_data'): minlength > maxlength (minlength=22, maxlength=15)"
# Test when minlength == maxlength
e = raises(ValueError, self.cls, 'my_data', minlength=7, maxlength=7)
assert str(e) == \
"Data('my_data'): minlength == maxlength; use length=7 instead"
class test_Bytes(ClassChecker):
"""
Test the `ipalib.parameters.Bytes` class.
"""
_cls = parameters.Bytes
def test_init(self):
"""
Test the `ipalib.parameters.Bytes.__init__` method.
"""
o = self.cls('my_bytes')
assert o.type is bytes
assert o.password is False
assert o.rules == tuple()
assert o.class_rules == tuple()
assert o.all_rules == tuple()
assert o.minlength is None
assert o.maxlength is None
assert o.length is None
assert o.pattern is None
assert o.re is None
# Test mixing length with minlength or maxlength:
o = self.cls('my_bytes', length=5)
assert o.length == 5
assert len(o.class_rules) == 1
assert len(o.rules) == 0
assert len(o.all_rules) == 1
permutations = [
dict(minlength=3),
dict(maxlength=7),
dict(minlength=3, maxlength=7),
]
for kw in permutations:
o = self.cls('my_bytes', **kw)
assert len(o.class_rules) == len(kw)
assert len(o.rules) == 0
assert len(o.all_rules) == len(kw)
for (key, value) in kw.items():
assert getattr(o, key) == value
e = raises(ValueError, self.cls, 'my_bytes', length=5, **kw)
assert str(e) == \
"Bytes('my_bytes'): cannot mix length with minlength or maxlength"
# Test when minlength or maxlength are less than 1:
e = raises(ValueError, self.cls, 'my_bytes', minlength=0)
assert str(e) == "Bytes('my_bytes'): minlength must be >= 1; got 0"
e = raises(ValueError, self.cls, 'my_bytes', maxlength=0)
assert str(e) == "Bytes('my_bytes'): maxlength must be >= 1; got 0"
# Test when minlength > maxlength:
e = raises(ValueError, self.cls, 'my_bytes', minlength=22, maxlength=15)
assert str(e) == \
"Bytes('my_bytes'): minlength > maxlength (minlength=22, maxlength=15)"
# Test when minlength == maxlength
e = raises(ValueError, self.cls, 'my_bytes', minlength=7, maxlength=7)
assert str(e) == \
"Bytes('my_bytes'): minlength == maxlength; use length=7 instead"
def test_rule_minlength(self):
"""
Test the `ipalib.parameters.Bytes._rule_minlength` method.
"""
o = self.cls('my_bytes', minlength=3)
assert o.minlength == 3
rule = o._rule_minlength
translation = u'minlength=%(minlength)r'
dummy = dummy_ugettext(translation)
assert dummy.translation is translation
# Test with passing values:
for value in (b'abc', b'four', b'12345'):
assert rule(dummy, value) is None
assert dummy.called() is False
# Test with failing values:
for value in (b'', b'a', b'12'):
assert_equal(
rule(dummy, value),
translation % dict(minlength=3)
)
assert dummy.message == 'must be at least %(minlength)d bytes'
assert dummy.called() is True
dummy.reset()
def test_rule_maxlength(self):
"""
Test the `ipalib.parameters.Bytes._rule_maxlength` method.
"""
o = self.cls('my_bytes', maxlength=4)
assert o.maxlength == 4
rule = o._rule_maxlength
translation = u'maxlength=%(maxlength)r'
dummy = dummy_ugettext(translation)
assert dummy.translation is translation
# Test with passing values:
for value in (b'ab', b'123', b'four'):
assert rule(dummy, value) is None
assert dummy.called() is False
# Test with failing values:
for value in (b'12345', b'sixsix'):
assert_equal(
rule(dummy, value),
translation % dict(maxlength=4)
)
assert dummy.message == 'can be at most %(maxlength)d bytes'
assert dummy.called() is True
dummy.reset()
def test_rule_length(self):
"""
Test the `ipalib.parameters.Bytes._rule_length` method.
"""
o = self.cls('my_bytes', length=4)
assert o.length == 4
rule = o._rule_length
translation = u'length=%(length)r'
dummy = dummy_ugettext(translation)
assert dummy.translation is translation
# Test with passing values:
for value in (b'1234', b'four'):
assert rule(dummy, value) is None
assert dummy.called() is False
# Test with failing values:
for value in (b'ab', b'123', b'12345', b'sixsix'):
assert_equal(
rule(dummy, value),
translation % dict(length=4),
)
assert dummy.message == 'must be exactly %(length)d bytes'
assert dummy.called() is True
dummy.reset()
def test_rule_pattern(self):
"""
Test the `ipalib.parameters.Bytes._rule_pattern` method.
"""
# Test our assumptions about Python re module and Unicode:
pat = b'\w+$'
r = re.compile(pat)
assert r.match(b'Hello_World') is not None
assert r.match(utf8_bytes) is None
assert r.match(binary_bytes) is None
# Create instance:
o = self.cls('my_bytes', pattern=pat)
assert o.pattern is pat
rule = o._rule_pattern
translation = u'pattern=%(pattern)r'
dummy = dummy_ugettext(translation)
# Test with passing values:
for value in (b'HELLO', b'hello', b'Hello_World'):
assert rule(dummy, value) is None
assert dummy.called() is False
# Test with failing values:
for value in (b'Hello!', b'Hello World', utf8_bytes, binary_bytes):
assert_equal(
rule(dummy, value),
translation % dict(pattern=pat),
)
assert_equal(dummy.message, 'must match pattern "%(pattern)s"')
assert dummy.called() is True
dummy.reset()
class test_Str(ClassChecker):
"""
Test the `ipalib.parameters.Str` class.
"""
_cls = parameters.Str
def test_init(self):
"""
Test the `ipalib.parameters.Str.__init__` method.
"""
o = self.cls('my_str')
assert o.type is unicode
assert o.password is False
assert o.minlength is None
assert o.maxlength is None
assert o.length is None
assert o.pattern is None
def test_convert_scalar(self):
"""
Test the `ipalib.parameters.Str._convert_scalar` method.
"""
o = self.cls('my_str')
mthd = o._convert_scalar
for value in (u'Hello', 42, 1.2, unicode_str):
assert mthd(value) == unicode(value)
bad = [True, b'Hello', dict(one=1), utf8_bytes]
for value in bad:
e = raises(errors.ConversionError, mthd, value)
assert e.name == 'my_str'
assert_equal(unicode(e.error), u'must be Unicode text')
bad = [(u'Hello',), [42.3]]
for value in bad:
e = raises(errors.ConversionError, mthd, value)
assert e.name == 'my_str'
assert_equal(unicode(e.error), u'Only one value is allowed')
assert o.convert(None) is None
def test_rule_minlength(self):
"""
Test the `ipalib.parameters.Str._rule_minlength` method.
"""
o = self.cls('my_str', minlength=3)
assert o.minlength == 3
rule = o._rule_minlength
translation = u'minlength=%(minlength)r'
dummy = dummy_ugettext(translation)
assert dummy.translation is translation
# Test with passing values:
for value in (u'abc', u'four', u'12345'):
assert rule(dummy, value) is None
assert dummy.called() is False
# Test with failing values:
for value in (u'', u'a', u'12'):
assert_equal(
rule(dummy, value),
translation % dict(minlength=3)
)
assert dummy.message == 'must be at least %(minlength)d characters'
assert dummy.called() is True
dummy.reset()
def test_rule_maxlength(self):
"""
Test the `ipalib.parameters.Str._rule_maxlength` method.
"""
o = self.cls('my_str', maxlength=4)
assert o.maxlength == 4
rule = o._rule_maxlength
translation = u'maxlength=%(maxlength)r'
dummy = dummy_ugettext(translation)
assert dummy.translation is translation
# Test with passing values:
for value in (u'ab', u'123', u'four'):
assert rule(dummy, value) is None
assert dummy.called() is False
# Test with failing values:
for value in (u'12345', u'sixsix'):
assert_equal(
rule(dummy, value),
translation % dict(maxlength=4)
)
assert dummy.message == 'can be at most %(maxlength)d characters'
assert dummy.called() is True
dummy.reset()
def test_rule_length(self):
"""
Test the `ipalib.parameters.Str._rule_length` method.
"""
o = self.cls('my_str', length=4)
assert o.length == 4
rule = o._rule_length
translation = u'length=%(length)r'
dummy = dummy_ugettext(translation)
assert dummy.translation is translation
# Test with passing values:
for value in (u'1234', u'four'):
assert rule(dummy, value) is None
assert dummy.called() is False
# Test with failing values:
for value in (u'ab', u'123', u'12345', u'sixsix'):
assert_equal(
rule(dummy, value),
translation % dict(length=4),
)
assert dummy.message == 'must be exactly %(length)d characters'
assert dummy.called() is True
dummy.reset()
def test_rule_pattern(self):
"""
Test the `ipalib.parameters.Str._rule_pattern` method.
"""
# Test our assumptions about Python re module and Unicode:
pat = '\w{5}$'
r1 = re.compile(pat)
r2 = re.compile(pat, re.UNICODE)
if six.PY2:
assert r1.match(unicode_str) is None
else:
assert r1.match(unicode_str) is not None
assert r2.match(unicode_str) is not None
# Create instance:
o = self.cls('my_str', pattern=pat)
assert o.pattern is pat
rule = o._rule_pattern
translation = u'pattern=%(pattern)r'
dummy = dummy_ugettext(translation)
# Test with passing values:
for value in (u'HELLO', u'hello', unicode_str):
assert rule(dummy, value) is None
assert dummy.called() is False
# Test with failing values:
for value in (u'H LLO', u'***lo', unicode_str + unicode_str):
assert_equal(
rule(dummy, value),
translation % dict(pattern=pat),
)
assert_equal(dummy.message, 'must match pattern "%(pattern)s"')
assert dummy.called() is True
dummy.reset()
class test_Password(ClassChecker):
"""
Test the `ipalib.parameters.Password` class.
"""
_cls = parameters.Password
def test_init(self):
"""
Test the `ipalib.parameters.Password.__init__` method.
"""
o = self.cls('my_password')
assert o.type is unicode
assert o.minlength is None
assert o.maxlength is None
assert o.length is None
assert o.pattern is None
assert o.password is True
def test_convert_scalar(self):
"""
Test the `ipalib.parameters.Password._convert_scalar` method.
"""
o = self.cls('my_password')
e = raises(errors.PasswordMismatch, o._convert_scalar, [u'one', u'two'])
assert e.name == 'my_password'
assert o._convert_scalar([u'one', u'one']) == u'one'
assert o._convert_scalar(u'one') == u'one'
class EnumChecker(ClassChecker):
"""
Test *Enum classes.
"""
_cls = parameters.StrEnum
def test_init(self):
"""Test the `__init__` method"""
values = self._test_values
o = self.cls(self._name, values=values)
assert o.type is self._datatype
assert o.values is values
assert o.class_rules == (o._rule_values,)
assert o.rules == tuple()
assert o.all_rules == (o._rule_values,)
def test_bad_types(self):
"""Test failure with incorrect types"""
badvalues = self._bad_type_values
e = raises(TypeError, self.cls, 'my_enum', values=badvalues)
assert str(e) == TYPE_ERROR % (
"%s('my_enum') values[1]" % self._cls.__name__,
self._datatype, badvalues[1], self._bad_type)
def test_empty(self):
"""Test that ValueError is raised when list of values is empty"""
badvalues = tuple()
e = raises(ValueError, self.cls, 'empty_enum', values=badvalues)
assert_equal(str(e), "%s('empty_enum'): list of values must not "
"be empty" % self._cls.__name__)
def test_rules_values(self):
"""Test the `_rule_values` method"""
def test_rules_with_passing_rules(self):
"""Test with passing values"""
o = self.cls('my_enum', values=self._test_values)
rule = o._rule_values
dummy = dummy_ugettext(self._translation)
for v in self._test_values:
assert rule(dummy, v) is None
assert dummy.called() is False
def test_rules_with_failing_rules(self):
"""Test with failing values"""
o = self.cls('my_enum', values=self._test_values)
rule = o._rule_values
dummy = dummy_ugettext(self._translation)
for val in self._bad_values:
assert_equal(
rule(dummy, val),
self._translation % dict(values=self._test_values),
)
assert_equal(dummy.message, "must be one of %(values)s")
dummy.reset()
def test_one_value(self):
"""test a special case when we have just one allowed value"""
values = (self._test_values[0], )
o = self.cls('my_enum', values=values)
rule = o._rule_values
dummy = dummy_ugettext(self._single_value_translation)
for val in self._bad_values:
assert_equal(
rule(dummy, val),
self._single_value_translation % dict(values=values),
)
assert_equal(dummy.message, "must be '%(value)s'")
dummy.reset()
class test_StrEnum(EnumChecker):
"""
Test the `ipalib.parameters.StrEnum` class.
"""
_cls = parameters.StrEnum
_name = 'my_strenum'
_datatype = unicode
_test_values = u'Hello', u'naughty', u'nurse!'
_bad_type_values = u'Hello', b'naughty', u'nurse!'
_bad_type = bytes
_translation = u"values='Hello', 'naughty', 'nurse!'"
_bad_values = u'Howdy', u'quiet', u'library!'
_single_value_translation = u"value='Hello'"
def check_int_scalar_conversions(o):
"""
Assure radix prefixes work, str objects fail,
floats (native & string) are truncated,
large magnitude values are promoted to long,
empty strings & invalid numerical representations fail
"""
# Assure invalid inputs raise error
for bad in ['hello', u'hello', True, None, u'', u'.', 8j, ()]:
e = raises(errors.ConversionError, o._convert_scalar, bad)
assert e.name == 'my_number'
# Assure large magnitude values are handled correctly
assert type(o._convert_scalar(sys.maxsize * 2)) == long
assert o._convert_scalar(sys.maxsize * 2) == sys.maxsize * 2
assert o._convert_scalar(unicode(sys.maxsize * 2)) == sys.maxsize * 2
assert o._convert_scalar(long(16)) == 16
# Assure normal conversions produce expected result
assert o._convert_scalar(u'16.99') == 16
assert o._convert_scalar(16.99) == 16
assert o._convert_scalar(u'16') == 16
assert o._convert_scalar(u'0x10') == 16
assert o._convert_scalar(u'020') == 16
assert o._convert_scalar(u'0o20') == 16
class test_IntEnum(EnumChecker):
"""
Test the `ipalib.parameters.IntEnum` class.
"""
_cls = parameters.IntEnum
_name = 'my_intenum'
_datatype = int
_test_values = 1, 2, -3
_bad_type_values = 1, 2.0, -3
_bad_type = float
_translation = u"values=1, 2, 3"
_bad_values = 4, 5, -6
_single_value_translation = u"value=1"
def test_convert_scalar(self):
"""
Test the `ipalib.parameters.IntEnum._convert_scalar` method.
"""
param = self.cls('my_number', values=(1, 2, 3, 4, 5))
check_int_scalar_conversions(param)
class test_Number(ClassChecker):
"""
Test the `ipalib.parameters.Number` class.
"""
_cls = parameters.Number
def test_init(self):
"""
Test the `ipalib.parameters.Number.__init__` method.
"""
o = self.cls('my_number')
assert o.type is type(None)
assert o.password is False
assert o.rules == tuple()
assert o.class_rules == tuple()
assert o.all_rules == tuple()
class test_Int(ClassChecker):
"""
Test the `ipalib.parameters.Int` class.
"""
_cls = parameters.Int
def test_init(self):
"""
Test the `ipalib.parameters.Int.__init__` method.
"""
# Test with no kwargs:
o = self.cls('my_number')
assert o.type == int
assert o.allowed_types == six.integer_types
assert isinstance(o, parameters.Int)
assert o.minvalue == int(MININT)
assert o.maxvalue == int(MAXINT)
# Test when min > max:
e = raises(ValueError, self.cls, 'my_number', minvalue=22, maxvalue=15)
assert str(e) == \
"Int('my_number'): minvalue > maxvalue (minvalue=22, maxvalue=15)"
def test_rule_minvalue(self):
"""
Test the `ipalib.parameters.Int._rule_minvalue` method.
"""
o = self.cls('my_number', minvalue=3)
assert o.minvalue == 3
rule = o._rule_minvalue
translation = u'minvalue=%(minvalue)r'
dummy = dummy_ugettext(translation)
assert dummy.translation is translation
# Test with passing values:
for value in (4, 99, 1001):
assert rule(dummy, value) is None
assert dummy.called() is False
# Test with failing values:
for value in (-1, 0, 2):
assert_equal(
rule(dummy, value),
translation % dict(minvalue=3)
)
assert dummy.message == 'must be at least %(minvalue)d'
assert dummy.called() is True
dummy.reset()
def test_rule_maxvalue(self):
"""
Test the `ipalib.parameters.Int._rule_maxvalue` method.
"""
o = self.cls('my_number', maxvalue=4)
assert o.maxvalue == 4
rule = o._rule_maxvalue
translation = u'maxvalue=%(maxvalue)r'
dummy = dummy_ugettext(translation)
assert dummy.translation is translation
# Test with passing values:
for value in (-1, 0, 4):
assert rule(dummy, value) is None
assert dummy.called() is False
# Test with failing values:
for value in (5, 99, 1009):
assert_equal(
rule(dummy, value),
translation % dict(maxvalue=4)
)
assert dummy.message == 'can be at most %(maxvalue)d'
assert dummy.called() is True
dummy.reset()
def test_convert_scalar(self):
"""
Test the `ipalib.parameters.Int._convert_scalar` method.
"""
param = self.cls('my_number')
check_int_scalar_conversions(param)
class test_Decimal(ClassChecker):
"""
Test the `ipalib.parameters.Decimal` class.
"""
_cls = parameters.Decimal
def test_init(self):
"""
Test the `ipalib.parameters.Decimal.__init__` method.
"""
# Test with no kwargs:
o = self.cls('my_number')
assert o.type is Decimal
assert isinstance(o, parameters.Decimal)
assert o.minvalue is None
assert o.maxvalue is None
# Test when min > max:
e = raises(ValueError, self.cls, 'my_number', minvalue=Decimal('22.5'), maxvalue=Decimal('15.1'))
assert str(e) == \
"Decimal('my_number'): minvalue > maxvalue (minvalue=22.5, maxvalue=15.1)"
def test_rule_minvalue(self):
"""
Test the `ipalib.parameters.Decimal._rule_minvalue` method.
"""
o = self.cls('my_number', minvalue='3.1')
assert o.minvalue == Decimal('3.1')
rule = o._rule_minvalue
translation = u'minvalue=%(minvalue)s'
dummy = dummy_ugettext(translation)
assert dummy.translation is translation
# Test with passing values:
for value in (Decimal('3.2'), Decimal('99.0')):
assert rule(dummy, value) is None
assert dummy.called() is False
# Test with failing values:
for value in (Decimal('-1.2'), Decimal('0.0'), Decimal('3.0')):
assert_equal(
rule(dummy, value),
translation % dict(minvalue=Decimal('3.1'))
)
assert dummy.message == 'must be at least %(minvalue)s'
assert dummy.called() is True
dummy.reset()
def test_rule_maxvalue(self):
"""
Test the `ipalib.parameters.Decimal._rule_maxvalue` method.
"""
o = self.cls('my_number', maxvalue='4.7')
assert o.maxvalue == Decimal('4.7')
rule = o._rule_maxvalue
translation = u'maxvalue=%(maxvalue)r'
dummy = dummy_ugettext(translation)
assert dummy.translation is translation
# Test with passing values:
for value in (Decimal('-1.0'), Decimal('0.1'), Decimal('4.2')):
assert rule(dummy, value) is None
assert dummy.called() is False
# Test with failing values:
for value in (Decimal('5.3'), Decimal('99.9')):
assert_equal(
rule(dummy, value),
translation % dict(maxvalue=Decimal('4.7'))
)
assert dummy.message == 'can be at most %(maxvalue)s'
assert dummy.called() is True
dummy.reset()
def test_precision(self):
"""
Test the `ipalib.parameters.Decimal` precision attribute
"""
# precission is None
param = self.cls('my_number')
for value in (Decimal('0'), Decimal('4.4'), Decimal('4.67')):
assert_equal(
param(value),
value)
# precision is 0
param = self.cls('my_number', precision=0)
for original,expected in ((Decimal('0'), '0'),
(Decimal('1.1'), '1'),
(Decimal('4.67'), '5')):
assert_equal(
str(param(original)),
expected)
# precision is 1
param = self.cls('my_number', precision=1)
for original,expected in ((Decimal('0'), '0.0'),
(Decimal('1.1'), '1.1'),
(Decimal('4.67'), '4.7')):
assert_equal(
str(param(original)),
expected)
# value has too many digits
param = self.cls('my_number', precision=1)
e = raises(ConversionError, param, '123456789012345678901234567890')
assert str(e).startswith("invalid 'my_number': ")
def test_exponential(self):
"""
Test the `ipalib.parameters.Decimal` exponential attribute
"""
param = self.cls('my_number', exponential=True)
for original,expected in ((Decimal('0'), '0'),
(Decimal('1E3'), '1E+3'),
(Decimal('3.4E2'), '3.4E+2')):
assert_equal(
str(param(original)),
expected)
param = self.cls('my_number', exponential=False)
for original,expected in ((Decimal('0'), '0'),
(Decimal('1E3'), '1000'),
(Decimal('3.4E2'), '340')):
assert_equal(
str(param(original)),
expected)
def test_numberclass(self):
"""
Test the `ipalib.parameters.Decimal` numberclass attribute
"""
# test default value: '-Normal', '+Zero', '+Normal'
param = self.cls('my_number')
for value,raises_verror in ((Decimal('0'), False),
(Decimal('-0'), True),
(Decimal('1E8'), False),
(Decimal('-1.1'), False),
(Decimal('-Infinity'), True),
(Decimal('+Infinity'), True),
(Decimal('NaN'), True)):
if raises_verror:
raises(ValidationError, param, value)
else:
param(value)
param = self.cls('my_number', exponential=True,
numberclass=('-Normal', '+Zero', '+Infinity'))
for value,raises_verror in ((Decimal('0'), False),
(Decimal('-0'), True),
(Decimal('1E8'), True),
(Decimal('-1.1'), False),
(Decimal('-Infinity'), True),
(Decimal('+Infinity'), False),
(Decimal('NaN'), True)):
if raises_verror:
raises(ValidationError, param, value)
else:
param(value)
class test_AccessTime(ClassChecker):
"""
Test the `ipalib.parameters.AccessTime` class.
"""
_cls = parameters.AccessTime
def test_init(self):
"""
Test the `ipalib.parameters.AccessTime.__init__` method.
"""
# Test with no kwargs:
o = self.cls('my_time')
assert o.type is unicode
assert isinstance(o, parameters.AccessTime)
assert o.multivalue is False
translation = u'length=%(length)r'
dummy = dummy_ugettext(translation)
assert dummy.translation is translation
rule = o._rule_required
# Check some good rules
for value in (u'absolute 201012161032 ~ 201012161033',
u'periodic monthly week 2 day Sat,Sun 0900-1300',
u'periodic yearly month 4 day 1-31 0800-1400',
u'periodic weekly day 7 0800-1400',
u'periodic daily 0800-1400',
):
assert rule(dummy, value) is None
assert dummy.called() is False
# And some bad ones
for value in (u'absolute 201012161032 - 201012161033',
u'absolute 201012161032 ~',
u'periodic monthly day Sat,Sun 0900-1300',
u'periodical yearly month 4 day 1-31 0800-1400',
u'periodic weekly day 8 0800-1400',
):
raises(ValidationError, o._rule_required, None, value)
def test_create_param():
"""
Test the `ipalib.parameters.create_param` function.
"""
f = parameters.create_param
# Test that Param instances are returned unchanged:
params = (
parameters.Param('one?'),
parameters.Int('two+'),
parameters.Str('three*'),
parameters.Bytes('four'),
)
for p in params:
assert f(p) is p
# Test that the spec creates an Str instance:
for spec in ('one?', 'two+', 'three*', 'four'):
(name, kw) = parameters.parse_param_spec(spec)
p = f(spec)
assert p.param_spec == spec
assert p.name == name
assert p.required is kw['required']
assert p.multivalue is kw['multivalue']
# Test that TypeError is raised when spec is neither a Param nor a str:
if six.PY2:
bad_value = u'one'
else:
bad_value = b'one'
for spec in (bad_value, 42, parameters.Param, parameters.Str):
e = raises(TypeError, f, spec)
assert str(e) == \
TYPE_ERROR % ('spec', (str, parameters.Param), spec, type(spec))
def test_messages():
"""
Test module level message in `ipalib.parameters`.
"""
for name in dir(parameters):
if name.startswith('_'):
continue
attr = getattr(parameters, name)
if not (isclass(attr) and issubclass(attr, parameters.Param)):
continue
assert type(attr.type_error) is str
assert attr.type_error in parameters.__messages
class test_IA5Str(ClassChecker):
"""
Test the `ipalib.parameters.IA5Str` class.
"""
_cls = parameters.IA5Str
def test_convert_scalar(self):
"""
Test the `ipalib.parameters.IA5Str._convert_scalar` method.
"""
o = self.cls('my_str')
mthd = o._convert_scalar
for value in (u'Hello', 42, 1.2):
assert mthd(value) == unicode(value)
bad = ['Helloá']
for value in bad:
e = raises(errors.ConversionError, mthd, value)
assert e.name == 'my_str'
if six.PY2:
assert_equal(e.error, u"The character '\\xc3' is not allowed.")
else:
assert_equal(e.error, u"The character 'á' is not allowed.")
class test_DateTime(ClassChecker):
"""
Test the `ipalib.parameters.DateTime` class.
"""
_cls = parameters.DateTime
def test_init(self):
"""
Test the `ipalib.parameters.DateTime.__init__` method.
"""
# Test with no kwargs:
o = self.cls('my_datetime')
assert o.type is datetime.datetime
assert isinstance(o, parameters.DateTime)
assert o.multivalue is False
# Check full time formats
date = datetime.datetime(1991, 12, 7, 6, 30, 5)
assert date == o.convert(u'19911207063005Z')
assert date == o.convert(u'1991-12-07T06:30:05Z')
assert date == o.convert(u'1991-12-07 06:30:05Z')
# Check time formats without seconds
date = datetime.datetime(1991, 12, 7, 6, 30)
assert date == o.convert(u'1991-12-07T06:30Z')
assert date == o.convert(u'1991-12-07 06:30Z')
# Check date formats
date = datetime.datetime(1991, 12, 7)
assert date == o.convert(u'1991-12-07Z')
# Check some wrong formats
for value in (u'19911207063005',
u'1991-12-07T06:30:05',
u'1991-12-07 06:30:05',
u'1991-12-07T06:30',
u'1991-12-07 06:30',
u'1991-12-07',
u'1991-31-12Z',
u'1991-12-07T25:30:05Z',
):
raises(ConversionError, o.convert, value)