From 29f243bf4e2f6cd82362849872d1481dab8543b8 Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Thu, 17 Dec 2009 06:16:18 -0700 Subject: Fuzzy feelings --- tests/test_util.py | 92 ++++++- tests/test_xmlrpc/objectclasses.py | 29 ++- tests/test_xmlrpc/test_group_plugin.py | 393 ++++++++++++++++++----------- tests/test_xmlrpc/test_host_plugin.py | 84 +++--- tests/test_xmlrpc/test_hostgroup_plugin.py | 120 ++++----- tests/test_xmlrpc/test_user_plugin.py | 223 ++++++++-------- tests/test_xmlrpc/xmlrpc_test.py | 46 ++-- tests/util.py | 61 ++++- 8 files changed, 653 insertions(+), 395 deletions(-) (limited to 'tests') diff --git a/tests/test_util.py b/tests/test_util.py index b2a53101..eca2d672 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -21,6 +21,7 @@ Test the `tests.util` module. """ +import re import util from util import raises, TYPE, VALUE, LEN, KEYS @@ -48,12 +49,99 @@ class Prop(object): prop = property(__get_prop, __set_prop, __del_prop) +class test_Fuzzy(object): + klass = util.Fuzzy + + def test_init(self): + inst = self.klass() + assert inst.regex is None + assert inst.type is None + assert inst.test is None + assert inst.re is None + + inst = self.klass('(foo|bar)') + assert inst.regex == '(foo|bar)' + assert inst.type is unicode + assert inst.test is None + assert isinstance(inst.re, re._pattern_type) + + inst = self.klass('(foo|bar)', type=str) + assert inst.regex == '(foo|bar)' + assert inst.type is str + assert inst.test is None + assert isinstance(inst.re, re._pattern_type) + + t = lambda other: other > 500 + + inst = self.klass(test=t) + assert inst.regex is None + assert inst.type is None + assert inst.test is t + assert inst.re is None + + inst = self.klass(type=(int, float), test=t) + assert inst.regex is None + assert inst.type == (int, float) + assert inst.test is t + assert inst.re is None + + def test_repr(self): + s = 'Fuzzy(regex=%r, type=%r, test=%r)' + t = lambda other: 0.0 <= other <= 1.0 + + inst = self.klass() + assert repr(inst) == s % (None, None, None) + + inst = self.klass('foo') + assert repr(inst) == s % ('foo', unicode, None) + + inst = self.klass(type=(int, float)) + assert repr(inst) == s % (None, (int, float), None) + + inst = self.klass(type=(int, float), test=t) + assert repr(inst) == s % (None, (int, float), t) + + inst = self.klass(test=t) + assert repr(inst) == s % (None, None, t) + + def test_eq(self): + assert (self.klass('bar') == u'foobar') is True + assert (self.klass('^bar') == u'foobar') is False + assert (self.klass('bar', type=str) == u'foobar') is False + + assert ('18' == self.klass()) is True + assert ('18' == self.klass(type=int)) is False + assert (18 == self.klass(type=int)) is True + assert ('18' == self.klass(type=(int, str))) is True + + assert (self.klass() == '18') is True + assert (self.klass(type=int) == '18') is False + assert (self.klass(type=int) == 18) is True + assert (self.klass(type=(int, str)) == '18') is True + + t = lambda other: other.endswith('bar') + assert (self.klass(test=t) == 'foobar') is True + assert (self.klass(test=t, type=unicode) == 'foobar') is False + assert (self.klass(test=t) == 'barfoo') is False + + assert (False == self.klass()) is True + assert (True == self.klass()) is True + assert (None == self.klass()) is True + + def test_assert_deepequal(): f = util.assert_deepequal # Test with good scalar values: - f(u'hello', u'hello', 'foo') - f(18, 18, 'foo') + f(u'hello', u'hello') + f(util.Fuzzy(), u'hello') + f(util.Fuzzy(type=unicode), u'hello') + f(util.Fuzzy('ell'), u'hello') + f(util.Fuzzy(test=lambda other: other.endswith('llo')), u'hello') + f(18, 18) + f(util.Fuzzy(), 18) + f(util.Fuzzy(type=int), 18) + f(util.Fuzzy(type=(int, float), test=lambda other: other > 17.9), 18) # Test with bad scalar values: e = raises(AssertionError, f, u'hello', u'world', 'foo') diff --git a/tests/test_xmlrpc/objectclasses.py b/tests/test_xmlrpc/objectclasses.py index 58a3671e..b2d88222 100644 --- a/tests/test_xmlrpc/objectclasses.py +++ b/tests/test_xmlrpc/objectclasses.py @@ -21,20 +21,41 @@ Defines the expected objectclass for various entries. """ -host = ( +user = [ + u'top', + u'person', + u'organizationalperson', + u'inetorgperson', + u'inetuser', + u'posixaccount', + u'krbprincipalaux', + u'radiusprofile', + u'ipaobject', +] + +group = [ + u'top', + u'groupofnames', + u'nestedgroup', + u'ipausergroup', + u'ipaobject', +] + +host = [ u'ipaobject', u'nshost', u'ipahost', u'pkiuser', + u'ipaservice', u'krbprincipalaux', u'krbprincipal', u'top', -) +] -hostgroup = ( +hostgroup = [ u'ipaobject', u'ipahostgroup', u'nestedGroup', u'groupOfNames', u'top', -) +] diff --git a/tests/test_xmlrpc/test_group_plugin.py b/tests/test_xmlrpc/test_group_plugin.py index 0469cc92..c33bfa3c 100644 --- a/tests/test_xmlrpc/test_group_plugin.py +++ b/tests/test_xmlrpc/test_group_plugin.py @@ -21,248 +21,331 @@ Test the `ipalib/plugins/group.py` module. """ -import sys -from xmlrpc_test import XMLRPC_test, assert_attr_equal from ipalib import api, errors -from xmlrpc_test import Declarative +from tests.test_xmlrpc import objectclasses +from xmlrpc_test import Declarative, fuzzy_digits, fuzzy_uuid - -group_objectclass = ( - u'top', - u'groupofnames', - u'nestedgroup', - u'ipausergroup', - u'ipaobject', -) +group1 = u'testgroup1' +group2 = u'testgroup2' class test_group(Declarative): cleanup_commands = [ - ('group_del', [u'testgroup1'], {}), - ('group_del', [u'testgroup2'], {}), + ('group_del', [group1], {}), + ('group_del', [group2], {}), ] tests = [ - # testgroup1: + + ################ + # create group1: + dict( + desc='Try to retrieve non-existant %r' % group1, + command=('group_show', [group1], {}), + expected=errors.NotFound(reason='no such entry'), + ), + + dict( - desc='Try to retrieve a non-existant testgroup1', - command=('group_show', [u'testgroup2'], {}), + desc='Try to update non-existant %r' % group1, + command=('group_mod', [group1], dict(description=u'Foo')), expected=errors.NotFound(reason='no such entry'), ), + dict( - desc='Create testgroup1', + desc='Try to delete non-existant %r' % group1, + command=('group_del', [group1], {}), + expected=errors.NotFound(reason='no such entry'), + ), + + + dict( + desc='Create %r' % group1, command=( - 'group_add', [u'testgroup1'], dict(description=u'Test desc 1') + 'group_add', [group1], dict(description=u'Test desc 1') ), expected=dict( - value=u'testgroup1', + value=group1, + summary=u'Added group "testgroup1"', result=dict( - cn=(u'testgroup1',), - description=(u'Test desc 1',), - objectclass=group_objectclass, + cn=[group1], + description=[u'Test desc 1'], + objectclass=objectclasses.group, + ipauniqueid=[fuzzy_uuid], + dn=u'cn=testgroup1,cn=groups,cn=accounts,' + api.env.basedn, ), - summary=u'Added group "testgroup1"', ), - ignore_values=['ipauniqueid', 'dn'], ), + dict( - desc='Try to create testgroup1 again', + desc='Try to create duplicate %r' % group1, command=( - 'group_add', [u'testgroup1'], dict(description=u'Test desc 1') + 'group_add', [group1], dict(description=u'Test desc 1') ), expected=errors.DuplicateEntry(), ), + dict( - desc='Retrieve testgroup1', - command=('group_show', [u'testgroup1'], {}), + desc='Retrieve %r' % group1, + command=('group_show', [group1], {}), expected=dict( - value=u'testgroup1', + value=group1, + summary=None, result=dict( - cn=(u'testgroup1',), - description=(u'Test desc 1',), + cn=[group1], + description=[u'Test desc 1'], + dn=u'cn=testgroup1,cn=groups,cn=accounts,' + api.env.basedn, ), - summary=None, ), - ignore_values=['dn'], ), + dict( - desc='Updated testgroup1', + desc='Updated %r' % group1, command=( - 'group_mod', [u'testgroup1'], dict(description=u'New desc 1') + 'group_mod', [group1], dict(description=u'New desc 1') ), expected=dict( result=dict( - description=(u'New desc 1',), + description=[u'New desc 1'], ), summary=u'Modified group "testgroup1"', - value=u'testgroup1', + value=group1, ), ), + dict( - desc='Retrieve testgroup1 to check update', - command=('group_show', [u'testgroup1'], {}), + desc='Retrieve %r to verify update' % group1, + command=('group_show', [group1], {}), expected=dict( - value=u'testgroup1', + value=group1, result=dict( - cn=(u'testgroup1',), - description=(u'New desc 1',), + cn=[group1], + description=[u'New desc 1'], + dn=u'cn=testgroup1,cn=groups,cn=accounts,' + api.env.basedn, ), summary=None, ), - ignore_values=['dn'], ), + # FIXME: The return value is totally different here than from the above # group_mod() test. I think that for all *_mod() commands we should # just return the entry exactly as *_show() does. dict( - desc='Updated testgroup1 to promote it to posix group', - command=('group_mod', [u'testgroup1'], dict(posix=True)), + desc='Updated %r to promote it to a posix group' % group1, + command=('group_mod', [group1], dict(posix=True)), expected=dict( result=dict( - cn=(u'testgroup1',), - description=(u'New desc 1',), - objectclass=group_objectclass + (u'posixgroup',), + cn=[group1], + description=[u'New desc 1'], + objectclass=objectclasses.group + [u'posixgroup'], + ipauniqueid=[fuzzy_uuid], + gidnumber=[fuzzy_digits], ), - value=u'testgroup1', + value=group1, summary=u'Modified group "testgroup1"', ), - ignore_values=['gidnumber', 'ipauniqueid'], ), + dict( - desc="Retrieve testgroup1 to check it's a posix group", - command=('group_show', [u'testgroup1'], {}), + desc="Retrieve %r to verify it's a posix group" % group1, + command=('group_show', [group1], {}), expected=dict( - value=u'testgroup1', + value=group1, result=dict( - cn=(u'testgroup1',), + cn=[group1], description=(u'New desc 1',), + dn=u'cn=testgroup1,cn=groups,cn=accounts,' + api.env.basedn, + gidnumber=[fuzzy_digits], ), summary=None, ), - ignore_values=['dn', 'gidnumber'], ), + dict( - desc='Search for testgroup1', - command=('group_find', [], dict(cn=u'testgroup1')), + desc='Search for %r' % group1, + command=('group_find', [], dict(cn=group1)), expected=dict( count=1, truncated=False, - result=( + result=[ dict( - cn=(u'testgroup1',), - description=(u'New desc 1',), + cn=[group1], + description=[u'New desc 1'], + gidnumber=[fuzzy_digits], ), - ), + ], summary=u'1 group matched', ), - ignore_values=['gidnumber'], ), - # testgroup2: + + ################ + # create group2: + dict( + desc='Try to retrieve non-existant %r' % group2, + command=('group_show', [group2], {}), + expected=errors.NotFound(reason='no such entry'), + ), + + dict( - desc='Try to retrieve a non-existant testgroup2', - command=('group_show', [u'testgroup2'], {}), + desc='Try to update non-existant %r' % group2, + command=('group_mod', [group2], dict(description=u'Foo')), expected=errors.NotFound(reason='no such entry'), ), + dict( - desc='Create testgroup2', + desc='Try to delete non-existant %r' % group2, + command=('group_del', [group2], {}), + expected=errors.NotFound(reason='no such entry'), + ), + + + dict( + desc='Create %r' % group2, command=( - 'group_add', [u'testgroup2'], dict(description=u'Test desc 2') + 'group_add', [group2], dict(description=u'Test desc 2') ), expected=dict( - value=u'testgroup2', + value=group2, + summary=u'Added group "testgroup2"', result=dict( - cn=(u'testgroup2',), - description=(u'Test desc 2',), - objectclass=group_objectclass, + cn=[group2], + description=[u'Test desc 2'], + objectclass=objectclasses.group, + ipauniqueid=[fuzzy_uuid], + dn=u'cn=testgroup2,cn=groups,cn=accounts,' + api.env.basedn, ), - summary=u'Added group "testgroup2"', ), - ignore_values=['ipauniqueid', 'dn'], ), + dict( - desc='Try to create testgroup2 again', + desc='Try to create duplicate %r' % group2, command=( - 'group_add', [u'testgroup2'], dict(description=u'Test desc 2') + 'group_add', [group2], dict(description=u'Test desc 2') ), expected=errors.DuplicateEntry(), ), + dict( - desc='Retrieve testgroup2', - command=('group_show', [u'testgroup2'], {}), + desc='Retrieve %r' % group2, + command=('group_show', [group2], {}), expected=dict( - value=u'testgroup2', + value=group2, + summary=None, result=dict( - cn=(u'testgroup2',), - description=(u'Test desc 2',), + cn=[group2], + description=[u'Test desc 2'], + dn=u'cn=testgroup2,cn=groups,cn=accounts,' + api.env.basedn, ), - summary=None, ), - ignore_values=['dn'], ), - dict( - desc='Search for testgroup2', - command=('group_find', [], dict(cn=u'testgroup2')), - expected=dict( - count=1, - truncated=False, - result=( - dict( - cn=(u'testgroup2',), - description=(u'Test desc 2',), - ), - ), - summary=u'1 group matched', - ), - ), dict( - desc='Updated testgroup2', + desc='Updated %r' % group2, command=( - 'group_mod', [u'testgroup2'], dict(description=u'New desc 2') + 'group_mod', [group2], dict(description=u'New desc 2') ), expected=dict( result=dict( - description=(u'New desc 2',), + description=[u'New desc 2'], ), - value=u'testgroup2', summary=u'Modified group "testgroup2"', + value=group2, ), ), + dict( - desc='Retrieve testgroup2 to check update', - command=('group_show', [u'testgroup2'], {}), + desc='Retrieve %r to verify update' % group2, + command=('group_show', [group2], {}), expected=dict( - value=u'testgroup2', + value=group2, result=dict( - cn=(u'testgroup2',), - description=(u'New desc 2',), + cn=[group2], + description=[u'New desc 2'], + dn=u'cn=testgroup2,cn=groups,cn=accounts,' + api.env.basedn, ), summary=None, ), - ignore_values=['dn'], ), + dict( + desc='Search for %r' % group2, + command=('group_find', [], dict(cn=group2)), + expected=dict( + count=1, + truncated=False, + result=[ + dict( + cn=[group2], + description=[u'New desc 2'], + ), + ], + summary=u'1 group matched', + ), + ), + + + dict( + desc='Search for all groups', + command=('group_find', [], {}), + expected=dict( + summary=u'5 groups matched', + count=5, + truncated=False, + result=[ + { + 'member user': [u'admin'], + 'gidnumber': [fuzzy_digits], + 'cn': [u'admins'], + 'description': [u'Account administrators group'], + }, + { + 'gidnumber': [fuzzy_digits], + 'cn': [u'ipausers'], + 'description': [u'Default group for all users'], + }, + { + 'gidnumber': [fuzzy_digits], + 'cn': [u'editors'], + 'description': [u'Limited admins who can edit other users'], + }, + dict( + cn=[group1], + description=[u'New desc 1'], + gidnumber=[fuzzy_digits], + ), + dict( + cn=[group2], + description=[u'New desc 2'], + ), + ], + ), + ), + + + + ############### # member stuff: dict( - desc='Make testgroup2 member of testgroup1', + desc='Add member %r to %r' % (group2, group1), command=( - 'group_add_member', [u'testgroup1'], dict(group=u'testgroup2') + 'group_add_member', [group1], dict(group=group2) ), expected=dict( completed=1, @@ -272,15 +355,15 @@ class test_group(Declarative): user=tuple(), ), ), - result={'member group': (u'testgroup2',)}, + result={'member group': (group2,)}, ), ), dict( # FIXME: Shouldn't this raise a NotFound instead? - desc='Try to add a non-existent member to testgroup1', + desc='Try to add non-existent member to %r' % group1, command=( - 'group_add_member', [u'testgroup1'], dict(group=u'notfound') + 'group_add_member', [group1], dict(group=u'notfound') ), expected=dict( completed=0, @@ -290,14 +373,14 @@ class test_group(Declarative): user=tuple(), ), ), - result={'member group': (u'testgroup2',)}, + result={'member group': (group2,)}, ), ), dict( - desc='Remove member testgroup2 from testgroup1', + desc='Remove member %r from %r' % (group2, group1), command=('group_remove_member', - [u'testgroup1'], dict(group=u'testgroup2') + [group1], dict(group=group2) ), expected=dict( completed=1, @@ -313,9 +396,9 @@ class test_group(Declarative): dict( # FIXME: Shouldn't this raise a NotFound instead? - desc='Try to remove a non-existent member from testgroup1', + desc='Try to remove non-existent member from %r' % group1, command=('group_remove_member', - [u'testgroup1'], dict(group=u'notfound') + [group1], dict(group=u'notfound') ), expected=dict( completed=0, @@ -330,69 +413,73 @@ class test_group(Declarative): ), - # Delete: + + ################ + # delete group1: dict( - desc='Delete testgroup1', - command=('group_del', [u'testgroup1'], {}), + desc='Delete %r' % group1, + command=('group_del', [group1], {}), expected=dict( result=True, - value=u'testgroup1', + value=group1, summary=u'Deleted group "testgroup1"', - ), + ) ), + dict( - desc='Delete testgroup2', - command=('group_del', [u'testgroup2'], {}), - expected=dict( - result=True, - value=u'testgroup2', - summary=u'Deleted group "testgroup2"', - ), + desc='Try to delete non-existant %r' % group1, + command=('group_del', [group1], {}), + expected=errors.NotFound(reason='no such entry'), ), - ############## - # Non-existent - ############## - - # testgroup1: dict( - desc='Try to retrieve non-existent testgroup1', - command=('group_show', [u'testgroup1'], {}), + desc='Try to retrieve non-existant %r' % group1, + command=('group_show', [group1], {}), expected=errors.NotFound(reason='no such entry'), ), + + dict( - desc='Try to update non-existent testgroup1', - command=( - 'group_mod', [u'testgroup1'], dict(description=u'New desc 1') - ), + desc='Try to update non-existant %r' % group1, + command=('group_mod', [group1], dict(description=u'Foo')), expected=errors.NotFound(reason='no such entry'), ), + + + + ################ + # delete group2: dict( - desc='Try to delete non-existent testgroup1', - command=('group_del', [u'testgroup1'], {}), - expected=errors.NotFound(reason='no such entry'), + desc='Delete %r' % group2, + command=('group_del', [group2], {}), + expected=dict( + result=True, + value=group2, + summary=u'Deleted group "testgroup2"', + ) ), - # testgroup2: + dict( - desc='Try to retrieve non-existent testgroup2', - command=('group_show', [u'testgroup2'], {}), + desc='Try to delete non-existant %r' % group2, + command=('group_del', [group2], {}), expected=errors.NotFound(reason='no such entry'), ), + + dict( - desc='Try to update non-existent testgroup2', - command=( - 'group_mod', [u'testgroup2'], dict(description=u'New desc 2') - ), + desc='Try to retrieve non-existant %r' % group2, + command=('group_show', [group2], {}), expected=errors.NotFound(reason='no such entry'), ), + + dict( - desc='Try to delete non-existent testgroup2', - command=('group_del', [u'testgroup2'], {}), + desc='Try to update non-existant %r' % group2, + command=('group_mod', [group2], dict(description=u'Foo')), expected=errors.NotFound(reason='no such entry'), ), - ] diff --git a/tests/test_xmlrpc/test_host_plugin.py b/tests/test_xmlrpc/test_host_plugin.py index dab96c94..167481a4 100644 --- a/tests/test_xmlrpc/test_host_plugin.py +++ b/tests/test_xmlrpc/test_host_plugin.py @@ -23,11 +23,12 @@ Test the `ipalib.plugins.host` module. """ from ipalib import api, errors -from tests.test_xmlrpc.xmlrpc_test import Declarative +from tests.test_xmlrpc.xmlrpc_test import Declarative, fuzzy_uuid from tests.test_xmlrpc import objectclasses fqdn1 = u'testhost1.%s' % api.env.domain +dn1 = u'fqdn=%s,cn=computers,cn=accounts,%s' % (fqdn1, api.env.basedn) class test_host(Declarative): @@ -71,16 +72,18 @@ class test_host(Declarative): value=fqdn1, summary=u'Added host "%s"' % fqdn1, result=dict( - cn=(fqdn1,), # FIXME: we should only return fqdn - fqdn=(fqdn1,), - description=(u'Test host 1',), - localityname=(u'Undisclosed location 1',), - krbprincipalname=(u'host/%s@%s' % (fqdn1, api.env.realm),), - serverhostname=(u'testhost1',), + dn=dn1, + cn=[fqdn1], # FIXME: we should only return fqdn + fqdn=[fqdn1], + description=[u'Test host 1'], + localityname=[u'Undisclosed location 1'], + krbprincipalname=[u'host/%s@%s' % (fqdn1, api.env.realm)], + serverhostname=[u'testhost1'], objectclass=objectclasses.host, + managedby=[dn1], + ipauniqueid=[fuzzy_uuid], ), ), - ignore_values=['ipauniqueid', 'dn'], ), @@ -103,12 +106,12 @@ class test_host(Declarative): value=fqdn1, summary=None, result=dict( - fqdn=(fqdn1,), - description=(u'Test host 1',), - localityname=(u'Undisclosed location 1',), + dn=dn1, + fqdn=[fqdn1], + description=[u'Test host 1'], + localityname=[u'Undisclosed location 1'], ), ), - ignore_values=['dn'], ), @@ -119,20 +122,22 @@ class test_host(Declarative): value=fqdn1, summary=None, result=dict( - cn=(fqdn1,), - fqdn=(fqdn1,), - description=(u'Test host 1',), + dn=dn1, + cn=[fqdn1], + fqdn=[fqdn1], + description=[u'Test host 1'], # FIXME: Why is 'localalityname' returned as 'l' with --all? # It is intuitive for --all to return additional attributes, # but not to return existing attributes under different # names. - l=(u'Undisclosed location 1',), - krbprincipalname=(u'host/%s@%s' % (fqdn1, api.env.realm),), - serverhostname=(u'testhost1',), + l=[u'Undisclosed location 1'], + krbprincipalname=[u'host/%s@%s' % (fqdn1, api.env.realm)], + serverhostname=[u'testhost1'], objectclass=objectclasses.host, + managedby=[dn1], + ipauniqueid=[fuzzy_uuid], ), ), - ignore_values=['dn', 'ipauniqueid'], ), @@ -143,13 +148,13 @@ class test_host(Declarative): count=1, truncated=False, summary=u'1 host matched', - result=( + result=[ dict( - fqdn=(fqdn1,), - description=(u'Test host 1',), - localityname=(u'Undisclosed location 1',), + fqdn=[fqdn1], + description=[u'Test host 1'], + localityname=[u'Undisclosed location 1'], ), - ), + ], ), ), @@ -161,25 +166,24 @@ class test_host(Declarative): count=1, truncated=False, summary=u'1 host matched', - result=( + result=[ dict( - cn=(fqdn1,), - fqdn=(fqdn1,), - description=(u'Test host 1',), + cn=[fqdn1], + fqdn=[fqdn1], + description=[u'Test host 1'], # FIXME: Why is 'localalityname' returned as 'l' with --all? # It is intuitive for --all to return additional attributes, # but not to return existing attributes under different # names. - l=(u'Undisclosed location 1',), - krbprincipalname=(u'host/%s@%s' % (fqdn1, api.env.realm),), - serverhostname=(u'testhost1',), + l=[u'Undisclosed location 1'], + krbprincipalname=[u'host/%s@%s' % (fqdn1, api.env.realm)], + serverhostname=[u'testhost1'], objectclass=objectclasses.host, + managedby=[dn1], + ipauniqueid=[fuzzy_uuid], ), - ), + ], ), - # FIXME: With --all, host_show() returns the 'dn', but host_find() - # doesn't. - ignore_values=['ipauniqueid'], ), @@ -190,7 +194,7 @@ class test_host(Declarative): value=fqdn1, summary=u'Modified host "%s"' % fqdn1, result=dict( - description=(u'Updated host 1',), + description=[u'Updated host 1'], ), ), ), @@ -203,12 +207,12 @@ class test_host(Declarative): value=fqdn1, summary=None, result=dict( - fqdn=(fqdn1,), - description=(u'Updated host 1',), - localityname=(u'Undisclosed location 1',), + dn=dn1, + fqdn=[fqdn1], + description=[u'Updated host 1'], + localityname=[u'Undisclosed location 1'], ), ), - ignore_values=['dn'], ), diff --git a/tests/test_xmlrpc/test_hostgroup_plugin.py b/tests/test_xmlrpc/test_hostgroup_plugin.py index 76a55cca..6f735508 100644 --- a/tests/test_xmlrpc/test_hostgroup_plugin.py +++ b/tests/test_xmlrpc/test_hostgroup_plugin.py @@ -23,32 +23,35 @@ Test the `ipalib.plugins.hostgroup` module. """ from ipalib import api, errors -from tests.test_xmlrpc.xmlrpc_test import Declarative +from tests.test_xmlrpc.xmlrpc_test import Declarative, fuzzy_uuid from tests.test_xmlrpc import objectclasses +hostgroup1 = u'testhostgroup1' +dn1 = u'cn=%s,cn=hostgroups,cn=accounts,%s' % (hostgroup1, api.env.basedn) fqdn1 = u'testhost1.%s' % api.env.domain +host_dn1 = u'fqdn=%s,cn=computers,cn=accounts,%s' % (fqdn1, api.env.basedn) class test_hostgroup(Declarative): cleanup_commands = [ - ('hostgroup_del', [u'testhostgroup1'], {}), + ('hostgroup_del', [hostgroup1], {}), ('host_del', [fqdn1], {}), ] tests=[ dict( - desc='Try to retrieve non-existent testhostgroup1', - command=('hostgroup_show', [u'testhostgroup1'], {}), + desc='Try to retrieve non-existent %r' % hostgroup1, + command=('hostgroup_show', [hostgroup1], {}), expected=errors.NotFound(reason='no such entry'), ), dict( - desc='Try to update non-existent testhostgroup1', - command=('hostgroup_mod', [u'testhostgroup1'], + desc='Try to update non-existent %r' % hostgroup1, + command=('hostgroup_mod', [hostgroup1], dict(description=u'Updated hostgroup 1') ), expected=errors.NotFound(reason='no such entry'), @@ -56,33 +59,34 @@ class test_hostgroup(Declarative): dict( - desc='Try to delete non-existent testhostgroup1', - command=('hostgroup_del', [u'testhostgroup1'], {}), + desc='Try to delete non-existent %r' % hostgroup1, + command=('hostgroup_del', [hostgroup1], {}), expected=errors.NotFound(reason='no such entry'), ), dict( - desc='Create hostgroup testhostgroup1', - command=('hostgroup_add', [u'testhostgroup1'], + desc='Create %r' % hostgroup1, + command=('hostgroup_add', [hostgroup1], dict(description=u'Test hostgroup 1') ), expected=dict( - value=u'testhostgroup1', + value=hostgroup1, summary=u'Added hostgroup "testhostgroup1"', result=dict( - cn=(u'testhostgroup1',), + dn=dn1, + cn=[hostgroup1], objectclass=objectclasses.hostgroup, - description=(u'Test hostgroup 1',), + description=[u'Test hostgroup 1'], + ipauniqueid=[fuzzy_uuid], ), ), - ignore_values=['ipauniqueid', 'dn'], ), dict( - desc='Try to create duplicate testhostgroup1', - command=('hostgroup_add', [u'testhostgroup1'], + desc='Try to create duplicate %r' % hostgroup1, + command=('hostgroup_add', [hostgroup1], dict(description=u'Test hostgroup 1') ), expected=errors.DuplicateEntry(), @@ -101,23 +105,25 @@ class test_hostgroup(Declarative): value=fqdn1, summary=u'Added host "%s"' % fqdn1, result=dict( - cn=(fqdn1,), # FIXME: we should only return fqdn - fqdn=(fqdn1,), - description=(u'Test host 1',), - localityname=(u'Undisclosed location 1',), - krbprincipalname=(u'host/%s@%s' % (fqdn1, api.env.realm),), - serverhostname=(u'testhost1',), + dn=host_dn1, + cn=[fqdn1], # FIXME: we should only return fqdn + fqdn=[fqdn1], + description=[u'Test host 1'], + localityname=[u'Undisclosed location 1'], + krbprincipalname=[u'host/%s@%s' % (fqdn1, api.env.realm)], + serverhostname=[u'testhost1'], objectclass=objectclasses.host, + managedby=[host_dn1], + ipauniqueid=[fuzzy_uuid], ), ), - ignore_values=['ipauniqueid', 'dn'], ), dict( - desc=u'Add %r to testhostgroup1' % fqdn1, + desc=u'Add host %r to %r' % (fqdn1, hostgroup1), command=( - 'hostgroup_add_member', [u'testhostgroup1'], dict(host=fqdn1) + 'hostgroup_add_member', [hostgroup1], dict(host=fqdn1) ), expected=dict( completed=1, @@ -128,80 +134,80 @@ class test_hostgroup(Declarative): ), ), result={ - 'member host': (fqdn1,), + 'member host': [fqdn1], }, ), ), dict( - desc='Retrieve testhostgroup1', - command=('hostgroup_show', [u'testhostgroup1'], {}), + desc='Retrieve %r' % hostgroup1, + command=('hostgroup_show', [hostgroup1], {}), expected=dict( - value=u'testhostgroup1', + value=hostgroup1, summary=None, result={ - 'member host': (u'testhost1.example.com',), - 'cn': (u'testhostgroup1',), - 'description': (u'Test hostgroup 1',) + 'dn': dn1, + 'member host': [u'testhost1.example.com'], + 'cn': [hostgroup1], + 'description': [u'Test hostgroup 1'], }, ), - ignore_values=['dn'], ), dict( - desc='Search for testhostgroup1', - command=('hostgroup_find', [], dict(cn=u'testhostgroup1')), + desc='Search for %r' % hostgroup1, + command=('hostgroup_find', [], dict(cn=hostgroup1)), expected=dict( count=1, truncated=False, summary=u'1 hostgroup matched', - result=( + result=[ { - 'member host': (u'testhost1.example.com',), - 'cn': (u'testhostgroup1',), - 'description': (u'Test hostgroup 1',), + 'member host': [u'testhost1.example.com'], + 'cn': [hostgroup1], + 'description': [u'Test hostgroup 1'], }, - ), + ], ), ), dict( - desc='Update testhostgroup1', - command=('hostgroup_mod', [u'testhostgroup1'], + desc='Update %r' % hostgroup1, + command=('hostgroup_mod', [hostgroup1], dict(description=u'Updated hostgroup 1') ), expected=dict( - value=u'testhostgroup1', + value=hostgroup1, summary=u'Modified hostgroup "testhostgroup1"', result=dict( - description=(u'Updated hostgroup 1',), + description=[u'Updated hostgroup 1'], ), ), ), dict( - desc='Retrieve testhostgroup1 to verify update', - command=('hostgroup_show', [u'testhostgroup1'], {}), + desc='Retrieve %r to verify update' % hostgroup1, + command=('hostgroup_show', [hostgroup1], {}), expected=dict( - value=u'testhostgroup1', + value=hostgroup1, summary=None, result={ - 'member host': (u'testhost1.example.com',), - 'cn': (u'testhostgroup1',), - 'description': (u'Updated hostgroup 1',) + 'dn': dn1, + 'member host': [u'testhost1.example.com'], + 'cn': [hostgroup1], + 'description': [u'Updated hostgroup 1'], }, ), - ignore_values=['dn'], ), dict( - desc='Remove %s from testhostgroup1', - command=('hostgroup_remove_member', [u'testhostgroup1'], + desc='Remove host %r from %r' % (fqdn1, hostgroup1), + command=('hostgroup_remove_member', [hostgroup1], dict(host=fqdn1) ), expected=dict( @@ -218,10 +224,10 @@ class test_hostgroup(Declarative): dict( - desc='Delete testhostgroup1', - command=('hostgroup_del', [u'testhostgroup1'], {}), + desc='Delete %r' % hostgroup1, + command=('hostgroup_del', [hostgroup1], {}), expected=dict( - value=u'testhostgroup1', + value=hostgroup1, summary=u'Deleted hostgroup "testhostgroup1"', result=True, ), @@ -229,7 +235,7 @@ class test_hostgroup(Declarative): dict( - desc='Delete %s' % fqdn1, + desc='Delete host %r' % fqdn1, command=('host_del', [fqdn1], {}), expected=dict( value=fqdn1, diff --git a/tests/test_xmlrpc/test_user_plugin.py b/tests/test_xmlrpc/test_user_plugin.py index b45300ce..b15e18cc 100644 --- a/tests/test_xmlrpc/test_user_plugin.py +++ b/tests/test_xmlrpc/test_user_plugin.py @@ -24,66 +24,71 @@ Test the `ipalib/plugins/user.py` module. """ from ipalib import api, errors -from xmlrpc_test import Declarative - -user_objectclass = ( - u'top', - u'person', - u'organizationalperson', - u'inetorgperson', - u'inetuser', - u'posixaccount', - u'krbprincipalaux', - u'radiusprofile', - u'ipaobject', -) +from tests.test_xmlrpc import objectclasses +from xmlrpc_test import Declarative, fuzzy_digits, fuzzy_uuid + user_memberof = (u'cn=ipausers,cn=groups,cn=accounts,dc=example,dc=com',) +user1=u'tuser1' class test_user(Declarative): cleanup_commands = [ - ('user_del', [u'tuser1'], {}), + ('user_del', [user1], {}), ] tests = [ dict( - desc='Try to retrieve non-existant user', - command=( - 'user_show', [u'tuser1'], {} - ), + desc='Try to retrieve non-existent %r' % user1, + command=('user_show', [user1], {}), + expected=errors.NotFound(reason='no such entry'), + ), + + + dict( + desc='Try to update non-existent %r' % user1, + command=('user_mod', [user1], dict(givenname=u'Foo')), expected=errors.NotFound(reason='no such entry'), ), dict( - desc='Create a user', + desc='Try to delete non-existent %r' % user1, + command=('user_del', [user1], {}), + expected=errors.NotFound(reason='no such entry'), + ), + + + dict( + desc='Create %r' % user1, command=( 'user_add', [], dict(givenname=u'Test', sn=u'User1') ), expected=dict( - value=u'tuser1', + value=user1, + summary=u'Added user "tuser1"', result=dict( - cn=(u'Test User1',), - gecos=(u'tuser1',), - givenname=(u'Test',), - homedirectory=(u'/home/tuser1',), - krbprincipalname=(u'tuser1@' + api.env.realm,), - loginshell=(u'/bin/sh',), - objectclass=user_objectclass, - sn=(u'User1',), - uid=(u'tuser1',), + cn=[u'Test User1'], + gecos=[user1], + givenname=[u'Test'], + homedirectory=[u'/home/tuser1'], + krbprincipalname=[u'tuser1@' + api.env.realm], + loginshell=[u'/bin/sh'], + objectclass=objectclasses.user, + sn=[u'User1'], + uid=[user1], + gidnumber=[fuzzy_digits], + ipauniqueid=[fuzzy_uuid], + dn=u'uid=tuser1,cn=users,cn=accounts,' + api.env.basedn, ), - summary=u'Added user "tuser1"', ), - ignore_values=['ipauniqueid', 'gidnumber', 'dn'], ), dict( - desc='Try to create another user with same login', + desc='Try to create duplicate %r' % user1, command=( 'user_add', [], dict(givenname=u'Test', sn=u'User1') ), @@ -92,68 +97,70 @@ class test_user(Declarative): dict( - desc='Retrieve the user', + desc='Retrieve %r' % user1, command=( - 'user_show', [u'tuser1'], {} + 'user_show', [user1], {} ), expected=dict( result=dict( dn=u'uid=tuser1,cn=users,cn=accounts,dc=example,dc=com', - givenname=(u'Test',), - homedirectory=(u'/home/tuser1',), - loginshell=(u'/bin/sh',), - sn=(u'User1',), - uid=(u'tuser1',), + givenname=[u'Test'], + homedirectory=[u'/home/tuser1'], + loginshell=[u'/bin/sh'], + sn=[u'User1'], + uid=[user1], ), - value=u'tuser1', + value=user1, summary=None, ), ), dict( - desc='Search for this user with all=True', + desc='Search for %r with all=True' % user1, command=( - 'user_find', [u'tuser1'], {'all': True} + 'user_find', [user1], {'all': True} ), expected=dict( - result=( + result=[ { - 'cn': (u'Test User1',), - 'gecos': (u'tuser1',), - 'givenname': (u'Test',), - 'homedirectory': (u'/home/tuser1',), - 'krbprincipalname': (u'tuser1@' + api.env.realm,), - 'loginshell': (u'/bin/sh',), - 'memberof group': (u'ipausers',), - 'objectclass': user_objectclass, - 'sn': (u'User1',), - 'uid': (u'tuser1',), + 'cn': [u'Test User1'], + 'gecos': [user1], + 'givenname': [u'Test'], + 'homedirectory': [u'/home/tuser1'], + 'krbprincipalname': [u'tuser1@' + api.env.realm], + 'loginshell': [u'/bin/sh'], + 'memberof group': [u'ipausers'], + 'objectclass': objectclasses.user, + 'sn': [u'User1'], + 'uid': [user1], + 'uidnumber': [fuzzy_digits], + 'gidnumber': [fuzzy_digits], + 'ipauniqueid': [fuzzy_uuid], }, - ), + ], summary=u'1 user matched', count=1, truncated=False, ), - ignore_values=['uidnumber', 'gidnumber', 'ipauniqueid'], ), dict( - desc='Search for this user with minimal attributes', + desc='Search for %r with minimal attributes' % user1, command=( - 'user_find', [u'tuser1'], {} + 'user_find', [user1], {} ), expected=dict( - result=( + result=[ dict( - givenname=(u'Test',), - homedirectory=(u'/home/tuser1',), - loginshell=(u'/bin/sh',), - sn=(u'User1',), - uid=(u'tuser1',), + givenname=[u'Test'], + homedirectory=[u'/home/tuser1'], + loginshell=[u'/bin/sh'], + sn=[u'User1'], + uid=[user1], ), - ), + ], summary=u'1 user matched', count=1, truncated=False, @@ -167,21 +174,21 @@ class test_user(Declarative): 'user_find', [], {} ), expected=dict( - result=( + result=[ dict( - homedirectory=(u'/home/admin',), - loginshell=(u'/bin/bash',), - sn=(u'Administrator',), - uid=(u'admin',), + homedirectory=[u'/home/admin'], + loginshell=[u'/bin/bash'], + sn=[u'Administrator'], + uid=[u'admin'], ), dict( - givenname=(u'Test',), - homedirectory=(u'/home/tuser1',), - loginshell=(u'/bin/sh',), - sn=(u'User1',), - uid=(u'tuser1',), + givenname=[u'Test'], + homedirectory=[u'/home/tuser1'], + loginshell=[u'/bin/sh'], + sn=[u'User1'], + uid=[user1], ), - ), + ], summary=u'2 users matched', count=2, truncated=False, @@ -190,95 +197,95 @@ class test_user(Declarative): dict( - desc='Lock user', + desc='Lock %r' % user1, command=( - 'user_lock', [u'tuser1'], {} + 'user_lock', [user1], {} ), expected=dict( result=True, - value=u'tuser1', + value=user1, summary=u'Locked user "tuser1"', ), ), dict( - desc='Unlock user', + desc='Unlock %r' % user1, command=( - 'user_unlock', [u'tuser1'], {} + 'user_unlock', [user1], {} ), expected=dict( result=True, - value=u'tuser1', + value=user1, summary=u'Unlocked user "tuser1"', ), ), dict( - desc='Update user', + desc='Update %r' % user1, command=( - 'user_mod', [u'tuser1'], dict(givenname=u'Finkle') + 'user_mod', [user1], dict(givenname=u'Finkle') ), expected=dict( result=dict( - givenname=(u'Finkle',), + givenname=[u'Finkle'], ), summary=u'Modified user "tuser1"', - value=u'tuser1', + value=user1, ), ), dict( - desc='Retrieve user to verify update', - command=( - 'user_show', [u'tuser1'], {} - ), + desc='Retrieve %r to verify update' % user1, + command=('user_show', [user1], {}), expected=dict( result=dict( dn=u'uid=tuser1,cn=users,cn=accounts,dc=example,dc=com', - givenname=(u'Finkle',), - homedirectory=(u'/home/tuser1',), - loginshell=(u'/bin/sh',), - sn=(u'User1',), - uid=(u'tuser1',), + givenname=[u'Finkle'], + homedirectory=[u'/home/tuser1'], + loginshell=[u'/bin/sh'], + sn=[u'User1'], + uid=[user1], ), summary=None, - value=u'tuser1', + value=user1, ), ), dict( - desc='Delete user', - command=( - 'user_del', [u'tuser1'], {} - ), + desc='Delete %r' % user1, + command=('user_del', [user1], {}), expected=dict( result=True, summary=u'Deleted user "tuser1"', - value=u'tuser1', + value=user1, ), ), dict( - desc='Do double delete', - command=( - 'user_del', [u'tuser1'], {} - ), + desc='Try to delete non-existent %r' % user1, + command=('user_del', [user1], {}), expected=errors.NotFound(reason='no such entry'), ), dict( - desc='Verify user is gone', - command=( - 'user_show', [u'tuser1'], {} - ), + desc='Try to retrieve non-existent %r' % user1, + command=('user_show', [user1], {}), + expected=errors.NotFound(reason='no such entry'), + ), + + + dict( + desc='Try to update non-existent %r' % user1, + command=('user_mod', [user1], dict(givenname=u'Foo')), expected=errors.NotFound(reason='no such entry'), ), + ] diff --git a/tests/test_xmlrpc/xmlrpc_test.py b/tests/test_xmlrpc/xmlrpc_test.py index a51a82bb..02b1f92c 100644 --- a/tests/test_xmlrpc/xmlrpc_test.py +++ b/tests/test_xmlrpc/xmlrpc_test.py @@ -24,11 +24,22 @@ Base class for all XML-RPC tests import sys import socket import nose -from tests.util import assert_deepequal +from tests.util import assert_deepequal, Fuzzy from ipalib import api, request from ipalib import errors +# Matches a gidnumber like '1391016742' +# FIXME: Does it make more sense to return gidnumber, uidnumber, etc. as `int` +# or `long`? If not, we still need to return them as `unicode` instead of `str`. +fuzzy_digits = Fuzzy('^\d+$', type=str) + +# Matches an ipauniqueid like u'784d85fd-eae7-11de-9d01-54520012478b' +fuzzy_uuid = Fuzzy( + '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$' +) + + try: if not api.Backend.xmlclient.isconnected(): api.Backend.xmlclient.connect() @@ -146,10 +157,10 @@ class Declarative(XMLRPC_test): tests = tuple() def cleanup_generate(self, stage): - for command in self.cleanup_commands: + for (i, command) in enumerate(self.cleanup_commands): func = lambda: self.cleanup(command) - func.description = '%s %s-cleanup: %r' % ( - self.__class__.__name__, stage, command + func.description = '%s %s-cleanup[%d]: %r' % ( + self.__class__.__name__, stage, i, command ) yield (func,) @@ -194,11 +205,10 @@ class Declarative(XMLRPC_test): if cmd not in api.Command: raise nose.SkipTest('%r not in api.Command' % cmd) expected = test['expected'] - ignore_values = test.get('ignore_values') if isinstance(expected, errors.PublicError): self.check_exception(nice, cmd, args, options, expected) else: - self.check_output(nice, cmd, args, options, expected, ignore_values) + self.check_output(nice, cmd, args, options, expected) def check_exception(self, nice, cmd, args, options, expected): klass = expected.__class__ @@ -224,28 +234,6 @@ class Declarative(XMLRPC_test): # KWARGS % (cmd, name, args, options, expected.kw, e.kw) # ) - def check_output(self, nice, cmd, args, options, expected, ignore_values): + def check_output(self, nice, cmd, args, options, expected): got = api.Command[cmd](*args, **options) - result = got['result'] - if ignore_values: - if isinstance(result, dict): - self.clean_entry( - nice, cmd, args, options, result, ignore_values - ) - elif isinstance(result, (list, tuple)): - for entry in result: - self.clean_entry( - nice, cmd, args, options, entry, ignore_values - ) assert_deepequal(expected, got, nice) - - def clean_entry(self, nice, cmd, args, options, entry, ignore_values): - """ - Remove attributes like 'ipauniqueid' whose value is unpredictable. - """ - for key in ignore_values: - if key not in entry: - raise AssertionError( - IGNORE % (cmd, key, args, options, entry) - ) - entry.pop(key) diff --git a/tests/util.py b/tests/util.py index 30cbbb5b..d93a967c 100644 --- a/tests/util.py +++ b/tests/util.py @@ -26,6 +26,7 @@ import os from os import path import tempfile import shutil +import re import ipalib from ipalib.plugable import Plugin from ipalib.request import context @@ -108,6 +109,58 @@ def assert_equal(val1, val2): assert val1 == val2, '%r != %r' % (val1, val2) +class Fuzzy(object): + """ + Perform a fuzzy (non-strict) equality test. + + `Fuzzy` instances will likely be used when comparing nesting data-structures + using `assert_deepequal()`. + """ + + def __init__(self, regex=None, type=None, test=None): + """ + Initialize. + + :param regex: A regular expression pattern to match, e.g. + ``u'^\d+foo'`` + + :param type: A type or tuple of types to test using ``isinstance()``, + e.g. ``(int, float)`` + + :param test: A callable used to perform equality test, e.g. + ``lambda other: other >= 18`` + """ + assert regex is None or isinstance(regex, basestring) + assert test is None or callable(test) + if regex is None: + self.re = None + else: + self.re = re.compile(regex) + if type is None: + type = unicode + assert type in (unicode, str) + self.regex = regex + self.type = type + self.test = test + + def __repr__(self): + return '%s(regex=%r, type=%r, test=%r)' % ( + self.__class__.__name__, self.regex, self.type, self.test + ) + + def __eq__(self, other): + if not (self.type is None or isinstance(other, self.type)): + return False + if not (self.re is None or self.re.search(other)): + return False + if not (self.test is None or self.test(other)): + return False + return True + + def __ne__(self, other): + return not self.__eq__(other) + + VALUE = """assert_deepequal: expected != got. %s expected = %r @@ -160,7 +213,11 @@ def assert_deepequal(expected, got, src='', stack=tuple()): got = 'nurse' path = (1, 'naughty') """ - if type(expected) is not type(got): + if isinstance(expected, tuple): + expected = list(expected) + if isinstance(got, tuple): + got = list(got) + if not (isinstance(expected, Fuzzy) or type(expected) is type(got)): raise AssertionError( TYPE % (src, type(expected), type(got), expected, got, stack) ) @@ -184,7 +241,7 @@ def assert_deepequal(expected, got, src='', stack=tuple()): e_sub = expected[key] g_sub = got[key] assert_deepequal(e_sub, g_sub, src, stack + (key,)) - if expected != got: + elif expected != got: raise AssertionError( VALUE % (src, expected, got, stack) ) -- cgit