Merge "Add unit tests for cratonclient.shell.v1.cells_shell"

This commit is contained in:
Jenkins 2016-10-31 10:07:25 +00:00 committed by Gerrit Code Review
commit bdace66273
5 changed files with 523 additions and 67 deletions

View File

@ -12,9 +12,11 @@
# License for the specific language governing permissions and limitations
# under the License.
"""Cells resource and resource shell wrapper."""
from __future__ import print_function
from cratonclient.common import cliutils
from cratonclient import exceptions as exc
from cratonclient.v1.cells import CELL_FIELDS as c_fields
from cratonclient.v1 import cells
@cliutils.arg('region',
@ -28,7 +30,7 @@ from cratonclient.v1.cells import CELL_FIELDS as c_fields
def do_cell_show(cc, args):
"""Show detailed information about a cell."""
cell = cc.inventory(args.region).cells.get(args.id)
data = {f: getattr(cell, f, '') for f in c_fields}
data = {f: getattr(cell, f, '') for f in cells.CELL_FIELDS}
cliutils.print_dict(data, wrap=72)
@ -51,6 +53,7 @@ def do_cell_show(cc, args):
@cliutils.arg('--sort-dir',
metavar='<direction>',
default='asc',
choices=('asc', 'desc'),
help='Sort direction: "asc" (default) or "desc".')
@cliutils.arg('--fields',
nargs='+',
@ -69,45 +72,41 @@ def do_cell_list(cc, args):
'non-negative limit, got {0}'
.format(args.limit))
params['limit'] = args.limit
if args.fields and args.detail:
raise exc.CommandError('Cannot specify both --fields and --detail.')
if args.detail:
fields = c_fields
fields = cells.CELL_FIELDS
params['detail'] = args.detail
elif args.fields:
fields = {x: c_fields[x] for x in args.fields}
else:
fields = {x: c_fields[x] for x in default_fields}
if args.sort_key is not None:
fields_map = dict(zip(fields.keys(), fields.keys()))
# TODO(cmspence): Do we want to allow sorting by field heading value?
try:
sort_key = fields_map[args.sort_key]
except KeyError:
fields = {x: cells.CELL_FIELDS[x] for x in args.fields}
except KeyError as keyerr:
raise exc.CommandError('Invalid field "{}"'.format(keyerr.args[0]))
else:
fields = {x: cells.CELL_FIELDS[x] for x in default_fields}
sort_key = args.sort_key and args.sort_key.lower()
if sort_key is not None:
if sort_key not in cells.CELL_FIELDS:
raise exc.CommandError(
'{0} is an invalid key for sorting, valid values for '
'--sort-key are: {1}'.format(args.sort_key, c_fields.keys())
('"--sort-key" value was "{}" but should '
'be one of: {}').format(
args.sort_key,
', '.join(cells.CELL_FIELDS.keys())
)
)
params['sort_key'] = sort_key
if args.sort_dir is not None:
if args.sort_dir not in ('asc', 'desc'):
raise exc.CommandError('Invalid sort direction specified. The '
'expected valid values for --sort-dir '
'are: "asc", "desc".')
params['sort_dir'] = args.sort_dir
params['sort_dir'] = args.sort_dir
cells = cc.inventory(args.region).cells.list(**params)
cliutils.print_list(cells, list(fields))
listed_cells = cc.inventory(args.region).cells.list(**params)
cliutils.print_list(listed_cells, list(fields))
@cliutils.arg('-n', '--name',
metavar='<name>',
required=True,
help='Name of the cell.')
@cliutils.arg('-p', '--project',
dest='project_id',
metavar='<project>',
type=int,
required=True,
help='ID of the project that the cell belongs to.')
@cliutils.arg('-r', '--region',
dest='region_id',
metavar='<region>',
@ -119,9 +118,9 @@ def do_cell_list(cc, args):
def do_cell_create(cc, args):
"""Register a new cell with the Craton service."""
fields = {k: v for (k, v) in vars(args).items()
if k in c_fields and not (v is None)}
if k in cells.CELL_FIELDS and not (v is None)}
cell = cc.inventory(args.region_id).cells.create(**fields)
data = {f: getattr(cell, f, '') for f in c_fields}
data = {f: getattr(cell, f, '') for f in cells.CELL_FIELDS}
cliutils.print_dict(data, wrap=72)
@ -135,28 +134,26 @@ def do_cell_create(cc, args):
help='ID of the cell.')
@cliutils.arg('-n', '--name',
metavar='<name>',
required=True,
help='Name of the cell.')
@cliutils.arg('-p', '--project',
dest='project_id',
metavar='<project>',
type=int,
required=True,
help='Desired ID of the project that the cell should change to.')
@cliutils.arg('-r', '--region',
dest='region_id',
metavar='<region>',
type=int,
required=True,
help='Desired ID of the region that the cell should change to.')
@cliutils.arg('--note',
help='Note about the cell.')
def do_cell_update(cc, args):
"""Update a cell that is registered with the Craton service."""
fields = {k: v for (k, v) in vars(args).items()
if k in c_fields and not (v is None)}
cell = cc.inventory(args.region).cells.update(**fields)
data = {f: getattr(cell, f, '') for f in c_fields}
if k in cells.CELL_FIELDS and not (v is None)}
cell_id = fields.pop('id')
if not fields:
raise exc.CommandError(
'Nothing to update... Please specify one of --name, --region, '
'or --note'
)
cell = cc.inventory(args.region).cells.update(cell_id, **fields)
data = {f: getattr(cell, f, '') for f in cells.CELL_FIELDS}
cliutils.print_dict(data, wrap=72)
@ -170,6 +167,14 @@ def do_cell_update(cc, args):
help='ID of the cell.')
def do_cell_delete(cc, args):
"""Delete a cell that is registered with the Craton service."""
response = cc.inventory(args.region).cells.delete(args.id)
print("Cell {0} was {1}successfully deleted.".
format(args.id, '' if response else 'un'))
try:
response = cc.inventory(args.region).cells.delete(args.id)
except exc.ClientException as client_exc:
raise exc.CommandError(
'Failed to delete cell {} due to "{}:{}"'.format(
args.id, client_exc.__class__, str(client_exc)
)
)
else:
print("Cell {0} was {1} deleted.".
format(args.id, 'successfully' if response else 'not'))

