Add Identity v2 project tests

* establish the the form of cliff command classes
* implement some common fake objects
* implement Identity command tests for v2 project
* fix stdout/stderr capture

Also re-work the project create and set commands for exclusive options
(--enable|--disable) to actually behave properly.  Yay tests!

Change-Id: Icbb313db544c1f8dd3c9af7709971838b5a4d115
This commit is contained in:
Dean Troyer 2013-07-03 16:47:40 -05:00
parent 93612bbf53
commit 493339d4da
8 changed files with 532 additions and 23 deletions

View File

@ -1,4 +1,8 @@
[DEFAULT]
test_command=OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 ${PYTHON:-python} -m subunit.run discover -t ./ . $LISTOPT $IDOPTION
test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \
OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \
OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-60} \
${PYTHON:-python} -m subunit.run discover -t ./ ./openstackclient/tests $LISTOPT $IDOPTION
test_id_option=--load-list $IDFILE
test_list_option=--list

View File

@ -45,26 +45,28 @@ class CreateProject(show.ShowOne):
enable_group = parser.add_mutually_exclusive_group()
enable_group.add_argument(
'--enable',
dest='enabled',
action='store_true',
default=True,
help='Enable project',
help='Enable project (default)',
)
enable_group.add_argument(
'--disable',
dest='enabled',
action='store_false',
action='store_true',
help='Disable project',
)
return parser
def take_action(self, parsed_args):
self.log.debug('take_action(%s)' % parsed_args)
identity_client = self.app.client_manager.identity
enabled = True
if parsed_args.disable:
enabled = False
project = identity_client.tenants.create(
parsed_args.project_name,
description=parsed_args.description,
enabled=parsed_args.enabled)
enabled=enabled,
)
info = {}
info.update(project._info)
@ -150,36 +152,44 @@ class SetProject(command.Command):
enable_group = parser.add_mutually_exclusive_group()
enable_group.add_argument(
'--enable',
dest='enabled',
action='store_true',
default=True,
help='Enable project (default)',
help='Enable project',
)
enable_group.add_argument(
'--disable',
dest='enabled',
action='store_false',
action='store_true',
help='Disable project',
)
return parser
def take_action(self, parsed_args):
self.log.debug('take_action(%s)' % parsed_args)
identity_client = self.app.client_manager.identity
project = utils.find_resource(
identity_client.tenants,
parsed_args.project,
)
kwargs = {}
kwargs = project._info
if parsed_args.name:
kwargs['name'] = parsed_args.name
if parsed_args.description:
kwargs['description'] = parsed_args.description
if 'enabled' in parsed_args:
kwargs['enabled'] = parsed_args.enabled
if parsed_args.enable:
kwargs['enabled'] = True
if parsed_args.disable:
kwargs['enabled'] = False
project.update(**kwargs)
return
if 'id' in kwargs:
del kwargs['id']
if 'name' in kwargs:
# Hack around borken Identity API arg names
kwargs['tenant_name'] = kwargs['name']
del kwargs['name']
if len(kwargs):
identity_client.tenants.update(project.id, **kwargs)
class ShowProject(show.ShowOne):

View File

@ -0,0 +1,62 @@
# Copyright 2013 Nebula Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
import sys
class FakeStdout:
def __init__(self):
self.content = []
def write(self, text):
self.content.append(text)
def make_string(self):
result = ''
for line in self.content:
result = result + line
return result
class FakeApp(object):
def __init__(self, _stdout):
self.stdout = _stdout
self.client_manager = None
self.stdin = sys.stdin
self.stdout = _stdout or sys.stdout
self.stderr = sys.stderr
class FakeClientManager(object):
def __init__(self):
pass
class FakeResource(object):
def __init__(self, manager, info, loaded=False):
self.manager = manager
self._info = info
self._add_details(info)
self._loaded = loaded
def _add_details(self, info):
for (k, v) in info.iteritems():
setattr(self, k, v)
def __repr__(self):
reprkeys = sorted(k for k in self.__dict__.keys() if k[0] != '_' and
k != 'manager')
info = ", ".join("%s=%s" % (k, getattr(self, k)) for k in reprkeys)
return "<%s %s>" % (self.__class__.__name__, info)

