Hi,

sending the refactored automember plugin test for review.

Filip
From 8c3abf76cfb899afb65f1a453df6a5b4815ad473 Mon Sep 17 00:00:00 2001
From: Filip Skola <fsk...@redhat.com>
Date: Mon, 11 Apr 2016 16:59:00 +0200
Subject: [PATCH] Refactor test_automember_plugin, create AutomemberTracker

---
 ipatests/test_xmlrpc/test_automember_plugin.py    | 2163 ++++++---------------
 ipatests/test_xmlrpc/tracker/automember_plugin.py |  338 ++++
 2 files changed, 945 insertions(+), 1556 deletions(-)
 create mode 100644 ipatests/test_xmlrpc/tracker/automember_plugin.py

diff --git a/ipatests/test_xmlrpc/test_automember_plugin.py b/ipatests/test_xmlrpc/test_automember_plugin.py
index be0f7390565ed739aa66bc0c5c6d23d25d67df92..2078b1fcbbea112f101348c6ae039fa2ff3a6d28 100644
--- a/ipatests/test_xmlrpc/test_automember_plugin.py
+++ b/ipatests/test_xmlrpc/test_automember_plugin.py
@@ -21,43 +21,20 @@
 Test the `ipalib/plugins/automember.py` module.
 """
 
+from ipatests.test_xmlrpc.tracker.user_plugin import UserTracker
+from ipatests.test_xmlrpc.tracker.host_plugin import HostTracker
+from ipatests.test_xmlrpc.tracker.group_plugin import GroupTracker
+from ipatests.test_xmlrpc.tracker.hostgroup_plugin import HostGroupTracker
+from ipatests.test_xmlrpc.tracker.automember_plugin import AutomemberTracker
 from ipalib import api, errors
 from ipapython.dn import DN
-from ipatests.test_xmlrpc import objectclasses
-from ipatests.test_xmlrpc.xmlrpc_test import (
-    Declarative, fuzzy_digits, fuzzy_uuid, fuzzy_automember_dn,
-    fuzzy_automember_message)
-from ipatests.test_xmlrpc.test_user_plugin import get_user_result
+from ipatests.test_xmlrpc.xmlrpc_test import XMLRPC_test, raises_exact
+from ipatests.util import assert_deepequal
 
 import pytest
 
-user1 = u'tuser1'
 user_does_not_exist = u'does_not_exist'
-manager1 = u'mscott'
-fqdn1 = u'web1.%s' % api.env.domain
-short1 = u'web1'
-fqdn2 = u'dev1.%s' % api.env.domain
-short2 = u'dev1'
-fqdn3 = u'web5.%s' % api.env.domain
-short3 = u'web5'
-fqdn4 = u'www5.%s' % api.env.domain
-short4 = u'www5'
-fqdn5 = u'webserver5.%s' % api.env.domain
-short5 = u'webserver5'
 fqdn_does_not_exist = u'does_not_exist.%s' % api.env.domain
-
-group1 = u'group1'
-group1_dn = DN(('cn', group1), ('cn', 'groups'),
-               ('cn', 'accounts'), api.env.basedn)
-defaultgroup1 = u'defaultgroup1'
-hostgroup1 = u'hostgroup1'
-hostgroup1_dn = DN(('cn', hostgroup1), ('cn', 'hostgroups'),
-                  ('cn', 'accounts'), api.env.basedn)
-hostgroup2 = u'hostgroup2'
-hostgroup3 = u'hostgroup3'
-hostgroup4 = u'hostgroup4'
-defaulthostgroup1 = u'defaulthostgroup1'
-
 group_include_regex = u'mscott'
 hostgroup_include_regex = u'^web[1-9]'
 hostgroup_include_regex2 = u'^www[1-9]'
@@ -67,1600 +44,674 @@ hostgroup_exclude_regex2 = u'^www5'
 hostgroup_exclude_regex3 = u'^webserver5'
 
 
-@pytest.mark.tier1
-class test_automember(Declarative):
-
-    cleanup_commands = [
-        ('user_del', [user1, manager1], {}),
-        ('group_del', [group1, defaultgroup1], {}),
-        ('host_del', [fqdn1, fqdn2, fqdn3, fqdn4, fqdn5], {}),
-        ('hostgroup_del', [hostgroup1, hostgroup2, hostgroup3, hostgroup4, defaulthostgroup1], {}),
-        ('automember_del', [group1], {'type': u'group'}),
-        ('automember_del', [hostgroup1], {'type': u'hostgroup'}),
-        ('automember_del', [hostgroup2], {'type': u'hostgroup'}),
-        ('automember_del', [hostgroup3], {'type': u'hostgroup'}),
-        ('automember_del', [hostgroup4], {'type': u'hostgroup'}),
-        ('automember_default_group_remove', [], {'type': u'hostgroup'}),
-        ('automember_default_group_remove', [], {'type': u'group'}),
-
-    ]
-
-    tests = [
-
-        dict(
-            desc='Try to retrieve non-existent group rule %r' % group1,
-            command=('automember_add', [group1],
-                dict(description=u'Test desc', type=u'group')),
-            expected=errors.NotFound(reason=u'group "%s" not found' % group1),
-        ),
-
-        dict(
-            desc='Try to update non-existent group rule %r' % group1,
-            command=('automember_add', [group1], dict(type=u'group')),
-            expected=errors.NotFound(reason=u'group "%s" not found' % group1),
-        ),
-
-        dict(
-            desc='Try to delete non-existent group rule %r' % group1,
-            command=('automember_del', [group1], dict(type=u'group')),
-            expected=errors.NotFound(reason=u': Automember rule not found'),
-        ),
-
-
-        dict(
-            desc='Try to retrieve non-existent hostgroup rule %r' % hostgroup1,
-            command=('automember_add', [hostgroup1],
-                dict(description=u'Test desc', type=u'hostgroup')),
-            expected=errors.NotFound(
-                reason=u'hostgroup "%s" not found' % hostgroup1),
-        ),
-
-        dict(
-            desc='Try to update non-existent hostgroup rule %r' % hostgroup1,
-            command=('automember_add', [hostgroup1], dict(type=u'hostgroup')),
-            expected=errors.NotFound(
-                reason=u'hostgroup "%s" not found' % hostgroup1),
-        ),
-
-        dict(
-            desc='Try to delete non-existent hostgroup rule %r' % hostgroup1,
-            command=('automember_del', [hostgroup1], dict(type=u'hostgroup')),
-            expected=errors.NotFound(reason=u': Automember rule not found'),
-        ),
-
-        # Automember rebuild membership tests
-        dict(
-            desc='Create hostgroup: %r' % hostgroup1,
-            command=(
-                'hostgroup_add', [hostgroup1], dict(description=u'Test desc')
-            ),
-            expected=dict(
-                value=hostgroup1,
-                summary=u'Added hostgroup "%s"' % hostgroup1,
-                result=dict(
-                    cn=[hostgroup1],
-                    description=[u'Test desc'],
-                    objectclass=objectclasses.hostgroup,
-                    ipauniqueid=[fuzzy_uuid],
-                    mepmanagedentry=[DN(('cn', hostgroup1), ('cn', 'ng'),
-                                        ('cn', 'alt'), api.env.basedn)],
-                    dn=hostgroup1_dn
-                ),
-            ),
-        ),
-
-        dict(
-            desc='Create host: %r' % fqdn1,
-            command=(
-                'host_add',
-                [fqdn1],
-                dict(
-                    description=u'Test host 1',
-                    l=u'Undisclosed location 1',
-                    force=True,
-                ),
-            ),
-            expected=dict(
-                value=fqdn1,
-                summary=u'Added host "%s"' % fqdn1,
-                result=dict(
-                    dn=DN(('fqdn', fqdn1), ('cn', 'computers'),
-                          ('cn', 'accounts'), api.env.basedn),
-                    fqdn=[fqdn1],
-                    description=[u'Test host 1'],
-                    l=[u'Undisclosed location 1'],
-                    krbprincipalname=[u'host/%s@%s' % (fqdn1, api.env.realm)],
-                    has_keytab=False,
-                    has_password=False,
-                    objectclass=objectclasses.host,
-                    ipauniqueid=[fuzzy_uuid],
-                    managedby_host=[fqdn1],
-                ),
-            ),
-        ),
+@pytest.fixture(scope='class')
+def manager1(request):
+    """ User tracker used as a manager account """
+    tracker = UserTracker(name=u'mscott', sn=u'Manager1',
+                          givenname=u'Automember test manager user1')
+    return tracker.make_fixture(request)
 
-        dict(
-            desc='Create automember rule: %r' % hostgroup1,
-            command=(
-                'automember_add', [hostgroup1], dict(
-                    description=u'Test desc', type=u'hostgroup',
-                )
-            ),
-            expected=dict(
-                value=hostgroup1,
-                summary=u'Added automember rule "%s"' % hostgroup1,
-                result=dict(
-                    cn=[hostgroup1],
-                    description=[u'Test desc'],
-                    automembertargetgroup=[hostgroup1_dn],
-                    objectclass=objectclasses.automember,
-                    dn=DN(('cn', hostgroup1), ('cn', 'hostgroup'),
-                          ('cn', 'automember'), ('cn', 'etc'), api.env.basedn),
-                ),
-            ),
-        ),
-
-        dict(
-            desc='Create automember condition: %r' % hostgroup1,
-            command=(
-                'automember_add_condition', [hostgroup1], dict(
-                    key=u'fqdn', type=u'hostgroup',
-                    automemberinclusiveregex=[hostgroup_include_regex],
-                )
-            ),
-            expected=dict(
-                value=hostgroup1,
-                summary=u'Added condition(s) to "%s"' % hostgroup1,
-                completed=1,
-                failed=dict(
-                    failed=dict(
-                        automemberinclusiveregex=tuple(),
-                        automemberexclusiveregex=tuple(),
-                    )
-                ),
-                result=dict(
-                    cn=[hostgroup1],
-                    description=[u'Test desc'],
-                    automemberinclusiveregex=[
-                        u'fqdn=%s' % hostgroup_include_regex
-                    ],
-                    automembertargetgroup=[hostgroup1_dn],
-                ),
-            ),
-        ),
 
-        dict(
-            desc='Retrieve hostgroup: %r' % hostgroup1,
-            command=('hostgroup_show', [hostgroup1], dict()),
-            expected=dict(
-                value=hostgroup1,
-                summary=None,
-                result=dict(
-                    dn=hostgroup1_dn,
-                    cn=[hostgroup1],
-                    description=[u'Test desc'],
-                ),
-            ),
-        ),
-
-        dict(
-            desc='Rebuild membership for hostgroups',
-            command=('automember_rebuild', [], dict(type=u'hostgroup')),
-            expected=dict(
-                value=None,
-                summary=fuzzy_automember_message,
-                result=dict()
-            ),
-        ),
-
-        dict(
-            desc='Rebuild membership for hostgroups asynchronously',
-            command=('automember_rebuild', [], dict(type=u'hostgroup',no_wait=True)),
-            expected=dict(
-                value=None,
-                summary=u'Automember rebuild membership task started',
-                result=dict(
-                    dn=fuzzy_automember_dn
-                ),
-            ),
-        ),
+@pytest.fixture(scope='class')
+def user1(request, manager1):
+    """ User tracker with assigned manager """
+    tracker = UserTracker(name=u'tuser1', sn=u'User1', manager=manager1.name,
+                          givenname=u'Automember test user1')
+    return tracker.make_fixture(request)
 
-        dict(
-            desc='Retrieve hostgroup: %r' % hostgroup1,
-            command=('hostgroup_show', [hostgroup1], dict()),
-            expected=dict(
-                value=hostgroup1,
-                summary=None,
-                result=dict(
-                    dn=hostgroup1_dn,
-                    member_host=[u'%s' % fqdn1],
-                    cn=[hostgroup1],
-                    description=[u'Test desc'],
-                ),
-            ),
-        ),
-
-        dict(
-            desc='Remove host %r from hostgroup %r' % (fqdn1, hostgroup1),
-            command=(
-                'hostgroup_remove_member',
-                [hostgroup1],
-                dict(host=fqdn1)
-            ),
-            expected=dict(
-                failed=dict(
-                    member=dict(
-                        host=tuple(),
-                        hostgroup=tuple(),
-                    ),
-                ),
-                completed=1,
-                result=dict(
-                    dn=hostgroup1_dn,
-                    cn=[hostgroup1],
-                    description=[u'Test desc'],
-                ),
-            ),
-        ),
 
-        dict(
-            desc='Retrieve hostgroup: %r' % hostgroup1,
-            command=('hostgroup_show', [hostgroup1], dict()),
-            expected=dict(
-                value=hostgroup1,
-                summary=None,
-                result=dict(
-                    dn=hostgroup1_dn,
-                    cn=[hostgroup1],
-                    description=[u'Test desc'],
-                ),
-            ),
-        ),
-
-        dict(
-            desc='Try to rebuild membership (no options)',
-            command=('automember_rebuild', [], dict()),
-            expected=errors.MutuallyExclusiveError(
-                reason=(u'at least one of options: type, users, hosts must be '
-                        'specified')
-            )
-        ),
-
-        dict(
-            desc='Try to rebuild membership (--users and --hosts together)',
-            command=(
-                'automember_rebuild',
-                [],
-                dict(users=user1, hosts=fqdn1)
-            ),
-            expected=errors.MutuallyExclusiveError(
-                reason=u'users and hosts cannot both be set'
-            )
-        ),
-
-        dict(
-            desc='Try to rebuild membership (--users and --type=hostgroup)',
-            command=(
-                'automember_rebuild',
-                [],
-                dict(users=user1, type=u'hostgroup')
-            ),
-            expected=errors.MutuallyExclusiveError(
-                reason="users cannot be set when type is 'hostgroup'"
-            )
-        ),
-
-        dict(
-            desc='Try to rebuild membership (--hosts and --type=group)',
-            command=(
-                'automember_rebuild',
-                [],
-                dict(hosts=fqdn1, type=u'group')
-            ),
-            expected=errors.MutuallyExclusiveError(
-                reason=u"hosts cannot be set when type is 'group'"
-            )
-        ),
-
-        dict(
-            desc='Rebuild membership for host: %s' % fqdn1,
-            command=('automember_rebuild', [], dict(hosts=fqdn1)),
-            expected=dict(
-                value=None,
-                summary=fuzzy_automember_message,
-                result=dict()
-            ),
-        ),
-
-        dict(
-            desc='Rebuild membership for host: %s asynchronously' % fqdn1,
-            command=('automember_rebuild', [], dict(hosts=fqdn1, no_wait=True)),
-            expected=dict(
-                value=None,
-                summary=u'Automember rebuild membership task started',
-                result=dict(
-                    dn=fuzzy_automember_dn
-                ),
-            ),
-        ),
+@pytest.fixture(scope='class')
+def group1(request):
+    tracker = GroupTracker(name=u'tgroup1',
+                           description=u'Automember test group1')
+    return tracker.make_fixture(request)
 
-        dict(
-            desc='Retrieve hostgroup: %r' % hostgroup1,
-            command=('hostgroup_show', [hostgroup1], dict()),
-            expected=dict(
-                value=hostgroup1,
-                summary=None,
-                result=dict(
-                    dn=hostgroup1_dn,
-                    member_host=[u'%s' % fqdn1],
-                    cn=[hostgroup1],
-                    description=[u'Test desc'],
-                ),
-            ),
-        ),
-
-        dict(
-            desc='Delete host: %r' % fqdn1,
-            command=('host_del', [fqdn1], dict()),
-            expected=dict(
-                value=[fqdn1],
-                summary=u'Deleted host "%s"' % fqdn1,
-                result=dict(failed=[]),
-            ),
-        ),
-
-        dict(
-            desc='Delete hostgroup: %r' % hostgroup1,
-            command=('hostgroup_del', [hostgroup1], dict()),
-            expected=dict(
-                value=[hostgroup1],
-                summary=u'Deleted hostgroup "%s"' % hostgroup1,
-                result=dict(failed=[]),
-            ),
-        ),
-
-        dict(
-            desc='Delete automember rule: %r' % hostgroup1,
-            command=('automember_del', [hostgroup1], dict(type=u'hostgroup')),
-            expected=dict(
-                value=[hostgroup1],
-                summary=u'Deleted automember rule "%s"' % hostgroup1,
-                result=dict(failed=[]),
-            ),
-        ),
 
-        dict(
-            desc='Create group: %r' % group1,
-            command=(
-                'group_add', [group1], dict(description=u'Test desc')
-            ),
-            expected=dict(
-                value=group1,
-                summary=u'Added group "%s"' % group1,
-                result=dict(
-                    cn=[group1],
-                    description=[u'Test desc'],
-                    objectclass=objectclasses.group + [u'posixgroup'],
-                    ipauniqueid=[fuzzy_uuid],
-                    gidnumber=[fuzzy_digits],
-                    dn=group1_dn
-                ),
-            ),
-        ),
+@pytest.fixture(scope='class')
+def defaultgroup1(request):
+    tracker = GroupTracker(name=u'defaultgroup1',
+                           description=u'Automember test defaultgroup1')
+    return tracker.make_fixture(request)
 
-        dict(
-            desc='Create user: %r' % manager1,
-            command=(
-                'user_add', [manager1], dict(givenname=u'Michael', sn=u'Scott')
-            ),
-            expected=dict(
-                value=manager1,
-                summary=u'Added user "mscott"',
-                result=get_user_result(manager1, u'Michael', u'Scott', 'add'),
-            ),
-        ),
-
-        dict(
-            desc='Create user: %r' % user1,
-            command=(
-                'user_add',
-                [user1],
-                dict(givenname=u'Test', sn=u'User1', manager=manager1)
-            ),
-            expected=dict(
-                value=user1,
-                summary=u'Added user "tuser1"',
-                result=get_user_result(
-                    user1, u'Test', u'User1', 'add',
-                    manager=[DN(('uid', 'mscott'), ('cn', 'users'),
-                                ('cn', 'accounts'), api.env.basedn)]
-                )
-            ),
-        ),
 
+@pytest.fixture(scope='class')
+def hostgroup1(request):
+    tracker = HostGroupTracker(name=u'thostgroup1',
+                               description=u'Automember test hostgroup1')
+    return tracker.make_fixture(request)
 
-        dict(
-            desc='Create automember rule: %r' % group1,
-            command=(
-                'automember_add', [group1], dict(
-                    description=u'Test desc', type=u'group',
-                )
-            ),
-            expected=dict(
-                value=group1,
-                summary=u'Added automember rule "%s"' % group1,
-                result=dict(
-                    cn=[group1],
-                    description=[u'Test desc'],
-                    automembertargetgroup=[group1_dn],
-                    objectclass=objectclasses.automember,
-                    dn=DN(('cn', group1), ('cn', 'group'),
-                          ('cn', 'automember'), ('cn', 'etc'), api.env.basedn),
-                ),
-            ),
-        ),
-
-        dict(
-            desc='Create automember condition: %r' % group1,
-            command=(
-                'automember_add_condition', [group1], dict(
-                    key=u'manager', type=u'group',
-                    automemberinclusiveregex=[group_include_regex],
-                )
-            ),
-            expected=dict(
-                value=group1,
-                summary=u'Added condition(s) to "%s"' % group1,
-                completed=1,
-                failed=dict(
-                    failed=dict(
-                        automemberinclusiveregex=tuple(),
-                        automemberexclusiveregex=tuple(),
-                    )
-                ),
-                result=dict(
-                    cn=[group1],
-                    description=[u'Test desc'],
-                    automemberinclusiveregex=[
-                        u'manager=%s' % group_include_regex
-                    ],
-                    automembertargetgroup=[group1_dn],
-                ),
-            ),
-        ),
 
-        dict(
-            desc='Retrieve group: %r' % group1,
-            command=('group_show', [group1], dict()),
-            expected=dict(
-                value=group1,
-                summary=None,
-                result=dict(
-                    dn=group1_dn,
-                    cn=[group1],
-                    description=[u'Test desc'],
-                    gidnumber=[fuzzy_digits],
-                ),
-            ),
-        ),
-
-        dict(
-            desc='Rebuild membership for groups',
-            command=('automember_rebuild', [], dict(type=u'group')),
-            expected=dict(
-                value=None,
-                summary=fuzzy_automember_message,
-                result=dict()
-            ),
-        ),
-
-        dict(
-            desc='Rebuild membership for groups asynchronously',
-            command=('automember_rebuild', [], dict(type=u'group', no_wait=True)),
-            expected=dict(
-                value=None,
-                summary=u'Automember rebuild membership task started',
-                result=dict(
-                    dn=fuzzy_automember_dn
-                ),
-            ),
-        ),
+@pytest.fixture(scope='class')
+def hostgroup2(request):
+    tracker = HostGroupTracker(name=u'thostgroup2',
+                               description=u'Automember test hostgroup2')
+    return tracker.make_fixture(request)
 
-        dict(
-            desc='Retrieve group: %r' % group1,
-            command=('group_show', [group1], dict()),
-            expected=dict(
-                value=group1,
-                summary=None,
-                result=dict(
-                    dn=group1_dn,
-                    member_user=[u'%s' % user1],
-                    cn=[group1],
-                    description=[u'Test desc'],
-                    gidnumber=[fuzzy_digits],
-                ),
-            ),
-        ),
-
-        dict(
-            desc='Remove user %r from group %r' % (fqdn1, hostgroup1),
-            command=(
-                'group_remove_member',
-                [group1],
-                dict(user=user1)
-            ),
-            expected=dict(
-                failed=dict(
-                    member=dict(
-                        user=tuple(),
-                        group=tuple(),
-                    ),
-                ),
-                completed=1,
-                result=dict(
-                    dn=group1_dn,
-                    cn=[group1],
-                    description=[u'Test desc'],
-                    gidnumber=[fuzzy_digits],
-                ),
-            ),
-        ),
 
-        dict(
-            desc='Retrieve group: %r' % group1,
-            command=('group_show', [group1], dict()),
-            expected=dict(
-                value=group1,
-                summary=None,
-                result=dict(
-                    dn=group1_dn,
-                    cn=[group1],
-                    description=[u'Test desc'],
-                    gidnumber=[fuzzy_digits],
-                ),
-            ),
-        ),
-
-        dict(
-            desc='Rebuild membership for user: %s' % user1,
-            command=('automember_rebuild', [], dict(users=user1)),
-            expected=dict(
-                value=None,
-                summary=fuzzy_automember_message,
-                result=dict()
-            ),
-        ),
-
-        dict(
-            desc='Rebuild membership for user: %s asynchronously' % user1,
-            command=('automember_rebuild', [], dict(users=user1, no_wait=True)),
-            expected=dict(
-                value=None,
-                summary=u'Automember rebuild membership task started',
-                result=dict(
-                    dn=fuzzy_automember_dn
-                ),
-            ),
-        ),
+@pytest.fixture(scope='class')
+def hostgroup3(request):
+    tracker = HostGroupTracker(name=u'thostgroup3',
+                               description=u'Automember test hostgroup3')
+    return tracker.make_fixture(request)
 
-        dict(
-            desc='Retrieve group: %r' % group1,
-            command=('group_show', [group1], dict()),
-            expected=dict(
-                value=group1,
-                summary=None,
-                result=dict(
-                    dn=group1_dn,
-                    member_user=[u'%s' % user1],
-                    cn=[group1],
-                    description=[u'Test desc'],
-                    gidnumber=[fuzzy_digits],
-                ),
-            ),
-        ),
-
-        dict(
-            desc='Delete user: %r' % user1,
-            command=('user_del', [user1], dict()),
-            expected=dict(
-                value=[user1],
-                summary=u'Deleted user "%s"' % user1,
-                result=dict(failed=[]),
-            ),
-        ),
-
-        dict(
-            desc='Delete user: %r' % manager1,
-            command=('user_del', [manager1], dict()),
-            expected=dict(
-                value=[manager1],
-                summary=u'Deleted user "%s"' % manager1,
-                result=dict(failed=[]),
-            ),
-        ),
-
-        dict(
-            desc='Delete group: %r' % group1,
-            command=('group_del', [group1], dict()),
-            expected=dict(
-                value=[group1],
-                summary=u'Deleted group "%s"' % group1,
-                result=dict(failed=[]),
-            ),
-        ),
-
-        dict(
-            desc='Delete automember rule: %r' % group1,
-            command=('automember_del', [group1], dict(type=u'group')),
-            expected=dict(
-                value=[group1],
-                summary=u'Deleted automember rule "%s"' % group1,
-                result=dict(failed=[]),
-            ),
-        ),
 
-        # End of automember rebuild membership tests
+@pytest.fixture(scope='class')
+def hostgroup4(request):
+    tracker = HostGroupTracker(name=u'thostgroup4',
+                               description=u'Automember test hostgroup4')
+    return tracker.make_fixture(request)
 
-        dict(
-            desc='Create %r' % group1,
-            command=(
-                'group_add', [group1], dict(description=u'Test desc')
-            ),
-            expected=dict(
-                value=group1,
-                summary=u'Added group "%s"' % group1,
-                result=dict(
-                    cn=[group1],
-                    description=[u'Test desc'],
-                    gidnumber=[fuzzy_digits],
-                    objectclass=objectclasses.group + [u'posixgroup'],
-                    ipauniqueid=[fuzzy_uuid],
-                    dn=DN(('cn', group1), ('cn', 'groups'), ('cn', 'accounts'), api.env.basedn),
-                ),
-            ),
-        ),
 
+@pytest.fixture(scope='class')
+def defaulthostgroup1(request):
+    tracker = HostGroupTracker(name=u'defaulthostgroup1',
+                               description=u'Automember test'
+                                           'defaulthostgroup1')
+    return tracker.make_fixture(request)
 
-        dict(
-            desc='Create %r' % hostgroup1,
-            command=(
-                'hostgroup_add', [hostgroup1], dict(description=u'Test desc')
-            ),
-            expected=dict(
-                value=hostgroup1,
-                summary=u'Added hostgroup "%s"' % hostgroup1,
-                result=dict(
-                    cn=[hostgroup1],
-                    description=[u'Test desc'],
-                    objectclass=objectclasses.hostgroup,
-                    ipauniqueid=[fuzzy_uuid],
-                    mepmanagedentry=[DN(('cn', hostgroup1), ('cn', 'ng'), ('cn', 'alt'), api.env.basedn)],
-                    dn=DN(('cn', hostgroup1), ('cn', 'hostgroups'), ('cn', 'accounts'), api.env.basedn),
-                ),
-            ),
-        ),
 
+@pytest.fixture(scope='class')
+def host1(request):
+    tracker = HostTracker(u'web1')
+    return tracker.make_fixture(request)
 
-        dict(
-            desc='Create %r' % hostgroup2,
-            command=(
-                'hostgroup_add', [hostgroup2], dict(description=u'Test desc')
-            ),
-            expected=dict(
-                value=hostgroup2,
-                summary=u'Added hostgroup "%s"' % hostgroup2,
-                result=dict(
-                    cn=[hostgroup2],
-                    description=[u'Test desc'],
-                    objectclass=objectclasses.hostgroup,
-                    ipauniqueid=[fuzzy_uuid],
-                    mepmanagedentry=[DN(('cn', hostgroup2), ('cn', 'ng'), ('cn', 'alt'), api.env.basedn)],
-                    dn=DN(('cn', hostgroup2), ('cn', 'hostgroups'), ('cn', 'accounts'), api.env.basedn),
-                ),
-            ),
-        ),
 
+@pytest.fixture(scope='class')
+def host2(request):
+    tracker = HostTracker(u'dev1')
+    return tracker.make_fixture(request)
 
-        dict(
-            desc='Create %r' % hostgroup3,
-            command=(
-                'hostgroup_add', [hostgroup3], dict(description=u'Test desc')
-            ),
-            expected=dict(
-                value=hostgroup3,
-                summary=u'Added hostgroup "%s"' % hostgroup3,
-                result=dict(
-                    cn=[hostgroup3],
-                    description=[u'Test desc'],
-                    objectclass=objectclasses.hostgroup,
-                    ipauniqueid=[fuzzy_uuid],
-                    mepmanagedentry=[DN(('cn', hostgroup3), ('cn', 'ng'), ('cn', 'alt'), api.env.basedn)],
-                    dn=DN(('cn', hostgroup3), ('cn', 'hostgroups'), ('cn', 'accounts'), api.env.basedn),
-                ),
-            ),
-        ),
 
+@pytest.fixture(scope='class')
+def host3(request):
+    tracker = HostTracker(u'web5')
+    return tracker.make_fixture(request)
 
-        dict(
-            desc='Create %r' % hostgroup4,
-            command=(
-                'hostgroup_add', [hostgroup4], dict(description=u'Test desc')
-            ),
-            expected=dict(
-                value=hostgroup4,
-                summary=u'Added hostgroup "%s"' % hostgroup4,
-                result=dict(
-                    cn=[hostgroup4],
-                    description=[u'Test desc'],
-                    objectclass=objectclasses.hostgroup,
-                    ipauniqueid=[fuzzy_uuid],
-                    mepmanagedentry=[DN(('cn', hostgroup4), ('cn', 'ng'), ('cn', 'alt'), api.env.basedn)],
-                    dn=DN(('cn', hostgroup4), ('cn', 'hostgroups'), ('cn', 'accounts'), api.env.basedn),
-                ),
-            ),
-        ),
 
+@pytest.fixture(scope='class')
+def host4(request):
+    tracker = HostTracker(u'www5')
+    return tracker.make_fixture(request)
 
-        dict(
-            desc='Create %r' % defaultgroup1,
-            command=(
-                'group_add', [defaultgroup1], dict(description=u'Default test desc')
-            ),
-            expected=dict(
-                value=defaultgroup1,
-                summary=u'Added group "%s"' % defaultgroup1,
-                result=dict(
-                    cn=[defaultgroup1],
-                    description=[u'Default test desc'],
-                    gidnumber=[fuzzy_digits],
-                    objectclass=objectclasses.group + [u'posixgroup'],
-                    ipauniqueid=[fuzzy_uuid],
-                    dn=DN(('cn', defaultgroup1), ('cn', 'groups'), ('cn', 'accounts'), api.env.basedn),
-                ),
-            ),
-        ),
 
+@pytest.fixture(scope='class')
+def host5(request):
+    tracker = HostTracker(u'webserver5')
+    return tracker.make_fixture(request)
 
-        dict(
-            desc='Create %r' % defaulthostgroup1,
-            command=(
-                'hostgroup_add', [defaulthostgroup1], dict(description=u'Default test desc')
-            ),
-            expected=dict(
-                value=defaulthostgroup1,
-                summary=u'Added hostgroup "%s"' % defaulthostgroup1,
-                result=dict(
-                    cn=[defaulthostgroup1],
-                    description=[u'Default test desc'],
-                    objectclass=objectclasses.hostgroup,
-                    ipauniqueid=[fuzzy_uuid],
-                    mepmanagedentry=[DN(('cn', defaulthostgroup1), ('cn', 'ng'), ('cn', 'alt'), api.env.basedn)],
-                    dn=DN(('cn', defaulthostgroup1), ('cn', 'hostgroups'), ('cn', 'accounts'), api.env.basedn),
-                ),
-            ),
-        ),
 
+@pytest.fixture(scope='class')
+def automember_group(request, group1):
+    tracker = AutomemberTracker(groupname=group1.cn,
+                                description=u'Automember group tracker',
+                                membertype=u'group')
+    return tracker.make_fixture(request)
 
-        dict(
-            desc='Create automember %r' % group1,
-            command=(
-                'automember_add', [group1], dict(description=u'Test desc', type=u'group')
-            ),
-            expected=dict(
-                value=group1,
-                summary=u'Added automember rule "%s"' % group1,
-                result=dict(
-                    cn=[group1],
-                    description=[u'Test desc'],
-                    automembertargetgroup=[DN(('cn', group1), ('cn', 'groups'), ('cn', 'accounts'), api.env.basedn)],
-                    objectclass=objectclasses.automember,
-                    dn=DN(('cn', group1), ('cn', 'group'), ('cn', 'automember'), ('cn', 'etc'), api.env.basedn),
-                ),
-            ),
-        ),
 
+@pytest.fixture(scope='class')
+def automember_hostgroup(request, hostgroup1):
+    tracker = AutomemberTracker(groupname=hostgroup1.cn,
+                                description=u'Automember hostgroup tracker',
+                                membertype=u'hostgroup')
+    return tracker.make_fixture(request)
 
-        dict(
-            desc='Create automember condition %r' % group1,
-            command=(
-                'automember_add_condition', [group1], dict(
-                    key=u'manager', type=u'group',
-                    automemberinclusiveregex=[group_include_regex],
-                )
-            ),
-            expected=dict(
-                value=group1,
-                summary=u'Added condition(s) to "%s"' % group1,
-                completed=1,
-                failed=dict(
-                    failed = dict(
-                        automemberinclusiveregex=tuple(),
-                        automemberexclusiveregex=tuple(),
-                    )
-                ),
-                result=dict(
-                    cn=[group1],
-                    description=[u'Test desc'],
-                    automemberinclusiveregex=[u'manager=%s' % group_include_regex],
-                    automembertargetgroup=[DN(('cn', group1), ('cn', 'groups'), ('cn', 'accounts'), api.env.basedn)],
-                ),
-            ),
-        ),
 
+@pytest.fixture(scope='class')
+def automember_hostgroup2(request, hostgroup2):
+    tracker = AutomemberTracker(groupname=hostgroup2.cn,
+                                description=u'Automember hostgroup tracker 2',
+                                membertype=u'hostgroup')
+    return tracker.make_fixture(request)
 
-        dict(
-            desc='Create automember %r' % hostgroup1,
-            command=(
-                'automember_add', [hostgroup1], dict(
-                    description=u'Test desc', type=u'hostgroup',
-                )
-            ),
-            expected=dict(
-                value=hostgroup1,
-                summary=u'Added automember rule "%s"' % hostgroup1,
-                result=dict(
-                    cn=[hostgroup1],
-                    description=[u'Test desc'],
-                    automembertargetgroup=[DN(('cn', hostgroup1), ('cn', 'hostgroups'), ('cn', 'accounts'), api.env.basedn)],
-                    objectclass=objectclasses.automember,
-                    dn=DN(('cn', hostgroup1), ('cn', 'hostgroup'), ('cn', 'automember'), ('cn', 'etc'), api.env.basedn),
-                ),
-            ),
-        ),
 
+@pytest.fixture(scope='class')
+def automember_hostgroup3(request, hostgroup3):
+    tracker = AutomemberTracker(groupname=hostgroup3.cn,
+                                description=u'Automember hostgroup tracker 3',
+                                membertype=u'hostgroup')
 
-        dict(
-            desc='Create automember condition %r' % hostgroup1,
-            command=(
-                'automember_add_condition', [hostgroup1], dict(
-                    key=u'fqdn', type=u'hostgroup',
-                    automemberinclusiveregex=[hostgroup_include_regex],
-                )
-            ),
-            expected=dict(
-                value=hostgroup1,
-                summary=u'Added condition(s) to "%s"' % hostgroup1,
-                completed=1,
-                failed=dict(
-                    failed = dict(
-                        automemberinclusiveregex=tuple(),
-                        automemberexclusiveregex=tuple(),
-                    )
-                ),
-                result=dict(
-                    cn=[hostgroup1],
-                    description=[u'Test desc'],
-                    automemberinclusiveregex=[u'fqdn=%s' % hostgroup_include_regex],
-                    automembertargetgroup=[DN(('cn', hostgroup1), ('cn', 'hostgroups'), ('cn', 'accounts'), api.env.basedn)],
-                ),
-            ),
-        ),
+    return tracker.make_fixture(request)
 
 
-        dict(
-            desc='Create duplicate automember condition %r' % hostgroup1,
-            command=(
-                'automember_add_condition', [hostgroup1], dict(
-                    key=u'fqdn', type=u'hostgroup',
-                    automemberinclusiveregex=[hostgroup_include_regex],
-                )
-            ),
-            expected=dict(
-                value=hostgroup1,
-                summary=u'Added condition(s) to "%s"' % hostgroup1,
-                completed=0,
-                failed=dict(
-                    failed = dict(
-                        automemberinclusiveregex=tuple(),
-                        automemberexclusiveregex=tuple(),
-                    )
-                ),
-                result=dict(
-                    automemberinclusiveregex=[u'fqdn=%s' % hostgroup_include_regex],
-                ),
-            ),
-        ),
+@pytest.fixture(scope='class')
+def automember_hostgroup4(request, hostgroup4):
+    tracker = AutomemberTracker(groupname=hostgroup4.cn,
+                                description=u'Automember hostgroup tracker 4',
+                                membertype=u'hostgroup')
+    return tracker.make_fixture(request)
 
 
-        dict(
-            desc='Create additional automember conditions %r' % hostgroup1,
-            command=(
-                'automember_add_condition', [hostgroup1], dict(
-                    key=u'fqdn', type=u'hostgroup',
-                    automemberinclusiveregex=[hostgroup_include_regex2, hostgroup_include_regex3],
-                    automemberexclusiveregex=[hostgroup_exclude_regex, hostgroup_exclude_regex2, hostgroup_exclude_regex3],
-                )
-            ),
-            expected=dict(
-                value=hostgroup1,
-                summary=u'Added condition(s) to "%s"' % hostgroup1,
-                completed=5,
-                failed=dict(
-                    failed = dict(
-                        automemberinclusiveregex=tuple(),
-                        automemberexclusiveregex=tuple(),
-                    )
-                ),
-                result=dict(
-                    cn=[hostgroup1],
-                    description=[u'Test desc'],
-                    automembertargetgroup=[DN(('cn', hostgroup1), ('cn', 'hostgroups'), ('cn', 'accounts'), api.env.basedn)],
-                    automemberinclusiveregex=[u'fqdn=%s' % hostgroup_include_regex,
-                                              u'fqdn=%s' % hostgroup_include_regex3,
-                                              u'fqdn=%s' % hostgroup_include_regex2,
-                    ],
-                    automemberexclusiveregex=[u'fqdn=%s' % hostgroup_exclude_regex2,
-                                              u'fqdn=%s' % hostgroup_exclude_regex3,
-                                              u'fqdn=%s' % hostgroup_exclude_regex,
-                    ],
-                ),
-            ),
-        ),
-
-
-        dict(
-            desc='Create automember %r' % hostgroup2,
-            command=(
-                'automember_add', [hostgroup2], dict(
-                    description=u'Test desc', type=u'hostgroup',
-                )
-            ),
-            expected=dict(
-                value=hostgroup2,
-                summary=u'Added automember rule "%s"' % hostgroup2,
-                result=dict(
-                    cn=[hostgroup2],
-                    description=[u'Test desc'],
-                    automembertargetgroup=[DN(('cn', hostgroup2), ('cn', 'hostgroups'), ('cn', 'accounts'), api.env.basedn)],
-                    objectclass=objectclasses.automember,
-                    dn=DN(('cn', hostgroup2), ('cn', 'hostgroup'), ('cn', 'automember'), ('cn', 'etc'), api.env.basedn),
-                ),
-            ),
-        ),
+@pytest.mark.tier1
+class TestNonexistentAutomember(XMLRPC_test):
+    def test_create_with_nonexistent_group(self, automember_group, group1):
+        """ Try to add a rule with non-existent group """
+        group1.ensure_missing()
+        command = automember_group.make_create_command()
+        with raises_exact(errors.NotFound(
+                reason=u'group "%s" not found' % group1.cn)):
+            command()
+
+    def test_delete_with_nonexistent_group(self, automember_group, group1):
+        """ Try to delete a rule with non-existent group """
+        group1.ensure_missing()
+        command = automember_group.make_delete_command()
+        with raises_exact(errors.NotFound(
+                reason=u': Automember rule not found')):
+            command()
+
+    def test_create_with_nonexistent_hostgroup(self, automember_hostgroup,
+                                               hostgroup1):
+        """ Try to add a rule with non-existent group """
+        hostgroup1.ensure_missing()
+        command = automember_hostgroup.make_create_command()
+        with raises_exact(errors.NotFound(
+                reason=u'hostgroup "%s" not found' % hostgroup1.cn)):
+            command()
+
+    def test_delete_with_nonexistent_hostgroup(self, automember_hostgroup,
+                                               hostgroup1):
+        """ Try to delete a rule with non-existent group """
+        hostgroup1.ensure_missing()
+        command = automember_hostgroup.make_delete_command()
+        with raises_exact(errors.NotFound(
+                reason=u': Automember rule not found')):
+            command()
 
 
-        dict(
-            desc='Create automember condition %r' % hostgroup2,
-            command=(
-                'automember_add_condition', [hostgroup2], dict(
-                    key=u'fqdn', type=u'hostgroup',
-                    automemberinclusiveregex=[hostgroup_exclude_regex],
-                )
-            ),
-            expected=dict(
-                value=hostgroup2,
-                summary=u'Added condition(s) to "%s"' % hostgroup2,
-                completed=1,
-                failed=dict(
-                    failed = dict(
-                        automemberinclusiveregex=tuple(),
-                        automemberexclusiveregex=tuple(),
-                    )
-                ),
-                result=dict(
-                    cn=[hostgroup2],
-                    description=[u'Test desc'],
-                    automemberinclusiveregex=[u'fqdn=%s' % hostgroup_exclude_regex],
-                    automembertargetgroup=[DN(('cn', hostgroup2), ('cn', 'hostgroups'), ('cn', 'accounts'), api.env.basedn)],
-                ),
-            ),
-        ),
+@pytest.mark.tier1
+class TestCRUDFOnAutomember(XMLRPC_test):
+    def test_basic_ops_on_group_automember(self, automember_group, group1):
+        """ Test create, retrieve, find, update,
+        and delete operations on a group automember """
+        group1.create()
+        automember_group.create()
+        automember_group.retrieve()
+        automember_group.find()
+        automember_group.update(dict(description=u'New description'))
+        automember_group.delete()
+
+    def test_basic_ops_on_hostgroup_automember(self, automember_hostgroup,
+                                               hostgroup1):
+        """ Test create, retrieve, find, update,
+        and delete operations on a hostgroup automember """
+        hostgroup1.create()
+        automember_hostgroup.create()
+        automember_hostgroup.retrieve()
+        automember_hostgroup.find()
+        automember_hostgroup.update(dict(description=u'New description'))
+        automember_hostgroup.delete()
 
 
-        dict(
-            desc='Create automember %r' % hostgroup3,
-            command=(
-                'automember_add', [hostgroup3], dict(
-                    description=u'Test desc', type=u'hostgroup',
-                )
-            ),
-            expected=dict(
-                value=hostgroup3,
-                summary=u'Added automember rule "%s"' % hostgroup3,
-                result=dict(
-                    cn=[hostgroup3],
-                    description=[u'Test desc'],
-                    automembertargetgroup=[DN(('cn', hostgroup3), ('cn', 'hostgroups'), ('cn', 'accounts'), api.env.basedn)],
-                    objectclass=objectclasses.automember,
-                    dn=DN(('cn', hostgroup3), ('cn', 'hostgroup'), ('cn', 'automember'), ('cn', 'etc'), api.env.basedn),
-                ),
-            ),
-        ),
+@pytest.mark.tier1
+class TestAutomemberRebuildHostMembership(XMLRPC_test):
+    def test_create_deps_for_rebuilding_hostgroups(self, hostgroup1, host1,
+                                                   automember_hostgroup):
+        """ Create host, hostgroup, and automember tracker for this class
+        of tests """
+        hostgroup1.ensure_exists()
+        host1.ensure_exists()
+        automember_hostgroup.ensure_exists()
+        automember_hostgroup.add_condition(
+            key=u'fqdn', type=u'hostgroup',
+            inclusiveregex=[hostgroup_include_regex]
+        )
+        hostgroup1.retrieve()
+
+    def test_rebuild_membership_hostgroups(self, automember_hostgroup,
+                                           hostgroup1, host1):
+        """ Rebuild automember membership for hosts, both synchonously and
+        asynchronously. Check the host has been added to the hostgroup. """
+        automember_hostgroup.rebuild()
+        automember_hostgroup.rebuild(no_wait=True)
+        hostgroup1.attrs.update(member_host=[host1.fqdn])
+        hostgroup1.retrieve()
+        hostgroup1.remove_member(dict(host=host1.fqdn))
+        hostgroup1.retrieve()
+
+    def test_rebuild_membership_for_host(self, host1, automember_hostgroup,
+                                         hostgroup1):
+        """ Rebuild automember membership for one host, both synchronously and
+        asynchronously. Check the host has been added to the hostgroup. """
+        command = automember_hostgroup.make_rebuild_command(hosts=host1.fqdn)
+        result = command()
+        automember_hostgroup.check_rebuild(result)
+
+        command = automember_hostgroup.make_rebuild_command(hosts=host1.fqdn,
+                                                            no_wait=True)
+        result = command()
+        automember_hostgroup.check_rebuild(result, no_wait=True)
+
+        hostgroup1.attrs.update(member_host=[host1.fqdn])
+        hostgroup1.retrieve()
+
+    def test_delete_deps_for_rebuilding_hostgroups(self, host1, hostgroup1,
+                                                   automember_hostgroup):
+        """ Delete dependences for this class of tests in desired order """
+        host1.delete()
+        hostgroup1.delete()
+        automember_hostgroup.delete()
 
 
-        dict(
-            desc='Create automember condition %r' % hostgroup3,
-            command=(
-                'automember_add_condition', [hostgroup3], dict(
-                    key=u'fqdn', type=u'hostgroup',
-                    automemberinclusiveregex=[hostgroup_exclude_regex2],
-                )
-            ),
-            expected=dict(
-                value=hostgroup3,
-                summary=u'Added condition(s) to "%s"' % hostgroup3,
-                completed=1,
-                failed=dict(
-                    failed = dict(
-                        automemberinclusiveregex=tuple(),
-                        automemberexclusiveregex=tuple(),
-                    )
-                ),
-                result=dict(
-                    cn=[hostgroup3],
-                    description=[u'Test desc'],
-                    automemberinclusiveregex=[u'fqdn=%s' % hostgroup_exclude_regex2],
-                    automembertargetgroup=[DN(('cn', hostgroup3), ('cn', 'hostgroups'), ('cn', 'accounts'), api.env.basedn)],
-                ),
-            ),
-        ),
+@pytest.mark.tier1
+class TestAutomemberRebuildGroupMembership(XMLRPC_test):
+    def test_create_deps_for_rebuilding_groups(self, group1, manager1, user1,
+                                               automember_group):
+        """ Create users, groups, and automember tracker for this class
+        of tests """
+        group1.ensure_exists()
+        manager1.ensure_exists()
+        user1.ensure_exists()
+        automember_group.ensure_exists()
+        automember_group.add_condition(
+            key=u'manager', type=u'group', inclusiveregex=[group_include_regex]
+        )
+        group1.retrieve()
+
+    def test_rebuild_membership_groups(self, automember_group, group1, user1):
+        """ Rebuild automember membership for groups, both synchonously and
+        asynchronously. Check the user has been added to the group. """
+        automember_group.rebuild()
+        automember_group.rebuild(no_wait=True)
+        group1.attrs.update(member_user=[user1.name])
+        group1.retrieve()
+        group1.remove_member(dict(user=user1.name))
+        group1.retrieve()
+
+    def test_rebuild_membership_for_user(self, user1, automember_group,
+                                         group1):
+        """ Rebuild automember membership for one user, both synchronously and
+        asynchronously. Check the user has been added to the group. """
+        command = automember_group.make_rebuild_command(users=user1.name)
+        result = command()
+        automember_group.check_rebuild(result)
+        command = automember_group.make_rebuild_command(users=user1.name,
+                                                        no_wait=True)
+        result = command()
+        automember_group.check_rebuild(result, no_wait=True)
+        group1.attrs.update(member_user=[user1.name])
+        group1.retrieve()
+
+    def test_delete_deps_for_rebuilding_groups(self, user1, manager1, group1,
+                                               automember_group):
+        """ Delete dependences for this class of tests in desired order """
+        user1.delete()
+        manager1.delete()
+        group1.delete()
+        automember_group.delete()
 
 
-        dict(
-            desc='Create automember %r' % hostgroup4,
-            command=(
-                'automember_add', [hostgroup4], dict(
-                    description=u'Test desc', type=u'hostgroup',
-                )
-            ),
-            expected=dict(
-                value=hostgroup4,
-                summary=u'Added automember rule "%s"' % hostgroup4,
-                result=dict(
-                    cn=[hostgroup4],
-                    description=[u'Test desc'],
-                    automembertargetgroup=[DN(('cn', hostgroup4), ('cn', 'hostgroups'), ('cn', 'accounts'), api.env.basedn)],
-                    objectclass=objectclasses.automember,
-                    dn=DN(('cn', hostgroup4), ('cn', 'hostgroup'), ('cn', 'automember'), ('cn', 'etc'), api.env.basedn),
-                ),
-            ),
-        ),
+@pytest.mark.tier1
+class TestAutomemberRebuildMembershipIncorrectly(XMLRPC_test):
+    def test_rebuild_membership_hosts_incorrectly(self, automember_hostgroup):
+        """ Try to issue rebuild automember command without 'type' parameter
+        """
+        command = automember_hostgroup.make_rebuild_command()
+        with raises_exact(errors.MutuallyExclusiveError(
+                reason=u'at least one of options: '
+                       'type, users, hosts must be specified')):
+            command()
+
+    def test_rebuild_membership_user_hosts(self, automember_hostgroup, user1,
+                                           host1):
+        """ Try to issue rebuild membership command with --users and --hosts
+        together """
+        command = automember_hostgroup.make_rebuild_command(users=user1.name,
+                                                            hosts=host1.fqdn)
+        with raises_exact(errors.MutuallyExclusiveError(
+                reason=u'users and hosts cannot both be set')):
+            command()
+
+    def test_rebuild_membership_users_hostgroup(self, automember_hostgroup,
+                                                user1):
+        """ Try to issue rebuild membership command with type --hosts and
+        users specified """
+        command = automember_hostgroup.make_rebuild_command(users=user1.name,
+                                                            type=u'hostgroup')
+        with raises_exact(errors.MutuallyExclusiveError(
+                reason=u"users cannot be set when type is 'hostgroup'")):
+            command()
+
+    def test_rebuild_membership_hosts_group(self, automember_hostgroup, user1,
+                                            host1):
+        """ Try to issue rebuild membership command with type --users and
+        hosts specified """
+        command = automember_hostgroup.make_rebuild_command(hosts=host1.fqdn,
+                                                            type=u'group')
+        with raises_exact(errors.MutuallyExclusiveError(
+                reason=u"hosts cannot be set when type is 'group'")):
+            command()
 
 
-        dict(
-            desc='Create automember condition %r' % hostgroup4,
-            command=(
-                'automember_add_condition', [hostgroup4], dict(
-                    key=u'fqdn', type=u'hostgroup',
-                    automemberinclusiveregex=[hostgroup_exclude_regex3],
-                )
-            ),
-            expected=dict(
-                value=hostgroup4,
-                summary=u'Added condition(s) to "%s"' % hostgroup4,
-                completed=1,
+@pytest.mark.tier1
+class TestMultipleAutomemberConditions(XMLRPC_test):
+    def test_create_deps_for_multiple_conditions(
+            self, group1, hostgroup1, hostgroup2, hostgroup3, hostgroup4,
+            defaultgroup1, defaulthostgroup1,
+            automember_group, automember_hostgroup
+            ):
+        """ Create groups, hostgroups, and automember conditions
+        for this class of tests """
+        group1.ensure_exists()
+        hostgroup1.ensure_exists()
+        hostgroup2.ensure_exists()
+        hostgroup3.ensure_exists()
+        hostgroup4.ensure_exists()
+        defaultgroup1.ensure_exists()
+        defaulthostgroup1.ensure_exists()
+
+        automember_group.ensure_exists()
+        automember_group.add_condition(key=u'manager', type=u'group',
+                                       inclusiveregex=[group_include_regex])
+        automember_hostgroup.ensure_exists()
+        automember_hostgroup.add_condition(
+            key=u'fqdn', type=u'hostgroup',
+            inclusiveregex=[hostgroup_include_regex]
+        )
+
+    def test_create_duplicate_automember_condition(self, automember_hostgroup,
+                                                   hostgroup1):
+        """ Try to create a duplicate automember condition """
+        command = automember_hostgroup.make_add_condition_command(
+            key=u'fqdn', type=u'hostgroup',
+            automemberinclusiveregex=[hostgroup_include_regex]
+        )
+        result = command()
+        automember_hostgroup.check_add_condition_negative(result)
+
+    def test_create_additional_automember_conditions(self,
+                                                     automember_hostgroup):
+        """ Add additional automember conditions to existing one, with both
+        inclusive and exclusive regular expressions the condition """
+        command = automember_hostgroup.make_add_condition_command(
+            key=u'fqdn', type=u'hostgroup',
+            automemberinclusiveregex=[hostgroup_include_regex2,
+                                      hostgroup_include_regex3],
+            automemberexclusiveregex=[hostgroup_exclude_regex,
+                                      hostgroup_exclude_regex2,
+                                      hostgroup_exclude_regex3]
+        )
+        result = command()
+
+        expected = dict(
+            value=automember_hostgroup.cn,
+            summary=u'Added condition(s) to "%s"' % automember_hostgroup.cn,
+            completed=5,
+            failed=dict(
                 failed=dict(
-                    failed = dict(
-                        automemberinclusiveregex=tuple(),
-                        automemberexclusiveregex=tuple(),
-                    )
-                ),
-                result=dict(
-                    cn=[hostgroup4],
-                    description=[u'Test desc'],
-                    automemberinclusiveregex=[u'fqdn=%s' % hostgroup_exclude_regex3],
-                    automembertargetgroup=[DN(('cn', hostgroup4), ('cn', 'hostgroups'), ('cn', 'accounts'), api.env.basedn)],
-                ),
-            ),
-        ),
-
-
-        dict(
-            desc="Retrieve automember rule for group %s" % group1,
-            command=('automember_show', [group1], dict(
-                type=u'group',
-                )
-            ),
-            expected=dict(
-                value=group1,
-                result=dict(
-                    cn=[group1],
-                    description=[u'Test desc'],
-                    automemberinclusiveregex=[u'manager=%s' % group_include_regex],
-                    automembertargetgroup=[DN(('cn', group1), ('cn', 'groups'), ('cn', 'accounts'), api.env.basedn)],
-                    dn=DN(('cn', group1), ('cn', 'group'), ('cn', 'automember'), ('cn', 'etc'), api.env.basedn),
-                ),
-                summary=None,
-            ),
-        ),
-
-
-        dict(
-            desc='Search for %r' % group1,
-            command=('automember_find', [group1], dict(
-                type=u'group'
-                )
-            ),
-            expected=dict(
-                count=1,
-                truncated=False,
-                result=[
-                    dict(
-                    cn=[group1],
-                    description=[u'Test desc'],
-                    automemberinclusiveregex=[u'manager=%s' % group_include_regex],
-                    automembertargetgroup=[DN(('cn', group1), ('cn', 'groups'), ('cn', 'accounts'), api.env.basedn)],
-                    dn=DN(('cn', group1), ('cn', 'group'), ('cn', 'automember'), ('cn', 'etc'), api.env.basedn),
-                    ),
-                ],
-                summary=u'1 rules matched',
-            ),
-        ),
-
-
-        dict(
-            desc='Updated automember rule %r' % group1,
-            command=(
-                'automember_mod', [group1], dict(
-                    type=u'group',
-                    description=u'New desc 1',
-                )
-            ),
-            expected=dict(
-                result=dict(
-                    cn=[group1],
-                    description=[u'New desc 1'],
-                    automemberinclusiveregex=[u'manager=%s' % group_include_regex],
-                    automembertargetgroup=[DN(('cn', group1), ('cn', 'groups'), ('cn', 'accounts'), api.env.basedn)],
-                ),
-                summary=u'Modified automember rule "%s"' % group1,
-                value=group1,
-            ),
-        ),
-
-
-        dict(
-            desc="Retrieve automember rule for hostgroup %s" % hostgroup1,
-            command=('automember_show', [hostgroup1], dict(
-                type=u'hostgroup',
-                )
-            ),
-            expected=dict(
-                value=hostgroup1,
-                result=dict(
-                    cn=[hostgroup1],
-                    description=[u'Test desc'],
-                    automembertargetgroup=[DN(('cn', hostgroup1), ('cn', 'hostgroups'), ('cn', 'accounts'), api.env.basedn)],
-                    automemberinclusiveregex=[u'fqdn=%s' % hostgroup_include_regex,
-                                              u'fqdn=%s' % hostgroup_include_regex3,
-                                              u'fqdn=%s' % hostgroup_include_regex2,
-                    ],
-                    automemberexclusiveregex=[u'fqdn=%s' % hostgroup_exclude_regex2,
-                                              u'fqdn=%s' % hostgroup_exclude_regex3,
-                                              u'fqdn=%s' % hostgroup_exclude_regex,
-                    ],
-                    dn=DN(('cn', hostgroup1), ('cn', 'hostgroup'), ('cn', 'automember'), ('cn', 'etc'), api.env.basedn),
-                ),
-                summary=None,
-            ),
-        ),
-
-
-        dict(
-            desc='Search for %r' % hostgroup1,
-            command=('automember_find', [hostgroup1], dict(
-                type=u'hostgroup'
+                    automemberinclusiveregex=tuple(),
+                    automemberexclusiveregex=tuple(),
                 )
             ),
-            expected=dict(
-                count=1,
-                truncated=False,
-                result=[
-                    dict(
-                    cn=[hostgroup1],
-                    description=[u'Test desc'],
-                    automembertargetgroup=[DN(('cn', hostgroup1), ('cn', 'hostgroups'), ('cn', 'accounts'), api.env.basedn)],
-                    automemberinclusiveregex=[u'fqdn=%s' % hostgroup_include_regex,
-                                              u'fqdn=%s' % hostgroup_include_regex3,
-                                              u'fqdn=%s' % hostgroup_include_regex2,
-                    ],
-                    automemberexclusiveregex=[u'fqdn=%s' % hostgroup_exclude_regex2,
-                                              u'fqdn=%s' % hostgroup_exclude_regex3,
-                                              u'fqdn=%s' % hostgroup_exclude_regex,
-                    ],
-                    dn=DN(('cn', hostgroup1), ('cn', 'hostgroup'), ('cn', 'automember'), ('cn', 'etc'), api.env.basedn),
-                    ),
-                ],
-                summary=u'1 rules matched',
-            ),
-        ),
-
-
-        dict(
-            desc='Updated automember rule %r' % hostgroup1,
-            command=(
-                'automember_mod', [hostgroup1], dict(
-                    type=u'hostgroup',
-                    description=u'New desc 1',
-                )
-            ),
-            expected=dict(
-                result=dict(
-                    cn=[hostgroup1],
-                    description=[u'New desc 1'],
-                    automembertargetgroup=[DN(('cn', hostgroup1), ('cn', 'hostgroups'), ('cn', 'accounts'), api.env.basedn)],
-                    automemberinclusiveregex=[u'fqdn=%s' % hostgroup_include_regex,
-                                              u'fqdn=%s' % hostgroup_include_regex3,
-                                              u'fqdn=%s' % hostgroup_include_regex2,
-                    ],
-                    automemberexclusiveregex=[u'fqdn=%s' % hostgroup_exclude_regex2,
-                                              u'fqdn=%s' % hostgroup_exclude_regex3,
-                                              u'fqdn=%s' % hostgroup_exclude_regex,
-                    ],
-                ),
-                summary=u'Modified automember rule "%s"' % hostgroup1,
-                value=hostgroup1,
-            ),
-        ),
-
-
-        dict(
-            desc='Set default automember group for groups',
-            command=(
-                'automember_default_group_set', [], dict(
-                    type=u'group',
-                    automemberdefaultgroup=defaultgroup1
-                    )
-            ),
-            expected=dict(
+            result=dict(
+                cn=[automember_hostgroup.cn],
+                description=[automember_hostgroup.description],
+                automembertargetgroup=[automember_hostgroup.attrs
+                                       ['automembertargetgroup'][0]],
+                automemberinclusiveregex=[u'fqdn=%s' %
+                                          hostgroup_include_regex,
+                                          u'fqdn=%s' %
+                                          hostgroup_include_regex3,
+                                          u'fqdn=%s' %
+                                          hostgroup_include_regex2,
+                                          ],
+                automemberexclusiveregex=[u'fqdn=%s' %
+                                          hostgroup_exclude_regex2,
+                                          u'fqdn=%s' %
+                                          hostgroup_exclude_regex3,
+                                          u'fqdn=%s' %
+                                          hostgroup_exclude_regex,
+                                          ],
+            ),
+        )
+        assert_deepequal(expected, result)
+
+        automember_hostgroup.attrs.update(
+            automemberinclusiveregex=[u'fqdn=%s' % hostgroup_include_regex,
+                                      u'fqdn=%s' % hostgroup_include_regex3,
+                                      u'fqdn=%s' % hostgroup_include_regex2,
+                                      ],
+            automemberexclusiveregex=[u'fqdn=%s' % hostgroup_exclude_regex2,
+                                      u'fqdn=%s' % hostgroup_exclude_regex3,
+                                      u'fqdn=%s' % hostgroup_exclude_regex,
+                                      ]
+        )  # modify automember_hostgroup tracker for next tests
+
+    def test_create_set_of_hostgroup_automembers(self, automember_hostgroup2,
+                                                 automember_hostgroup3,
+                                                 automember_hostgroup4):
+        """ Create three more hostgroup automembers """
+        automember_hostgroup2.ensure_exists()
+        automember_hostgroup2.add_condition(
+            key=u'fqdn', type=u'hostgroup',
+            inclusiveregex=[hostgroup_exclude_regex]
+        )
+        automember_hostgroup3.ensure_exists()
+        automember_hostgroup3.add_condition(
+            key=u'fqdn', type=u'hostgroup',
+            inclusiveregex=[hostgroup_exclude_regex2]
+        )
+        automember_hostgroup4.ensure_exists()
+        automember_hostgroup4.add_condition(
+            key=u'fqdn', type=u'hostgroup',
+            inclusiveregex=[hostgroup_exclude_regex3]
+        )
+
+    def test_set_default_group_for_automembers(self, defaultgroup1):
+        """ Set new default group for group automembers """
+        result = api.Command['automember_default_group_set'](
+            type=u'group',
+            automemberdefaultgroup=defaultgroup1.cn
+        )
+
+        assert_deepequal(
+            dict(
                 result=dict(
                     cn=[u'Group'],
-                    automemberdefaultgroup=[DN(('cn', defaultgroup1), ('cn', 'groups'), ('cn', 'accounts'), api.env.basedn)],
+                    automemberdefaultgroup=[DN(('cn', defaultgroup1.cn),
+                                            ('cn', 'groups'),
+                                            ('cn', 'accounts'),
+                                            api.env.basedn)],
                 ),
                 value=u'group',
-                summary=u'Set default (fallback) group for automember "group"',
-            ),
-        ),
-
-
-        dict(
-            desc='Retrieve default automember group for groups',
-            command=(
-                'automember_default_group_show', [], dict(type=u'group')
-            ),
-            expected=dict(
-                result=dict(
-                    dn=DN(('cn', 'group'), ('cn', 'automember'), ('cn', 'etc'), api.env.basedn),
-                    cn=[u'Group'],
-                    automemberdefaultgroup=[DN(('cn', defaultgroup1), ('cn', 'groups'), ('cn', 'accounts'), api.env.basedn)],
-                ),
+                summary=u'Set default (fallback) group for automember "group"'
+                ),
+            result)
+
+        result = api.Command['automember_default_group_show'](
+            type=u'group',
+        )
+
+        assert_deepequal(
+            dict(
+                result=dict(dn=DN(('cn', 'group'),
+                                  ('cn', 'automember'),
+                                  ('cn', 'etc'), api.env.basedn),
+                            cn=[u'Group'],
+                            automemberdefaultgroup=[
+                                DN(('cn', defaultgroup1.cn),
+                                   ('cn', 'groups'),
+                                   ('cn', 'accounts'),
+                                   api.env.basedn)
+                                ],
+                            ),
                 value=u'group',
                 summary=None,
             ),
-        ),
+            result)
 
+    def test_set_default_hostgroup_for_automembers(self, defaulthostgroup1):
+        """ Set new default hostgroup for hostgroup automembers """
+        result = api.Command['automember_default_group_set'](
+            type=u'hostgroup',
+            automemberdefaultgroup=defaulthostgroup1.cn
+        )
 
-        dict(
-            desc='Set default (fallback) automember group for hostgroups',
-            command=(
-                'automember_default_group_set', [], dict(
-                    type=u'hostgroup',
-                    automemberdefaultgroup=defaulthostgroup1,
-                )
-            ),
-            expected=dict(
+        assert_deepequal(
+            dict(
                 result=dict(
                     cn=[u'Hostgroup'],
-                    automemberdefaultgroup=[DN(('cn', defaulthostgroup1), ('cn', 'hostgroups'), ('cn', 'accounts'), api.env.basedn)],
-                ),
+                    automemberdefaultgroup=[DN(('cn', defaulthostgroup1.cn),
+                                            ('cn', 'hostgroups'),
+                                            ('cn', 'accounts'),
+                                            api.env.basedn)],
+                    ),
                 value=u'hostgroup',
-                summary=u'Set default (fallback) group for automember "hostgroup"',
-            ),
-        ),
-
-
-        dict(
-            desc='Retrieve default automember group for hostgroups',
-            command=(
-                'automember_default_group_show', [], dict(
-                    type=u'hostgroup',
-                )
-            ),
-            expected=dict(
-                result=dict(
-                    dn=DN(('cn', 'hostgroup'), ('cn', 'automember'), ('cn', 'etc'), api.env.basedn),
-                    cn=[u'Hostgroup'],
-                    automemberdefaultgroup=[DN(('cn', defaulthostgroup1), ('cn', 'hostgroups'), ('cn', 'accounts'), api.env.basedn)],
-                ),
+                summary=u'Set default (fallback) group for '
+                        'automember "hostgroup"'),
+            result)
+
+        result = api.Command['automember_default_group_show'](
+            type=u'hostgroup',
+        )
+
+        assert_deepequal(
+            dict(
+                result=dict(dn=DN(('cn', 'hostgroup'),
+                            ('cn', 'automember'),
+                            ('cn', 'etc'), api.env.basedn),
+                            cn=[u'Hostgroup'],
+                            automemberdefaultgroup=[
+                                DN(('cn', defaulthostgroup1.cn),
+                                   ('cn', 'hostgroups'),
+                                   ('cn', 'accounts'),
+                                   api.env.basedn)],
+                            ),
                 value=u'hostgroup',
                 summary=None,
             ),
-        ),
-
-
-        dict(
-            desc='Create %r' % manager1,
-            command=(
-                'user_add', [manager1], dict(givenname=u'Michael', sn=u'Scott')
-            ),
-            expected=dict(
-                value=manager1,
-                summary=u'Added user "mscott"',
-                result=get_user_result(
-                    manager1, u'Michael', u'Scott', 'add',
-                    memberof_group=[defaultgroup1, u'ipausers']),
-            ),
-        ),
-
-
-        dict(
-            desc='Create %r' % user1,
-            command=(
-                'user_add', [user1], dict(givenname=u'Test', sn=u'User1', manager=manager1)
-            ),
-            expected=dict(
-                value=user1,
-                summary=u'Added user "tuser1"',
-                result=get_user_result(
-                    user1, u'Test', u'User1', 'add',
-                    memberof_group=[group1, u'ipausers'],
-                    manager=[DN(('uid', 'mscott'), ('cn', 'users'),
-                                ('cn', 'accounts'), api.env.basedn)]
-                ),
-            ),
-        ),
-
-
-        dict(
-            desc='Create %r' % fqdn1,
-            command=('host_add', [fqdn1],
-                dict(
-                    description=u'Test host 1',
-                    l=u'Undisclosed location 1',
-                    force=True,
-                ),
-            ),
-            expected=dict(
-                value=fqdn1,
-                summary=u'Added host "%s"' % fqdn1,
-                result=dict(
-                    dn=DN(('fqdn', fqdn1), ('cn', 'computers'), ('cn', 'accounts'), api.env.basedn),
-                    fqdn=[fqdn1],
-                    description=[u'Test host 1'],
-                    l=[u'Undisclosed location 1'],
-                    krbprincipalname=[u'host/%s@%s' % (fqdn1, api.env.realm)],
-                    has_keytab=False,
-                    has_password=False,
-                    objectclass=objectclasses.host,
-                    ipauniqueid=[fuzzy_uuid],
-                    managedby_host=[fqdn1],
-                    memberof_hostgroup=[hostgroup1],
-                    memberofindirect_netgroup=[hostgroup1],
-                ),
-            ),
-        ),
-
-
-        dict(
-            desc='Create %r' % fqdn2,
-            command=('host_add', [fqdn2],
-                dict(
-                    description=u'Test host 2',
-                    l=u'Undisclosed location 1',
-                    force=True,
-                ),
-            ),
-            expected=dict(
-                value=fqdn2,
-                summary=u'Added host "%s"' % fqdn2,
-                result=dict(
-                    dn=DN(('fqdn', fqdn2), ('cn', 'computers'), ('cn', 'accounts'), api.env.basedn),
-                    fqdn=[fqdn2],
-                    description=[u'Test host 2'],
-                    l=[u'Undisclosed location 1'],
-                    krbprincipalname=[u'host/%s@%s' % (fqdn2, api.env.realm)],
-                    has_keytab=False,
-                    has_password=False,
-                    objectclass=objectclasses.host,
-                    ipauniqueid=[fuzzy_uuid],
-                    managedby_host=[fqdn2],
-                    memberof_hostgroup=[defaulthostgroup1],
-                    memberofindirect_netgroup=[defaulthostgroup1],
-                ),
-            ),
-        ),
-
-
-        dict(
-            desc='Create %r' % fqdn3,
-            command=('host_add', [fqdn3],
-                dict(
-                    description=u'Test host 3',
-                    l=u'Undisclosed location 1',
-                    force=True,
-                ),
-            ),
-            expected=dict(
-                value=fqdn3,
-                summary=u'Added host "%s"' % fqdn3,
-                result=dict(
-                    dn=DN(('fqdn', fqdn3), ('cn', 'computers'), ('cn', 'accounts'), api.env.basedn),
-                    fqdn=[fqdn3],
-                    description=[u'Test host 3'],
-                    l=[u'Undisclosed location 1'],
-                    krbprincipalname=[u'host/%s@%s' % (fqdn3, api.env.realm)],
-                    has_keytab=False,
-                    has_password=False,
-                    objectclass=objectclasses.host,
-                    ipauniqueid=[fuzzy_uuid],
-                    managedby_host=[fqdn3],
-                    memberof_hostgroup=[hostgroup2],
-                    memberofindirect_netgroup=[hostgroup2],
-                ),
-            ),
-        ),
-
-
-        dict(
-            desc='Create %r' % fqdn4,
-            command=('host_add', [fqdn4],
-                dict(
-                    description=u'Test host 4',
-                    l=u'Undisclosed location 1',
-                    force=True,
-                ),
-            ),
-            expected=dict(
-                value=fqdn4,
-                summary=u'Added host "%s"' % fqdn4,
-                result=dict(
-                    dn=DN(('fqdn', fqdn4), ('cn', 'computers'), ('cn', 'accounts'), api.env.basedn),
-                    fqdn=[fqdn4],
-                    description=[u'Test host 4'],
-                    l=[u'Undisclosed location 1'],
-                    krbprincipalname=[u'host/%s@%s' % (fqdn4, api.env.realm)],
-                    has_keytab=False,
-                    has_password=False,
-                    objectclass=objectclasses.host,
-                    ipauniqueid=[fuzzy_uuid],
-                    managedby_host=[fqdn4],
-                    memberof_hostgroup=[hostgroup3],
-                    memberofindirect_netgroup=[hostgroup3],
-                ),
-            ),
-        ),
+            result)
+
+    def test_create_deps_under_new_conditions(
+            self, manager1, user1, host1, host2, host3, host4, host5,
+            hostgroup1, hostgroup2, hostgroup3, hostgroup4,
+            defaulthostgroup1, defaultgroup1, group1
+            ):
+        """ Create users and hosts under previously defined
+        automember conditions """
+        defaulthostgroup1.retrieve()
+        defaultgroup1.retrieve()
+        manager1.ensure_missing()
+        user1.ensure_missing()
+
+        manager1.track_create()
+        manager1.attrs.update(memberof_group=[defaultgroup1.cn, u'ipausers'])
+        command = manager1.make_create_command()
+        result = command()
+        manager1.check_create(result)
+
+        user1.track_create()
+        user1.attrs.update(memberof_group=[group1.cn, u'ipausers'])
+        command = user1.make_create_command()
+        result = command()
+        user1.check_create(result)
+
+        host1.track_create()
+        host1.attrs.update(memberofindirect_netgroup=[hostgroup1.cn],
+                           memberof_hostgroup=[hostgroup1.cn])
+        command = host1.make_create_command()
+        result = command()
+        hostgroup1.attrs.update(member_host=[host1.fqdn])
+
+        host2.track_create()
+        host2.attrs.update(memberof_hostgroup=[defaulthostgroup1.cn],
+                           memberofindirect_netgroup=[defaulthostgroup1.cn])
+        command = host2.make_create_command()
+        result = command()
+        defaulthostgroup1.attrs.update(member_host=[host2.fqdn])
+
+        host3.track_create()
+        host3.attrs.update(memberofindirect_netgroup=[hostgroup2.cn],
+                           memberof_hostgroup=[hostgroup2.cn])
+        command = host3.make_create_command()
+        result = command()
+        hostgroup2.attrs.update(member_host=[host3.fqdn])
+
+        host4.track_create()
+        host4.attrs.update(memberofindirect_netgroup=[hostgroup3.cn],
+                           memberof_hostgroup=[hostgroup3.cn])
+        command = host4.make_create_command()
+        result = command()
+        hostgroup3.attrs.update(member_host=[host4.fqdn])
+
+        host5.track_create()
+        host5.attrs.update(memberofindirect_netgroup=[hostgroup4.cn],
+                           memberof_hostgroup=[hostgroup4.cn])
+        command = host5.make_create_command()
+        result = command()
+        hostgroup4.attrs.update(member_host=[host5.fqdn])
+
+        hostgroup1.retrieve()
+        hostgroup2.retrieve()
+        hostgroup3.retrieve()
+        hostgroup4.retrieve()
+
+    def test_rebuild_membership_for_one_host(self, automember_hostgroup,
+                                             host1):
+        """ Rebuild hostgroup automember membership for one host """
+        command = automember_hostgroup.make_rebuild_command(type=u'hostgroup',
+                                                            hosts=host1.fqdn)
+        result = command()
+        automember_hostgroup.check_rebuild(result)
+
+    def test_rebuild_membership_for_one_user(self, automember_group, user1):
+        """ Rebuild group automember membership for one user """
+        command = automember_group.make_rebuild_command(type=u'group',
+                                                        users=user1.name)
+        result = command()
+        automember_group.check_rebuild(result)
+
+    def test_rebuild_membership_with_invalid_hosts_in_hosts(
+            self, automember_hostgroup):
+        """ Try to rebuild membership with invalid host in --hosts """
+        command = automember_hostgroup.make_rebuild_command(
+            hosts=fqdn_does_not_exist)
+        with raises_exact(errors.NotFound(
+                reason=u'%s: host not found' % fqdn_does_not_exist)):
+            command()
+
+    def test_rebuild_membership_with_invalid_user_in_users(self,
+                                                           automember_group):
+        """ Try to rebuild membership with invalid user in --users """
+        command = automember_group.make_rebuild_command(
+            users=user_does_not_exist)
+        with raises_exact(errors.NotFound(
+                reason=u'%s: user not found' % user_does_not_exist)):
+            command()
+
+    def test_reset_automember_default_groups(self, defaultgroup1, user1,
+                                             defaulthostgroup1, manager1):
+        """ Reset automember group defaults """
+        manager1.delete()
+        user1.delete()
+        result = api.Command['automember_default_group_remove'](
+            type=u'group',
+        )
+
+        assert_deepequal(
+            dict(
+                result=dict(
+                    automemberdefaultgroup=u'No default (fallback) group set',
+                    cn=([u'Group'])
+                    ),
+                value=u'group',
+                summary=u'Removed default (fallback) group'
+                        ' for automember "group"'),
+            result)
 
+        result = api.Command['automember_default_group_remove'](
+            type=u'hostgroup',
+        )
 
-        dict(
-            desc='Create %r' % fqdn5,
-            command=('host_add', [fqdn5],
-                dict(
-                    description=u'Test host 5',
-                    l=u'Undisclosed location 1',
-                    force=True,
-                ),
-            ),
-            expected=dict(
-                value=fqdn5,
-                summary=u'Added host "%s"' % fqdn5,
+        assert_deepequal(
+            dict(
                 result=dict(
-                    dn=DN(('fqdn', fqdn5), ('cn', 'computers'), ('cn', 'accounts'), api.env.basedn),
-                    fqdn=[fqdn5],
-                    description=[u'Test host 5'],
-                    l=[u'Undisclosed location 1'],
-                    krbprincipalname=[u'host/%s@%s' % (fqdn5, api.env.realm)],
-                    has_keytab=False,
-                    has_password=False,
-                    objectclass=objectclasses.host,
-                    ipauniqueid=[fuzzy_uuid],
-                    managedby_host=[fqdn5],
-                    memberof_hostgroup=[hostgroup4],
-                    memberofindirect_netgroup=[hostgroup4],
-                ),
-            ),
-        ),
-
-
-        dict(
-            desc='Retrieve %r' % hostgroup1,
-            command=('hostgroup_show', [hostgroup1], {}),
-            expected=dict(
-                value=hostgroup1,
-                summary=None,
-                result={
-                    'dn': DN(('cn', hostgroup1), ('cn', 'hostgroups'), ('cn', 'accounts'), api.env.basedn),
-                    'member_host': [u'%s' % fqdn1],
-                    'cn': [hostgroup1],
-                    'description': [u'Test desc'],
-                },
-            ),
-        ),
-
-
-        dict(
-            desc='Retrieve %r' % defaulthostgroup1,
-            command=('hostgroup_show', [defaulthostgroup1], {}),
-            expected=dict(
-                value=defaulthostgroup1,
-                summary=None,
-                result={
-                    'dn': DN(('cn', defaulthostgroup1), ('cn', 'hostgroups'), ('cn', 'accounts'), api.env.basedn),
-                    'member_host': [u'%s' % fqdn2],
-                    'cn': [defaulthostgroup1],
-                    'description': [u'Default test desc'],
-                },
-            ),
-        ),
-
-
-        dict(
-            desc='Retrieve %r' % hostgroup2,
-            command=('hostgroup_show', [hostgroup2], {}),
-            expected=dict(
-                value=hostgroup2,
-                summary=None,
-                result={
-                    'dn': DN(('cn', hostgroup2), ('cn', 'hostgroups'), ('cn', 'accounts'), api.env.basedn),
-                    'member_host': [u'%s' % fqdn3],
-                    'cn': [hostgroup2],
-                    'description': [u'Test desc'],
-                },
-            ),
-        ),
-
-
-        dict(
-            desc='Retrieve %r' % hostgroup3,
-            command=('hostgroup_show', [hostgroup3], {}),
-            expected=dict(
-                value=hostgroup3,
-                summary=None,
-                result={
-                    'dn': DN(('cn', hostgroup3), ('cn', 'hostgroups'), ('cn', 'accounts'), api.env.basedn),
-                    'member_host': [u'%s' % fqdn4],
-                    'cn': [hostgroup3],
-                    'description': [u'Test desc'],
-                },
-            ),
-        ),
-
+                    automemberdefaultgroup=u'No default (fallback) group set',
+                    cn=([u'Hostgroup'])
+                    ),
+                value=u'hostgroup',
+                summary=u'Removed default (fallback) group'
+                        ' for automember "hostgroup"'),
+            result)
 
-        dict(
-            desc='Retrieve %r' % hostgroup4,
-            command=('hostgroup_show', [hostgroup4], {}),
-            expected=dict(
-                value=hostgroup4,
-                summary=None,
-                result={
-                    'dn': DN(('cn', hostgroup4), ('cn', 'hostgroups'), ('cn', 'accounts'), api.env.basedn),
-                    'member_host': [u'%s' % fqdn5],
-                    'cn': [hostgroup4],
-                    'description': [u'Test desc'],
-                },
-            ),
-        ),
-
-        dict(
-            desc='Rebuild membership with type hostgroup and --hosts',
-            command=('automember_rebuild', [], {u'type': u'hostgroup', u'hosts': fqdn1}),
-            expected=dict(
-                value=None,
-                summary=u'Automember rebuild task finished. Processed (1) entries.',
-                result={
-                }
-            ),
-        ),
-
-        dict(
-            desc='Rebuild membership with type group and --users',
-            command=('automember_rebuild', [], {u'type': u'group', u'users': user1}),
-            expected=dict(
-                value=None,
-                summary=u'Automember rebuild task finished. Processed (1) entries.',
-                result={
-                }
-            ),
-        ),
-
-        dict(
-            desc='Try to rebuild membership with invalid host in --hosts',
-            command=('automember_rebuild', [], {u'type': u'hostgroup', u'hosts': fqdn_does_not_exist}),
-            expected=errors.NotFound(reason='%s: host not found' % fqdn_does_not_exist),
-        ),
-
-        dict(
-            desc='Try to rebuild membership with invalid user in --users',
-            command=('automember_rebuild', [], {u'type': u'group', u'users': user_does_not_exist}),
-            expected=errors.NotFound(reason='%s: user not found' % user_does_not_exist),
-        ),
-    ]
+        defaultgroup1.ensure_missing()
+        defaulthostgroup1.ensure_missing()
diff --git a/ipatests/test_xmlrpc/tracker/automember_plugin.py b/ipatests/test_xmlrpc/tracker/automember_plugin.py
new file mode 100644
index 0000000000000000000000000000000000000000..08b17b05168197db46271290aefb1ec7da45cbb5
--- /dev/null
+++ b/ipatests/test_xmlrpc/tracker/automember_plugin.py
@@ -0,0 +1,338 @@
+#
+# Copyright (C) 2015  FreeIPA Contributors see COPYING for license
+#
+
+from ipatests.test_xmlrpc import objectclasses
+from ipatests.test_xmlrpc.xmlrpc_test import (
+    fuzzy_uuid, fuzzy_automember_message, fuzzy_automember_dn)
+
+from ipatests.test_xmlrpc.tracker.base import Tracker
+from ipatests.util import assert_deepequal
+
+from ipalib import api
+from ipapython.dn import DN
+
+
+class AutomemberTracker(Tracker):
+    """ Class for tracking automembers """
+    retrieve_keys = {u'dn', u'cn', u'member_host', u'description',
+                     u'member_automember', u'memberindirect_host',
+                     u'automemberinclusiveregex', u'automemberexclusiveregex',
+                     u'automembertargetgroup'}
+    retrieve_all_keys = retrieve_keys | {u'objectclass',
+                                         u'automembertargetgroup'}
+
+    create_keys = retrieve_all_keys
+    update_keys = retrieve_keys - {u'dn'}
+
+    add_member_keys = retrieve_keys | {u'member_host'}
+    add_condition_keys = retrieve_keys - {u'dn'} |\
+        {u'automemberinclusiveregex', u'automembertargetgroup'}
+    add_condition_negative_keys = {u'automemberinclusiveregex'}
+
+    def __init__(self, groupname, membertype, description=u'Automember desc'):
+        super(AutomemberTracker, self).__init__(default_version=None)
+        self.cn = groupname
+        self.description = description
+        self.membertype = membertype
+        self.dn = DN(('cn', self.cn), ('cn', self.membertype.title()),
+                     ('cn', 'automember'), ('cn', 'etc'), api.env.basedn)
+
+    def make_create_command(self,
+                            force=True, *args, **kwargs):
+        """ Make function that creates an automember using 'automember-add' """
+        return self.make_command('automember_add', self.cn,
+                                 description=self.description,
+                                 type=self.membertype,
+                                 *args, **kwargs)
+
+    def make_delete_command(self):
+        """ Make function that deletes an automember using 'automember-del' """
+        return self.make_command('automember_del', self.cn,
+                                 **dict(type=self.membertype))
+
+    def make_retrieve_command(self, all=False, raw=False, membertype=None):
+        """ Make function that retrieves an automember
+        using 'automember-show' """
+        if membertype is None:
+            membertype = self.membertype
+        return self.make_command('automember_show', self.cn, type=membertype)
+
+    def make_find_command(self, *args, **kwargs):
+        """ Make function that searches for an automember
+            using 'automember-find' """
+        return self.make_command('automember_find', self.cn,
+                                 type=self.membertype)
+
+    def make_update_command(self, updates):
+        """ Make function that updates an automember using 'automember-mod' """
+        return self.make_command('automember_mod', self.cn,
+                                 type=self.membertype, **updates)
+
+    def make_add_member_command(self, options={}):
+        """ Make function that adds a member to an automember """
+        return self.make_command('automember_add_member', self.cn, **options)
+
+    def make_remove_member_command(self, options={}):
+        """ Make function that removes a member from an automember """
+        return self.make_command('automember_remove_member',
+                                 self.cn, **options)
+
+    def make_rebuild_command(self, *args, **kwargs):
+        """ Make function that issues automember_rebuild.
+        This function can be executed with arbitrary automember tracker """
+        return self.make_command('automember_rebuild', *args, **kwargs)
+
+    def make_add_condition_command(self, *args, **kwargs):
+        """ Make function that issues automember_add_condition """
+        return self.make_command('automember_add_condition', self.cn,
+                                 *args, **kwargs)
+
+    def track_create(self):
+        """ Updates expected state for automember creation"""
+        self.attrs = dict(
+            dn=self.dn,
+            mepmanagedentry=[DN(('cn', self.cn), ('cn', 'ng'),
+                                ('cn', 'alt'), api.env.basedn)],
+            cn=[self.cn],
+            description=[self.description],
+            ipauniqueid=[fuzzy_uuid],
+            objectclass=objectclasses.automember,
+            automembertargetgroup=[DN(('cn', self.cn),
+                                      ('cn', self.membertype + 's'),
+                                      ('cn', 'accounts'), api.env.basedn)]
+
+            )
+        self.exists = True
+
+    def add_member(self, options):
+        """ Add a member host to automember and perform check """
+        if u'group' in options:
+            try:
+                self.attrs[u'group'] =\
+                    self.attrs[u'group'] + [options[u'group']]
+            except KeyError as ex:
+                self.attrs[u'group'] = [options[u'group']]
+            # search for hosts in the target automember and
+            # add them as memberindirect hosts
+        elif u'hostgroup' in options:
+            try:
+                self.attrs[u'hostgroup'] =\
+                    self.attrs[u'hostgroup'] + [options[u'hostgroup']]
+            except KeyError as ex:
+                self.attrs[u'hostgroup'] = [options[u'hostgroup']]
+
+        command = self.make_add_member_command(options)
+        result = command()
+        self.check_add_member(result)
+
+    def remove_member(self, options):
+        """ Remove a member host from automember and perform check """
+        if u'host' in options:
+            self.attrs[u'member_host'].remove(options[u'host'])
+        elif u'automember' in options:
+            self.attrs[u'member_automember'].remove(options[u'automember'])
+
+        try:
+            if not self.attrs[u'member_host']:
+                del self.attrs[u'member_host']
+        except KeyError as ex:
+            pass
+        try:
+            if not self.attrs[u'member_automember']:
+                del self.attrs[u'member_automember']
+        except KeyError as ex:
+            pass
+
+        command = self.make_remove_member_command(options)
+        result = command()
+        self.check_remove_member(result)
+
+    def update(self, updates, expected_updates=None):
+        """Helper function to update this user and check the result
+
+        Overriding Tracker method for setting self.attrs correctly;
+         * most attributes stores its value in list
+         * the rest can be overridden by expected_updates
+         * allow deleting parametrs if update value is None
+        """
+        if expected_updates is None:
+            expected_updates = {}
+
+        self.ensure_exists()
+        command = self.make_update_command(updates)
+        result = command()
+
+        for key, value in updates.items():
+            if value is None:
+                del self.attrs[key]
+            else:
+                self.attrs[key] = [value]
+        for key, value in expected_updates.items():
+            if value is None:
+                del self.attrs[key]
+            else:
+                self.attrs[key] = value
+
+        self.check_update(
+            result,
+            extra_keys=set(updates.keys()) | set(expected_updates.keys())
+        )
+
+    def add_condition(self, key, type, inclusiveregex):
+        """ Add a condition with given inclusive regex and check for result.
+        Only one condition can be added. For more specific uses please
+        use make_add_condition_command instead. """
+        command = self.make_add_condition_command(
+            key=key, type=type, automemberinclusiveregex=inclusiveregex)
+        self.attrs['automemberinclusiveregex'] = [u'%s=%s' %
+                                                  (key, inclusiveregex[0])]
+        result = command()
+        self.check_add_condition(result)
+
+    def rebuild(self, no_wait=False):
+        """ Rebuild automember conditions and check for result """
+        command = self.make_rebuild_command(type=self.membertype,
+                                            no_wait=no_wait)
+        result = command()
+        self.check_rebuild(result, no_wait=no_wait)
+
+    def check_rebuild(self, result, no_wait=False):
+        """ Check result of automember_rebuild command """
+        if no_wait is False:
+            assert_deepequal(dict(
+                value=None, result=dict(),
+                summary=fuzzy_automember_message
+                ), result)
+        else:
+            assert_deepequal(dict(
+                value=None,
+                result=dict(dn=fuzzy_automember_dn),
+                summary=u'Automember rebuild membership task started'
+                ), result)
+
+    def check_add_condition(self, result):
+        """ Check result of automember_add_condition command """
+        assert_deepequal(dict(
+            value=self.cn,
+            summary=u'Added condition(s) to "%s"' % self.cn,
+            completed=1,
+            failed=dict(
+                failed=dict(automemberinclusiveregex=tuple(),
+                            automemberexclusiveregex=tuple(),
+                            )
+            ),
+            result=self.filter_attrs(self.add_condition_keys)
+            ), result)
+
+    def check_add_condition_negative(self, result):
+        """ Check result of automember_add_condition command
+        when the operation didn't add anything. """
+        assert_deepequal(dict(
+            value=self.cn,
+            summary=u'Added condition(s) to "%s"' % self.cn,
+            completed=0,
+            failed=dict(
+                failed=dict(automemberinclusiveregex=tuple(),
+                            automemberexclusiveregex=tuple(),
+                            )
+            ),
+            result=self.filter_attrs(self.add_condition_negative_keys)
+            ), result)
+
+    def check_create(self, result):
+        """ Checks 'automember_add' command result """
+        assert_deepequal(dict(
+            value=self.cn,
+            summary=u'Added automember rule "%s"' % self.cn,
+            result=self.filter_attrs(self.create_keys)
+            ), result)
+
+    def check_delete(self, result):
+        """ Checks 'automember_del' command result """
+        assert_deepequal(dict(
+            value=[self.cn],
+            summary=u'Deleted automember rule "%s"' % self.cn,
+            result=dict(failed=[]),
+            ), result)
+
+    def check_retrieve(self, result, all=False, raw=False):
+        """ Checks 'automember_show' command result """
+        if all:
+            expected = self.filter_attrs(self.retrieve_all_keys)
+        else:
+            expected = self.filter_attrs(self.retrieve_keys)
+
+        assert_deepequal(dict(
+            value=self.cn,
+            summary=None,
+            result=expected
+            ), result)
+
+    def check_find(self, result, all=False, raw=False):
+        """ Checks 'automember_find' command result """
+        if all:
+            expected = self.filter_attrs(self.retrieve_all_keys)
+        else:
+            expected = self.filter_attrs(self.retrieve_keys)
+
+        assert_deepequal(dict(
+            count=1,
+            truncated=False,
+            summary=u'1 rules matched',
+            result=[expected],
+        ), result)
+
+    def check_update(self, result, extra_keys={}):
+        """ Checks 'automember_mod' command result """
+        assert_deepequal(dict(
+            value=self.cn,
+            summary=u'Modified automember rule "%s"' % self.cn,
+            result=self.filter_attrs(self.update_keys | set(extra_keys))
+        ), result)
+
+    def check_add_member(self, result):
+        """ Checks 'automember_add_member' command result """
+        assert_deepequal(dict(
+            completed=1,
+            failed={u'member': {u'host': (), u'automember': ()}},
+            result=self.filter_attrs(self.add_member_keys)
+        ), result)
+
+    def check_add_member_negative(self, result, options):
+        """ Checks 'automember_add_member' command result
+        when expected result is failure of the operation"""
+        expected = dict(
+            completed=0,
+            failed={u'member': {u'automember': (), u'user': ()}},
+            result=self.filter_attrs(self.add_member_keys)
+        )
+        if u'host' in options:
+            expected[u'failed'][u'member'][u'host'] = [(
+                options[u'host'], u'no such entry')]
+        elif u'automember' in options:
+            expected[u'failed'][u'member'][u'automember'] = [(
+                options[u'automember'], u'no such entry')]
+
+        assert_deepequal(expected, result)
+
+    def check_remove_member_negative(self, result, options):
+        """ Checks 'automember_remove_member' command result
+        when expected result is failure of the operation"""
+        expected = dict(
+            completed=0,
+            failed={u'member': {u'automember': (), u'host': ()}},
+            result=self.filter_attrs(self.add_member_keys)
+        )
+        if u'user' in options:
+            expected[u'failed'][u'member'][u'host'] = [(
+                options[u'user'], u'This entry is not a member')]
+        elif u'automember' in options:
+            expected[u'failed'][u'member'][u'automember'] = [(
+                options[u'automember'], u'This entry is not a member')]
+
+        assert_deepequal(expected, result)
+
+    def check_remove_member(self, result):
+        """ Checks 'automember_remove_member' command result """
+        self.check_add_member(result)
-- 
2.5.5

-- 
Manage your subscription for the Freeipa-devel mailing list:
https://www.redhat.com/mailman/listinfo/freeipa-devel
Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code

Reply via email to