View File

@ -58,7 +58,7 @@ class TestCellsShell(base.ShellTestCase):
def test_cell_list_limit_0_success(self, mock_list):
"""Verify that --limit 0 prints out all project cells."""
self.shell('cell-list -r 1 --limit 0')
mock_list.assert_called_once_with(limit=0)
mock_list.assert_called_once_with(limit=0, sort_dir='asc')
@mock.patch('cratonclient.v1.cells.CellManager.list')
def test_cell_list_limit_positive_num_success(self, mock_list):
@ -67,7 +67,7 @@ class TestCellsShell(base.ShellTestCase):
The command will print out X number of project cells.
"""
self.shell('cell-list -r 1 --limit 1')
mock_list.assert_called_once_with(limit=1)
mock_list.assert_called_once_with(limit=1, sort_dir='asc')
def test_cell_list_limit_negative_num_failure(self):
"""Verify --limit X, where X is a negative integer, fails.
@ -82,24 +82,18 @@ class TestCellsShell(base.ShellTestCase):
def test_cell_list_detail_success(self, mock_list):
"""Verify --detail argument successfully pass detail to Client."""
self.shell('cell-list -r 1 --detail')
mock_list.assert_called_once_with(detail=True)
mock_list.assert_called_once_with(detail=True, sort_dir='asc')
@mock.patch('cratonclient.v1.cells.CellManager.list')
@mock.patch('cratonclient.common.cliutils.print_list')
def test_cell_list_fields_success(self, mock_printlist, mock_list):
"""Verify --fields argument successfully passed to Client."""
self.shell('cell-list -r 1 --fields id name')
mock_list.assert_called_once_with()
mock_list.assert_called_once_with(sort_dir='asc')
mock_printlist.assert_called_once_with(mock.ANY,
list({'id': 'ID',
'name': 'Name'}))
@mock.patch('cratonclient.v1.cells.CellManager.list')
def test_cell_list_detail_and_fields_specified(self, mock_list):
"""Verify --fields ignored when --detail argument passed in."""
self.shell('cell-list -r 1 --fields id name --detail')
mock_list.assert_called_once_with(detail=True)
@mock.patch('cratonclient.v1.cells.CellManager.list')
def test_cell_list_sort_key_field_key_success(self, mock_list):
"""Verify --sort-key arguments successfully passed to Client."""
@ -113,12 +107,6 @@ class TestCellsShell(base.ShellTestCase):
self.shell,
'cell-list -r 1 --sort-key invalid')
@mock.patch('cratonclient.v1.cells.CellManager.list')
def test_cell_list_sort_dir_not_passed_without_sort_key(self, mock_list):
"""Verify --sort-dir arg ignored without --sort-key."""
self.shell('cell-list -r 1 --sort-dir desc')
mock_list.assert_called_once_with()
@mock.patch('cratonclient.v1.cells.CellManager.list')
def test_cell_list_sort_dir_asc_success(self, mock_list):
"""Verify --sort-dir asc successfully passed to Client."""
@ -135,9 +123,10 @@ class TestCellsShell(base.ShellTestCase):
def test_cell_list_sort_dir_invalid_value(self):
"""Verify --sort-dir with invalid args, fails with Command Error."""
self.assertRaises(exc.CommandError,
self.shell,
'cell-list -r 1 --sort-key name --sort-dir invalid')
(_, error) = self.shell(
'cell-list -r 1 --sort-key name --sort-dir invalid'
)
self.assertIn("invalid choice: 'invalid'", error)
def test_cell_create_missing_required_args(self):
"""Verify that missing required args results in error message."""
@ -203,8 +192,7 @@ class TestCellsShell(base.ShellTestCase):
id=1,
name='mock_cell')
cells_shell.do_cell_update(client, valid_input)
vars(valid_input).pop('region')
mock_update.assert_called_once_with(**vars(valid_input))
mock_update.assert_called_once_with(1, name='mock_cell')
@mock.patch('cratonclient.v1.cells.CellManager.update')
def test_do_cell_update_ignores_unknown_fields(self, mock_update):
@ -221,9 +209,7 @@ class TestCellsShell(base.ShellTestCase):
name='mock_cell',
invalid=True)
cells_shell.do_cell_update(client, invalid_input)
vars(invalid_input).pop('region')
vars(invalid_input).pop('invalid')
mock_update.assert_called_once_with(**vars(invalid_input))
mock_update.assert_called_once_with(1, name='mock_cell')
def test_cell_show_missing_required_args(self):
"""Verify that missing required args results in error message."""