View File

@ -1,4 +1,4 @@
# Copyright 2013 OpenStack, LLC.
# Copyright 2013 OpenStack Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain

View File

@ -0,0 +1,40 @@
# Copyright 2013 Nebula Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
import mock
from openstackclient.tests import fakes
class FakeIdentityv2Client(object):
def __init__(self, **kwargs):
self.tenants = mock.Mock()
self.tenants.resource_class = fakes.FakeResource(None, {})
self.users = mock.Mock()
self.users.resource_class = fakes.FakeResource(None, {})
self.ec2 = mock.Mock()
self.ec2.resource_class = fakes.FakeResource(None, {})
self.auth_tenant_id = 'fake-tenant'
self.auth_user_id = 'fake-user'
class FakeIdentityv3Client(object):
def __init__(self, **kwargs):
self.domains = mock.Mock()
self.domains.resource_class = fakes.FakeResource(None, {})
self.projects = mock.Mock()
self.projects.resource_class = fakes.FakeResource(None, {})
self.users = mock.Mock()
self.users.resource_class = fakes.FakeResource(None, {})

View File

@ -0,0 +1,14 @@
# Copyright 2013 OpenStack Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#

View File

@ -0,0 +1,355 @@
# Copyright 2013 Nebula Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
import copy
from openstackclient.identity.v2_0 import project
from openstackclient.tests import fakes
from openstackclient.tests.identity import fakes as identity_fakes
from openstackclient.tests import utils
IDENTITY_API_VERSION = "2.0"
user_id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'
user_name = 'paul'
user_description = 'Sir Paul'
project_id = '8-9-64'
project_name = 'beatles'
project_description = 'Fab Four'
USER = {
'id': user_id,
'name': user_name,
'tenantId': project_id,
}
PROJECT = {
'id': project_id,
'name': project_name,
'description': project_description,
'enabled': True,
}
class TestProject(utils.TestCommand):
def setUp(self):
super(TestProject, self).setUp()
self.app.client_manager.identity = \
identity_fakes.FakeIdentityv2Client()
# Get a shortcut to the TenantManager Mock
self.projects_mock = self.app.client_manager.identity.tenants
class TestProjectCreate(TestProject):
def setUp(self):
super(TestProjectCreate, self).setUp()
self.projects_mock.create.return_value = fakes.FakeResource(
None,
copy.deepcopy(PROJECT),
loaded=True,
)
# Get the command object to test
self.cmd = project.CreateProject(self.app, None)
def test_project_create_no_options(self):
arglist = [project_name]
verifylist = [
('project_name', project_name),
('enable', False),
('disable', False),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# DisplayCommandBase.take_action() returns two tuples
columns, data = self.cmd.take_action(parsed_args)
# Set expected values
kwargs = {
'description': None,
'enabled': True,
}
self.projects_mock.create.assert_called_with(project_name, **kwargs)
collist = ('description', 'enabled', 'id', 'name')
self.assertEqual(columns, collist)
datalist = (project_description, True, project_id, project_name)
self.assertEqual(data, datalist)
def test_project_create_description(self):
arglist = ['--description', 'new desc', project_name]
verifylist = [('description', 'new desc')]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# DisplayCommandBase.take_action() returns two tuples
columns, data = self.cmd.take_action(parsed_args)
# Set expected values
kwargs = {
'description': 'new desc',
'enabled': True,
}
self.projects_mock.create.assert_called_with(project_name, **kwargs)
collist = ('description', 'enabled', 'id', 'name')
self.assertEqual(columns, collist)
datalist = (project_description, True, project_id, project_name)
self.assertEqual(data, datalist)
def test_project_create_enable(self):
arglist = ['--enable', project_name]
verifylist = [('enable', True), ('disable', False)]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# DisplayCommandBase.take_action() returns two tuples
columns, data = self.cmd.take_action(parsed_args)
# Set expected values
kwargs = {
'description': None,
'enabled': True,
}
self.projects_mock.create.assert_called_with(project_name, **kwargs)
collist = ('description', 'enabled', 'id', 'name')
self.assertEqual(columns, collist)
datalist = (project_description, True, project_id, project_name)
self.assertEqual(data, datalist)
def test_project_create_disable(self):
arglist = ['--disable', project_name]
verifylist = [('enable', False), ('disable', True)]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# DisplayCommandBase.take_action() returns two tuples
columns, data = self.cmd.take_action(parsed_args)
# Set expected values
kwargs = {
'description': None,
'enabled': False,
}
self.projects_mock.create.assert_called_with(project_name, **kwargs)
collist = ('description', 'enabled', 'id', 'name')
self.assertEqual(columns, collist)
datalist = (project_description, True, project_id, project_name)
self.assertEqual(data, datalist)
class TestProjectDelete(TestProject):
def setUp(self):
super(TestProjectDelete, self).setUp()
# This is the return value for utils.find_resource()
self.projects_mock.get.return_value = fakes.FakeResource(
None,
copy.deepcopy(PROJECT),
loaded=True,
)
self.projects_mock.delete.return_value = None
# Get the command object to test
self.cmd = project.DeleteProject(self.app, None)
def test_project_delete_no_options(self):
arglist = [user_id]
verifylist = [('project', user_id)]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.run(parsed_args)
self.assertEqual(result, 0)
self.projects_mock.delete.assert_called_with(project_id)
class TestProjectList(TestProject):
def setUp(self):
super(TestProjectList, self).setUp()
self.projects_mock.list.return_value = [
fakes.FakeResource(
None,
copy.deepcopy(PROJECT),
loaded=True,
),
]
# Get the command object to test
self.cmd = project.ListProject(self.app, None)
def test_project_list_no_options(self):
arglist = []
verifylist = []
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# DisplayCommandBase.take_action() returns two tuples
columns, data = self.cmd.take_action(parsed_args)
self.projects_mock.list.assert_called_with()
collist = ('ID', 'Name')
self.assertEqual(columns, collist)
datalist = ((project_id, project_name), )
self.assertEqual(tuple(data), datalist)
def test_project_list_long(self):
arglist = ['--long']
verifylist = [('long', True)]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# DisplayCommandBase.take_action() returns two tuples
columns, data = self.cmd.take_action(parsed_args)
self.projects_mock.list.assert_called_with()
collist = ('ID', 'Name', 'Description', 'Enabled')
self.assertEqual(columns, collist)
datalist = ((project_id, project_name, project_description, True), )
self.assertEqual(tuple(data), datalist)
class TestProjectSet(TestProject):
def setUp(self):
super(TestProjectSet, self).setUp()
self.projects_mock.get.return_value = fakes.FakeResource(
None,
copy.deepcopy(PROJECT),
loaded=True,
)
# Get the command object to test
self.cmd = project.SetProject(self.app, None)
def test_project_set_no_options(self):
arglist = [project_name]
verifylist = [
('project', project_name),
('enable', False),
('disable', False),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.run(parsed_args)
self.assertEqual(result, 0)
# Set expected values
kwargs = {
'description': project_description,
'enabled': True,
'tenant_name': project_name,
}
self.projects_mock.update.assert_called_with(project_id, **kwargs)
def test_project_set_name(self):
arglist = ['--name', 'qwerty', project_name]
verifylist = [('name', 'qwerty')]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.run(parsed_args)
self.assertEqual(result, 0)
# Set expected values
kwargs = {
'description': project_description,
'enabled': True,
'tenant_name': 'qwerty',
}
self.projects_mock.update.assert_called_with(project_id, **kwargs)
def test_project_set_description(self):
arglist = ['--description', 'new desc', project_name]
verifylist = [('description', 'new desc')]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.run(parsed_args)
self.assertEqual(result, 0)
# Set expected values
kwargs = {
'description': 'new desc',
'enabled': True,
'tenant_name': project_name,
}
self.projects_mock.update.assert_called_with(project_id, **kwargs)
def test_project_set_enable(self):
arglist = ['--enable', project_name]
verifylist = [('enable', True), ('disable', False)]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.run(parsed_args)
self.assertEqual(result, 0)
# Set expected values
kwargs = {
'description': project_description,
'enabled': True,
'tenant_name': project_name,
}
self.projects_mock.update.assert_called_with(project_id, **kwargs)
def test_project_set_disable(self):
arglist = ['--disable', project_name]
verifylist = [('enable', False), ('disable', True)]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.run(parsed_args)
self.assertEqual(result, 0)
# Set expected values
kwargs = {
'description': project_description,
'enabled': False,
'tenant_name': project_name,
}
self.projects_mock.update.assert_called_with(project_id, **kwargs)
class TestProjectShow(TestProject):
def setUp(self):
super(TestProjectShow, self).setUp()
self.projects_mock.get.return_value = fakes.FakeResource(
None,
copy.deepcopy(PROJECT),
loaded=True,
)
# Get the command object to test
self.cmd = project.ShowProject(self.app, None)
def test_project_show(self):
arglist = [user_id]
verifylist = [('project', user_id)]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
# DisplayCommandBase.take_action() returns two tuples
columns, data = self.cmd.take_action(parsed_args)
self.projects_mock.get.assert_called_with(user_id)
collist = ('description', 'enabled', 'id', 'name')
self.assertEqual(columns, collist)
datalist = (project_description, True, project_id, project_name)
self.assertEqual(data, datalist)

View File

@ -1,4 +1,5 @@
# Copyright 2012-2013 OpenStack, LLC.
# Copyright 2012-2013 OpenStack Foundation
# Copyright 2013 Nebula Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
@ -19,18 +20,20 @@ import fixtures
import sys
import testtools
from openstackclient.tests import fakes
class TestCase(testtools.TestCase):
def setUp(self):
testtools.TestCase.setUp(self)
if (os.environ.get("OS_STDOUT_NOCAPTURE") == "True" and
os.environ.get("OS_STDOUT_NOCAPTURE") == "1"):
if (os.environ.get("OS_STDOUT_CAPTURE") == "True" or
os.environ.get("OS_STDOUT_CAPTURE") == "1"):
stdout = self.useFixture(fixtures.StringStream("stdout")).stream
self.useFixture(fixtures.MonkeyPatch("sys.stdout", stdout))
if (os.environ.get("OS_STDERR_NOCAPTURE") == "True" and
os.environ.get("OS_STDERR_NOCAPTURE") == "1"):
if (os.environ.get("OS_STDERR_CAPTURE") == "True" or
os.environ.get("OS_STDERR_CAPTURE") == "1"):
stderr = self.useFixture(fixtures.StringStream("stderr")).stream
self.useFixture(fixtures.MonkeyPatch("sys.stderr", stderr))
@ -57,3 +60,24 @@ class TestCase(testtools.TestCase):
else:
standardMsg = '%r != %r' % (d1, d2)
self.fail(standardMsg)
class TestCommand(TestCase):
"""Test cliff command classes"""
def setUp(self):
super(TestCommand, self).setUp()
# Build up a fake app
self.fake_stdout = fakes.FakeStdout()
self.app = fakes.FakeApp(self.fake_stdout)
self.app.client_manager = fakes.FakeClientManager()
def check_parser(self, cmd, args, verify_args):
cmd_parser = cmd.get_parser('check_parser')
parsed_args = cmd_parser.parse_args(args)
for av in verify_args:
attr, value = av
if attr:
self.assertIn(attr, parsed_args)
self.assertEqual(getattr(parsed_args, attr), value)
return parsed_args