Skip to content

Commit 9b51127

Browse files
author
Huanxuan Ao
committed
Support error handling for delete commands in volumev2
Some delete conmmands in volumev2 did not support error handling, this patch add them and also add the unit tests for bulk deletion Change-Id: I56ade6f9c7396c78fb989547476c4d94ccd76eae
1 parent 63a6789 commit 9b51127

9 files changed

Lines changed: 364 additions & 40 deletions

File tree

openstackclient/tests/volume/v2/fakes.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,26 @@ def create_backups(attrs=None, count=2):
418418

419419
return backups
420420

421+
@staticmethod
422+
def get_backups(backups=None, count=2):
423+
"""Get an iterable MagicMock object with a list of faked backups.
424+
425+
If backups list is provided, then initialize the Mock object with the
426+
list. Otherwise create one.
427+
428+
:param List volumes:
429+
A list of FakeResource objects faking backups
430+
:param Integer count:
431+
The number of backups to be faked
432+
:return
433+
An iterable Mock object with side_effect set to a list of faked
434+
backups
435+
"""
436+
if backups is None:
437+
backups = FakeBackup.create_backups(count)
438+
439+
return mock.MagicMock(side_effect=backups)
440+
421441

422442
class FakeExtension(object):
423443
"""Fake one or more extension."""
@@ -529,6 +549,26 @@ def create_qoses(attrs=None, count=2):
529549

530550
return qoses
531551

552+
@staticmethod
553+
def get_qoses(qoses=None, count=2):
554+
"""Get an iterable MagicMock object with a list of faked qoses.
555+
556+
If qoses list is provided, then initialize the Mock object with the
557+
list. Otherwise create one.
558+
559+
:param List volumes:
560+
A list of FakeResource objects faking qoses
561+
:param Integer count:
562+
The number of qoses to be faked
563+
:return
564+
An iterable Mock object with side_effect set to a list of faked
565+
qoses
566+
"""
567+
if qoses is None:
568+
qoses = FakeQos.create_qoses(count)
569+
570+
return mock.MagicMock(side_effect=qoses)
571+
532572

533573
class FakeSnapshot(object):
534574
"""Fake one or more snapshot."""
@@ -582,6 +622,26 @@ def create_snapshots(attrs=None, count=2):
582622

583623
return snapshots
584624

625+
@staticmethod
626+
def get_snapshots(snapshots=None, count=2):
627+
"""Get an iterable MagicMock object with a list of faked snapshots.
628+
629+
If snapshots list is provided, then initialize the Mock object with the
630+
list. Otherwise create one.
631+
632+
:param List volumes:
633+
A list of FakeResource objects faking snapshots
634+
:param Integer count:
635+
The number of snapshots to be faked
636+
:return
637+
An iterable Mock object with side_effect set to a list of faked
638+
snapshots
639+
"""
640+
if snapshots is None:
641+
snapshots = FakeSnapshot.create_snapshots(count)
642+
643+
return mock.MagicMock(side_effect=snapshots)
644+
585645

586646
class FakeType(object):
587647
"""Fake one or more type."""

openstackclient/tests/volume/v2/test_backup.py

Lines changed: 62 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@
1212
# under the License.
1313
#
1414

15+
import mock
16+
from mock import call
17+
18+
from osc_lib import exceptions
19+
from osc_lib import utils
20+
1521
from openstackclient.tests.volume.v2 import fakes as volume_fakes
1622
from openstackclient.volume.v2 import backup
1723

@@ -138,47 +144,95 @@ def test_backup_create_without_name(self):
138144

139145
class TestBackupDelete(TestBackup):
140146

141-
backup = volume_fakes.FakeBackup.create_one_backup()
147+
backups = volume_fakes.FakeBackup.create_backups(count=2)
142148

143149
def setUp(self):
144150
super(TestBackupDelete, self).setUp()
145151

146-
self.backups_mock.get.return_value = self.backup
152+
self.backups_mock.get = (
153+
volume_fakes.FakeBackup.get_backups(self.backups))
147154
self.backups_mock.delete.return_value = None
148155

149156
# Get the command object to mock
150157
self.cmd = backup.DeleteBackup(self.app, None)
151158

152159
def test_backup_delete(self):
153160
arglist = [
154-
self.backup.id
161+
self.backups[0].id
155162
]
156163
verifylist = [
157-
("backups", [self.backup.id])
164+
("backups", [self.backups[0].id])
158165
]
159166
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
160167

161168
result = self.cmd.take_action(parsed_args)
162169

163-
self.backups_mock.delete.assert_called_with(self.backup.id, False)
170+
self.backups_mock.delete.assert_called_with(
171+
self.backups[0].id, False)
164172
self.assertIsNone(result)
165173

166174
def test_backup_delete_with_force(self):
167175
arglist = [
168176
'--force',
169-
self.backup.id,
177+
self.backups[0].id,
170178
]
171179
verifylist = [
172180
('force', True),
173-
("backups", [self.backup.id])
181+
("backups", [self.backups[0].id])
174182
]
175183
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
176184

177185
result = self.cmd.take_action(parsed_args)
178186

179-
self.backups_mock.delete.assert_called_with(self.backup.id, True)
187+
self.backups_mock.delete.assert_called_with(self.backups[0].id, True)
180188
self.assertIsNone(result)
181189

