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

[PR #1163/e3da3d50 backport][stable-5] expand module_utils.transformation unit tests #1164

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions changelogs/fragments/1163-map_complex_type.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
bugfixes:
- module_utils.transformations - ensure that ``map_complex_type`` still returns transformed items if items exists that are not in the type_map (https://github.com/ansible-collections/amazon.aws/pull/1163).
2 changes: 1 addition & 1 deletion plugins/module_utils/transformation.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ def map_complex_type(complex_type, type_map):
complex_type[key],
type_map[key])
else:
return complex_type
new_type[key] = complex_type[key]
elif isinstance(complex_type, list):
for i in range(len(complex_type)):
new_type.append(map_complex_type(
Expand Down
26 changes: 13 additions & 13 deletions tests/unit/module_utils/cloud/test_cloud_retry.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class TestCloudRetry():
error_codes = [400, 500, 600]
custom_error_codes = [100, 200, 300]

class TestException(Exception):
class OurTestException(Exception):
"""
custom exception class for testing
"""
Expand Down Expand Up @@ -81,7 +81,7 @@ def test_retry_backoff(self):
def test_retry_func():
if test_retry_func.counter < 2:
test_retry_func.counter += 1
raise self.TestException(status=random.choice(TestCloudRetry.error_codes))
raise self.OurTestException(status=random.choice(TestCloudRetry.error_codes))
else:
return True

Expand All @@ -99,7 +99,7 @@ def test_retry_exponential_backoff(self):
def test_retry_func():
if test_retry_func.counter < 2:
test_retry_func.counter += 1
raise self.TestException(status=random.choice(TestCloudRetry.error_codes))
raise self.OurTestException(status=random.choice(TestCloudRetry.error_codes))
else:
return True

Expand All @@ -108,19 +108,19 @@ def test_retry_func():
assert ret is True

def test_retry_exponential_backoff_with_unexpected_exception(self):
unexpected_except = self.TestException(status=100)
unexpected_except = self.OurTestException(status=100)

@TestCloudRetry.UnitTestsRetry.exponential_backoff(retries=3, delay=1, backoff=1.1, max_delay=3,
catch_extra_error_codes=TestCloudRetry.error_codes)
def test_retry_func():
if test_retry_func.counter == 0:
test_retry_func.counter += 1
raise self.TestException(status=random.choice(TestCloudRetry.error_codes))
raise self.OurTestException(status=random.choice(TestCloudRetry.error_codes))
else:
raise unexpected_except

test_retry_func.counter = 0
with pytest.raises(self.TestException) as context:
with pytest.raises(self.OurTestException) as context:
test_retry_func()

assert context.value.status == unexpected_except.status
Expand All @@ -134,7 +134,7 @@ def test_retry_jitter_backoff(self):
def test_retry_func():
if test_retry_func.counter < 2:
test_retry_func.counter += 1
raise self.TestException(status=random.choice(TestCloudRetry.error_codes))
raise self.OurTestException(status=random.choice(TestCloudRetry.error_codes))
else:
return True

Expand All @@ -143,19 +143,19 @@ def test_retry_func():
assert ret is True

def test_retry_jittered_backoff_with_unexpected_exception(self):
unexpected_except = self.TestException(status=100)
unexpected_except = self.OurTestException(status=100)

@TestCloudRetry.UnitTestsRetry.jittered_backoff(retries=3, delay=1, max_delay=3,
catch_extra_error_codes=TestCloudRetry.error_codes)
def test_retry_func():
if test_retry_func.counter == 0:
test_retry_func.counter += 1
raise self.TestException(status=random.choice(TestCloudRetry.error_codes))
raise self.OurTestException(status=random.choice(TestCloudRetry.error_codes))
else:
raise unexpected_except

test_retry_func.counter = 0
with pytest.raises(self.TestException) as context:
with pytest.raises(self.OurTestException) as context:
test_retry_func()

assert context.value.status == unexpected_except.status
Expand All @@ -172,7 +172,7 @@ def build_response():
def test_retry_func():
if test_retry_func.counter < 2:
test_retry_func.counter += 1
raise self.TestException(build_response())
raise self.OurTestException(build_response())
else:
return True

Expand All @@ -188,13 +188,13 @@ def test_wrapped_function_called_several_times(self):
@TestCloudRetry.UnitTestsRetry.exponential_backoff(retries=2, delay=2, backoff=4, max_delay=100,
catch_extra_error_codes=TestCloudRetry.error_codes)
def _fail():
raise self.TestException(status=random.choice(TestCloudRetry.error_codes))
raise self.OurTestException(status=random.choice(TestCloudRetry.error_codes))

# run the method 3 times and assert that each it is retrying after 2secs
# the elapsed execution time should be closed to 2sec
for _i in range(3):
start = datetime.now()
with pytest.raises(self.TestException):
with pytest.raises(self.OurTestException):
_fail()
duration = (datetime.now() - start).seconds
assert duration == 2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from ansible_collections.amazon.aws.plugins.module_utils.transformation import ansible_dict_to_boto3_filter_list


class AnsibleDictToBoto3FilterListTestSuite():
class TestAnsibleDictToBoto3FilterList():

# ========================================================
# ec2.ansible_dict_to_boto3_filter_list
Expand All @@ -28,7 +28,7 @@ def test_ansible_dict_with_string_to_boto3_filter_list(self):
]

converted_filters_list = ansible_dict_to_boto3_filter_list(filters)
self.assertEqual(converted_filters_list, filter_list_string)
assert converted_filters_list == filter_list_string

def test_ansible_dict_with_boolean_to_boto3_filter_list(self):
filters = {'enabled': True}
Expand All @@ -42,7 +42,7 @@ def test_ansible_dict_with_boolean_to_boto3_filter_list(self):
]

converted_filters_bool = ansible_dict_to_boto3_filter_list(filters)
self.assertEqual(converted_filters_bool, filter_list_boolean)
assert converted_filters_bool == filter_list_boolean

def test_ansible_dict_with_integer_to_boto3_filter_list(self):
filters = {'version': 1}
Expand All @@ -56,4 +56,18 @@ def test_ansible_dict_with_integer_to_boto3_filter_list(self):
]

