Просмотр исходного кода

Add tests for `coriolis.tasks.base` module

Daniel Vincze 2 лет назад
Родитель
Сommit
cbe6576c33

+ 0 - 0
coriolis/tests/tasks/__init__.py


+ 37 - 0
coriolis/tests/tasks/data/get_shared_libs_for_providers.yml

@@ -0,0 +1,37 @@
+- config:
+    platform: source
+    source_lib_dirs:
+      - source_dir1
+      - source_dir2
+    return_source_dirs: true
+  expected_result:
+    - source_dir1
+    - source_dir2
+
+- config:
+    platform: destination
+    source_lib_dirs:
+      - source_dir
+    destination_lib_dirs:
+      - dest_dir
+    return_dest_dirs: true
+  expected_result:
+    - dest_dir
+
+- config:
+    platform: bilateral
+    source_lib_dirs:
+      - source_dir1
+    destination_lib_dirs:
+      - dest_dir1
+      - dest_dir2
+    return_source_dirs: true
+    return_dest_dirs: true
+  expected_result:
+    - source_dir1
+    - dest_dir1
+    - dest_dir2
+
+- config:
+    platform: invalid
+  expected_result: []

+ 54 - 0
coriolis/tests/tasks/data/run.yml

@@ -0,0 +1,54 @@
+# Missing required property
+- config:
+    required_properties:
+      - "prop1"
+      - "prop2"
+    task_info:
+      prop1: value1
+  exception_expected: true
+
+# Result is not dict
+- config:
+    required_properties:
+      - "prop1"
+      - "prop2"
+    task_info:
+      prop1: value1
+      prop2: value2
+    run_result: invalid_obj
+  exception_expected: true
+
+# missing result keys
+- config:
+    returned_properties:
+      - "prop1"
+      - "prop2"
+    run_result:
+      prop1: value1
+    sanitize_check: true
+  exception_expected: true
+
+# additional result keys (undeclared)
+- config:
+    returned_properties:
+      - "prop1"
+    run_result:
+      prop1: value1
+      prop2: value2
+  exception_expected: true
+
+# valid data
+- config:
+    required_properties:
+      - "prop1"
+      - "prop2"
+    task_info:
+      prop1: value1
+      prop2: value2
+    returned_properties:
+      - "prop1"
+      - "prop3"
+    run_result:
+      prop1: value1
+      prop3: value3
+  exception_expected: false

+ 214 - 0
coriolis/tests/tasks/test_base.py

