diff --git a/senlinclient/tests/unit/v1/__init__.py b/senlinclient/tests/unit/v1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/senlinclient/tests/unit/v1/test_shell.py b/senlinclient/tests/unit/v1/test_shell.py new file mode 100644 index 0000000..93021c8 --- /dev/null +++ b/senlinclient/tests/unit/v1/test_shell.py @@ -0,0 +1,278 @@ +# 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 +import mock +import six +import testtools + +from senlinclient.common import exc +from senlinclient.common.i18n import _ +from senlinclient.common import utils +from senlinclient.v1 import shell as sh + + +class ShellTest(testtools.TestCase): + + def setUp(self): + super(ShellTest, self).setUp() + self.profile_args = { + 'spec_file': mock.Mock(), + 'name': 'stack_spec', + 'permission': 'ok', + 'metadata': {'user': 'demo'} + } + self.profile_spec = { + 'type': 'os.heat.stack', + 'version': 1.0, + 'properties': { + 'name': 'stack1', + 'template': {"Template": "data"} + } + } + + def _make_args(self, args): + '''Convert a dict to an object.''' + class Args(object): + def __init__(self, entries): + self.__dict__.update(entries) + + return Args(args) + + @mock.patch.object(utils, 'print_dict') + def test_do_build_info(self, mock_print): + client = mock.Mock() + result = mock.Mock() + client.get_build_info = mock.MagicMock(return_value=result) + sh.do_build_info(client) + formatters = { + 'api': utils.json_formatter, + 'engine': utils.json_formatter, + } + mock_print.assert_called_once_with(result, formatters=formatters) + self.assertTrue(client.get_build_info.called) + + @mock.patch.object(utils, 'print_list') + def test_do_profile_type_list(self, mock_print): + client = mock.Mock() + types = mock.Mock() + client.profile_types = mock.MagicMock(return_value=types) + sh.do_profile_type_list(client) + mock_print.assert_called_once_with(types, ['name'], sortby_index=0) + self.assertTrue(client.profile_types.called) + + @mock.patch.object(utils, 'format_output') + def test_do_profile_type_schema(self, mock_format): + client = mock.Mock() + schema = {'foo': 'bar'} + client.get_profile_type_schema = mock.MagicMock(return_value=schema) + args = { + 'format': 'list', + 'profile_type': 'os.nova.server' + } + args = self._make_args(args) + sh.do_profile_type_schema(client, args) + mock_format.assert_called_with(schema, format=args.format) + client.get_profile_type_schema.assert_called_with( + 'os.nova.server') + args.format = None + sh.do_profile_type_schema(client, args) + mock_format.assert_called_with(schema) + + def test_do_profile_type_schema_type_not_found(self): + client = mock.Mock() + args = {'profile_type': 'wrong_type'} + args = self._make_args(args) + ex = exc.HTTPNotFound + client.get_profile_type_schema = mock.MagicMock(side_effect=ex) + ex = self.assertRaises(exc.CommandError, + sh.do_profile_type_schema, + client, args) + self.assertEqual(_('Profile Type wrong_type not found.'), + six.text_type(ex)) + + @mock.patch.object(utils, 'print_list') + def test_do_profile_list(self, mock_print): + client = mock.Mock() + short_id = mock.Mock() + sh._short_id = short_id + profiles = mock.Mock() + client.profiles = mock.MagicMock(return_value=profiles) + fields = ['id', 'name', 'type', 'created_time'] + args = { + 'show_deleted': False, + 'limit': 20, + 'marker': 'mark_id', + 'full_id': True + } + queries = { + 'show_deleted': False, + 'limit': 20, + 'marker': 'mark_id', + } + formatters = {} + args = self._make_args(args) + sh.do_profile_list(client, args) + client.profiles.assert_called_once_with(**queries) + mock_print.assert_called_with(profiles, fields, formatters=formatters, + sortby_index=1) + + # short_id is requested + args.full_id = False + sh.do_profile_list(client, args) + formatters = {'id': short_id} + mock_print.assert_called_with(profiles, fields, formatters=formatters, + sortby_index=1) + + @mock.patch.object(utils, 'nested_dict_formatter') + @mock.patch.object(utils, 'print_dict') + def test_show_profile(self, mock_print, mock_dict): + client = mock.Mock() + profile = mock.Mock() + profile_id = mock.Mock() + client.get_profile = mock.MagicMock(return_value=profile) + pro_to_dict = mock.Mock() + profile.to_dict = mock.MagicMock(return_value=pro_to_dict) + json_formatter = mock.Mock() + utils.json_formatter = json_formatter + dict_formatter = mock.Mock() + mock_dict.return_value = dict_formatter + formatters = { + 'metadata': json_formatter, + 'spec': dict_formatter + } + sh._show_profile(client, profile_id) + client.get_profile.assert_called_once_with(profile_id) + mock_dict.assert_called_once_with(['type', 'version', 'properties'], + ['property', 'value']) + mock_print.assert_called_once_with(pro_to_dict, formatters=formatters) + + def test_show_profile_not_found(self): + client = mock.Mock() + ex = exc.HTTPNotFound + client.get_profile = mock.MagicMock(side_effect=ex) + profile_id = 'wrong_id' + ex = self.assertRaises(exc.CommandError, + sh._show_profile, + client, profile_id) + self.assertEqual(_('Profile not found: wrong_id'), six.text_type(ex)) + client.get_profile.assert_called_once_with(profile_id) + + @mock.patch.object(sh, '_show_profile') + @mock.patch.object(utils, 'format_parameters') + @mock.patch.object(utils, 'process_stack_spec') + @mock.patch.object(utils, 'get_spec_content') + def test_do_profile_create(self, mock_get, mock_proc, mock_format, + mock_show): + args = copy.deepcopy(self.profile_args) + args = self._make_args(args) + spec = copy.deepcopy(self.profile_spec) + mock_get.return_value = spec + stack_properties = mock.Mock() + mock_proc.return_value = stack_properties + mock_format.return_value = {'user': 'demo'} + params = { + 'name': 'stack_spec', + 'spec': spec, + 'permission': 'ok', + 'metadata': {'user': 'demo'}, + } + client = mock.Mock() + profile = mock.Mock() + profile_id = mock.Mock() + profile.id = profile_id + client.create_profile = mock.MagicMock(return_value=profile) + sh.do_profile_create(client, args) + mock_get.assert_called_once_with(args.spec_file) + mock_proc.assert_called_once_with(self.profile_spec['properties']) + mock_format.assert_called_once_with(args.metadata) + client.create_profile.assert_called_once_with(**params) + mock_show.assert_called_once_with(client, profile_id) + + # Miss 'type' key in spec file + del spec['type'] + ex = self.assertRaises(exc.CommandError, + sh.do_profile_create, + client, args) + self.assertEqual(_("Missing 'type' key in spec file."), + six.text_type(ex)) + # Miss 'version' key in spec file + spec['type'] = 'os.heat.stack' + del spec['version'] + ex = self.assertRaises(exc.CommandError, + sh.do_profile_create, + client, args) + self.assertEqual(_("Missing 'version' key in spec file."), + six.text_type(ex)) + # Miss 'properties' key in spec file + spec['version'] = 1.0 + del spec['properties'] + ex = self.assertRaises(exc.CommandError, + sh.do_profile_create, + client, args) + self.assertEqual(_("Missing 'properties' key in spec file."), + six.text_type(ex)) + + @mock.patch.object(sh, '_show_profile') + def test_do_profile_show(self, mock_show): + client = mock.Mock() + args = {'id': 'profile_id'} + args = self._make_args(args) + sh.do_profile_show(client, args) + mock_show.assert_called_once_with(client, args.id) + + @mock.patch.object(sh, '_show_profile') + @mock.patch.object(utils, 'format_parameters') + def test_do_profile_update(self, mock_format, mock_show): + args = copy.deepcopy(self.profile_args) + args = self._make_args(args) + mock_format.return_value = {'user': 'demo'} + client = mock.Mock() + profile = mock.Mock() + profile_id = mock.Mock() + profile.id = profile_id + args.id = 'FAKE_ID' + client.get_profile = mock.MagicMock(return_value=profile) + sh.do_profile_update(client, args) + mock_format.assert_called_once_with(args.metadata) + client.get_profile.assert_called_once_with('FAKE_ID') + params = { + 'name': 'stack_spec', + 'permission': 'ok', + 'metadata': {'user': 'demo'}, + 'id': profile_id, + } + client.update_profile.assert_called_once_with(params) + mock_show.assert_called_once_with(client, profile_id) + + # specified profile can't be found + ex = exc.HTTPNotFound + client.get_profile = mock.MagicMock(side_effect=ex) + ex = self.assertRaises(exc.CommandError, + sh.do_profile_update, + client, args) + self.assertEqual(_('Profile not found: FAKE_ID'), + six.text_type(ex)) + + def test_do_profile_delete(self): + client = mock.Mock() + args = {'id': ['profile1', 'profile2']} + args = self._make_args(args) + sh.do_profile_delete(client, args) + ex = Exception() + client.delete_profile = mock.MagicMock(side_effect=ex) + ex = self.assertRaises(exc.CommandError, + sh.do_profile_delete, + client, args) + self.assertEqual(_('Failed to delete some of the specified ' + 'profile(s).'), six.text_type(ex)) + client.delete_profile.assert_called_with('profile2') diff --git a/senlinclient/v1/shell.py b/senlinclient/v1/shell.py index 9acd4bb..ded4086 100644 --- a/senlinclient/v1/shell.py +++ b/senlinclient/v1/shell.py @@ -74,6 +74,10 @@ def do_profile_type_schema(sc, args): # PROFILES +def _short_id(obj): + return obj.id[:8] + + @utils.arg('-D', '--show-deleted', default=False, action="store_true", help=_('Include soft-deleted profiles if any.')) @utils.arg('-l', '--limit', metavar='', @@ -84,9 +88,6 @@ def do_profile_type_schema(sc, args): help=_('Print full IDs in list.')) def do_profile_list(sc, args=None): '''List profiles that meet the criteria.''' - def _short_id(obj): - return obj.id[:8] - fields = ['id', 'name', 'type', 'created_time'] queries = { 'show_deleted': args.show_deleted,