# 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 import io import tempfile from unittest import mock from cinderclient import api_versions from openstack import exceptions as sdk_exceptions from osc_lib.cli import format_columns from osc_lib import exceptions from openstackclient.image.v2 import image as _image from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes from openstackclient.tests.unit.image.v2 import fakes as image_fakes from openstackclient.tests.unit.volume.v3 import fakes as volume_fakes class TestImage(image_fakes.TestImagev2, volume_fakes.TestVolume): def setUp(self): super().setUp() # Get shortcuts to mocked image client self.client = self.app.client_manager.image # Get shortcut to the Mocks in identity client self.project_mock = self.app.client_manager.identity.projects self.project_mock.reset_mock() self.domain_mock = self.app.client_manager.identity.domains self.domain_mock.reset_mock() self.volumes_mock = self.app.client_manager.volume.volumes fake_body = { 'os-volume_upload_image': {'volume_type': {'name': 'fake_type'}} } self.volumes_mock.upload_to_image.return_value = (200, fake_body) self.volumes_mock.reset_mock() def setup_images_mock(self, count): images = image_fakes.create_images(count=count) return images class TestImageCreate(TestImage): project = identity_fakes.FakeProject.create_one_project() domain = identity_fakes.FakeDomain.create_one_domain() def setUp(self): super().setUp() self.new_image = image_fakes.create_one_image() self.client.create_image.return_value = self.new_image self.project_mock.get.return_value = self.project self.domain_mock.get.return_value = self.domain self.client.update_image.return_value = self.new_image (self.expected_columns, self.expected_data) = zip( *sorted(_image._format_image(self.new_image).items()) ) # Get the command object to test self.cmd = _image.CreateImage(self.app, None) @mock.patch("sys.stdin", side_effect=[None]) def test_image_reserve_no_options(self, raw_input): arglist = [self.new_image.name] verifylist = [ ('container_format', _image.DEFAULT_CONTAINER_FORMAT), ('disk_format', _image.DEFAULT_DISK_FORMAT), ('name', self.new_image.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) # In base command class ShowOne in cliff, abstract method take_action() # returns a two-part tuple with a tuple of column names and a tuple of # data to be shown. columns, data = self.cmd.take_action(parsed_args) # ImageManager.create(name=, **) self.client.create_image.assert_called_with( name=self.new_image.name, allow_duplicates=True, container_format=_image.DEFAULT_CONTAINER_FORMAT, disk_format=_image.DEFAULT_DISK_FORMAT, ) self.assertEqual(self.expected_columns, columns) self.assertCountEqual(self.expected_data, data) @mock.patch('sys.stdin', side_effect=[None]) def test_image_reserve_options(self, raw_input): arglist = [ '--container-format', 'ovf', '--disk-format', 'ami', '--min-disk', '10', '--min-ram', '4', '--protected' if self.new_image.is_protected else '--unprotected', ( '--private' if self.new_image.visibility == 'private' else '--public' ), '--project', self.new_image.owner_id, '--project-domain', self.domain.id, self.new_image.name, ] verifylist = [ ('container_format', 'ovf'), ('disk_format', 'ami'), ('min_disk', 10), ('min_ram', 4), ('is_protected', self.new_image.is_protected), ('visibility', self.new_image.visibility), ('project', self.new_image.owner_id), ('project_domain', self.domain.id), ('name', self.new_image.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) # In base command class ShowOne in cliff, abstract method take_action() # returns a two-part tuple with a tuple of column names and a tuple of # data to be shown. columns, data = self.cmd.take_action(parsed_args) # ImageManager.create(name=, **) self.client.create_image.assert_called_with( name=self.new_image.name, allow_duplicates=True, container_format='ovf', disk_format='ami', min_disk=10, min_ram=4, owner_id=self.project.id, is_protected=self.new_image.is_protected, visibility=self.new_image.visibility, ) self.assertEqual(self.expected_columns, columns) self.assertCountEqual(self.expected_data, data) def test_image_create_with_unexist_project(self): self.project_mock.get.side_effect = exceptions.NotFound(None) self.project_mock.find.side_effect = exceptions.NotFound(None) arglist = [ '--container-format', 'ovf', '--disk-format', 'ami', '--min-disk', '10', '--min-ram', '4', '--protected', '--private', '--project', 'unexist_owner', 'graven', ] verifylist = [ ('container_format', 'ovf'), ('disk_format', 'ami'), ('min_disk', 10), ('min_ram', 4), ('is_protected', True), ('visibility', 'private'), ('project', 'unexist_owner'), ('name', 'graven'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args, ) def test_image_create_file(self): imagefile = tempfile.NamedTemporaryFile(delete=False) imagefile.write(b'\0') imagefile.close() arglist = [ '--file', imagefile.name, ( '--unprotected' if not self.new_image.is_protected else '--protected' ), ( '--public' if self.new_image.visibility == 'public' else '--private' ), '--property', 'Alpha=1', '--property', 'Beta=2', '--tag', self.new_image.tags[0], '--tag', self.new_image.tags[1], self.new_image.name, ] verifylist = [ ('filename', imagefile.name), ('is_protected', self.new_image.is_protected), ('visibility', self.new_image.visibility), ('properties', {'Alpha': '1', 'Beta': '2'}), ('tags', self.new_image.tags), ('name', self.new_image.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) # In base command class ShowOne in cliff, abstract method take_action() # returns a two-part tuple with a tuple of column names and a tuple of # data to be shown. columns, data = self.cmd.take_action(parsed_args) # ImageManager.create(name=, **) self.client.create_image.assert_called_with( name=self.new_image.name, allow_duplicates=True, container_format=_image.DEFAULT_CONTAINER_FORMAT, disk_format=_image.DEFAULT_DISK_FORMAT, is_protected=self.new_image.is_protected, visibility=self.new_image.visibility, Alpha='1', Beta='2', tags=self.new_image.tags, filename=imagefile.name, ) self.assertEqual(self.expected_columns, columns) self.assertCountEqual(self.expected_data, data) @mock.patch('openstackclient.image.v2.image.get_data_from_stdin') def test_image_create__progress_ignore_with_stdin( self, mock_get_data_from_stdin, ): fake_stdin = io.BytesIO(b'some fake data') mock_get_data_from_stdin.return_value = fake_stdin arglist = [ '--progress', self.new_image.name, ] verifylist = [ ('progress', True), ('name', self.new_image.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.client.create_image.assert_called_with( name=self.new_image.name, allow_duplicates=True, container_format=_image.DEFAULT_CONTAINER_FORMAT, disk_format=_image.DEFAULT_DISK_FORMAT, data=fake_stdin, validate_checksum=False, ) self.assertEqual(self.expected_columns, columns) self.assertCountEqual(self.expected_data, data) def test_image_create_dead_options(self): arglist = [ '--store', 'somewhere', self.new_image.name, ] verifylist = [ ('name', self.new_image.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) @mock.patch('sys.stdin', side_effect=[None]) def test_image_create_import(self, raw_input): arglist = [ '--import', self.new_image.name, ] verifylist = [ ('name', self.new_image.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) # ImageManager.create(name=, **) self.client.create_image.assert_called_with( name=self.new_image.name, allow_duplicates=True, container_format=_image.DEFAULT_CONTAINER_FORMAT, disk_format=_image.DEFAULT_DISK_FORMAT, use_import=True, ) @mock.patch('osc_lib.utils.find_resource') @mock.patch('openstackclient.image.v2.image.get_data_from_stdin') def test_image_create_from_volume(self, mock_get_data_f, mock_get_vol): fake_vol_id = 'fake-volume-id' mock_get_data_f.return_value = None class FakeVolume: id = fake_vol_id mock_get_vol.return_value = FakeVolume() arglist = [ '--volume', fake_vol_id, self.new_image.name, ] verifylist = [ ('name', self.new_image.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.volumes_mock.upload_to_image.assert_called_with( fake_vol_id, False, self.new_image.name, 'bare', 'raw' ) @mock.patch('osc_lib.utils.find_resource') @mock.patch('openstackclient.image.v2.image.get_data_from_stdin') def test_image_create_from_volume_fail( self, mock_get_data_f, mock_get_vol ): fake_vol_id = 'fake-volume-id' mock_get_data_f.return_value = None class FakeVolume: id = fake_vol_id mock_get_vol.return_value = FakeVolume() arglist = ['--volume', fake_vol_id, self.new_image.name, '--public'] verifylist = [ ('name', self.new_image.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) @mock.patch('osc_lib.utils.find_resource') @mock.patch('openstackclient.image.v2.image.get_data_from_stdin') def test_image_create_from_volume_v31(self, mock_get_data_f, mock_get_vol): self.app.client_manager.volume.api_version = api_versions.APIVersion( '3.1' ) fake_vol_id = 'fake-volume-id' mock_get_data_f.return_value = None class FakeVolume: id = fake_vol_id mock_get_vol.return_value = FakeVolume() arglist = ['--volume', fake_vol_id, self.new_image.name, '--public'] verifylist = [ ('name', self.new_image.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.volumes_mock.upload_to_image.assert_called_with( fake_vol_id, False, self.new_image.name, 'bare', 'raw', visibility='public', protected=False, ) class TestAddProjectToImage(TestImage): project = identity_fakes.FakeProject.create_one_project() domain = identity_fakes.FakeDomain.create_one_domain() _image = image_fakes.create_one_image() new_member = image_fakes.create_one_image_member( attrs={'image_id': _image.id, 'member_id': project.id} ) columns = ( 'created_at', 'image_id', 'member_id', 'schema', 'status', 'updated_at', ) datalist = ( new_member.created_at, _image.id, new_member.member_id, new_member.schema, new_member.status, new_member.updated_at, ) def setUp(self): super().setUp() # This is the return value for utils.find_resource() self.client.find_image.return_value = self._image # Update the image_id in the MEMBER dict self.client.add_member.return_value = self.new_member self.project_mock.get.return_value = self.project self.domain_mock.get.return_value = self.domain # Get the command object to test self.cmd = _image.AddProjectToImage(self.app, None) def test_add_project_to_image_no_option(self): arglist = [ self._image.id, self.project.id, ] verifylist = [ ('image', self._image.id), ('project', self.project.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) # In base command class ShowOne in cliff, abstract method take_action() # returns a two-part tuple with a tuple of column names and a tuple of # data to be shown. columns, data = self.cmd.take_action(parsed_args) self.client.add_member.assert_called_with( image=self._image.id, member_id=self.project.id ) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) def test_add_project_to_image_with_option(self): arglist = [ self._image.id, self.project.id, '--project-domain', self.domain.id, ] verifylist = [ ('image', self._image.id), ('project', self.project.id), ('project_domain', self.domain.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) # In base command class ShowOne in cliff, abstract method take_action() # returns a two-part tuple with a tuple of column names and a tuple of # data to be shown. columns, data = self.cmd.take_action(parsed_args) self.client.add_member.assert_called_with( image=self._image.id, member_id=self.project.id ) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) class TestImageDelete(TestImage): def setUp(self): super().setUp() self.client.delete_image.return_value = None # Get the command object to test self.cmd = _image.DeleteImage(self.app, None) def test_image_delete_no_options(self): images = self.setup_images_mock(count=1) arglist = [ images[0].id, ] verifylist = [ ('images', [images[0].id]), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.client.find_image.side_effect = images result = self.cmd.take_action(parsed_args) self.client.delete_image.assert_called_with( images[0].id, store=parsed_args.store, ignore_missing=False ) self.assertIsNone(result) def test_image_delete_from_store(self): images = self.setup_images_mock(count=1) arglist = [ images[0].id, '--store', 'store1', ] verifylist = [('images', [images[0].id]), ('store', 'store1')] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.client.find_image.side_effect = images result = self.cmd.take_action(parsed_args) self.client.delete_image.assert_called_with( images[0].id, store=parsed_args.store, ignore_missing=False ) self.assertIsNone(result) def test_image_delete_multi_images(self): images = self.setup_images_mock(count=3) arglist = [i.id for i in images] verifylist = [ ('images', arglist), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.client.find_image.side_effect = images result = self.cmd.take_action(parsed_args) calls = [ mock.call(i.id, store=parsed_args.store, ignore_missing=False) for i in images ] self.client.delete_image.assert_has_calls(calls) self.assertIsNone(result) def test_image_delete_from_store_without_multi_backend(self): images = self.setup_images_mock(count=1) arglist = [images[0].id, '--store', 'store1'] verifylist = [('images', [images[0].id]), ('store', 'store1')] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.client.find_image.side_effect = images self.client.delete_image.side_effect = sdk_exceptions.ResourceNotFound exc = self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args, ) self.assertIn( "Multi Backend support not enabled", str(exc), ) def test_image_delete_multi_images_exception(self): images = image_fakes.create_images(count=2) arglist = [ images[0].id, images[1].id, 'x-y-x', ] verifylist = [('images', arglist)] parsed_args = self.check_parser(self.cmd, arglist, verifylist) # Fake exception in utils.find_resource() # In image v2, we use utils.find_resource() to find a network. # It calls get() several times, but find() only one time. So we # choose to fake get() always raise exception, then pass through. # And fake find() to find the real network or not. ret_find = [images[0], images[1], sdk_exceptions.ResourceNotFound()] self.client.find_image.side_effect = ret_find self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) calls = [ mock.call(i.id, store=parsed_args.store, ignore_missing=False) for i in images ] self.client.delete_image.assert_has_calls(calls) class TestImageList(TestImage): _image = image_fakes.create_one_image() columns = ( 'ID', 'Name', 'Status', ) datalist = ( ( _image.id, _image.name, None, ), ) def setUp(self): super().setUp() self.client.images.side_effect = [[self._image], []] # Get the command object to test self.cmd = _image.ListImage(self.app, None) def test_image_list_no_options(self): arglist = [] verifylist = [ ('visibility', None), ('long', False), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) # In base command class Lister in cliff, abstract method take_action() # returns a tuple containing the column names and an iterable # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) self.client.images.assert_called_with( # marker=self._image.id, ) self.assertEqual(self.columns, columns) self.assertCountEqual(self.datalist, tuple(data)) def test_image_list_public_option(self): arglist = [ '--public', ] verifylist = [ ('visibility', 'public'), ('long', False), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) # In base command class Lister in cliff, abstract method take_action() # returns a tuple containing the column names and an iterable # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) self.client.images.assert_called_with( visibility='public', ) self.assertEqual(self.columns, columns) self.assertCountEqual(self.datalist, tuple(data)) def test_image_list_private_option(self): arglist = [ '--private', ] verifylist = [ ('visibility', 'private'), ('long', False), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) # In base command class Lister in cliff, abstract method take_action() # returns a tuple containing the column names and an iterable # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) self.client.images.assert_called_with( visibility='private', ) self.assertEqual(self.columns, columns) self.assertCountEqual(self.datalist, tuple(data)) def test_image_list_community_option(self): arglist = [ '--community', ] verifylist = [ ('visibility', 'community'), ('long', False), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) # In base command class Lister in cliff, abstract method take_action() # returns a tuple containing the column names and an iterable # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) self.client.images.assert_called_with( visibility='community', ) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, tuple(data)) def test_image_list_shared_option(self): arglist = [ '--shared', ] verifylist = [ ('visibility', 'shared'), ('long', False), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) # In base command class Lister in cliff, abstract method take_action() # returns a tuple containing the column names and an iterable # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) self.client.images.assert_called_with( visibility='shared', ) self.assertEqual(self.columns, columns) self.assertCountEqual(self.datalist, tuple(data)) def test_image_list_all_option(self): arglist = [ '--all', ] verifylist = [ ('visibility', 'all'), ('long', False), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) # In base command class Lister in cliff, abstract method take_action() # returns a tuple containing the column names and an iterable # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) self.client.images.assert_called_with( visibility='all', ) self.assertEqual(self.columns, columns) self.assertCountEqual(self.datalist, tuple(data)) def test_image_list_shared_member_status_option(self): arglist = ['--shared', '--member-status', 'all'] verifylist = [ ('visibility', 'shared'), ('long', False), ('member_status', 'all'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) # In base command class Lister in cliff, abstract method take_action() # returns a tuple containing the column names and an iterable # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) self.client.images.assert_called_with( visibility='shared', member_status='all', ) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, tuple(data)) def test_image_list_shared_member_status_lower(self): arglist = ['--shared', '--member-status', 'ALl'] verifylist = [ ('visibility', 'shared'), ('long', False), ('member_status', 'all'), ] self.check_parser(self.cmd, arglist, verifylist) def test_image_list_long_option(self): arglist = [ '--long', ] verifylist = [ ('long', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) # In base command class Lister in cliff, abstract method take_action() # returns a tuple containing the column names and an iterable # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) self.client.images.assert_called_with() collist = ( 'ID', 'Name', 'Disk Format', 'Container Format', 'Size', 'Checksum', 'Status', 'Visibility', 'Protected', 'Project', 'Tags', ) self.assertEqual(collist, columns) datalist = ( ( self._image.id, self._image.name, None, None, None, None, None, self._image.visibility, self._image.is_protected, self._image.owner_id, format_columns.ListColumn(self._image.tags), ), ) self.assertCountEqual(datalist, tuple(data)) @mock.patch('osc_lib.api.utils.simple_filter') def test_image_list_property_option(self, sf_mock): sf_mock.return_value = [copy.deepcopy(self._image)] arglist = [ '--property', 'a=1', ] verifylist = [ ('property', {'a': '1'}), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) # In base command class Lister in cliff, abstract method take_action() # returns a tuple containing the column names and an iterable # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) self.client.images.assert_called_with() sf_mock.assert_called_with( [self._image], attr='a', value='1', property_field='properties', ) self.assertEqual(self.columns, columns) self.assertCountEqual(self.datalist, tuple(data)) @mock.patch('osc_lib.utils.sort_items') def test_image_list_sort_option(self, si_mock): si_mock.return_value = [copy.deepcopy(self._image)] arglist = ['--sort', 'name:asc'] verifylist = [('sort', 'name:asc')] parsed_args = self.check_parser(self.cmd, arglist, verifylist) # In base command class Lister in cliff, abstract method take_action() # returns a tuple containing the column names and an iterable # containing the data to be listed. columns, data = self.cmd.take_action(parsed_args) self.client.images.assert_called_with() si_mock.assert_called_with( [self._image], 'name:asc', str, ) self.assertEqual(self.columns, columns) self.assertCountEqual(self.datalist, tuple(data)) def test_image_list_limit_option(self): ret_limit = 1 arglist = [ '--limit', str(ret_limit), ] verifylist = [ ('limit', ret_limit), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.client.images.assert_called_with( limit=ret_limit, paginated=False # marker=None ) self.assertEqual(self.columns, columns) self.assertEqual(ret_limit, len(tuple(data))) def test_image_list_project_option(self): self.client.find_image = mock.Mock(return_value=self._image) arglist = [ '--project', 'nova', ] verifylist = [ ('project', 'nova'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.assertEqual(self.columns, columns) self.assertCountEqual(self.datalist, tuple(data)) @mock.patch('osc_lib.utils.find_resource') def test_image_list_marker_option(self, fr_mock): self.client.find_image = mock.Mock(return_value=self._image) arglist = [ '--marker', 'graven', ] verifylist = [ ('marker', 'graven'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.client.images.assert_called_with( marker=self._image.id, ) self.client.find_image.assert_called_with( 'graven', ignore_missing=False, ) def test_image_list_name_option(self): arglist = [ '--name', 'abc', ] verifylist = [ ('name', 'abc'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.client.images.assert_called_with( name='abc', # marker=self._image.id ) def test_image_list_status_option(self): arglist = [ '--status', 'active', ] verifylist = [ ('status', 'active'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.client.images.assert_called_with(status='active') def test_image_list_hidden_option(self): arglist = [ '--hidden', ] verifylist = [ ('is_hidden', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.client.images.assert_called_with(is_hidden=True) def test_image_list_tag_option(self): arglist = ['--tag', 'abc', '--tag', 'cba'] verifylist = [ ('tag', ['abc', 'cba']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.client.images.assert_called_with(tag=['abc', 'cba']) class TestListImageProjects(TestImage): project = identity_fakes.FakeProject.create_one_project() _image = image_fakes.create_one_image() member = image_fakes.create_one_image_member( attrs={'image_id': _image.id, 'member_id': project.id} ) columns = ("Image ID", "Member ID", "Status") datalist = [ ( _image.id, member.member_id, member.status, ) ] def setUp(self): super().setUp() self.client.find_image.return_value = self._image self.client.members.return_value = [self.member] self.cmd = _image.ListImageProjects(self.app, None) def test_image_member_list(self): arglist = [self._image.id] verifylist = [('image', self._image.id)] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) self.client.members.assert_called_with(image=self._image.id) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, list(data)) class TestRemoveProjectImage(TestImage): project = identity_fakes.FakeProject.create_one_project() domain = identity_fakes.FakeDomain.create_one_domain() def setUp(self): super().setUp() self._image = image_fakes.create_one_image() # This is the return value for utils.find_resource() self.client.find_image.return_value = self._image self.project_mock.get.return_value = self.project self.domain_mock.get.return_value = self.domain self.client.remove_member.return_value = None # Get the command object to test self.cmd = _image.RemoveProjectImage(self.app, None) def test_remove_project_image_no_options(self): arglist = [ self._image.id, self.project.id, ] verifylist = [ ('image', self._image.id), ('project', self.project.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.client.find_image.assert_called_with( self._image.id, ignore_missing=False ) self.client.remove_member.assert_called_with( member=self.project.id, image=self._image.id, ) self.assertIsNone(result) def test_remove_project_image_with_options(self): arglist = [ self._image.id, self.project.id, '--project-domain', self.domain.id, ] verifylist = [ ('image', self._image.id), ('project', self.project.id), ('project_domain', self.domain.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.client.remove_member.assert_called_with( member=self.project.id, image=self._image.id, ) self.assertIsNone(result) class TestImageSet(TestImage): project = identity_fakes.FakeProject.create_one_project() domain = identity_fakes.FakeDomain.create_one_domain() _image = image_fakes.create_one_image({'tags': []}) def setUp(self): super().setUp() self.project_mock.get.return_value = self.project self.domain_mock.get.return_value = self.domain self.client.find_image.return_value = self._image self.app.client_manager.auth_ref = mock.Mock( project_id=self.project.id, ) # Get the command object to test self.cmd = _image.SetImage(self.app, None) def test_image_set_no_options(self): arglist = [ '0f41529e-7c12-4de8-be2d-181abb825b3c', ] verifylist = [('image', '0f41529e-7c12-4de8-be2d-181abb825b3c')] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.assertIsNone(result) # we'll have called this but not set anything self.app.client_manager.image.update_image.assert_called_once_with( self._image.id, ) def test_image_set_membership_option_accept(self): membership = image_fakes.create_one_image_member( attrs={ 'image_id': '0f41529e-7c12-4de8-be2d-181abb825b3c', 'member_id': self.project.id, } ) self.client.update_member.return_value = membership arglist = [ '--accept', self._image.id, ] verifylist = [('membership', 'accepted'), ('image', self._image.id)] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.client.update_member.assert_called_once_with( image=self._image.id, member=self.app.client_manager.auth_ref.project_id, status='accepted', ) # Assert that the 'update image" route is also called, in addition to # the 'update membership' route. self.client.update_image.assert_called_with(self._image.id) def test_image_set_membership_option_reject(self): membership = image_fakes.create_one_image_member( attrs={ 'image_id': '0f41529e-7c12-4de8-be2d-181abb825b3c', 'member_id': self.project.id, } ) self.client.update_member.return_value = membership arglist = [ '--reject', '0f41529e-7c12-4de8-be2d-181abb825b3c', ] verifylist = [ ('membership', 'rejected'), ('image', '0f41529e-7c12-4de8-be2d-181abb825b3c'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.client.update_member.assert_called_once_with( image=self._image.id, member=self.app.client_manager.auth_ref.project_id, status='rejected', ) # Assert that the 'update image" route is also called, in addition to # the 'update membership' route. self.client.update_image.assert_called_with(self._image.id) def test_image_set_membership_option_pending(self): membership = image_fakes.create_one_image_member( attrs={ 'image_id': '0f41529e-7c12-4de8-be2d-181abb825b3c', 'member_id': self.project.id, } ) self.client.update_member.return_value = membership arglist = [ '--pending', '0f41529e-7c12-4de8-be2d-181abb825b3c', ] verifylist = [ ('membership', 'pending'), ('image', '0f41529e-7c12-4de8-be2d-181abb825b3c'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.client.update_member.assert_called_once_with( image=self._image.id, member=self.app.client_manager.auth_ref.project_id, status='pending', ) # Assert that the 'update image" route is also called, in addition to # the 'update membership' route. self.client.update_image.assert_called_with(self._image.id) def test_image_set_options(self): arglist = [ '--name', 'new-name', '--min-disk', '2', '--min-ram', '4', '--container-format', 'ovf', '--disk-format', 'vmdk', '--project', self.project.name, '--project-domain', self.domain.id, self._image.id, ] verifylist = [ ('name', 'new-name'), ('min_disk', 2), ('min_ram', 4), ('container_format', 'ovf'), ('disk_format', 'vmdk'), ('project', self.project.name), ('project_domain', self.domain.id), ('image', self._image.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) kwargs = { 'name': 'new-name', 'owner_id': self.project.id, 'min_disk': 2, 'min_ram': 4, 'container_format': 'ovf', 'disk_format': 'vmdk', } # ImageManager.update(image, **kwargs) self.client.update_image.assert_called_with(self._image.id, **kwargs) self.assertIsNone(result) def test_image_set_with_unexist_project(self): self.project_mock.get.side_effect = exceptions.NotFound(None) self.project_mock.find.side_effect = exceptions.NotFound(None) arglist = [ '--project', 'unexist_owner', '0f41529e-7c12-4de8-be2d-181abb825b3c', ] verifylist = [ ('project', 'unexist_owner'), ('image', '0f41529e-7c12-4de8-be2d-181abb825b3c'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) def test_image_set_bools1(self): arglist = [ '--protected', '--private', 'graven', ] verifylist = [ ('is_protected', True), ('visibility', 'private'), ('image', 'graven'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) kwargs = { 'is_protected': True, 'visibility': 'private', } # ImageManager.update(image, **kwargs) self.client.update_image.assert_called_with(self._image.id, **kwargs) self.assertIsNone(result) def test_image_set_bools2(self): arglist = [ '--unprotected', '--public', 'graven', ] verifylist = [ ('is_protected', False), ('visibility', 'public'), ('image', 'graven'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) kwargs = { 'is_protected': False, 'visibility': 'public', } # ImageManager.update(image, **kwargs) self.client.update_image.assert_called_with(self._image.id, **kwargs) self.assertIsNone(result) def test_image_set_properties(self): arglist = [ '--property', 'Alpha=1', '--property', 'Beta=2', 'graven', ] verifylist = [ ('properties', {'Alpha': '1', 'Beta': '2'}), ('image', 'graven'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) kwargs = { 'Alpha': '1', 'Beta': '2', } # ImageManager.update(image, **kwargs) self.client.update_image.assert_called_with(self._image.id, **kwargs) self.assertIsNone(result) def test_image_set_fake_properties(self): arglist = [ '--architecture', 'z80', '--instance-id', '12345', '--kernel-id', '67890', '--os-distro', 'cpm', '--os-version', '2.2H', '--ramdisk-id', 'xyzpdq', 'graven', ] verifylist = [ ('architecture', 'z80'), ('instance_id', '12345'), ('kernel_id', '67890'), ('os_distro', 'cpm'), ('os_version', '2.2H'), ('ramdisk_id', 'xyzpdq'), ('image', 'graven'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) kwargs = { 'architecture': 'z80', 'instance_id': '12345', 'kernel_id': '67890', 'os_distro': 'cpm', 'os_version': '2.2H', 'ramdisk_id': 'xyzpdq', } # ImageManager.update(image, **kwargs) self.client.update_image.assert_called_with(self._image.id, **kwargs) self.assertIsNone(result) def test_image_set_tag(self): arglist = [ '--tag', 'test-tag', 'graven', ] verifylist = [ ('tags', ['test-tag']), ('image', 'graven'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) kwargs = { 'tags': ['test-tag'], } # ImageManager.update(image, **kwargs) self.client.update_image.assert_called_with(self._image.id, **kwargs) self.assertIsNone(result) def test_image_set_activate(self): arglist = [ '--tag', 'test-tag', '--activate', 'graven', ] verifylist = [ ('tags', ['test-tag']), ('image', 'graven'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) kwargs = { 'tags': ['test-tag'], } self.client.reactivate_image.assert_called_with( self._image.id, ) # ImageManager.update(image, **kwargs) self.client.update_image.assert_called_with(self._image.id, **kwargs) self.assertIsNone(result) def test_image_set_deactivate(self): arglist = [ '--tag', 'test-tag', '--deactivate', 'graven', ] verifylist = [ ('tags', ['test-tag']), ('image', 'graven'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) kwargs = { 'tags': ['test-tag'], } self.client.deactivate_image.assert_called_with( self._image.id, ) # ImageManager.update(image, **kwargs) self.client.update_image.assert_called_with(self._image.id, **kwargs) self.assertIsNone(result) def test_image_set_tag_merge(self): old_image = self._image old_image['tags'] = ['old1', 'new2'] self.client.find_image.return_value = old_image arglist = [ '--tag', 'test-tag', 'graven', ] verifylist = [ ('tags', ['test-tag']), ('image', 'graven'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) kwargs = { 'tags': ['old1', 'new2', 'test-tag'], } # ImageManager.update(image, **kwargs) a, k = self.client.update_image.call_args self.assertEqual(self._image.id, a[0]) self.assertIn('tags', k) self.assertEqual(set(kwargs['tags']), set(k['tags'])) self.assertIsNone(result) def test_image_set_tag_merge_dupe(self): old_image = self._image old_image['tags'] = ['old1', 'new2'] self.client.find_image.return_value = old_image arglist = [ '--tag', 'old1', 'graven', ] verifylist = [ ('tags', ['old1']), ('image', 'graven'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) kwargs = { 'tags': ['new2', 'old1'], } # ImageManager.update(image, **kwargs) a, k = self.client.update_image.call_args self.assertEqual(self._image.id, a[0]) self.assertIn('tags', k) self.assertEqual(set(kwargs['tags']), set(k['tags'])) self.assertIsNone(result) def test_image_set_dead_options(self): arglist = [ '--visibility', '1-mile', 'graven', ] verifylist = [ ('dead_visibility', '1-mile'), ('image', 'graven'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args ) def test_image_set_numeric_options_to_zero(self): arglist = [ '--min-disk', '0', '--min-ram', '0', 'graven', ] verifylist = [ ('min_disk', 0), ('min_ram', 0), ('image', 'graven'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) kwargs = { 'min_disk': 0, 'min_ram': 0, } # ImageManager.update(image, **kwargs) self.client.update_image.assert_called_with(self._image.id, **kwargs) self.assertIsNone(result) def test_image_set_hidden(self): arglist = [ '--hidden', '--public', 'graven', ] verifylist = [ ('is_hidden', True), ('visibility', 'public'), ('image', 'graven'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) kwargs = { 'is_hidden': True, 'visibility': 'public', } # ImageManager.update(image, **kwargs) self.client.update_image.assert_called_with(self._image.id, **kwargs) self.assertIsNone(result) def test_image_set_unhidden(self): arglist = [ '--unhidden', '--public', 'graven', ] verifylist = [ ('is_hidden', False), ('visibility', 'public'), ('image', 'graven'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) kwargs = { 'is_hidden': False, 'visibility': 'public', } # ImageManager.update(image, **kwargs) self.client.update_image.assert_called_with(self._image.id, **kwargs) self.assertIsNone(result) class TestImageShow(TestImage): new_image = image_fakes.create_one_image(attrs={'size': 1000}) _data = image_fakes.create_one_image() columns = ('id', 'name', 'owner', 'protected', 'tags', 'visibility') data = ( _data.id, _data.name, _data.owner_id, _data.is_protected, format_columns.ListColumn(_data.tags), _data.visibility, ) def setUp(self): super().setUp() self.client.find_image = mock.Mock(return_value=self._data) # Get the command object to test self.cmd = _image.ShowImage(self.app, None) def test_image_show(self): arglist = [ '0f41529e-7c12-4de8-be2d-181abb825b3c', ] verifylist = [ ('image', '0f41529e-7c12-4de8-be2d-181abb825b3c'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) # In base command class ShowOne in cliff, abstract method take_action() # returns a two-part tuple with a tuple of column names and a tuple of # data to be shown. columns, data = self.cmd.take_action(parsed_args) self.client.find_image.assert_called_with( '0f41529e-7c12-4de8-be2d-181abb825b3c', ignore_missing=False ) self.assertEqual(self.columns, columns) self.assertCountEqual(self.data, data) def test_image_show_human_readable(self): self.client.find_image.return_value = self.new_image arglist = [ '--human-readable', self.new_image.id, ] verifylist = [ ('human_readable', True), ('image', self.new_image.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) # In base command class ShowOne in cliff, abstract method take_action() # returns a two-part tuple with a tuple of column names and a tuple of # data to be shown. columns, data = self.cmd.take_action(parsed_args) self.client.find_image.assert_called_with( self.new_image.id, ignore_missing=False ) size_index = columns.index('size') self.assertEqual(data[size_index], '1K') class TestImageUnset(TestImage): def setUp(self): super().setUp() attrs = {} attrs['tags'] = ['test'] attrs['hw_rng_model'] = 'virtio' attrs['prop'] = 'test' attrs['prop2'] = 'fake' self.image = image_fakes.create_one_image(attrs) self.client.find_image.return_value = self.image self.client.remove_tag.return_value = self.image self.client.update_image.return_value = self.image # Get the command object to test self.cmd = _image.UnsetImage(self.app, None) def test_image_unset_no_options(self): arglist = [ '0f41529e-7c12-4de8-be2d-181abb825b3c', ] verifylist = [('image', '0f41529e-7c12-4de8-be2d-181abb825b3c')] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.assertIsNone(result) def test_image_unset_tag_option(self): arglist = [ '--tag', 'test', self.image.id, ] verifylist = [ ('tags', ['test']), ('image', self.image.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.client.remove_tag.assert_called_with(self.image.id, 'test') self.assertIsNone(result) def test_image_unset_property_option(self): arglist = [ '--property', 'hw_rng_model', '--property', 'prop', self.image.id, ] verifylist = [ ('properties', ['hw_rng_model', 'prop']), ('image', self.image.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.client.update_image.assert_called_with( self.image, properties={'prop2': 'fake'} ) self.assertIsNone(result) def test_image_unset_mixed_option(self): arglist = [ '--tag', 'test', '--property', 'hw_rng_model', '--property', 'prop', self.image.id, ] verifylist = [ ('tags', ['test']), ('properties', ['hw_rng_model', 'prop']), ('image', self.image.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) result = self.cmd.take_action(parsed_args) self.client.update_image.assert_called_with( self.image, properties={'prop2': 'fake'} ) self.client.remove_tag.assert_called_with(self.image.id, 'test') self.assertIsNone(result) class TestImageStage(TestImage): image = image_fakes.create_one_image({}) def setUp(self): super().setUp() self.client.find_image.return_value = self.image self.cmd = _image.StageImage(self.app, None) def test_stage_image__from_file(self): imagefile = tempfile.NamedTemporaryFile(delete=False) imagefile.write(b'\0') imagefile.close() arglist = [ '--file', imagefile.name, self.image.name, ] verifylist = [ ('filename', imagefile.name), ('image', self.image.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.client.stage_image.assert_called_once_with( self.image, filename=imagefile.name, ) @mock.patch('openstackclient.image.v2.image.get_data_from_stdin') def test_stage_image__from_stdin(self, mock_get_data_from_stdin): fake_stdin = io.BytesIO(b"some initial binary data: \x00\x01") mock_get_data_from_stdin.return_value = fake_stdin arglist = [ self.image.name, ] verifylist = [ ('image', self.image.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.client.stage_image.assert_called_once_with( self.image, data=fake_stdin, ) class TestImageImport(TestImage): image = image_fakes.create_one_image( { 'container_format': 'bare', 'disk_format': 'qcow2', } ) import_info = image_fakes.create_one_import_info() def setUp(self): super().setUp() self.client.find_image.return_value = self.image self.client.get_import_info.return_value = self.import_info self.cmd = _image.ImportImage(self.app, None) def test_import_image__glance_direct(self): self.image.status = 'uploading' arglist = [ self.image.name, ] verifylist = [ ('image', self.image.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.client.import_image.assert_called_once_with( self.image, method='glance-direct', uri=None, remote_region=None, remote_image_id=None, remote_service_interface=None, stores=None, all_stores=None, all_stores_must_succeed=False, ) def test_import_image__web_download(self): self.image.status = 'queued' arglist = [ self.image.name, '--method', 'web-download', '--uri', 'https://example.com/', ] verifylist = [ ('image', self.image.name), ('import_method', 'web-download'), ('uri', 'https://example.com/'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.client.import_image.assert_called_once_with( self.image, method='web-download', uri='https://example.com/', remote_region=None, remote_image_id=None, remote_service_interface=None, stores=None, all_stores=None, all_stores_must_succeed=False, ) # NOTE(stephenfin): We don't do this for all combinations since that would # be tedious af. You get the idea... def test_import_image__web_download_missing_options(self): arglist = [ self.image.name, '--method', 'web-download', ] verifylist = [ ('image', self.image.name), ('import_method', 'web-download'), ('uri', None), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) exc = self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args, ) self.assertIn("The '--uri' option is required ", str(exc)) self.client.import_image.assert_not_called() # NOTE(stephenfin): Ditto def test_import_image__web_download_invalid_options(self): arglist = [ self.image.name, '--method', 'glance-direct', # != web-download '--uri', 'https://example.com/', ] verifylist = [ ('image', self.image.name), ('import_method', 'glance-direct'), ('uri', 'https://example.com/'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) exc = self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args, ) self.assertIn("The '--uri' option is only supported ", str(exc)) self.client.import_image.assert_not_called() def test_import_image__web_download_invalid_image_state(self): self.image.status = 'uploading' # != 'queued' arglist = [ self.image.name, '--method', 'web-download', '--uri', 'https://example.com/', ] verifylist = [ ('image', self.image.name), ('import_method', 'web-download'), ('uri', 'https://example.com/'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) exc = self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args, ) self.assertIn( "The 'web-download' import method can only be used with " "an image in status 'queued'", str(exc), ) self.client.import_image.assert_not_called() def test_import_image__copy_image(self): self.image.status = 'active' arglist = [ self.image.name, '--method', 'copy-image', '--store', 'fast', ] verifylist = [ ('image', self.image.name), ('import_method', 'copy-image'), ('stores', ['fast']), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.client.import_image.assert_called_once_with( self.image, method='copy-image', uri=None, remote_region=None, remote_image_id=None, remote_service_interface=None, stores=['fast'], all_stores=None, all_stores_must_succeed=False, ) def test_import_image__glance_download(self): arglist = [ self.image.name, '--method', 'glance-download', '--remote-region', 'eu/dublin', '--remote-image', 'remote-image-id', '--remote-service-interface', 'private', ] verifylist = [ ('image', self.image.name), ('import_method', 'glance-download'), ('remote_region', 'eu/dublin'), ('remote_image', 'remote-image-id'), ('remote_service_interface', 'private'), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.client.import_image.assert_called_once_with( self.image, method='glance-download', uri=None, remote_region='eu/dublin', remote_image_id='remote-image-id', remote_service_interface='private', stores=None, all_stores=None, all_stores_must_succeed=False, ) class TestImageSave(TestImage): image = image_fakes.create_one_image({}) def setUp(self): super().setUp() self.client.find_image.return_value = self.image self.client.download_image.return_value = self.image # Get the command object to test self.cmd = _image.SaveImage(self.app, None) def test_save_data(self): arglist = ['--file', '/path/to/file', self.image.id] verifylist = [ ('filename', '/path/to/file'), ('image', self.image.id), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) self.cmd.take_action(parsed_args) self.client.download_image.assert_called_once_with( self.image.id, stream=True, output='/path/to/file' ) class TestImageGetData(TestImage): def test_get_data_from_stdin(self): fd = io.BytesIO(b"some initial binary data: \x00\x01") with mock.patch('sys.stdin') as stdin: stdin.return_value = fd stdin.isatty.return_value = False stdin.buffer = fd test_fd = _image.get_data_from_stdin() # Ensure data written to temp file is correct self.assertEqual(fd, test_fd) def test_get_data_from_stdin__interactive(self): fd = io.BytesIO(b"some initial binary data: \x00\x01") with mock.patch('sys.stdin') as stdin: # There is stdin, but interactive stdin.return_value = fd test_fd = _image.get_data_from_stdin() self.assertIsNone(test_fd) class TestStoresInfo(TestImage): stores_info = image_fakes.create_one_stores_info() def setUp(self): super().setUp() self.client.stores.return_value = self.stores_info self.cmd = _image.StoresInfo(self.app, None) def test_stores_info(self): arglist = [] parsed_args = self.check_parser(self.cmd, arglist, []) self.cmd.take_action(parsed_args) self.client.stores.assert_called() def test_stores_info_with_detail(self): arglist = ['--detail'] parsed_args = self.check_parser(self.cmd, arglist, []) self.cmd.take_action(parsed_args) self.client.stores.assert_called_with(details=True) def test_stores_info_neg(self): arglist = [] parsed_args = self.check_parser(self.cmd, arglist, []) self.client.stores.side_effect = sdk_exceptions.ResourceNotFound() exc = self.assertRaises( exceptions.CommandError, self.cmd.take_action, parsed_args, ) self.assertIn( "Multi Backend support not enabled", str(exc), )