diff options
author | Jason Gerard DeRose <jderose@redhat.com> | 2009-12-09 09:09:53 -0700 |
---|---|---|
committer | Jason Gerard DeRose <jderose@redhat.com> | 2009-12-10 08:29:15 -0700 |
commit | b6e4972e7f6aa08e0392a2cf441b60ab0e7d88b7 (patch) | |
tree | 7e5329a51af169ce34a7d275a1bbd63c1e31c026 /tests/test_ipalib | |
parent | d08b8858ddc3bf6265f6ea8acae6661b9fff5112 (diff) | |
download | freeipa-b6e4972e7f6aa08e0392a2cf441b60ab0e7d88b7.tar.gz freeipa-b6e4972e7f6aa08e0392a2cf441b60ab0e7d88b7.tar.xz freeipa-b6e4972e7f6aa08e0392a2cf441b60ab0e7d88b7.zip |
Take 2: Extensible return values and validation; steps toward a single output_for_cli(); enable more webUI stuff
Diffstat (limited to 'tests/test_ipalib')
-rw-r--r-- | tests/test_ipalib/test_backend.py | 14 | ||||
-rw-r--r-- | tests/test_ipalib/test_crud.py | 131 | ||||
-rw-r--r-- | tests/test_ipalib/test_frontend.py | 206 | ||||
-rw-r--r-- | tests/test_ipalib/test_output.py | 88 | ||||
-rw-r--r-- | tests/test_ipalib/test_parameters.py | 58 | ||||
-rw-r--r-- | tests/test_ipalib/test_text.py | 120 |
6 files changed, 553 insertions, 64 deletions
diff --git a/tests/test_ipalib/test_backend.py b/tests/test_ipalib/test_backend.py index 9ddbbf252..3c29ee35f 100644 --- a/tests/test_ipalib/test_backend.py +++ b/tests/test_ipalib/test_backend.py @@ -176,7 +176,7 @@ class test_Executioner(ClassChecker): takes_options = ('option1?', 'option2?') def execute(self, *args, **options): assert type(args[1]) is tuple - return args + (options,) + return dict(result=args + (options,)) api.register(echo) class good(Command): @@ -198,7 +198,7 @@ class test_Executioner(ClassChecker): """ takes_options = 'name' def execute(self, **options): - return options['name'].upper() + return dict(result=options['name'].upper()) api.register(with_name) api.finalize() @@ -222,13 +222,17 @@ class test_Executioner(ClassChecker): conn = Connection('The connection.', Disconnect()) context.someconn = conn - assert o.execute('echo', arg1, arg2, **options) == (arg1, arg2, options) + assert o.execute('echo', arg1, arg2, **options) == dict( + result=(arg1, arg2, options) + ) assert conn.disconnect.called is True # Make sure destroy_context() was called assert context.__dict__.keys() == [] conn = Connection('The connection.', Disconnect()) context.someconn = conn - assert o.execute('echo', *args, **options) == (arg1, arg2, options) + assert o.execute('echo', *args, **options) == dict( + result=(arg1, arg2, options) + ) assert conn.disconnect.called is True # Make sure destroy_context() was called assert context.__dict__.keys() == [] @@ -251,4 +255,4 @@ class test_Executioner(ClassChecker): # Test with option 'name': conn = Connection('The connection.', Disconnect()) context.someconn = conn - assert o.execute('with_name', name=u'test') == u'TEST' + assert o.execute('with_name', name=u'test') == dict(result=u'TEST') diff --git a/tests/test_ipalib/test_crud.py b/tests/test_ipalib/test_crud.py index a7f49f878..47d51c5dc 100644 --- a/tests/test_ipalib/test_crud.py +++ b/tests/test_ipalib/test_crud.py @@ -34,7 +34,6 @@ class CrudChecker(ClassChecker): """ Return a finalized `ipalib.plugable.API` instance. """ - assert self.cls.__bases__ == (frontend.Method,) (api, home) = get_api() class user(frontend.Object): takes_params = ( @@ -52,6 +51,136 @@ class CrudChecker(ClassChecker): return api +class test_Create(CrudChecker): + """ + Test the `ipalib.crud.Create` class. + """ + + _cls = crud.Create + + def test_get_args(self): + """ + Test the `ipalib.crud.Create.get_args` method. + """ + api = self.get_api() + assert list(api.Method.user_verb.args) == ['uid'] + assert api.Method.user_verb.args.uid.required is True + + def test_get_options(self): + """ + Test the `ipalib.crud.Create.get_options` method. + """ + api = self.get_api() + assert list(api.Method.user_verb.options) == \ + ['givenname', 'sn', 'initials'] + for param in api.Method.user_verb.options(): + assert param.required is True + api = self.get_api(options=('extra?',)) + assert list(api.Method.user_verb.options) == \ + ['givenname', 'sn', 'initials', 'extra'] + assert api.Method.user_verb.options.extra.required is False + + +class test_Update(CrudChecker): + """ + Test the `ipalib.crud.Update` class. + """ + + _cls = crud.Update + + def test_get_args(self): + """ + Test the `ipalib.crud.Update.get_args` method. + """ + api = self.get_api() + assert list(api.Method.user_verb.args) == ['uid'] + assert api.Method.user_verb.args.uid.required is True + + def test_get_options(self): + """ + Test the `ipalib.crud.Update.get_options` method. + """ + api = self.get_api() + assert list(api.Method.user_verb.options) == \ + ['givenname', 'sn', 'initials'] + for param in api.Method.user_verb.options(): + assert param.required is False + + +class test_Retrieve(CrudChecker): + """ + Test the `ipalib.crud.Retrieve` class. + """ + + _cls = crud.Retrieve + + def test_get_args(self): + """ + Test the `ipalib.crud.Retrieve.get_args` method. + """ + api = self.get_api() + assert list(api.Method.user_verb.args) == ['uid'] + assert api.Method.user_verb.args.uid.required is True + + def test_get_options(self): + """ + Test the `ipalib.crud.Retrieve.get_options` method. + """ + api = self.get_api() + assert list(api.Method.user_verb.options) == [] + assert len(api.Method.user_verb.options) == 0 + + +class test_Delete(CrudChecker): + """ + Test the `ipalib.crud.Delete` class. + """ + + _cls = crud.Delete + + def test_get_args(self): + """ + Test the `ipalib.crud.Delete.get_args` method. + """ + api = self.get_api() + assert list(api.Method.user_verb.args) == ['uid'] + assert api.Method.user_verb.args.uid.required is True + + def test_get_options(self): + """ + Test the `ipalib.crud.Delete.get_options` method. + """ + api = self.get_api() + assert list(api.Method.user_verb.options) == [] + assert len(api.Method.user_verb.options) == 0 + + +class test_Search(CrudChecker): + """ + Test the `ipalib.crud.Search` class. + """ + + _cls = crud.Search + + def test_get_args(self): + """ + Test the `ipalib.crud.Search.get_args` method. + """ + api = self.get_api() + assert list(api.Method.user_verb.args) == ['criteria'] + assert api.Method.user_verb.args.criteria.required is False + + def test_get_options(self): + """ + Test the `ipalib.crud.Search.get_options` method. + """ + api = self.get_api() + assert list(api.Method.user_verb.options) == \ + ['givenname', 'sn', 'uid', 'initials'] + for param in api.Method.user_verb.options(): + assert param.required is False + + class test_CrudBackend(ClassChecker): """ Test the `ipalib.crud.CrudBackend` class. diff --git a/tests/test_ipalib/test_frontend.py b/tests/test_ipalib/test_frontend.py index 71d902403..4cdb8774e 100644 --- a/tests/test_ipalib/test_frontend.py +++ b/tests/test_ipalib/test_frontend.py @@ -27,6 +27,7 @@ from tests.util import assert_equal from ipalib.constants import TYPE_ERROR from ipalib.base import NameSpace from ipalib import frontend, backend, plugable, errors, parameters, config +from ipalib import output def test_RULE_FLAG(): assert frontend.RULE_FLAG == 'validation_rule' @@ -317,6 +318,73 @@ class test_Command(ClassChecker): assert ns.files.required is False assert ns.files.multivalue is True + def test_output(self): + """ + Test the ``ipalib.frontend.Command.output`` instance attribute. + """ + inst = self.cls() + assert inst.output is None + inst.finalize() + assert type(inst.output) is plugable.NameSpace + assert list(inst.output) == ['result'] + assert type(inst.output.result) is output.Output + + def test_iter_output(self): + """ + Test the ``ipalib.frontend.Command._iter_output`` instance attribute. + """ + class Example(self.cls): + pass + inst = Example() + + inst.has_output = tuple() + assert list(inst._iter_output()) == [] + + wrong = ['hello', 'world'] + inst.has_output = wrong + e = raises(TypeError, list, inst._iter_output()) + assert str(e) == 'Example.has_output: need a %r; got a %r: %r' % ( + tuple, list, wrong + ) + + wrong = ('hello', 17) + inst.has_output = wrong + e = raises(TypeError, list, inst._iter_output()) + assert str(e) == 'Example.has_output[1]: need a %r; got a %r: %r' % ( + (str, output.Output), int, 17 + ) + + okay = ('foo', output.Output('bar'), 'baz') + inst.has_output = okay + items = list(inst._iter_output()) + assert len(items) == 3 + assert list(o.name for o in items) == ['foo', 'bar', 'baz'] + for o in items: + assert type(o) is output.Output + + def test_soft_validate(self): + """ + Test the `ipalib.frontend.Command.soft_validate` method. + """ + class user_add(frontend.Command): + takes_args = parameters.Str('uid', + normalizer=lambda value: value.lower(), + default_from=lambda givenname, sn: givenname[0] + sn, + ) + + takes_options = ('givenname', 'sn') + + cmd = user_add() + cmd.finalize() + assert list(cmd.params) == ['givenname', 'sn', 'uid'] + ret = cmd.soft_validate({}) + assert len(ret['values']) == 0 + assert len(ret['errors']) == 3 + assert cmd.soft_validate(dict(givenname=u'First', sn=u'Last')) == dict( + values=dict(givenname=u'First', sn=u'Last', uid=u'flast'), + errors=dict(), + ) + def test_convert(self): """ Test the `ipalib.frontend.Command.convert` method. @@ -517,6 +585,84 @@ class test_Command(ClassChecker): assert o.run.im_func is self.cls.run.im_func assert ('forward', args, kw) == o.run(*args, **kw) + def test_validate_output(self): + """ + Test the `ipalib.frontend.Command.validate_output` method. + """ + class Example(self.cls): + has_output = ('foo', 'bar', 'baz') + + inst = Example() + inst.finalize() + + # Test with wrong type: + wrong = ('foo', 'bar', 'baz') + e = raises(TypeError, inst.validate_output, wrong) + assert str(e) == '%s.validate_output(): need a %r; got a %r: %r' % ( + 'Example', dict, tuple, wrong + ) + + # Test with a missing keys: + wrong = dict(bar='hello') + e = raises(ValueError, inst.validate_output, wrong) + assert str(e) == '%s.validate_output(): missing keys %r in %r' % ( + 'Example', ['baz', 'foo'], wrong + ) + + # Test with extra keys: + wrong = dict(foo=1, bar=2, baz=3, fee=4, azz=5) + e = raises(ValueError, inst.validate_output, wrong) + assert str(e) == '%s.validate_output(): unexpected keys %r in %r' % ( + 'Example', ['azz', 'fee'], wrong + ) + + # Test with per item type validation: + class Complex(self.cls): + has_output = ( + output.Output('foo', int), + output.Output('bar', list), + ) + inst = Complex() + inst.finalize() + + wrong = dict(foo=17.9, bar=[18]) + e = raises(TypeError, inst.validate_output, wrong) + assert str(e) == '%s:\n output[%r]: need %r; got %r: %r' % ( + 'Complex.validate_output()', 'foo', int, float, 17.9 + ) + + wrong = dict(foo=18, bar=17) + e = raises(TypeError, inst.validate_output, wrong) + assert str(e) == '%s:\n output[%r]: need %r; got %r: %r' % ( + 'Complex.validate_output()', 'bar', list, int, 17 + ) + + class Subclass(output.ListOfEntries): + pass + + # Test nested validation: + class nested(self.cls): + has_output = ( + output.Output('hello', int), + Subclass('world'), + ) + inst = nested() + inst.finalize() + okay = dict(foo='bar') + nope = ('aye', 'bee') + + wrong = dict(hello=18, world=[okay, nope, okay]) + e = raises(TypeError, inst.validate_output, wrong) + assert str(e) == output.emsg % ( + 'nested', 'Subclass', 'world', 1, dict, tuple, nope + ) + + wrong = dict(hello=18, world=[okay, okay, okay, okay, nope]) + e = raises(TypeError, inst.validate_output, wrong) + assert str(e) == output.emsg % ( + 'nested', 'Subclass', 'world', 4, dict, tuple, nope + ) + class test_LocalOrRemote(ClassChecker): """ @@ -544,32 +690,46 @@ class test_LocalOrRemote(ClassChecker): takes_args = 'key?' def forward(self, *args, **options): - return ('forward', args, options) + return dict(result=('forward', args, options)) def execute(self, *args, **options): - return ('execute', args, options) + return dict(result=('execute', args, options)) # Test when in_server=False: (api, home) = create_test_api(in_server=False) api.register(example) api.finalize() cmd = api.Command.example - assert cmd() == ('execute', (None,), dict(server=False)) - assert cmd(u'var') == ('execute', (u'var',), dict(server=False)) - assert cmd(server=True) == ('forward', (None,), dict(server=True)) - assert cmd(u'var', server=True) == \ - ('forward', (u'var',), dict(server=True)) + assert cmd() == dict( + result=('execute', (None,), dict(server=False)) + ) + assert cmd(u'var') == dict( + result=('execute', (u'var',), dict(server=False)) + ) + assert cmd(server=True) == dict( + result=('forward', (None,), dict(server=True)) + ) + assert cmd(u'var', server=True) == dict( + result=('forward', (u'var',), dict(server=True)) + ) # Test when in_server=True (should always call execute): (api, home) = create_test_api(in_server=True) api.register(example) api.finalize() cmd = api.Command.example - assert cmd() == ('execute', (None,), dict(server=False)) - assert cmd(u'var') == ('execute', (u'var',), dict(server=False)) - assert cmd(server=True) == ('execute', (None,), dict(server=True)) - assert cmd(u'var', server=True) == \ - ('execute', (u'var',), dict(server=True)) + assert cmd() == dict( + result=('execute', (None,), dict(server=False)) + ) + assert cmd(u'var') == dict( + result=('execute', (u'var',), dict(server=False)) + ) + assert cmd(server=True) == dict( + result=('execute', (None,), dict(server=True)) + ) + assert cmd(u'var', server=True) == dict( + result=('execute', (u'var',), dict(server=True)) + ) class test_Object(ClassChecker): @@ -834,6 +994,26 @@ class test_Method(ClassChecker): """ _cls = frontend.Method + def get_api(self, args=tuple(), options=tuple()): + """ + Return a finalized `ipalib.plugable.API` instance. + """ + (api, home) = create_test_api() + class user(frontend.Object): + takes_params = ( + 'givenname', + 'sn', + frontend.Param('uid', primary_key=True), + 'initials', + ) + class user_verb(self.cls): + takes_args = args + takes_options = options + api.register(user) + api.register(user_verb) + api.finalize() + return api + def test_class(self): """ Test the `ipalib.frontend.Method` class. @@ -856,6 +1036,8 @@ class test_Method(ClassChecker): assert frontend.Attribute.implemented_by(o) + + class test_Property(ClassChecker): """ Test the `ipalib.frontend.Property` class. diff --git a/tests/test_ipalib/test_output.py b/tests/test_ipalib/test_output.py new file mode 100644 index 000000000..ceb825c44 --- /dev/null +++ b/tests/test_ipalib/test_output.py @@ -0,0 +1,88 @@ +# Authors: +# Jason Gerard DeRose <jderose@redhat.com> +# +# Copyright (C) 2009 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 + +""" +Test the `ipalib.output` module. +""" + +from tests.util import raises, ClassChecker +from ipalib import output +from ipalib.frontend import Command + +class test_Output(ClassChecker): + """ + Test the `ipalib.output.Output` class. + """ + + _cls = output.Output + + def test_init(self): + """ + Test the `ipalib.output.Output.__init__` method. + """ + o = self.cls('result') + assert o.name == 'result' + assert o.type is None + assert o.doc is None + + def test_repr(self): + """ + Test the `ipalib.output.Output.__repr__` method. + """ + o = self.cls('aye') + assert repr(o) == "Output('aye', None, None)" + o = self.cls('aye', type=int, doc='An A, aye?') + assert repr(o) == "Output('aye', %r, 'An A, aye?')" % int + + class Entry(self.cls): + pass + o = Entry('aye') + assert repr(o) == "Entry('aye', None, None)" + o = Entry('aye', type=int, doc='An A, aye?') + assert repr(o) == "Entry('aye', %r, 'An A, aye?')" % int + + +class test_ListOfEntries(ClassChecker): + """ + Test the `ipalib.output.ListOfEntries` class. + """ + + _cls = output.ListOfEntries + + def test_validate(self): + """ + Test the `ipalib.output.ListOfEntries.validate` method. + """ + class example(Command): + pass + cmd = example() + inst = self.cls('stuff') + + okay = dict(foo='bar') + nope = ('aye', 'bee') + + e = raises(TypeError, inst.validate, cmd, [okay, okay, nope]) + assert str(e) == output.emsg % ( + 'example', 'ListOfEntries', 'stuff', 2, dict, tuple, nope + ) + + e = raises(TypeError, inst.validate, cmd, [nope, okay, nope]) + assert str(e) == output.emsg % ( + 'example', 'ListOfEntries', 'stuff', 0, dict, tuple, nope + ) diff --git a/tests/test_ipalib/test_parameters.py b/tests/test_ipalib/test_parameters.py index d1c5f7f92..1f0a7aec8 100644 --- a/tests/test_ipalib/test_parameters.py +++ b/tests/test_ipalib/test_parameters.py @@ -178,8 +178,8 @@ class test_Param(ClassChecker): # Test default kwarg values: assert o.cli_name is name - assert o.label is None - assert o.doc == '' + assert o.label == '<my_param>' + assert o.doc == '<my_param>' assert o.required is True assert o.multivalue is False assert o.primary_key is False @@ -195,6 +195,16 @@ class test_Param(ClassChecker): assert o.exclude is None assert o.flags == frozenset() + # Test that doc defaults from label: + o = self.cls('my_param', doc='Hello world') + assert o.label == '<my_param>' + assert o.doc == '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): @@ -352,50 +362,6 @@ class test_Param(ClassChecker): assert clone.param_spec == 'my_param' assert clone.name == 'my_param' - def test_get_label(self): - """ - Test the `ipalib.parameters.get_label` method. - """ - context = request.context - cli_name = 'the_cli_name' - message = 'The Label' - label = lambda _: _(message) - o = self.cls('name', cli_name=cli_name, label=label) - assert o.label is label - - ## Scenario 1: label=callable (a lambda form) - - # Test with no context.ugettext: - assert not hasattr(context, 'ugettext') - assert_equal(o.get_label(), u'The Label') - - # Test with dummy context.ugettext: - assert not hasattr(context, 'ugettext') - dummy = dummy_ugettext() - context.ugettext = dummy - assert o.get_label() is dummy.translation - assert dummy.message is message - del context.ugettext - - ## Scenario 2: label=None - o = self.cls('name', cli_name=cli_name) - assert o.label is None - - # Test with no context.ugettext: - assert not hasattr(context, 'ugettext') - assert_equal(o.get_label(), u'the_cli_name') - - # Test with dummy context.ugettext: - assert not hasattr(context, 'ugettext') - dummy = dummy_ugettext() - context.ugettext = dummy - assert_equal(o.get_label(), u'the_cli_name') - assert not hasattr(dummy, 'message') - - # Cleanup - del context.ugettext - assert not hasattr(context, 'ugettext') - def test_convert(self): """ Test the `ipalib.parameters.Param.convert` method. diff --git a/tests/test_ipalib/test_text.py b/tests/test_ipalib/test_text.py new file mode 100644 index 000000000..924534a03 --- /dev/null +++ b/tests/test_ipalib/test_text.py @@ -0,0 +1,120 @@ +# Authors: +# Jason Gerard DeRose <jderose@redhat.com> +# +# Copyright (C) 2009 Red Hat +# see file 'COPYING' for use and warranty contextrmation +# +# 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 + +""" +Test the `ipalib.text` module. +""" + +from tests.util import raises, assert_equal +from tests.data import utf8_bytes, unicode_str +from ipalib import text + +singular = '%(count)d goose makes a %(dish)s' +plural = '%(count)d geese make a %(dish)s' + + +class test_LazyText(object): + + klass = text.LazyText + + def test_init(self): + inst = self.klass('foo', 'bar') + assert inst.domain == 'foo' + assert inst.localedir == 'bar' + + +class test_Gettext(object): + + klass = text.Gettext + + def test_init(self): + inst = self.klass(utf8_bytes, 'foo', 'bar') + assert inst.domain == 'foo' + assert inst.localedir == 'bar' + assert inst.msg is utf8_bytes + + def test_unicode(self): + inst = self.klass(utf8_bytes, 'foo', 'bar') + assert unicode(inst) == unicode_str + + def test_mod(self): + inst = self.klass('hello %(adj)s nurse', 'foo', 'bar') + assert inst % dict(adj='naughty', stuff='junk') == 'hello naughty nurse' + + +class test_NGettext(object): + + klass = text.NGettext + + def test_init(self): + inst = self.klass(singular, plural, 'foo', 'bar') + assert inst.singular is singular + assert inst.plural is plural + assert inst.domain == 'foo' + assert inst.localedir == 'bar' + + def test_call(self): + inst = self.klass(singular, plural, 'foo', 'bar') + assert inst(0) == plural + assert inst(1) == singular + assert inst(2) == plural + assert inst(3) == plural + + def test_mod(self): + inst = self.klass(singular, plural, 'foo', 'bar') + assert inst % dict(count=0, dish='frown') == '0 geese make a frown' + assert inst % dict(count=1, dish='stew') == '1 goose makes a stew' + assert inst % dict(count=2, dish='pie') == '2 geese make a pie' + + +class test_gettext_factory(object): + + klass = text.gettext_factory + + def test_init(self): + inst = self.klass('foo', 'bar') + assert inst.domain == 'foo' + assert inst.localedir == 'bar' + + def test_call(self): + inst = self.klass('foo', 'bar') + g = inst(utf8_bytes) + assert type(g) is text.Gettext + assert g.msg is utf8_bytes + assert g.domain == 'foo' + assert g.localedir == 'bar' + + +class test_ngettext_factory(object): + + klass = text.ngettext_factory + + def test_init(self): + inst = self.klass('foo', 'bar') + assert inst.domain == 'foo' + assert inst.localedir == 'bar' + + def test_call(self): + inst = self.klass('foo', 'bar') + ng = inst(singular, plural, 7) + assert type(ng) is text.NGettext + assert ng.singular is singular + assert ng.plural is plural + assert ng.domain == 'foo' + assert ng.localedir == 'bar' |