mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-01-16 11:21:56 -06:00
092dd8db12
Having float type as a base type for floating point parameters in ipalib introduces several issues, e.g. problem with representation or value comparison. Python language provides a Decimal type which help overcome these issues. This patch replaces a float type and Float parameter with a decimal.Decimal type in Decimal parameter. A precision attribute was added to Decimal parameter that can be used to limit a number of decimal places in parameter representation. This approach fixes a problem with API.txt validation where comparison of float values may fail on different architectures due to float representation error. In order to safely transfer the parameter value over RPC it is being converted to string which is then converted back to decimal.Decimal number on a server side. https://fedorahosted.org/freeipa/ticket/2260
1483 lines
51 KiB
Python
1483 lines
51 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.
|
|
"""
|
|
|
|
import re
|
|
import sys
|
|
from types import NoneType
|
|
from decimal import Decimal
|
|
from inspect import isclass
|
|
from tests.util import raises, ClassChecker, read_only
|
|
from tests.util import dummy_ugettext, assert_equal
|
|
from tests.data import binary_bytes, utf8_bytes, unicode_str
|
|
from ipalib import parameters, text, errors, config
|
|
from ipalib.constants import TYPE_ERROR, CALLABLE_ERROR, NULLS
|
|
from ipalib.errors import ValidationError
|
|
from ipalib import _
|
|
from xmlrpclib import MAXINT, MININT
|
|
|
|
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)
|
|
|
|
def test_repr(self):
|
|
"""
|
|
Test the `ipalib.parameters.DefaultFrom.__repr__` method.
|
|
"""
|
|
def stuff(one, two):
|
|
pass
|
|
|
|
o = self.cls(stuff)
|
|
assert repr(o) == "DefaultFrom(stuff, 'one', 'two')"
|
|
|
|
o = self.cls(stuff, 'aye', 'bee', 'see')
|
|
assert repr(o) == "DefaultFrom(stuff, 'aye', 'bee', 'see')"
|
|
|
|
cb = lambda first, last: first[0] + last
|
|
|
|
o = self.cls(cb)
|
|
assert repr(o) == "DefaultFrom(<lambda>, 'first', 'last')"
|
|
|
|
o = self.cls(cb, 'aye', 'bee', 'see')
|
|
assert repr(o) == "DefaultFrom(<lambda>, '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:
|
|
e = raises(TypeError, f, u'name?')
|
|
assert str(e) == TYPE_ERROR % ('spec', str, u'name?', unicode)
|
|
|
|
|
|
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.create_default is None
|
|
assert o._get_default 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
|
|
assert o.csv is False
|
|
assert o.csv_separator == ','
|
|
assert o.csv_skipspace is True
|
|
|
|
# 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 Subclass(self.cls):
|
|
kwargs = self.cls.kwargs + (
|
|
('convert', callable, None),
|
|
)
|
|
e = raises(ValueError, Subclass, name)
|
|
assert str(e) == "kwarg 'convert' conflicts with attribute on Subclass"
|
|
|
|
# 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 default_from and
|
|
# create_default:
|
|
e = raises(ValueError, self.cls, 'my_param',
|
|
default_from=lambda first, last: first[0] + last,
|
|
create_default=lambda **kw: 'The Default'
|
|
)
|
|
assert str(e) == '%s: cannot have both %r and %r' % (
|
|
"Param('my_param')", 'default_from', 'create_default',
|
|
)
|
|
|
|
# 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 ValueError is raised if csv is set and multivalue is not set:
|
|
e = raises(ValueError, self.cls, 'my_param', csv=True)
|
|
assert str(e) == '%s: cannot have csv without multivalue' % "Param('my_param')"
|
|
|
|
# Test that _get_default gets set:
|
|
call1 = lambda first, last: first[0] + last
|
|
call2 = lambda **kw: 'The Default'
|
|
o = self.cls('my_param', default_from=call1)
|
|
assert o.default_from.callback is call1
|
|
assert o._get_default is o.default_from
|
|
o = self.cls('my_param', create_default=call2)
|
|
assert o.create_default is call2
|
|
assert o._get_default is call2
|
|
|
|
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', required=False)"
|
|
o = self.cls('name', multivalue=True)
|
|
assert repr(o) == "Param('name', multivalue=True)"
|
|
|
|
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) is 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) 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(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:
|
|
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 == 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
|
|
e = raises(errors.ConversionError, o._convert_scalar, 'hello', index=17)
|
|
|
|
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, 'cli')
|
|
assert e.name == 'my_param'
|
|
|
|
# Test in default state that cli_name gets returned in the exception
|
|
# when context == 'cli'
|
|
o = self.cls('my_param', cli_name='short')
|
|
e = raises(errors.RequirementError, o.validate, None, 'cli')
|
|
assert e.name == 'short'
|
|
|
|
# Test with required=False
|
|
o = self.cls('my_param', required=False)
|
|
assert o.required is False
|
|
assert o.validate(None, 'cli') 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, 'cli')
|
|
assert_equal(e.name, 'my_param')
|
|
|
|
# Test with multivalue=True:
|
|
o = self.cls('my_param', multivalue=True)
|
|
e = raises(TypeError, o.validate, [], 'cli')
|
|
assert str(e) == TYPE_ERROR % ('value', tuple, [], list)
|
|
e = raises(ValueError, o.validate, tuple(), 'cli')
|
|
assert str(e) == 'value: empty tuple must be converted to None'
|
|
|
|
# Test with wrong (scalar) type:
|
|
e = raises(ValidationError, o.validate, (None, None, 42, None), 'cli')
|
|
assert str(e) == 'invalid %s' % (TYPE_ERROR % ('\'my_param\'', NoneType, 42, int))
|
|
o = self.cls('my_param')
|
|
e = raises(ValidationError, o.validate, 'Hello', 'cli')
|
|
assert str(e) == 'invalid %s' % (TYPE_ERROR % ('\'my_param\'', NoneType, '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, 'cli') 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, 'cli')
|
|
assert e.name == 'example'
|
|
assert e.error == u'no good'
|
|
assert e.index is None
|
|
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), 'cli') 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), 'cli')
|
|
assert e.name == 'multi_example'
|
|
assert e.error == u'this one is not good'
|
|
assert e.index == 0
|
|
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(ValidationError, o._validate_scalar, 0)
|
|
assert str(e) == 'invalid %s' % (TYPE_ERROR % ('\'my_param\'', bool, 0, int))
|
|
e = raises(ValidationError, o._validate_scalar, 'Hi', index=4)
|
|
assert str(e) == 'invalid %s' % (TYPE_ERROR % ('\'my_param\'', bool, 'Hi', str))
|
|
e = raises(TypeError, o._validate_scalar, True, index=3.0)
|
|
assert str(e) == TYPE_ERROR % ('index', int, 3.0, float)
|
|
|
|
# Test with passing rule:
|
|
assert o._validate_scalar(True, index=None) is None
|
|
assert o._validate_scalar(False, index=None) 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'
|
|
assert e.index is None
|
|
e = raises(errors.ValidationError, o._validate_scalar, False, index=2)
|
|
assert e.name == 'my_param'
|
|
assert e.error == u'this describes the error'
|
|
assert e.index == 2
|
|
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):
|
|
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
|
|
|
|
# Test with create_default:
|
|
o = Str('my_str',
|
|
normalizer=PassThrough(),
|
|
default=u'Static Default',
|
|
create_default=lambda **kw: u'The created default',
|
|
)
|
|
default = o.get_default(first=u'john', last='doe')
|
|
assert_equal(default, u'The created default')
|
|
assert o._convert_scalar.value is default
|
|
assert o.normalizer.value is default
|
|
|
|
def test_csv_normalize(self):
|
|
"""
|
|
Test the `ipalib.parameters.Param.normalize` method with csv.
|
|
"""
|
|
o = self.cls('my_list+', csv=True)
|
|
n = o.normalize('a,b')
|
|
assert type(n) is tuple
|
|
assert len(n) is 2
|
|
|
|
n = o.normalize('bar, "hi, there",foo')
|
|
assert type(n) is tuple
|
|
assert len(n) is 3
|
|
|
|
def test_csv_normalize_separator(self):
|
|
"""
|
|
Test the `ipalib.parameters.Param.normalize` method with csv and a separator.
|
|
"""
|
|
o = self.cls('my_list+', csv=True, csv_separator='|')
|
|
|
|
n = o.normalize('a')
|
|
assert type(n) is tuple
|
|
assert len(n) is 1
|
|
|
|
n = o.normalize('a|b')
|
|
assert type(n) is tuple
|
|
assert len(n) is 2
|
|
|
|
def test_csv_normalize_skipspace(self):
|
|
"""
|
|
Test the `ipalib.parameters.Param.normalize` method with csv without skipping spaces.
|
|
"""
|
|
o = self.cls('my_list+', csv=True, csv_skipspace=False)
|
|
|
|
n = o.normalize('a')
|
|
assert type(n) is tuple
|
|
assert len(n) is 1
|
|
|
|
n = o.normalize('a, "b,c", d')
|
|
assert type(n) is tuple
|
|
# the output w/o skipspace is ['a',' "b','c"',' d']
|
|
assert len(n) is 4
|
|
|
|
|
|
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, NoneType)
|
|
|
|
# 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 NoneType
|
|
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
|
|
|
|
# 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.iteritems():
|
|
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 str
|
|
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.iteritems():
|
|
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 ('abc', 'four', '12345'):
|
|
assert rule(dummy, value) is None
|
|
assert dummy.called() is False
|
|
|
|
# Test with failing values:
|
|
for value in ('', 'a', '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 ('ab', '123', 'four'):
|
|
assert rule(dummy, value) is None
|
|
assert dummy.called() is False
|
|
|
|
# Test with failing values:
|
|
for value in ('12345', '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 ('1234', 'four'):
|
|
assert rule(dummy, value) is None
|
|
assert dummy.called() is False
|
|
|
|
# Test with failing values:
|
|
for value in ('ab', '123', '12345', '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 = '\w+$'
|
|
r = re.compile(pat)
|
|
assert r.match('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 ('HELLO', 'hello', 'Hello_World'):
|
|
assert rule(dummy, value) is None
|
|
assert dummy.called() is False
|
|
|
|
# Test with failing values:
|
|
for value in ('Hello!', '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, 'Hello', dict(one=1), utf8_bytes]
|
|
for value in bad:
|
|
e = raises(errors.ConversionError, mthd, value)
|
|
assert e.name == 'my_str'
|
|
assert e.index is None
|
|
assert_equal(unicode(e.error), u'must be Unicode text')
|
|
e = raises(errors.ConversionError, mthd, value, index=18)
|
|
assert e.name == 'my_str'
|
|
assert e.index == 18
|
|
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 e.index is None
|
|
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)
|
|
assert r1.match(unicode_str) is 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 e.index is None
|
|
assert o._convert_scalar([u'one', u'one']) == u'one'
|
|
assert o._convert_scalar(u'one') == u'one'
|
|
|
|
|
|
class test_StrEnum(ClassChecker):
|
|
"""
|
|
Test the `ipalib.parameters.StrEnum` class.
|
|
"""
|
|
_cls = parameters.StrEnum
|
|
|
|
def test_init(self):
|
|
"""
|
|
Test the `ipalib.parameters.StrEnum.__init__` method.
|
|
"""
|
|
values = (u'Hello', u'naughty', u'nurse!')
|
|
o = self.cls('my_strenum', values=values)
|
|
assert o.type is unicode
|
|
assert o.values is values
|
|
assert o.class_rules == (o._rule_values,)
|
|
assert o.rules == tuple()
|
|
assert o.all_rules == (o._rule_values,)
|
|
|
|
badvalues = (u'Hello', 'naughty', u'nurse!')
|
|
e = raises(TypeError, self.cls, 'my_enum', values=badvalues)
|
|
assert str(e) == TYPE_ERROR % (
|
|
"StrEnum('my_enum') values[1]", unicode, 'naughty', str
|
|
)
|
|
|
|
def test_rules_values(self):
|
|
"""
|
|
Test the `ipalib.parameters.StrEnum._rule_values` method.
|
|
"""
|
|
values = (u'Hello', u'naughty', u'nurse!')
|
|
o = self.cls('my_enum', values=values)
|
|
rule = o._rule_values
|
|
translation = u'values=%(values)s'
|
|
dummy = dummy_ugettext(translation)
|
|
|
|
# Test with passing values:
|
|
for v in values:
|
|
assert rule(dummy, v) is None
|
|
assert dummy.called() is False
|
|
|
|
# Test with failing values:
|
|
for val in (u'Howdy', u'quiet', u'library!'):
|
|
assert_equal(
|
|
rule(dummy, val),
|
|
translation % dict(values=values),
|
|
)
|
|
assert_equal(dummy.message, 'must be one of %(values)r')
|
|
dummy.reset()
|
|
|
|
|
|
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 NoneType
|
|
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 is int
|
|
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.
|
|
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
|
|
"""
|
|
o = self.cls('my_number')
|
|
# Assure invalid inputs raise error
|
|
for bad in ['hello', u'hello', True, None, u'', u'.']:
|
|
e = raises(errors.ConversionError, o._convert_scalar, bad)
|
|
assert e.name == 'my_number'
|
|
assert e.index is None
|
|
# Assure large magnatude values are handled correctly
|
|
assert type(o._convert_scalar(sys.maxint*2)) == long
|
|
assert o._convert_scalar(sys.maxint*2) == sys.maxint*2
|
|
assert o._convert_scalar(unicode(sys.maxint*2)) == sys.maxint*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
|
|
|
|
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()
|
|
|
|
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',
|
|
):
|
|
e = 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 is 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:
|
|
for spec in (u'one', 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'
|
|
assert e.index is None
|
|
assert_equal(e.error, "The character \''\\xc3'\' is not allowed.")
|