View File

@ -0,0 +1 @@
"""Unit tests for cratonclient.shell submodules."""

View File

@ -0,0 +1 @@
"""Unit tests for cratonclient.shell.v1 submodules."""

View File

@ -0,0 +1,463 @@
# -*- coding: utf-8 -*-
# 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.
"""Tests for the shell functions for the cells resource."""
import argparse
import mock
from cratonclient import exceptions
from cratonclient.shell.v1 import cells_shell
from cratonclient.tests import base
from cratonclient.v1 import cells
class TestCells(base.TestCase):
"""Base class for cells_shell commands."""
def setUp(self):
"""Initialize test fixtures."""
super(TestCells, self).setUp()
self.craton_client = mock.Mock()
self.inventory = mock.Mock()
self.craton_client.inventory.return_value = self.inventory
def assertRaisesCommandErrorWith(self, func, args):
"""Assert do_cell_create raises CommandError."""
self.assertRaises(
exceptions.CommandError,
func, self.craton_client, args,
)
class TestCellsPrintDict(TestCells):
"""Base class for commands using print_dict."""
def setUp(self):
"""Initialize test fixtures."""
super(TestCellsPrintDict, self).setUp()
self.print_dict_patch = mock.patch(
'cratonclient.common.cliutils.print_dict'
)
self.print_dict = self.print_dict_patch.start()
def tearDown(self):
"""Clean-up test fixtures."""
super(TestCellsPrintDict, self).tearDown()
self.print_dict_patch.stop()
def assertNothingWasCalled(self):
"""Assert inventory, list, and print_dict were not called."""
self.assertFalse(self.craton_client.inventory.called)
self.assertFalse(self.inventory.cells.list.called)
self.assertFalse(self.print_dict.called)
class TestDoShellShow(TestCellsPrintDict):
"""Unit tests for the cell show command."""
def test_simple_usage(self):
"""Verify the behaviour of do_cell_show."""
args = argparse.Namespace(
region=123,
id=456,
)
cells_shell.do_cell_show(self.craton_client, args)
self.craton_client.inventory.assert_called_once_with(123)
self.inventory.cells.get.assert_called_once_with(456)
self.print_dict.assert_called_once_with(
{f: mock.ANY for f in cells.CELL_FIELDS},
wrap=72,
)
class TestDoCellList(TestCells):
"""Unit tests for the cell list command."""
def setUp(self):
"""Initialize test fixtures."""
super(TestDoCellList, self).setUp()
self.print_list_patch = mock.patch(
'cratonclient.common.cliutils.print_list'
)
self.print_list = self.print_list_patch.start()
def tearDown(self):
"""Clean-up test fixtures."""
super(TestDoCellList, self).tearDown()
self.print_list_patch.stop()
def assertNothingWasCalled(self):
"""Assert inventory, list, and print_list were not called."""
self.assertFalse(self.craton_client.inventory.called)
self.assertFalse(self.inventory.cells.list.called)
self.assertFalse(self.print_list.called)
def test_with_defaults(self):
"""Verify the behaviour of do_cell_list with mostly default values."""
args = argparse.Namespace(
region=123,
detail=False,
limit=None,
sort_key=None,
sort_dir='asc',
fields=[],
)
cells_shell.do_cell_list(self.craton_client, args)
self.craton_client.inventory.assert_called_once_with(123)
self.inventory.cells.list.assert_called_once_with(sort_dir='asc')
self.assertTrue(self.print_list.called)
self.assertEqual(['id', 'name'],
sorted(self.print_list.call_args[0][-1]))
def test_negative_limit(self):
"""Ensure we raise an exception for negative limits."""
args = argparse.Namespace(
region=123,
detail=False,
limit=-1,
sort_key=None,
sort_dir='asc',
fields=[],
)
self.assertRaisesCommandErrorWith(cells_shell.do_cell_list, args)
self.assertNothingWasCalled()
def test_positive_limit(self):
"""Verify that we pass positive limits to the call to list."""
args = argparse.Namespace(
region=123,
detail=False,
limit=5,
sort_key=None,
sort_dir='asc',
fields=[],
)
cells_shell.do_cell_list(self.craton_client, args)
self.craton_client.inventory.assert_called_once_with(123)
self.inventory.cells.list.assert_called_once_with(
limit=5,
sort_dir='asc',
)
self.assertTrue(self.print_list.called)
self.assertEqual(['id', 'name'],
sorted(self.print_list.call_args[0][-1]))
def test_valid_sort_key(self):
"""Verify that we pass on our sort key."""
args = argparse.Namespace(
region=123,
detail=False,
limit=None,
sort_key='name',
sort_dir='asc',
fields=[],
)
cells_shell.do_cell_list(self.craton_client, args)
self.craton_client.inventory.assert_called_once_with(123)
self.inventory.cells.list.assert_called_once_with(
sort_dir='asc',
sort_key='name',
)
self.assertTrue(self.print_list.called)
self.assertEqual(['id', 'name'],
sorted(self.print_list.call_args[0][-1]))
def test_invalid_sort_key(self):
"""Verify that do not we pass on our sort key."""
args = argparse.Namespace(
region=123,
detail=False,
limit=None,
sort_key='fake-sort-key',
sort_dir='asc',
fields=[],
)
self.assertRaisesCommandErrorWith(cells_shell.do_cell_list, args)
self.assertNothingWasCalled()
def test_detail(self):
"""Verify the behaviour of specifying --detail."""
args = argparse.Namespace(
region=123,
detail=True,
limit=None,
sort_key=None,
sort_dir='asc',
fields=[],
)
cells_shell.do_cell_list(self.craton_client, args)
self.craton_client.inventory.assert_called_once_with(123)
self.inventory.cells.list.assert_called_once_with(
sort_dir='asc',
detail=True,
)
self.assertEqual(sorted(list(cells.CELL_FIELDS)),
sorted(self.print_list.call_args[0][-1]))
def test_raises_exception_with_detail_and_fields(self):
"""Verify that we fail when users specify --detail and --fields."""
args = argparse.Namespace(
region=123,
detail=True,
limit=None,
sort_key=None,
sort_dir='asc',
fields=['id', 'name'],
)
self.assertRaisesCommandErrorWith(cells_shell.do_cell_list, args)
self.assertNothingWasCalled()
def test_fields(self):
"""Verify that we print out specific fields."""
args = argparse.Namespace(
region=123,
detail=False,
limit=None,
sort_key=None,
sort_dir='asc',
fields=['id', 'name', 'note'],
)
cells_shell.do_cell_list(self.craton_client, args)
self.craton_client.inventory.assert_called_once_with(123)
self.inventory.cells.list.assert_called_once_with(
sort_dir='asc',
)
self.assertEqual(['id', 'name', 'note'],
sorted(self.print_list.call_args[0][-1]))
def test_invalid_fields(self):
"""Verify that we error out with invalid fields."""
args = argparse.Namespace(
region=123,
detail=False,
limit=None,
sort_key=None,
sort_dir='asc',
fields=['uuid', 'not-name', 'nate'],
)
self.assertRaisesCommandErrorWith(cells_shell.do_cell_list, args)
self.assertNothingWasCalled()
class TestDoCellCreate(TestCellsPrintDict):
"""Unit tests for the cell create command."""
def test_create_without_note(self):
"""Verify our parameters to cells.create."""
args = argparse.Namespace(
name='New Cell',
region_id=123,
note=None,
)
cells_shell.do_cell_create(self.craton_client, args)
self.craton_client.inventory.assert_called_once_with(123)
self.inventory.cells.create.assert_called_once_with(
name='New Cell',
region_id=123,
)
self.print_dict.assert_called_once_with(mock.ANY, wrap=72)
def test_create_with_note(self):
"""Verify that we include the note argument when present."""
args = argparse.Namespace(
name='New Cell',
region_id=123,
note='This is a note',
)
cells_shell.do_cell_create(self.craton_client, args)
self.craton_client.inventory.assert_called_once_with(123)
self.inventory.cells.create.assert_called_once_with(
name='New Cell',
region_id=123,
note='This is a note',
)
self.print_dict.assert_called_once_with(mock.ANY, wrap=72)
class TestDoCellUpdate(TestCellsPrintDict):
"""Unit tests for the cell update command."""
def test_update_without_name_region_or_note_fails(self):
"""Verify we raise a command error when there's nothing to update."""
args = argparse.Namespace(
id=123,
region=345,
name=None,
region_id=None,
note=None,
)
self.assertRaisesCommandErrorWith(cells_shell.do_cell_update, args)
self.assertNothingWasCalled()
def test_update_with_name(self):
"""Verify we update with only the new name."""
args = argparse.Namespace(
id=123,
region=345,
name='New name',
region_id=None,
note=None,
)
cells_shell.do_cell_update(self.craton_client, args)
self.craton_client.inventory.assert_called_once_with(345)
self.inventory.cells.update.assert_called_once_with(
123,
name='New name',
)
self.print_dict.assert_called_once_with(mock.ANY, wrap=72)
def test_update_with_new_region(self):
"""Verify we update with only the new region id."""
args = argparse.Namespace(
id=123,
region=345,
name=None,
region_id=678,
note=None,
)
cells_shell.do_cell_update(self.craton_client, args)
self.craton_client.inventory.assert_called_once_with(345)
self.inventory.cells.update.assert_called_once_with(
123,
region_id=678,
)
self.print_dict.assert_called_once_with(mock.ANY, wrap=72)
def test_update_with_new_note(self):
"""Verify we update with only the new note text."""
args = argparse.Namespace(
id=123,
region=345,
name=None,
region_id=None,
note='A new note',
)
cells_shell.do_cell_update(self.craton_client, args)
self.craton_client.inventory.assert_called_once_with(345)
self.inventory.cells.update.assert_called_once_with(
123,
note='A new note',
)
self.print_dict.assert_called_once_with(mock.ANY, wrap=72)
def test_update_with_everything(self):
"""Verify we update with everything."""
args = argparse.Namespace(
id=123,
region=345,
name='A new name for a new region',
region_id=678,
note='A new note',
)
cells_shell.do_cell_update(self.craton_client, args)
self.craton_client.inventory.assert_called_once_with(345)
self.inventory.cells.update.assert_called_once_with(
123,
name='A new name for a new region',
region_id=678,
note='A new note',
)
self.print_dict.assert_called_once_with(mock.ANY, wrap=72)
class TestDoCellDelete(TestCells):
"""Tests for the do_cell_delete command."""
def setUp(self):
"""Initialize our print mock."""
super(TestDoCellDelete, self).setUp()
self.print_func_mock = mock.patch(
'cratonclient.shell.v1.cells_shell.print'
)
self.print_func = self.print_func_mock.start()
def tearDown(self):
"""Clean up our print mock."""
super(TestDoCellDelete, self).tearDown()
self.print_func_mock.stop()
def test_successful(self):
"""Verify the message we print when successful."""
self.inventory.cells.delete.return_value = True
args = argparse.Namespace(
region=123,
id=456,
)
cells_shell.do_cell_delete(self.craton_client, args)
self.craton_client.inventory.assert_called_once_with(123)
self.inventory.cells.delete.assert_called_once_with(456)
self.print_func.assert_called_once_with(
'Cell 456 was successfully deleted.'
)
def test_failed(self):
"""Verify the message we print when deletion fails."""
self.inventory.cells.delete.return_value = False
args = argparse.Namespace(
region=123,
id=456,
)
cells_shell.do_cell_delete(self.craton_client, args)
self.craton_client.inventory.assert_called_once_with(123)
self.inventory.cells.delete.assert_called_once_with(456)
self.print_func.assert_called_once_with(
'Cell 456 was not deleted.'
)
def test_failed_with_exception(self):
"""Verify the message we print when deletion fails."""
self.inventory.cells.delete.side_effect = exceptions.NotFound
args = argparse.Namespace(
region=123,
id=456,
)
self.assertRaisesCommandErrorWith(cells_shell.do_cell_delete, args)
self.craton_client.inventory.assert_called_once_with(123)
self.inventory.cells.delete.assert_called_once_with(456)
self.assertFalse(self.print_func.called)