@@ -0,0 +1,214 @@
+# Copyright 2023 Cloudbase Solutions Srl
+# All Rights Reserved.
+
+from unittest import mock
+
+import ddt
+import paramiko
+
+from coriolis import constants
+from coriolis import exception
+from coriolis.tasks import base
+from coriolis.tests import test_base
+
+
+@ddt.ddt
+class BaseTasksTestCase(test_base.CoriolisBaseTestCase):
+
+    @mock.patch.object(base.TaskRunner, '__abstractmethods__', set())
+    def setUp(self):
+        super(BaseTasksTestCase, self).setUp()
+        self.task_runner = base.TaskRunner()
+
+    @mock.patch.object(base.TaskRunner, 'get_required_platform')
+    @mock.patch('coriolis.tasks.base.get_shared_lib_dirs_for_provider')
+    @ddt.file_data('data/get_shared_libs_for_providers.yml')
+    @ddt.unpack
+    def test_get_shared_libs_for_providers(
+            self, mock_get_shared_lib_dirs,
+            mock_get_required_platform, config, expected_result):
+        # shared_libs = {}
+        platform = config.get('platform', '')
+        mock_get_required_platform.return_value = platform
+        source_lib_dirs = config.get('source_lib_dirs', [])
+        dest_lib_dirs = config.get('destination_lib_dirs', [])
+        return_source_dirs = config.get('return_source_dirs', False)
+        return_dest_dirs = config.get('return_dest_dirs', False)
+        shared_lib_dirs_side_effect = []
+        expected_calls = []
+        if return_source_dirs:
+            shared_lib_dirs_side_effect.append(source_lib_dirs)
+            expected_calls.append(
+                mock.call.mock_get_shared_lib_dirs(
+                    mock.sentinel.ctxt, mock.sentinel.origin,
+                    mock.sentinel.event_handler))
+        if return_dest_dirs:
+            shared_lib_dirs_side_effect.append(dest_lib_dirs)
+            expected_calls.append(
+                mock.call.mock_get_shared_lib_dirs(
+                    mock.sentinel.ctxt, mock.sentinel.destination,
+                    mock.sentinel.event_handler))
+        mock_get_shared_lib_dirs.side_effect = shared_lib_dirs_side_effect
+
+        result = self.task_runner.get_shared_libs_for_providers(
+            mock.sentinel.ctxt, mock.sentinel.origin,
+            mock.sentinel.destination, mock.sentinel.event_handler)
+        mock_get_required_platform.assert_called_once_with()
+        self.assertEqual(result, expected_result)
+        mock_get_shared_lib_dirs.assert_has_calls(expected_calls)
+
+    @mock.patch.object(base.TaskRunner, 'get_required_task_info_properties')
+    @mock.patch.object(base.TaskRunner, '_run')
+    @mock.patch.object(base.TaskRunner, 'get_returned_task_info_properties')
+    @mock.patch('coriolis.utils.sanitize_task_info')
+    @ddt.file_data('data/run.yml')
+    @ddt.unpack
+    def test_run(
+            self, mock_sanitize_task_info, mock_get_returned_task_info_props,
+            mock_run, mock_get_required_task_info_props, config,
+            exception_expected):
+        required_properties = config.get('required_properties', [])
+        returned_properties = config.get('returned_properties', [])
+        task_info = config.get('task_info', {})
+        run_result = config.get('run_result', {})
+        sanitize_check = config.get('sanitize_check', False)
+        mock_get_required_task_info_props.return_value = required_properties
+        mock_get_returned_task_info_props.return_value = returned_properties
+        mock_run.return_value = run_result
+
+        if exception_expected:
+            self.assertRaises(
+                exception.CoriolisException, self.task_runner.run,
+                mock.sentinel.ctxt, mock.sentinel.instance,
+                mock.sentinel.origin, mock.sentinel.destination,
+                task_info, mock.sentinel.event_handler)
+            if sanitize_check:
+                mock_sanitize_task_info.assert_called_once_with(
+                    mock_run.return_value)
+            return
+
+        result = self.task_runner.run(
+            mock.sentinel.ctxt, mock.sentinel.instance, mock.sentinel.origin,
+            mock.sentinel.destination, task_info, mock.sentinel.event_handler)
+        mock_get_required_task_info_props.assert_called_once_with()
+        mock_get_returned_task_info_props.assert_called_with()
+        mock_run.assert_called_once_with(
+            mock.sentinel.ctxt, mock.sentinel.instance, mock.sentinel.origin,
+            mock.sentinel.destination, task_info, mock.sentinel.event_handler)
+        self.assertEqual(result, mock_run.return_value)
+
+    @mock.patch('coriolis.providers.factory.get_provider')
+    @mock.patch('coriolis.tasks.base.get_connection_info')
+    def test_get_shared_lib_dirs_for_provider(self, mock_get_conn_info,
+                                              mock_get_provider):
+        endpoint = mock.MagicMock()
+        mock_get_provider.return_value = mock.MagicMock()
+        mock_get_shared_lib_dirs = (
+            mock_get_provider.return_value.get_shared_library_directories)
+
+        result = base.get_shared_lib_dirs_for_provider(
+            mock.sentinel.ctxt, endpoint,
+            mock.sentinel.event_handler)
+
+        mock_get_provider.assert_called_once_with(
+            endpoint['type'], constants.PROVIDER_TYPE_SETUP_LIBS,
+            mock.sentinel.event_handler, raise_if_not_found=False)
+        mock_get_conn_info.assert_called_once_with(
+            mock.sentinel.ctxt, endpoint)
+        mock_get_shared_lib_dirs.assert_called_once_with(
+            mock.sentinel.ctxt, mock_get_conn_info.return_value)
+        self.assertEqual(result, mock_get_shared_lib_dirs.return_value)
+
+    @mock.patch('coriolis.providers.factory.get_provider')
+    @mock.patch('coriolis.tasks.base.get_connection_info')
+    def test_get_shared_lib_dirs_for_providers_no_provider(
+            self, mock_get_conn_info, mock_get_provider):
+        endpoint = mock.MagicMock()
+        mock_get_provider.return_value = None
+
+        result = base.get_shared_lib_dirs_for_provider(
+            mock.sentinel.ctxt, endpoint,
+            mock.sentinel.event_handler)
+        mock_get_provider.assert_called_once_with(
+            endpoint['type'], constants.PROVIDER_TYPE_SETUP_LIBS,
+            mock.sentinel.event_handler, raise_if_not_found=False)
+        mock_get_conn_info.assert_not_called()
+        self.assertEqual(result, [])
+
+    @mock.patch('coriolis.utils.get_secret_connection_info')
+    @ddt.data(
+        ({"connection_info": None}, {}),
+        ({"connection_info": {}}, {}),
+        ({"connection_info": {"secret": "password"}}, {"secret": "password"}),
+        ({"connection_info": "str_value"}, "str_value"),
+        ({}, {}),
+    )
+    def test_get_connection_info(self, data, mock_get_secret_conn_info):
+        expected_conn_info = data[1]
+
+        result = base.get_connection_info(
+            mock.sentinel.ctxt, data[0])
+
+        mock_get_secret_conn_info.assert_called_once_with(
+            mock.sentinel.ctxt, expected_conn_info)
+        self.assertEqual(result, mock_get_secret_conn_info.return_value)
+
+    def test_marshal_migr_conn_info_none(self):
+        result = base.marshal_migr_conn_info(None)
+        self.assertEqual(result, None)
+
+    def test_marshal_migr_conn_info_no_key(self):
+        migr_conn_info = {"no_pkey": "value"}
+        result = base.marshal_migr_conn_info(migr_conn_info)
+        self.assertEqual(result, migr_conn_info)
+
+    def test_marshal_migr_conn_info_serialized(self):
+        migr_conn_info = {"pkey": "serialized_key"}
+
+        result = base.marshal_migr_conn_info(migr_conn_info)
+        self.assertEqual(result, migr_conn_info)
+
+    @mock.patch('coriolis.utils.serialize_key')
+    @mock.patch('coriolis.tasks.base.CONF')
+    def test_marshal_migr_conn_info(self, mock_conf, mock_serialize_key):
+        pkey = mock.MagicMock()
+        custom_pkey_field_name = "custom_key"
+        migr_conn_info = {custom_pkey_field_name: pkey}
+        expected_conn_info = {
+            custom_pkey_field_name: mock_serialize_key.return_value}
+
+        result = base.marshal_migr_conn_info(
+            migr_conn_info, private_key_field_name=custom_pkey_field_name)
+        mock_serialize_key.assert_called_once_with(
+            pkey, mock_conf.serialization.temp_keypair_password)
+        self.assertEqual(result, expected_conn_info)
+
+    def test_unmarshal_migr_conn_info_none(self):
+        result = base.unmarshal_migr_conn_info(None)
+        self.assertEqual(result, None)
+
+    def test_unmarshal_migr_conn_info_no_key(self):
+        migr_conn_info = {"no_pkey": "value"}
+        result = base.unmarshal_migr_conn_info(migr_conn_info)
+        self.assertEqual(result, migr_conn_info)
+
+    def test_unmarshal_migr_conn_info_serialized(self):
+        pkey = paramiko.RSAKey.generate(bits=2048)
+        migr_conn_info = {"pkey": pkey}
+
+        result = base.unmarshal_migr_conn_info(migr_conn_info)
+        self.assertEqual(result, migr_conn_info)
+
+    @mock.patch('coriolis.utils.deserialize_key')
+    @mock.patch('coriolis.tasks.base.CONF')
+    def test_unmarshal_migr_conn_info(self, mock_conf, mock_deserialize_key):
+        custom_pkey_field_name = "custom_key"
+        migr_conn_info = {custom_pkey_field_name: "pkey"}
+        expected_conn_info = {
+            custom_pkey_field_name: mock_deserialize_key.return_value}
+
+        result = base.unmarshal_migr_conn_info(
+            migr_conn_info, private_key_field_name=custom_pkey_field_name)
+        mock_deserialize_key.assert_called_once_with(
+            "pkey", mock_conf.serialization.temp_keypair_password)
+        self.assertEqual(result, expected_conn_info)