190+
def test_delete_multiple_backups(self):
191+
arglist = []
192+
for b in self.backups:
193+
arglist.append(b.id)
194+
verifylist = [
195+
('backups', arglist),
196+
]
197+
198+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
199+
result = self.cmd.take_action(parsed_args)
200+
201+
calls = []
202+
for b in self.backups:
203+
calls.append(call(b.id, False))
204+
self.backups_mock.delete.assert_has_calls(calls)
205+
self.assertIsNone(result)
206+
207+
def test_delete_multiple_backups_with_exception(self):
208+
arglist = [
209+
self.backups[0].id,
210+
'unexist_backup',
211+
]
212+
verifylist = [
213+
('backups', arglist),
214+
]
215+
216+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
217+
218+
find_mock_result = [self.backups[0], exceptions.CommandError]
219+
with mock.patch.object(utils, 'find_resource',
220+
side_effect=find_mock_result) as find_mock:
221+
try:
222+
self.cmd.take_action(parsed_args)
223+
self.fail('CommandError should be raised.')
224+
except exceptions.CommandError as e:
225+
self.assertEqual('1 of 2 backups failed to delete.',
226+
str(e))
227+
228+
find_mock.assert_any_call(self.backups_mock, self.backups[0].id)
229+
find_mock.assert_any_call(self.backups_mock, 'unexist_backup')
230+
231+
self.assertEqual(2, find_mock.call_count)
232+
self.backups_mock.delete.assert_called_once_with(
233+
self.backups[0].id, False
234+
)
235+
182236

183237
class TestBackupList(TestBackup):
184238

openstackclient/tests/volume/v2/test_qos_specs.py

Lines changed: 61 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313
# under the License.
1414
#
1515

16+
import mock
17+
from mock import call
18+
19+
from osc_lib import exceptions
1620
from osc_lib import utils
1721

1822
from openstackclient.tests.volume.v2 import fakes as volume_fakes
@@ -156,45 +160,94 @@ def test_qos_create_with_properties(self):
156160

157161
class TestQosDelete(TestQos):
158162

159-
qos_spec = volume_fakes.FakeQos.create_one_qos()
163+
qos_specs = volume_fakes.FakeQos.create_qoses(count=2)
160164

161165
def setUp(self):
162166
super(TestQosDelete, self).setUp()
163167

164-
self.qos_mock.get.return_value = self.qos_spec
168+
self.qos_mock.get = (
169+
volume_fakes.FakeQos.get_qoses(self.qos_specs))
165170
# Get the command object to test
166171
self.cmd = qos_specs.DeleteQos(self.app, None)
167172

168173
def test_qos_delete(self):
169174
arglist = [
170-
self.qos_spec.id
175+
self.qos_specs[0].id
171176
]
172177
verifylist = [
173-
('qos_specs', [self.qos_spec.id])
178+
('qos_specs', [self.qos_specs[0].id])
174179
]
175180
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
176181

177182
result = self.cmd.take_action(parsed_args)
178183

179-
self.qos_mock.delete.assert_called_with(self.qos_spec.id, False)
184+
self.qos_mock.delete.assert_called_with(
185+
self.qos_specs[0].id, False)
180186
self.assertIsNone(result)
181187

182188
def test_qos_delete_with_force(self):
183189
arglist = [
184190
'--force',
185-
self.qos_spec.id
191+
self.qos_specs[0].id
186192
]
187193
verifylist = [
188194
('force', True),
189-
('qos_specs', [self.qos_spec.id])
195+
('qos_specs', [self.qos_specs[0].id])
190196
]
191197
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
192198

193199
result = self.cmd.take_action(parsed_args)
194200

195-
self.qos_mock.delete.assert_called_with(self.qos_spec.id, True)
201+
self.qos_mock.delete.assert_called_with(
202+
self.qos_specs[0].id, True)
196203
self.assertIsNone(result)
197204

205+
def test_delete_multiple_qoses(self):
206+
arglist = []
207+
for q in self.qos_specs:
208+
arglist.append(q.id)
209+
verifylist = [
210+
('qos_specs', arglist),
211+
]
212+
213+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
214+
result = self.cmd.take_action(parsed_args)
215+
216+
calls = []
217+
for q in self.qos_specs:
218+
calls.append(call(q.id, False))
219+
self.qos_mock.delete.assert_has_calls(calls)
220+
self.assertIsNone(result)
221+
222+
def test_delete_multiple_qoses_with_exception(self):
223+
arglist = [
224+
self.qos_specs[0].id,
225+
'unexist_qos',
226+
]
227+
verifylist = [
228+
('qos_specs', arglist),
229+
]
230+
231+
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
232+
233+
find_mock_result = [self.qos_specs[0], exceptions.CommandError]
234+
with mock.patch.object(utils, 'find_resource',
235+
side_effect=find_mock_result) as find_mock:
236+
try:
237+
self.cmd.take_action(parsed_args)
238+
self.fail('CommandError should be raised.')
239+
except exceptions.CommandError as e:
240+
self.assertEqual(
241+
'1 of 2 QoS specifications failed to delete.', str(e))
242+
243+
find_mock.assert_any_call(self.qos_mock, self.qos_specs[0].id)
244+
find_mock.assert_any_call(self.qos_mock, 'unexist_qos')
245+
246+
self.assertEqual(2, find_mock.call_count)
247+
self.qos_mock.delete.assert_called_once_with(
248+
self.qos_specs[0].id, False
249+
)
250+
198251

199252
class TestQosDisassociate(TestQos):
200253

0 commit comments

Comments
 (0)