converted_filters_int = ansible_dict_to_boto3_filter_list(filters)
self.assertEqual(converted_filters_int, filter_list_integer)
assert converted_filters_int == filter_list_integer

def test_ansible_dict_with_list_to_boto3_filter_list(self):
filters = {'version': ['1', '2', '3']}
filter_list_integer = [
{
'Name': 'version',
'Values': [
'1', '2', '3'
]
}
]

converted_filters_int = ansible_dict_to_boto3_filter_list(filters)
assert converted_filters_int == filter_list_integer
93 changes: 86 additions & 7 deletions tests/unit/module_utils/transformation/test_map_complex_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,93 @@

from ansible_collections.amazon.aws.plugins.module_utils.transformation import map_complex_type

from ansible_collections.amazon.aws.tests.unit.compat.mock import sentinel

class TestMapComplexTypeTestSuite():

def test_map_complex_type_over_dict(self):
type_map = {'minimum_healthy_percent': 'int', 'maximum_percent': 'int'}
complex_type_dict = {'minimum_healthy_percent': "75", 'maximum_percent': "150"}
complex_type_expected = {'minimum_healthy_percent': 75, 'maximum_percent': 150}
def test_map_complex_type_over_dict():
type_map = {'minimum_healthy_percent': 'int', 'maximum_percent': 'int'}
complex_type_dict = {'minimum_healthy_percent': "75", 'maximum_percent': "150"}
complex_type_expected = {'minimum_healthy_percent': 75, 'maximum_percent': 150}

complex_type_mapped = map_complex_type(complex_type_dict, type_map)
complex_type_mapped = map_complex_type(complex_type_dict, type_map)

assert complex_type_mapped == complex_type_expected
assert complex_type_mapped == complex_type_expected


def test_map_complex_type_empty():
type_map = {'minimum_healthy_percent': 'int', 'maximum_percent': 'int'}
assert map_complex_type({}, type_map) == {}
assert map_complex_type([], type_map) == []
assert map_complex_type(None, type_map) is None


def test_map_complex_type_no_type():
type_map = {'some_entry': 'int'}
complex_dict = {'another_entry': sentinel.UNSPECIFIED_MAPPING}
mapped_dict = map_complex_type(complex_dict, type_map)
assert mapped_dict == complex_dict
# we should have the original sentinel object, even if it's a new dictionary
assert mapped_dict['another_entry'] is sentinel.UNSPECIFIED_MAPPING


def test_map_complex_type_list():
type_map = {'some_entry': 'int'}
complex_dict = {'some_entry': ["1", "2", "3"]}
expected_dict = {'some_entry': [1, 2, 3]}
mapped_dict = map_complex_type(complex_dict, type_map)
assert mapped_dict == expected_dict


def test_map_complex_type_list_type():
type_map = {'some_entry': ['int']}
complex_dict = {'some_entry': ["1", "2", "3"]}
expected_dict = {'some_entry': [1, 2, 3]}
mapped_dict = map_complex_type(complex_dict, type_map)
assert mapped_dict == expected_dict

type_map = {'some_entry': ['int']}
complex_dict = {'some_entry': "1"}
expected_dict = {'some_entry': 1}
mapped_dict = map_complex_type(complex_dict, type_map)
assert mapped_dict == expected_dict


def test_map_complex_type_complex():
type_map = {
'my_integer': 'int',
'my_bool': 'bool',
'my_string': 'str',
'my_typelist_of_int': ['int'],
'my_maplist_of_int': 'int',
'my_unused': 'bool',
}
complex_dict = {
'my_integer': '-24',
'my_bool': 'true',
'my_string': 43,
'my_typelist_of_int': '5',
'my_maplist_of_int': ['-26', '47'],
'my_unconverted': sentinel.UNSPECIFIED_MAPPING,
}
expected_dict = {
'my_integer': -24,
'my_bool': True,
'my_string': '43',
'my_typelist_of_int': 5,
'my_maplist_of_int': [-26, 47],
'my_unconverted': sentinel.UNSPECIFIED_MAPPING,
}

mapped_dict = map_complex_type(complex_dict, type_map)

assert mapped_dict == expected_dict
assert mapped_dict['my_unconverted'] is sentinel.UNSPECIFIED_MAPPING
assert mapped_dict['my_bool'] is True


def test_map_complex_type_nested_list():
type_map = {'my_integer': 'int'}
complex_dict = [{'my_integer': '5'}, {'my_integer': '-24'}]
expected_dict = [{'my_integer': 5}, {'my_integer': -24}]
mapped_dict = map_complex_type(complex_dict, type_map)
assert mapped_dict == expected_dict