Skip to content

Commit 9ad867f

Browse files
committed
Add deployments unit tests
1 parent a831998 commit 9ad867f

File tree

3 files changed

+466
-2
lines changed

3 files changed

+466
-2
lines changed

coriolisclient/cli/deployments.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ def get_parser(self, prog_name):
200200
return parser
201201

202202
def take_action(self, args):
203-
m = self.app.client_manager.coriolis.deployments
203+
d = self.app.client_manager.coriolis.deployments
204204
user_scripts = cli_utils.compose_user_scripts(
205205
args.global_scripts, args.instance_scripts)
206206
instance_osmorphing_minion_pool_mappings = None
@@ -209,7 +209,7 @@ def take_action(self, args):
209209
mp['instance_id']: mp['pool_id']
210210
for mp in args.instance_osmorphing_minion_pool_mappings}
211211

212-
deployment = m.create_from_transfer(
212+
deployment = d.create_from_transfer(
213213
args.transfer,
214214
args.clone_disks,
215215
args.force,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,340 @@
1+
# Copyright (c) 2024 Cloudbase Solutions Srl
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12+
# implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
16+
import argparse
17+
import copy
18+
import os
19+
from unittest import mock
20+
21+
from coriolisclient.cli import deployments
22+
from coriolisclient.tests import test_base
23+
from coriolisclient.v1 import common
24+
from coriolisclient.v1 import deployments as v1_deployments
25+
26+
DEPLOYMENT_ID = "depl_id"
27+
DEPLOYMENT_DATA = {
28+
"storage_mappings": None,
29+
"id": DEPLOYMENT_ID,
30+
"last_execution_status": "COMPLETED",
31+
"created_at": None,
32+
"updated_at": None,
33+
"transfer_id": "transfer_1",
34+
"transfer_scenario_type": "replica",
35+
"reservation_id": "res1",
36+
"instances": ["instance1"],
37+
"notes": "",
38+
"origin_endpoint_id": "source1",
39+
"origin_minion_pool_id": None,
40+
"destination_endpoint_id": "dest1",
41+
"destination_minion_pool_id": None,
42+
"instance_osmorphing_minion_pool_mappings": None,
43+
"shutdown_instances": False,
44+
"destination_environment": {"opt1": "env1"},
45+
"source_environment": None,
46+
"network_map": {"net_source": "net_dest"},
47+
"user_scripts": None,
48+
"tasks": [],
49+
"transfer_result": None,
50+
}
51+
DEPLOYMENT_FORMATTED_DATA = [
52+
"depl_id", "COMPLETED", None, None, "transfer_1", "replica", "res1",
53+
"instance1", "", "source1", None, "dest1", None, '{}', False,
54+
'{\n "opt1": "env1"\n}', '{}', '{\n "net_source": "net_dest"\n}', "", "",
55+
None, '{}', "", '{}']
56+
57+
DEPLOYMENT_LIST_DATA = {
58+
"id": "1",
59+
"transfer_id": "2",
60+
"last_execution_status": "RUNNING",
61+
"instances": ["instance1", "instance2"],
62+
"notes": "test_notes",
63+
"created_at": "2024-11-28T15:18:25.000000",
64+
}
65+
DEPLOYMENT_LIST_FORMATTED_DATA = (
66+
"1", "2", "RUNNING", "instance1\ninstance2", "test_notes",
67+
"2024-11-28T15:18:25.000000")
68+
APP_MOCK = mock.MagicMock()
69+
70+
71+
class DeploymentFormatterTestCase(test_base.CoriolisBaseTestCase):
72+
73+
def setUp(self):
74+
super(DeploymentFormatterTestCase, self).setUp()
75+
self.formatter = deployments.DeploymentFormatter()
76+
77+
def test__get_sorted_list(self):
78+
obj1 = mock.Mock(created_at="2024-10-24T18:51:32.000000")
79+
obj2 = mock.Mock(created_at="2024-11-18T15:18:25.000000")
80+
obj3 = mock.Mock(created_at="2024-11-28T15:18:25.000000")
81+
obj_list = [obj1, obj3, obj2]
82+
self.assertEqual(
83+
[obj1, obj2, obj3], self.formatter._get_sorted_list(obj_list))
84+
85+
def test__get_formatted_data(self):
86+
obj = mock.Mock(**DEPLOYMENT_LIST_DATA)
87+
self.assertEqual(
88+
DEPLOYMENT_LIST_FORMATTED_DATA,
89+
self.formatter._get_formatted_data(obj))
90+
91+
92+
class DeploymentDetailFormatterTestCase(test_base.CoriolisBaseTestCase):
93+
94+
def setUp(self):
95+
super(DeploymentDetailFormatterTestCase, self).setUp()
96+
self.formatter = deployments.DeploymentDetailFormatter(
97+
show_instances_data=True)
98+
self.progress_updates = [
99+
{"created_at": "2024-10-24T18:51:32.000000",
100+
"index": 0,
101+
"message": "message 0"},
102+
{"created_at": "2024-11-18T15:18:25.000000",
103+
"index": 2,
104+
"message": "message 2"},
105+
{"created_at": "2024-11-28T15:18:25.000000",
106+
"index": 1,
107+
"message": "message 1"},
108+
]
109+
self.formatted_progress_updates = (
110+
f"2024-10-24T18:51:32.000000 message 0{os.linesep}"
111+
f"2024-11-28T15:18:25.000000 message 1{os.linesep}"
112+
f"2024-11-18T15:18:25.000000 message 2")
113+
self.tasks_data = [
114+
{
115+
"id": "1",
116+
"task_type": "type1",
117+
"instance": "instance1",
118+
"status": "COMPLETED",
119+
"depends_on": ["task0"],
120+
"exception_details": None,
121+
"progress_updates": self.progress_updates,
122+
},
123+
{
124+
"id": "2",
125+
"task_type": "type2",
126+
"instance": "instance1",
127+
"status": "COMPLETED",
128+
"depends_on": ["task0"],
129+
"exception_details": None,
130+
"progress_updates": self.progress_updates,
131+
}
132+
]
133+
self.formatted_tasks = [
134+
f'id: 1{os.linesep}'
135+
f'task_type: type1{os.linesep}'
136+
f'instance: instance1{os.linesep}'
137+
f'status: COMPLETED{os.linesep}'
138+
f'depends_on: task0{os.linesep}'
139+
f'exception_details: {os.linesep}'
140+
f'progress_updates:{os.linesep}'
141+
f'{self.formatted_progress_updates}',
142+
143+
f'id: 2{os.linesep}'
144+
f'task_type: type2{os.linesep}'
145+
f'instance: instance1{os.linesep}'
146+
f'status: COMPLETED{os.linesep}'
147+
f'depends_on: task0{os.linesep}'
148+
f'exception_details: {os.linesep}'
149+
f'progress_updates:{os.linesep}'
150+
f'{self.formatted_progress_updates}'
151+
]
152+
self.manager_mock = mock.MagicMock()
153+
154+
def test_init_no_instances_data(self):
155+
formatter = deployments.DeploymentDetailFormatter(
156+
show_instances_data=False)
157+
self.assertNotIn('instances_data', formatter.columns)
158+
159+
def test__format_instances(self):
160+
obj = mock.Mock(instances=["instance2", "instance3", "instance1"])
161+
self.assertEqual(
162+
f"instance1{os.linesep}instance2{os.linesep}instance3",
163+
self.formatter._format_instances(obj))
164+
165+
def test__format_progress_updates(self):
166+
task_dict = {
167+
"progress_updates": self.progress_updates}
168+
self.assertEqual(
169+
self.formatted_progress_updates,
170+
self.formatter._format_progress_updates(task_dict))
171+
172+
def test__format_task(self):
173+
task_data = self.tasks_data[0]
174+
175+
task = common.Task(self.manager_mock, task_data)
176+
177+
self.assertEqual(
178+
self.formatted_tasks[0], self.formatter._format_task(task))
179+
180+
def test__format_tasks(self):
181+
obj = mock.Mock(
182+
tasks=[common.Task(self.manager_mock, task_data)
183+
for task_data in self.tasks_data])
184+
expected_result = (f'{self.formatted_tasks[0]}{os.linesep}{os.linesep}'
185+
f'{self.formatted_tasks[1]}')
186+
self.assertEqual(expected_result, self.formatter._format_tasks(obj))
187+
188+
def test__get_formatted_data(self):
189+
obj_data = {
190+
**DEPLOYMENT_DATA,
191+
"info": {"depl": "info"},
192+
}
193+
obj = mock.Mock(**obj_data)
194+
obj.to_dict.return_value = obj_data
195+
expected_result = copy.copy(DEPLOYMENT_FORMATTED_DATA)
196+
expected_result.append(obj_data['info'])
197+
198+
self.assertEqual(
199+
expected_result, self.formatter._get_formatted_data(obj))
200+
201+
202+
class CreateDeploymentTestCase(test_base.CoriolisBaseTestCase):
203+
204+
def setUp(self):
205+
super(CreateDeploymentTestCase, self).setUp()
206+
self.mock_app = APP_MOCK
207+
self.cli = deployments.CreateDeployment(self.mock_app, 'app_arg')
208+
209+
def test_get_parser(self):
210+
parser = self.cli.get_parser('coriolis')
211+
global_script = "linux=/linux/path"
212+
instance_script = "instance1=/instance1/path"
213+
args = parser.parse_args([
214+
'transfer_id', '--force', '--dont-clone-disks',
215+
'--skip-os-morphing', '--user-script-global', global_script,
216+
'--user-script-instance', instance_script])
217+
self.assertEqual(
218+
('transfer_id', True, False, True, [global_script],
219+
[instance_script]),
220+
(args.transfer, args.force, args.clone_disks,
221+
args.skip_os_morphing, args.global_scripts,
222+
args.instance_scripts))
223+
224+
def test_take_action(self):
225+
args = mock.Mock(global_scripts=None, instance_scripts=None)
226+
args.instance_osmorphing_minion_pool_mappings = [
227+
{"instance_id": "instance1", "pool_id": "pool1"}]
228+
mock_fun = (self.mock_app.client_manager.coriolis.deployments.
229+
create_from_transfer)
230+
mock_fun.return_value = (
231+
v1_deployments.Deployment(mock.MagicMock(), DEPLOYMENT_DATA))
232+
expected_pool_mappings = {"instance1": "pool1"}
233+
expected_user_scripts = {"global": {}, "instances": {}}
234+
expected_data = copy.copy(DEPLOYMENT_FORMATTED_DATA)
235+
236+
columns, data = self.cli.take_action(args)
237+
238+
self.assertEqual(
239+
deployments.DeploymentDetailFormatter().columns, columns)
240+
self.assertEqual(expected_data, data)
241+
mock_fun.assert_called_once_with(
242+
args.transfer, args.clone_disks, args.force, args.skip_os_morphing,
243+
user_scripts=expected_user_scripts,
244+
instance_osmorphing_minion_pool_mappings=expected_pool_mappings)
245+
246+
247+
class ShowDeploymentTestCase(test_base.CoriolisBaseTestCase):
248+
249+
def setUp(self):
250+
super(ShowDeploymentTestCase, self).setUp()
251+
self.mock_app = APP_MOCK
252+
self.cli = deployments.ShowDeployment(self.mock_app, 'app_args')
253+
254+
def test_get_parser(self):
255+
parser = self.cli.get_parser('coriolis')
256+
args = parser.parse_args([DEPLOYMENT_ID, '--show-instances-data'])
257+
self.assertEqual(
258+
(DEPLOYMENT_ID, True),
259+
(args.id, args.show_instances_data))
260+
261+
def test_take_action(self):
262+
show_instances_data = False
263+
args = mock.Mock(
264+
id=DEPLOYMENT_ID, show_instances_data=show_instances_data)
265+
mock_fun = self.mock_app.client_manager.coriolis.deployments.get
266+
mock_fun.return_value = v1_deployments.Deployment(
267+
mock.MagicMock(), DEPLOYMENT_DATA)
268+
expected_data = DEPLOYMENT_FORMATTED_DATA
269+
270+
columns, data = self.cli.take_action(args)
271+
272+
self.assertEqual(
273+
deployments.DeploymentDetailFormatter(
274+
show_instances_data=show_instances_data).columns, columns)
275+
self.assertEqual(expected_data, data)
276+
mock_fun.assert_called_once_with(DEPLOYMENT_ID)
277+
278+
279+
class CancelDeploymentTestCase(test_base.CoriolisBaseTestCase):
280+
281+
def setUp(self):
282+
super(CancelDeploymentTestCase, self).setUp()
283+
self.mock_app = APP_MOCK
284+
self.cli = deployments.CancelDeployment(self.mock_app, 'app_args')
285+
286+
def test_get_parser(self):
287+
parser = self.cli.get_parser('coriolis')
288+
args = parser.parse_args([DEPLOYMENT_ID, '--force'])
289+
self.assertEqual((DEPLOYMENT_ID, True), (args.id, args.force))
290+
291+
def test_take_action(self):
292+
force = True
293+
args = mock.Mock(id=DEPLOYMENT_ID, force=force)
294+
mock_fun = self.mock_app.client_manager.coriolis.deployments.cancel
295+
296+
self.cli.take_action(args)
297+
298+
mock_fun.assert_called_once_with(DEPLOYMENT_ID, force)
299+
300+
301+
class DeleteDeploymentTestCase(test_base.CoriolisBaseTestCase):
302+
303+
def setUp(self):
304+
super(DeleteDeploymentTestCase, self).setUp()
305+
self.mock_app = APP_MOCK
306+
self.cli = deployments.DeleteDeployment(self.mock_app, 'app_args')
307+
308+
def test_get_parser(self):
309+
parser = self.cli.get_parser('coriolis')
310+
args = parser.parse_args([DEPLOYMENT_ID])
311+
self.assertEqual(DEPLOYMENT_ID, args.id)
312+
313+
def test_take_action(self):
314+
args = mock.Mock(id=DEPLOYMENT_ID)
315+
mock_fun = self.mock_app.client_manager.coriolis.deployments.delete
316+
317+
self.cli.take_action(args)
318+
mock_fun.assert_called_once_with(DEPLOYMENT_ID)
319+
320+
321+
class ListDeploymentTestCase(test_base.CoriolisBaseTestCase):
322+
323+
def setUp(self):
324+
super(ListDeploymentTestCase, self).setUp()
325+
self.mock_app = APP_MOCK
326+
self.cli = deployments.ListDeployment(self.mock_app, 'app_args')
327+
328+
def test_get_parser(self):
329+
parser = self.cli.get_parser('coriolis')
330+
self.assertIsInstance(parser, argparse.ArgumentParser)
331+
332+
def test_take_action(self):
333+
mock_fun = self.mock_app.client_manager.coriolis.deployments.list
334+
mock_fun.return_value = [
335+
v1_deployments.Deployment(mock.MagicMock(), DEPLOYMENT_LIST_DATA)]
336+
337+
columns, data = self.cli.take_action(mock.ANY)
338+
339+
self.assertEqual(deployments.DeploymentFormatter().columns, columns)
340+
self.assertEqual([DEPLOYMENT_LIST_FORMATTED_DATA], list(data))

0 commit comments

Comments
 (0)