I'm still not quite finished with my revised 027 patch, but I wanted to
give everyone a quick status update on the testing enhancements I've
been working on.

I wrote a new test base class xmlrpc_test.Declarative (which borrows
from Rob's XMLRPC_test).  It allows you to write tests completely
(surprise!) declaratively by using simple data structures to describe a
sequence of api.Command.* calls and the expected return value of each.

The first step is to define a `cleanup_commands` class attribute,
something like this:

  class test_user(Declarative):

      cleanup_commands = [
          ['user_del', [u'tuser'], {}],
          ['group_del', [u'tgroup'], {}],
      ]


The `cleanup_commands` attribute is a list of commands (and their
arguments and options) that, when run, should delete any entries your
tests might have created but not deleted (because of a partial failure
or whatever).  The `Declarative` class will run these commands before
and after your tests run.  It also runs each cleanup command in a
try/except, so trying to delete a non-existent entry wont botch things
up.

The next (and only other) step is to define a `tests` class attribute,
which is a simple list of tests in the order they should be run,
something like this:

      tests = [

          {
              'desc': 'Create a user',
              'command': [
                  'user_add', [u'tuser'], {'givenname': u'Test', 'sn': u'User'}
              ),
              'expected': {
                  'result': {
                      'uid': u'tuser',
                      'givenname': u'Test',
                      'sn': u'User',
                  },
                  'summary': 'Added user "tuser"',
              },
              'ignore_values': ['ipauniqueid']
          },


          {
              'desc': 'More cowbell!',
              'command': ['user_find', [], {}],
              'expected': [<entry_1>...<entry_n>],
          },

      ]


Each test in the `tests` list is a dictionary, which must contain at
least the `command` and `expected` members.  The `expected` member is
the exact data structure the command should return... couldn't be
simpler.  `Declarative` takes care of running the test and comparing the
expected value to the actual value, and reporting errors when they don't
match up.

The `desc` member is an optional human-readable description of the test.
If something goes wrong, this description will show up in the trace to
help orient you more quickly.

The other optional member is `ignore_values`, which is a list of entry
attributes whose values you don't want `Declarative` to match.  The
attributes still must be present, but they are removed before comparing
with your `expected` value.  This is for random values like
`ipauniqueid`... they need to be present, but you don't care about the
exact value.  For example, if your test had an `expected` and
`ignore_values` like this:

  {
      'expected': {
          'result': {'foo': u'bar'},
      },
      'ignore_values': ['bee'],
  }


Then a return value of {'result': {'foo': u'bar', 'bee': u'bop'}} is
fine as is {'result': {'foo': u'bar', 'bee': u'movie'}}.  But if `bee`
is absent, then the test will fail.

It's also easy to declare tests in which an exception is raised.  In
this case, your `expected` should be a PublicException instance, like
this:

  {
      'desc': 'Non existent user',
      'command': ['user_show', [u'nope], {}],
      'expected': errors.NotFound(reason='no way!'),
  }


I attached the new declarative tests for the user plugins if anyone
wants to see more examples.  This definitely makes the tests faster to
write easier to read.  But this is just the beginning.  The plugin
framework has kept things pretty regular and it will be easy to
automatically generate streams of truly abusive tests for `Declarative`
to run.  We really need more coverage on the plugin side, I feel, and
this is a fast, dirty, and fun way to get there.


-Jason "patch soon" DeRose
               
              


# Authors:
#   Rob Crittenden <rcrit...@redhat.com>
#   Pavel Zuna <pz...@redhat.com>
#   Jason Gerard DeRose <jder...@redhat.com>
#
# Copyright (C) 2008, 2009  Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; version 2 only
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA

"""
Test the `ipalib/plugins/user.py` module.
"""

from ipalib import api, errors
from xmlrpc_test import Declarative

user_objectclass = (
    u'top',
    u'person',
    u'organizationalperson',
    u'inetorgperson',
    u'inetuser',
    u'posixaccount',
    u'krbprincipalaux',
    u'radiusprofile',
    u'ipaobject',
)

user_memberof = (u'cn=ipausers,cn=groups,cn=accounts,dc=example,dc=com',)


class test_user(Declarative):

    cleanup_commands = [
        ('user_del', [u'tuser1'], {}),
    ]

    tests = [

        dict(
            desc='Try to retrieve non-existant user',
            command=(
                'user_show', [u'tuser1'], {}
            ),
            expected=errors.NotFound(reason='no such entry'),
        ),


        dict(
            desc='Create a user',
            command=(
                'user_add', [], dict(givenname=u'Test', sn=u'User1')
            ),
            expected=dict(
                primary_key=u'tuser1',
                result=dict(
                    cn=(u'Test User1',),
                    gecos=(u'tuser1',),
                    givenname=(u'Test',),
                    homedirectory=(u'/home/tuser1',),
                    krbprincipalname=(u'tuser1@' + api.env.realm,),
                    loginshell=(u'/bin/sh',),
                    objectclass=user_objectclass,
                    sn=(u'User1',),
                    uid=(u'tuser1',),
                ),
            ),
            ignore_values=(
                'ipauniqueid', 'gidnumber'
            ),
        ),


        dict(
            desc='Try to create another user with same login',
            command=(
                'user_add', [], dict(givenname=u'Test', sn=u'User1')
            ),
            expected=errors.DuplicateEntry(),
        ),


        dict(
            desc='Retrieve the user',
            command=(
                'user_show', [u'tuser1'], {}
            ),
            expected=dict(
                result=dict(
                    dn=u'uid=tuser1,cn=users,cn=accounts,dc=example,dc=com',
                    givenname=(u'Test',),
                    homedirectory=(u'/home/tuser1',),
                    loginshell=(u'/bin/sh',),
                    sn=(u'User1',),
                    uid=(u'tuser1',),
                ),
                primary_key=u'tuser1',
            ),
        ),


        dict(
            desc='Search for this user with all=True',
            command=(
                'user_find', [u'tuser1'], {'all': True}
            ),
            expected=dict(
                result=(
                    dict(
                        cn=(u'Test User1',),
                        gecos=(u'tuser1',),
                        givenname=(u'Test',),
                        homedirectory=(u'/home/tuser1',),
                        krbprincipalname=(u'tuser1@' + api.env.realm,),
                        loginshell=(u'/bin/sh',),
                        memberof=user_memberof,
                        objectclass=user_objectclass,
                        sn=(u'User1',),
                        uid=(u'tuser1',),
                    ),
                ),
                count=1,
                truncated=False,
            ),
            ignore_values=['uidnumber', 'gidnumber', 'ipauniqueid'],
        ),


        dict(
            desc='Search for this user with minimal attributes',
            command=(
                'user_find', [u'tuser1'], {}
            ),
            expected=dict(
                result=(
                    dict(
                        givenname=(u'Test',),
                        homedirectory=(u'/home/tuser1',),
                        loginshell=(u'/bin/sh',),
                        sn=(u'User1',),
                        uid=(u'tuser1',),
                    ),
                ),
                count=1,
                truncated=False,
            ),
        ),


        dict(
            desc='Search for all users',
            command=(
                'user_find', [], {}
            ),
            expected=dict(
                result=(
                    dict(
                        homedirectory=(u'/home/admin',),
                        loginshell=(u'/bin/bash',),
                        sn=(u'Administrator',),
                        uid=(u'admin',),
                    ),
                    dict(
                        givenname=(u'Test',),
                        homedirectory=(u'/home/tuser1',),
                        loginshell=(u'/bin/sh',),
                        sn=(u'User1',),
                        uid=(u'tuser1',),
                    ),
                ),
                count=2,
                truncated=False,
            ),
        ),


        dict(
            desc='Lock user',
            command=(
                'user_lock', [u'tuser1'], {}
            ),
            expected=dict(
                result=True,
                primary_key=u'tuser1',
            ),
        ),


        dict(
            desc='Unlock user',
            command=(
                'user_unlock', [u'tuser1'], {}
            ),
            expected=dict(
                result=True,
                primary_key=u'tuser1',
            ),
        ),


        dict(
            desc='Update user',
            command=(
                'user_mod', [u'tuser1'], dict(givenname=u'Finkle')
            ),
            expected=dict(
                result=dict(
                    givenname=(u'Finkle',),
                ),
                primary_key=u'tuser1',
            ),
        ),


        dict(
            desc='Retrieve user to verify update',
            command=(
                'user_show', [u'tuser1'], {}
            ),
            expected=dict(
                result=dict(
                    dn=u'uid=tuser1,cn=users,cn=accounts,dc=example,dc=com',
                    givenname=(u'Finkle',),
                    homedirectory=(u'/home/tuser1',),
                    loginshell=(u'/bin/sh',),
                    sn=(u'User1',),
                    uid=(u'tuser1',),
                ),
                primary_key=u'tuser1',
            ),

        ),


        dict(
            desc='Delete user',
            command=(
                'user_del', [u'tuser1'], {}
            ),
            expected=dict(
                result=True,
                primary_key=u'tuser1',
            ),
        ),


        dict(
            desc='Do double delete',
            command=(
                'user_del', [u'tuser1'], {}
            ),
            expected=errors.NotFound(reason='no such entry'),
        ),


        dict(
            desc='Verify user is gone',
            command=(
                'user_show', [u'tuser1'], {}
            ),
            expected=errors.NotFound(reason='no such entry'),
        ),

    ]
_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to