Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add unit test for coriolis.api.v1.* modules #283

Merged
merged 28 commits into from
Feb 1, 2024

Conversation

Cristi1324
Copy link
Contributor

@Cristi1324 Cristi1324 commented Nov 24, 2023

This PR add unit tests for Coriolis/api/v1/*.py

coriolis/tests/api/v1/test_diagnostics.py Outdated Show resolved Hide resolved
body = mock.sentinel.body
is_valid = 'mock_status'
message = 'mock_message'
mock_validate_connection.return_value = [is_valid, message]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a pair, not a list. Please assign (is_valid, message) to this return_value. Also, is_valid should be a bool value.

@mock.patch.object(api.API, 'validate_connection')
@mock.patch('coriolis.policies.endpoints.ENDPOINTS_POLICY_PREFIX',
'mock_prefix')
def test_validate_connection_exept_not_found(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please fix typo: test_validate_connection_except_not_found

@mock.patch.object(api.API, 'validate_connection')
@mock.patch('coriolis.policies.endpoints.ENDPOINTS_POLICY_PREFIX',
'mock_prefix')
def test_validate_connection_exept_invalid_parameter_value(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above, fix exept typo

self,
mock_get_endpoint_destination_minion_pool_options,
mock_destination_minion_pool_options_collection,
mock_get_diagnostics_policy_label,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: please rename this to mock_decode_base64_param or similar

mock_context = mock.Mock()
mock_req.environ = {'coriolis.context': mock_context}
body = mock.sentinel.body
mock__validate_create_body.return_value = [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a tuple, not a list. Please replace

coriolis/tests/api/v1/test_endpoints.py Show resolved Hide resolved
coriolis/tests/api/v1/test_migrations.py Show resolved Hide resolved
result
)

if data['migration']['replica_id'] is not None:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not repeat the original method's logic. It'd be helpful to add a "expected_api_method" and "validation_expected" keys alongside the migration data (not inside it). Therefore, you can mock the api method accordinlgy:

# `pop()` will remove the key: value from data, so you can pass it along to `create`
expected_api_method = data.pop('expected_api_method')
validation_expected = data.pop('validation_expected', False)
# ...
with mock.patch.object(api.API, expected_api_method) as mock_api_method:
    # ...
    result = self.migrations.create(mock_req, data)
    # ....
   # we don't care about the call args that much here
   if validation_expected:
       mock__validate_migration_input.assert_called_once()
    mock_api_method.assert_called_once() 
    mock_single.assert_called_with(mock_api_method.return_value)

mock_req = mock.Mock()
mock_context = mock.Mock()
mock_req.environ = {'coriolis.context': mock_context}
mock_values = [(f'mock_value_{i}') for i in range(1, 15)]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah.. let's not do this please. If you REALLY want to check these, use mock sentinels instead. IMO you can skip these long checks, the only thing that really matters is that the right API method is called if 'replica_id' is in migration or not. You can also check validate_user_scripts and normalize_user_scripts calls since you have to mock them.

@Cristi1324 Cristi1324 force-pushed the api-v1-unit-tests branch 3 times, most recently from 67d7b5a to 7b666f8 Compare November 28, 2023 13:08

result = self.diag_api.index(mock_req)

mock_get_diagnostics_policy_label.assert_called_with("get")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please change all these assertions to assert_called_once_with, since these only get called once (as they should in this, and most cases)

(mock_get_endpoint_instance.
assert_called_once_with)(
mock_context, endpoint_id,
mock_decode_base64_param.return_value,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please set side_effect to mock_decode_base_64_param, so you don't pass the same thing twice here:

mock_decode_base64_param.side_effect = [id, env]
# ...
mock_get_endpoint_instance.assert_called_once_with(
    mock_context, endpoint_id, id, env)


def setUp(self):
super(
EndpointSourceOptionsControllerTestCase,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't format code like this if the character limit allows otherwise. This fits in the limit, please adopt this style, format only where necessary:

    def setUp(self):
        super(EndpointDestinationOptionsControllerTestCase, self).setUp()
        self.endpoint_api = endpoint.EndpointDestinationOptionsController()

Please apply this treatment wherever you see fit

option_names=options)
(mock_source_options_collection.
assert_called_once_with)(
mock_get_endpoint_source_options.return_value)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also looks like unnecessary formatting. Please replace with something like this:

        mock_get_endpoint_destination_options.assert_called_once_with(
            mock_context, endpoint_id,
            env=env,
            option_names=options)
        mock_destination_options_collection.assert_called_once_with(
            mock_get_endpoint_destination_options.return_value)

Apply this also wherever you see fit.

}
endpoint = testutils.get_wrapped_function(
self.endpoint_api._validate_create_body)(
self,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please pass self.endpoint_api here, as that's an object of the class. self is a TestCase object here

}
endpoint = testutils.get_wrapped_function(
self.endpoint_api._validate_update_body)(
self,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above

}
endpoint = testutils.get_wrapped_function(
self.endpoint_api._validate_update_body)(
self,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above

KeyError,
testutils.get_wrapped_function(
self.endpoint_api._validate_update_body),
self,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above

@mock.patch('coriolis.api.v1.migrations.CONF')
def test_show(
self,
mock_CONF,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: please rename this to mock_conf

mock__validate_migration_input.assert_called_with(
mock_context, data)
mock_api_method.assert_called_once()
mock_single.assert_called_with(mock_api_method.return_value)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is otherwise good so far, please add tests for the rest of the methods of coriolis.api.v1.migrations

@Cristi1324 Cristi1324 force-pushed the api-v1-unit-tests branch 3 times, most recently from a55dd79 to 23bf910 Compare November 28, 2023 18:35
from unittest import mock

from coriolis.api.v1 import endpoint_source_minion_pool_options \
as endpoint
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This import should be a single line please

super(
EndpointSourceMinionPoolOptionsControllerTestCase,
self
).setUp()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also fits in a single line

super(EndpointSourceOptionsControllerTestCase, self).setUp()
self.endpoint_api = endpoint.EndpointSourceOptionsController()

@mock.patch('coriolis.policies.endpoints.ENDPOINTS_POLICY_PREFIX',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason you mocked this? This is actually a constant value, it doesn't need mocking. Please remove this mock, and also remove it from the other places you patched this constant.

from coriolis.tests import test_base
from coriolis.tests import testutils

from webob import exc
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This import should be in the "third-party" group. Please move it above the coriolis imports:

from unittest import mock

from webob import exc

from coriolis.api.v1 import endpoints
# ...

Also, please apply this to al the places you have this imported

super(EndpointControllerTestCase, self).setUp()
self.endpoint_api = endpoints.EndpointController()

@mock.patch.object(endpoint_policies, 'get_endpoints_policy_label')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method also just returns a string, nothing special that needs mocking. Please remove this mock and others like it.

'origin_minion_pool_id': 'mock_origin_minion_pool_id',
'instance_osmorphing_minion_pool_mappings': {
'mock_instance_1', 'mock_instance_2'},
'instances': {},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is going to fail, yes, but not because you set replication_count to 12, but because you didn't set the same instances list as instance_osmorphing_minion_pool_mappings. Please set instances to [mock_instance_1', 'mock_instance_2']

include_task_info=False
)

@ddt.data(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use ddt.file_data here, so you can neatly put your test data inside a yml file

'mock_destination_minion_pool_id',
'instance_osmorphing_minion_pool_mappings': {
'mock_instance_1', 'mock_instance_2'},
'instances': {'mock_instance_1', 'mock_instance_2'},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To more accurately match the real-world data, please assign instance_osmorphing_minion_pool_mappings a map, like this:
{'mock_instance_1': 'mock_pool', 'mock_instance_2': 'mock_pool'} ;
and instances should be a list of instances:
['mock_instance_1', 'mock_instance_2']

data['migration']['instances'],
)

@ddt.data(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above, please use a yml file using ddt.file_data

coriolis/tests/api/v1/test_migrations.py Show resolved Hide resolved
@Cristi1324 Cristi1324 force-pushed the api-v1-unit-tests branch 2 times, most recently from 0d09662 to 47a8267 Compare December 4, 2023 12:56


class DiagnosticsControllerTestCase(test_base.CoriolisBaseTestCase):
"Test suite for the Coriolis api v1."""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please either remove this, or make sure this is a docstring, with a more suggestive string:

Suggested change
"Test suite for the Coriolis api v1."""
"""Test suite for the Coriolis Diagnostics v1 API"""


@mock.patch.object(api.API, 'get')
@mock.patch.object(diagnostic_view, 'collection')
@mock.patch.object(diagnostics, 'get_diagnostics_policy_label')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't mock this please. Instead I've asked you to mock context, so you can check whether mock_context.can is called with the expected policy tag, which in this case should be migration:diagnostics:get

coriolis/tests/api/v1/test_endpoints.py Show resolved Hide resolved
super(MigrationActionsControllerTestCase, self).setUp()
self.migration_actions = migration_actions.MigrationActionsController()

@mock.patch.object(migration_policies, 'get_migrations_policy_label')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please stop mocking anything related to policy_label.

mock_validate_network_map,
mock_validate_target_environment,
mock_validate_storage_mappings,
migrations,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shadows from coriolis.api.v1 import migrations from this file. Please rename it to somehing like config

@Cristi1324 Cristi1324 force-pushed the api-v1-unit-tests branch 2 times, most recently from 56afda7 to d8ebcef Compare December 5, 2023 10:14


class DiagnosticsControllerTestCase(test_base.CoriolisBaseTestCase):
"Test suite for the Coriolis Diagnostics v1 API"""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still not a docstring. A Python docstring is defined between 3 double quotes, like this: """ doc string messsage """
Please pay more attention on the changes I propose in comments:

Suggested change
"Test suite for the Coriolis Diagnostics v1 API"""
"""Test suite for the Coriolis Diagnostics v1 API"""

class EndpointDestinationOptionsControllerTestCase(
test_base.CoriolisBaseTestCase
):
"Test suite for the Coriolis Endpoint Destination Options v1 API"""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Turn it into a docstring:

Suggested change
"Test suite for the Coriolis Endpoint Destination Options v1 API"""
"""Test suite for the Coriolis Endpoint Destination Options v1 API"""

@Cristi1324 Cristi1324 force-pushed the api-v1-unit-tests branch 3 times, most recently from fdd764c to dad61b4 Compare December 5, 2023 10:34
@@ -0,0 +1,5 @@
- config:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This config is practically empty. I think you could just pass the exception names directly:

- NotFound
- InvalidParameterValue

@mock.patch.object(minion_pool_view, 'single')
@mock.patch.object(api.API, 'deallocate_minion_pool')
@ddt.file_data('data/minion_pool_exceptions.yml')
@ddt.unpack
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above

@Cristi1324 Cristi1324 force-pushed the api-v1-unit-tests branch 8 times, most recently from b08fc28 to 1139e1a Compare December 5, 2023 15:28
coriolis/tests/api/v1/test_minion_pools.py Show resolved Hide resolved
Comment on lines 1 to 2
- config:
exception_raised: True
pool_retention_strategy: null
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can lose the config, as it is empty, therefore irrelevant:

Suggested change
- config:
exception_raised: True
pool_retention_strategy: null
- exception_raised: True
pool_retention_strategy: null

@ddt.file_data('data/minion_pool_retention_strategy.yml')
def test__check_pool_retention_strategy(
self,
config,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove this, as it is not used

- config:
minimum_minions: 1
maximum_minions: 1
minion_max_idle_time: 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This in fact raises an exception, but you never call it to check. Please check explanation below

coriolis/tests/api/v1/test_minion_pools.py Show resolved Hide resolved
coriolis/tests/api/v1/test_minion_pools.py Show resolved Hide resolved
@Cristi1324 Cristi1324 changed the title Add unit test for Coriolis api v1 Add unit test for coriolis/tests/api/v1/* Dec 6, 2023
Copy link
Contributor

@Dany9966 Dany9966 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM so far. Please continue adding tests for the rest of V1 modules.

coriolis/api/v1/utils.py Show resolved Hide resolved
coriolis/tests/api/v1/test_utils.py Outdated Show resolved Hide resolved
coriolis/tests/api/v1/test_utils.py Outdated Show resolved Hide resolved
coriolis/tests/api/v1/test_utils.py Outdated Show resolved Hide resolved
coriolis/tests/api/v1/test_utils.py Outdated Show resolved Hide resolved
self.assertEqual(
expected_result,
result
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add test for format_keyerror_message

@Dany9966 Dany9966 merged commit 578a37b into cloudbase:master Feb 1, 2024
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants