From 910f3adff2c312f1430e875df66150af27a8ac8c Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Tue, 31 Mar 2020 10:42:38 +0200 Subject: [PATCH] Move modules and module_utils unit tests to correct place (#81) * Move modules and module_utils unit tests to correct place. * Update ignore.txt * Fix imports. * Fix typos. * Fix more typos. --- tests/unit/plugins/module_utils/__init__.py | 0 .../unit/plugins/module_utils/test_common.py | 518 ++++++++++++++++++ tests/unit/plugins/modules/__init__.py | 0 .../plugins/modules/test_docker_container.py | 22 + .../plugins/modules/test_docker_network.py | 31 ++ .../modules/test_docker_swarm_service.py | 510 +++++++++++++++++ .../plugins/modules/test_docker_volume.py | 36 ++ 7 files changed, 1117 insertions(+) create mode 100644 tests/unit/plugins/module_utils/__init__.py create mode 100644 tests/unit/plugins/module_utils/test_common.py create mode 100644 tests/unit/plugins/modules/__init__.py create mode 100644 tests/unit/plugins/modules/test_docker_container.py create mode 100644 tests/unit/plugins/modules/test_docker_network.py create mode 100644 tests/unit/plugins/modules/test_docker_swarm_service.py create mode 100644 tests/unit/plugins/modules/test_docker_volume.py diff --git a/tests/unit/plugins/module_utils/__init__.py b/tests/unit/plugins/module_utils/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/unit/plugins/module_utils/test_common.py b/tests/unit/plugins/module_utils/test_common.py new file mode 100644 index 00000000..b9f747db --- /dev/null +++ b/tests/unit/plugins/module_utils/test_common.py @@ -0,0 +1,518 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import pytest + +from ansible_collections.community.docker.plugins.module_utils.common import ( + compare_dict_allow_more_present, + compare_generic, + convert_duration_to_nanosecond, + parse_healthcheck +) + +DICT_ALLOW_MORE_PRESENT = ( + { + 'av': {}, + 'bv': {'a': 1}, + 'result': True + }, + { + 'av': {'a': 1}, + 'bv': {'a': 1, 'b': 2}, + 'result': True + }, + { + 'av': {'a': 1}, + 'bv': {'b': 2}, + 'result': False + }, + { + 'av': {'a': 1}, + 'bv': {'a': None, 'b': 1}, + 'result': False + }, + { + 'av': {'a': None}, + 'bv': {'b': 1}, + 'result': False + }, +) + +COMPARE_GENERIC = [ + ######################################################################################## + # value + { + 'a': 1, + 'b': 2, + 'method': 'strict', + 'type': 'value', + 'result': False + }, + { + 'a': 'hello', + 'b': 'hello', + 'method': 'strict', + 'type': 'value', + 'result': True + }, + { + 'a': None, + 'b': 'hello', + 'method': 'strict', + 'type': 'value', + 'result': False + }, + { + 'a': None, + 'b': None, + 'method': 'strict', + 'type': 'value', + 'result': True + }, + { + 'a': 1, + 'b': 2, + 'method': 'ignore', + 'type': 'value', + 'result': True + }, + { + 'a': None, + 'b': 2, + 'method': 'ignore', + 'type': 'value', + 'result': True + }, + ######################################################################################## + # list + { + 'a': [ + 'x', + ], + 'b': [ + 'y', + ], + 'method': 'strict', + 'type': 'list', + 'result': False + }, + { + 'a': [ + 'x', + ], + 'b': [ + 'x', + 'x', + ], + 'method': 'strict', + 'type': 'list', + 'result': False + }, + { + 'a': [ + 'x', + 'y', + ], + 'b': [ + 'x', + 'y', + ], + 'method': 'strict', + 'type': 'list', + 'result': True + }, + { + 'a': [ + 'x', + 'y', + ], + 'b': [ + 'y', + 'x', + ], + 'method': 'strict', + 'type': 'list', + 'result': False + }, + { + 'a': [ + 'x', + 'y', + ], + 'b': [ + 'x', + ], + 'method': 'allow_more_present', + 'type': 'list', + 'result': False + }, + { + 'a': [ + 'x', + ], + 'b': [ + 'x', + 'y', + ], + 'method': 'allow_more_present', + 'type': 'list', + 'result': True + }, + { + 'a': [ + 'x', + 'x', + 'y', + ], + 'b': [ + 'x', + 'y', + ], + 'method': 'allow_more_present', + 'type': 'list', + 'result': False + }, + { + 'a': [ + 'x', + 'z', + ], + 'b': [ + 'x', + 'y', + 'x', + 'z', + ], + 'method': 'allow_more_present', + 'type': 'list', + 'result': True + }, + { + 'a': [ + 'x', + 'y', + ], + 'b': [ + 'y', + 'x', + ], + 'method': 'ignore', + 'type': 'list', + 'result': True + }, + ######################################################################################## + # set + { + 'a': [ + 'x', + ], + 'b': [ + 'y', + ], + 'method': 'strict', + 'type': 'set', + 'result': False + }, + { + 'a': [ + 'x', + ], + 'b': [ + 'x', + 'x', + ], + 'method': 'strict', + 'type': 'set', + 'result': True + }, + { + 'a': [ + 'x', + 'y', + ], + 'b': [ + 'x', + 'y', + ], + 'method': 'strict', + 'type': 'set', + 'result': True + }, + { + 'a': [ + 'x', + 'y', + ], + 'b': [ + 'y', + 'x', + ], + 'method': 'strict', + 'type': 'set', + 'result': True + }, + { + 'a': [ + 'x', + 'y', + ], + 'b': [ + 'x', + ], + 'method': 'allow_more_present', + 'type': 'set', + 'result': False + }, + { + 'a': [ + 'x', + ], + 'b': [ + 'x', + 'y', + ], + 'method': 'allow_more_present', + 'type': 'set', + 'result': True + }, + { + 'a': [ + 'x', + 'x', + 'y', + ], + 'b': [ + 'x', + 'y', + ], + 'method': 'allow_more_present', + 'type': 'set', + 'result': True + }, + { + 'a': [ + 'x', + 'z', + ], + 'b': [ + 'x', + 'y', + 'x', + 'z', + ], + 'method': 'allow_more_present', + 'type': 'set', + 'result': True + }, + { + 'a': [ + 'x', + 'a', + ], + 'b': [ + 'y', + 'z', + ], + 'method': 'ignore', + 'type': 'set', + 'result': True + }, + ######################################################################################## + # set(dict) + { + 'a': [ + {'x': 1}, + ], + 'b': [ + {'y': 1}, + ], + 'method': 'strict', + 'type': 'set(dict)', + 'result': False + }, + { + 'a': [ + {'x': 1}, + ], + 'b': [ + {'x': 1}, + ], + 'method': 'strict', + 'type': 'set(dict)', + 'result': True + }, + { + 'a': [ + {'x': 1}, + ], + 'b': [ + {'x': 1, 'y': 2}, + ], + 'method': 'strict', + 'type': 'set(dict)', + 'result': True + }, + { + 'a': [ + {'x': 1}, + {'x': 2, 'y': 3}, + ], + 'b': [ + {'x': 1}, + {'x': 2, 'y': 3}, + ], + 'method': 'strict', + 'type': 'set(dict)', + 'result': True + }, + { + 'a': [ + {'x': 1}, + ], + 'b': [ + {'x': 1, 'z': 2}, + {'x': 2, 'y': 3}, + ], + 'method': 'allow_more_present', + 'type': 'set(dict)', + 'result': True + }, + { + 'a': [ + {'x': 1, 'y': 2}, + ], + 'b': [ + {'x': 1}, + {'x': 2, 'y': 3}, + ], + 'method': 'allow_more_present', + 'type': 'set(dict)', + 'result': False + }, + { + 'a': [ + {'x': 1, 'y': 3}, + ], + 'b': [ + {'x': 1}, + {'x': 1, 'y': 3, 'z': 4}, + ], + 'method': 'allow_more_present', + 'type': 'set(dict)', + 'result': True + }, + { + 'a': [ + {'x': 1}, + {'x': 2, 'y': 3}, + ], + 'b': [ + {'x': 1}, + ], + 'method': 'ignore', + 'type': 'set(dict)', + 'result': True + }, + ######################################################################################## + # dict + { + 'a': {'x': 1}, + 'b': {'y': 1}, + 'method': 'strict', + 'type': 'dict', + 'result': False + }, + { + 'a': {'x': 1}, + 'b': {'x': 1, 'y': 2}, + 'method': 'strict', + 'type': 'dict', + 'result': False + }, + { + 'a': {'x': 1}, + 'b': {'x': 1}, + 'method': 'strict', + 'type': 'dict', + 'result': True + }, + { + 'a': {'x': 1, 'z': 2}, + 'b': {'x': 1, 'y': 2}, + 'method': 'strict', + 'type': 'dict', + 'result': False + }, + { + 'a': {'x': 1, 'z': 2}, + 'b': {'x': 1, 'y': 2}, + 'method': 'ignore', + 'type': 'dict', + 'result': True + }, +] + [{ + 'a': entry['av'], + 'b': entry['bv'], + 'method': 'allow_more_present', + 'type': 'dict', + 'result': entry['result'] +} for entry in DICT_ALLOW_MORE_PRESENT] + + +@pytest.mark.parametrize("entry", DICT_ALLOW_MORE_PRESENT) +def test_dict_allow_more_present(entry): + assert compare_dict_allow_more_present(entry['av'], entry['bv']) == entry['result'] + + +@pytest.mark.parametrize("entry", COMPARE_GENERIC) +def test_compare_generic(entry): + assert compare_generic(entry['a'], entry['b'], entry['method'], entry['type']) == entry['result'] + + +def test_convert_duration_to_nanosecond(): + nanoseconds = convert_duration_to_nanosecond('5s') + assert nanoseconds == 5000000000 + nanoseconds = convert_duration_to_nanosecond('1m5s') + assert nanoseconds == 65000000000 + with pytest.raises(ValueError): + convert_duration_to_nanosecond([1, 2, 3]) + with pytest.raises(ValueError): + convert_duration_to_nanosecond('10x') + + +def test_parse_healthcheck(): + result, disabled = parse_healthcheck({ + 'test': 'sleep 1', + 'interval': '1s', + }) + assert disabled is False + assert result == { + 'test': ['CMD-SHELL', 'sleep 1'], + 'interval': 1000000000 + } + + result, disabled = parse_healthcheck({ + 'test': ['NONE'], + }) + assert result is None + assert disabled + + result, disabled = parse_healthcheck({ + 'test': 'sleep 1', + 'interval': '1s423ms' + }) + assert result == { + 'test': ['CMD-SHELL', 'sleep 1'], + 'interval': 1423000000 + } + assert disabled is False + + result, disabled = parse_healthcheck({ + 'test': 'sleep 1', + 'interval': '1h1m2s3ms4us' + }) + assert result == { + 'test': ['CMD-SHELL', 'sleep 1'], + 'interval': 3662003004000 + } + assert disabled is False diff --git a/tests/unit/plugins/modules/__init__.py b/tests/unit/plugins/modules/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/unit/plugins/modules/test_docker_container.py b/tests/unit/plugins/modules/test_docker_container.py new file mode 100644 index 00000000..00701961 --- /dev/null +++ b/tests/unit/plugins/modules/test_docker_container.py @@ -0,0 +1,22 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import unittest + +from ansible_collections.community.docker.plugins.modules.docker_container import TaskParameters + + +class TestTaskParameters(unittest.TestCase): + """Unit tests for TaskParameters.""" + + def test_parse_exposed_ports_tcp_udp(self): + """ + Ensure _parse_exposed_ports does not cancel ports with the same + number but different protocol. + """ + task_params = TaskParameters.__new__(TaskParameters) + task_params.exposed_ports = None + result = task_params._parse_exposed_ports([80, '443', '443/udp']) + self.assertTrue((80, 'tcp') in result) + self.assertTrue((443, 'tcp') in result) + self.assertTrue((443, 'udp') in result) diff --git a/tests/unit/plugins/modules/test_docker_network.py b/tests/unit/plugins/modules/test_docker_network.py new file mode 100644 index 00000000..4b36a52c --- /dev/null +++ b/tests/unit/plugins/modules/test_docker_network.py @@ -0,0 +1,31 @@ +"""Unit tests for docker_network.""" + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import pytest + +from ansible_collections.community.docker.plugins.modules.docker_network import validate_cidr + + +@pytest.mark.parametrize("cidr,expected", [ + ('192.168.0.1/16', 'ipv4'), + ('192.168.0.1/24', 'ipv4'), + ('192.168.0.1/32', 'ipv4'), + ('fdd1:ac8c:0557:7ce2::/64', 'ipv6'), + ('fdd1:ac8c:0557:7ce2::/128', 'ipv6'), +]) +def test_validate_cidr_positives(cidr, expected): + assert validate_cidr(cidr) == expected + + +@pytest.mark.parametrize("cidr", [ + '192.168.0.1', + '192.168.0.1/34', + '192.168.0.1/asd', + 'fdd1:ac8c:0557:7ce2::', +]) +def test_validate_cidr_negatives(cidr): + with pytest.raises(ValueError) as e: + validate_cidr(cidr) + assert '"{0}" is not a valid CIDR'.format(cidr) == str(e.value) diff --git a/tests/unit/plugins/modules/test_docker_swarm_service.py b/tests/unit/plugins/modules/test_docker_swarm_service.py new file mode 100644 index 00000000..1fae8daf --- /dev/null +++ b/tests/unit/plugins/modules/test_docker_swarm_service.py @@ -0,0 +1,510 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import pytest + + +class APIErrorMock(Exception): + def __init__(self, message, response=None, explanation=None): + self.message = message + self.response = response + self.explanation = explanation + + +@pytest.fixture(autouse=True) +def docker_module_mock(mocker): + docker_module_mock = mocker.MagicMock() + docker_utils_module_mock = mocker.MagicMock() + docker_errors_module_mock = mocker.MagicMock() + docker_errors_module_mock.APIError = APIErrorMock + mock_modules = { + 'docker': docker_module_mock, + 'docker.utils': docker_utils_module_mock, + 'docker.errors': docker_errors_module_mock, + } + return mocker.patch.dict('sys.modules', **mock_modules) + + +@pytest.fixture(autouse=True) +def docker_swarm_service(): + from ansible_collections.community.docker.plugins.modules import docker_swarm_service + + return docker_swarm_service + + +def test_retry_on_out_of_sequence_error(mocker, docker_swarm_service): + run_mock = mocker.MagicMock( + side_effect=APIErrorMock( + message='', + response=None, + explanation='rpc error: code = Unknown desc = update out of sequence', + ) + ) + manager = docker_swarm_service.DockerServiceManager(client=None) + manager.run = run_mock + with pytest.raises(APIErrorMock): + manager.run_safe() + assert run_mock.call_count == 3 + + +def test_no_retry_on_general_api_error(mocker, docker_swarm_service): + run_mock = mocker.MagicMock( + side_effect=APIErrorMock(message='', response=None, explanation='some error') + ) + manager = docker_swarm_service.DockerServiceManager(client=None) + manager.run = run_mock + with pytest.raises(APIErrorMock): + manager.run_safe() + assert run_mock.call_count == 1 + + +def test_get_docker_environment(mocker, docker_swarm_service): + env_file_result = {'TEST1': 'A', 'TEST2': 'B', 'TEST3': 'C'} + env_dict = {'TEST3': 'CC', 'TEST4': 'D'} + env_string = "TEST3=CC,TEST4=D" + + env_list = ['TEST3=CC', 'TEST4=D'] + expected_result = sorted(['TEST1=A', 'TEST2=B', 'TEST3=CC', 'TEST4=D']) + mocker.patch.object( + docker_swarm_service, 'parse_env_file', return_value=env_file_result + ) + mocker.patch.object( + docker_swarm_service, + 'format_environment', + side_effect=lambda d: ['{0}={1}'.format(key, value) for key, value in d.items()], + ) + # Test with env dict and file + result = docker_swarm_service.get_docker_environment( + env_dict, env_files=['dummypath'] + ) + assert result == expected_result + # Test with env list and file + result = docker_swarm_service.get_docker_environment( + env_list, + env_files=['dummypath'] + ) + assert result == expected_result + # Test with env string and file + result = docker_swarm_service.get_docker_environment( + env_string, env_files=['dummypath'] + ) + assert result == expected_result + + assert result == expected_result + # Test with empty env + result = docker_swarm_service.get_docker_environment( + [], env_files=None + ) + assert result == [] + # Test with empty env_files + result = docker_swarm_service.get_docker_environment( + None, env_files=[] + ) + assert result == [] + + +def test_get_nanoseconds_from_raw_option(docker_swarm_service): + value = docker_swarm_service.get_nanoseconds_from_raw_option('test', None) + assert value is None + + value = docker_swarm_service.get_nanoseconds_from_raw_option('test', '1m30s535ms') + assert value == 90535000000 + + value = docker_swarm_service.get_nanoseconds_from_raw_option('test', 10000000000) + assert value == 10000000000 + + with pytest.raises(ValueError): + docker_swarm_service.get_nanoseconds_from_raw_option('test', []) + + +def test_has_dict_changed(docker_swarm_service): + assert not docker_swarm_service.has_dict_changed( + {"a": 1}, + {"a": 1}, + ) + assert not docker_swarm_service.has_dict_changed( + {"a": 1}, + {"a": 1, "b": 2} + ) + assert docker_swarm_service.has_dict_changed( + {"a": 1}, + {"a": 2, "b": 2} + ) + assert docker_swarm_service.has_dict_changed( + {"a": 1, "b": 1}, + {"a": 1} + ) + assert not docker_swarm_service.has_dict_changed( + None, + {"a": 2, "b": 2} + ) + assert docker_swarm_service.has_dict_changed( + {}, + {"a": 2, "b": 2} + ) + assert docker_swarm_service.has_dict_changed( + {"a": 1}, + {} + ) + assert docker_swarm_service.has_dict_changed( + {"a": 1}, + None + ) + assert not docker_swarm_service.has_dict_changed( + {}, + {} + ) + assert not docker_swarm_service.has_dict_changed( + None, + None + ) + assert not docker_swarm_service.has_dict_changed( + {}, + None + ) + assert not docker_swarm_service.has_dict_changed( + None, + {} + ) + + +def test_has_list_changed(docker_swarm_service): + + # List comparisons without dictionaries + # I could improve the indenting, but pycodestyle wants this instead + assert not docker_swarm_service.has_list_changed(None, None) + assert not docker_swarm_service.has_list_changed(None, []) + assert not docker_swarm_service.has_list_changed(None, [1, 2]) + + assert not docker_swarm_service.has_list_changed([], None) + assert not docker_swarm_service.has_list_changed([], []) + assert docker_swarm_service.has_list_changed([], [1, 2]) + + assert docker_swarm_service.has_list_changed([1, 2], None) + assert docker_swarm_service.has_list_changed([1, 2], []) + + assert docker_swarm_service.has_list_changed([1, 2, 3], [1, 2]) + assert docker_swarm_service.has_list_changed([1, 2], [1, 2, 3]) + + # Check list sorting + assert not docker_swarm_service.has_list_changed([1, 2], [2, 1]) + assert docker_swarm_service.has_list_changed( + [1, 2], + [2, 1], + sort_lists=False + ) + + # Check type matching + assert docker_swarm_service.has_list_changed([None, 1], [2, 1]) + assert docker_swarm_service.has_list_changed([2, 1], [None, 1]) + assert docker_swarm_service.has_list_changed( + "command --with args", + ['command', '--with', 'args'] + ) + assert docker_swarm_service.has_list_changed( + ['sleep', '3400'], + [u'sleep', u'3600'], + sort_lists=False + ) + + # List comparisons with dictionaries + assert not docker_swarm_service.has_list_changed( + [{'a': 1}], + [{'a': 1}], + sort_key='a' + ) + + assert not docker_swarm_service.has_list_changed( + [{'a': 1}, {'a': 2}], + [{'a': 1}, {'a': 2}], + sort_key='a' + ) + + with pytest.raises(Exception): + docker_swarm_service.has_list_changed( + [{'a': 1}, {'a': 2}], + [{'a': 1}, {'a': 2}] + ) + + # List sort checking with sort key + assert not docker_swarm_service.has_list_changed( + [{'a': 1}, {'a': 2}], + [{'a': 2}, {'a': 1}], + sort_key='a' + ) + assert docker_swarm_service.has_list_changed( + [{'a': 1}, {'a': 2}], + [{'a': 2}, {'a': 1}], + sort_lists=False + ) + + assert docker_swarm_service.has_list_changed( + [{'a': 1}, {'a': 2}, {'a': 3}], + [{'a': 2}, {'a': 1}], + sort_key='a' + ) + assert docker_swarm_service.has_list_changed( + [{'a': 1}, {'a': 2}], + [{'a': 1}, {'a': 2}, {'a': 3}], + sort_lists=False + ) + + # Additional dictionary elements + assert not docker_swarm_service.has_list_changed( + [ + {"src": 1, "dst": 2}, + {"src": 1, "dst": 2, "protocol": "udp"}, + ], + [ + {"src": 1, "dst": 2, "protocol": "tcp"}, + {"src": 1, "dst": 2, "protocol": "udp"}, + ], + sort_key='dst' + ) + assert not docker_swarm_service.has_list_changed( + [ + {"src": 1, "dst": 2, "protocol": "udp"}, + {"src": 1, "dst": 3, "protocol": "tcp"}, + ], + [ + {"src": 1, "dst": 2, "protocol": "udp"}, + {"src": 1, "dst": 3, "protocol": "tcp"}, + ], + sort_key='dst' + ) + assert docker_swarm_service.has_list_changed( + [ + {"src": 1, "dst": 2, "protocol": "udp"}, + {"src": 1, "dst": 2}, + {"src": 3, "dst": 4}, + ], + [ + {"src": 1, "dst": 3, "protocol": "udp"}, + {"src": 1, "dst": 2, "protocol": "tcp"}, + {"src": 3, "dst": 4, "protocol": "tcp"}, + ], + sort_key='dst' + ) + assert docker_swarm_service.has_list_changed( + [ + {"src": 1, "dst": 3, "protocol": "tcp"}, + {"src": 1, "dst": 2, "protocol": "udp"}, + ], + [ + {"src": 1, "dst": 2, "protocol": "tcp"}, + {"src": 1, "dst": 2, "protocol": "udp"}, + ], + sort_key='dst' + ) + assert docker_swarm_service.has_list_changed( + [ + {"src": 1, "dst": 2, "protocol": "udp"}, + {"src": 1, "dst": 2, "protocol": "tcp", "extra": {"test": "foo"}}, + ], + [ + {"src": 1, "dst": 2, "protocol": "udp"}, + {"src": 1, "dst": 2, "protocol": "tcp"}, + ], + sort_key='dst' + ) + assert not docker_swarm_service.has_list_changed( + [{'id': '123', 'aliases': []}], + [{'id': '123'}], + sort_key='id' + ) + + +def test_have_networks_changed(docker_swarm_service): + assert not docker_swarm_service.have_networks_changed( + None, + None + ) + + assert not docker_swarm_service.have_networks_changed( + [], + None + ) + + assert not docker_swarm_service.have_networks_changed( + [{'id': 1}], + [{'id': 1}] + ) + + assert docker_swarm_service.have_networks_changed( + [{'id': 1}], + [{'id': 1}, {'id': 2}] + ) + + assert not docker_swarm_service.have_networks_changed( + [{'id': 1}, {'id': 2}], + [{'id': 1}, {'id': 2}] + ) + + assert not docker_swarm_service.have_networks_changed( + [{'id': 1}, {'id': 2}], + [{'id': 2}, {'id': 1}] + ) + + assert not docker_swarm_service.have_networks_changed( + [ + {'id': 1}, + {'id': 2, 'aliases': []} + ], + [ + {'id': 1}, + {'id': 2} + ] + ) + + assert docker_swarm_service.have_networks_changed( + [ + {'id': 1}, + {'id': 2, 'aliases': ['alias1']} + ], + [ + {'id': 1}, + {'id': 2} + ] + ) + + assert docker_swarm_service.have_networks_changed( + [ + {'id': 1}, + {'id': 2, 'aliases': ['alias1', 'alias2']} + ], + [ + {'id': 1}, + {'id': 2, 'aliases': ['alias1']} + ] + ) + + assert not docker_swarm_service.have_networks_changed( + [ + {'id': 1}, + {'id': 2, 'aliases': ['alias1', 'alias2']} + ], + [ + {'id': 1}, + {'id': 2, 'aliases': ['alias1', 'alias2']} + ] + ) + + assert not docker_swarm_service.have_networks_changed( + [ + {'id': 1}, + {'id': 2, 'aliases': ['alias1', 'alias2']} + ], + [ + {'id': 1}, + {'id': 2, 'aliases': ['alias2', 'alias1']} + ] + ) + + assert not docker_swarm_service.have_networks_changed( + [ + {'id': 1, 'options': {}}, + {'id': 2, 'aliases': ['alias1', 'alias2']}], + [ + {'id': 1}, + {'id': 2, 'aliases': ['alias2', 'alias1']} + ] + ) + + assert not docker_swarm_service.have_networks_changed( + [ + {'id': 1, 'options': {'option1': 'value1'}}, + {'id': 2, 'aliases': ['alias1', 'alias2']}], + [ + {'id': 1, 'options': {'option1': 'value1'}}, + {'id': 2, 'aliases': ['alias2', 'alias1']} + ] + ) + + assert docker_swarm_service.have_networks_changed( + [ + {'id': 1, 'options': {'option1': 'value1'}}, + {'id': 2, 'aliases': ['alias1', 'alias2']}], + [ + {'id': 1, 'options': {'option1': 'value2'}}, + {'id': 2, 'aliases': ['alias2', 'alias1']} + ] + ) + + +def test_get_docker_networks(docker_swarm_service): + network_names = [ + 'network_1', + 'network_2', + 'network_3', + 'network_4', + ] + networks = [ + network_names[0], + {'name': network_names[1]}, + {'name': network_names[2], 'aliases': ['networkalias1']}, + {'name': network_names[3], 'aliases': ['networkalias2'], 'options': {'foo': 'bar'}}, + ] + network_ids = { + network_names[0]: '1', + network_names[1]: '2', + network_names[2]: '3', + network_names[3]: '4', + } + parsed_networks = docker_swarm_service.get_docker_networks( + networks, + network_ids + ) + assert len(parsed_networks) == 4 + for i, network in enumerate(parsed_networks): + assert 'name' not in network + assert 'id' in network + expected_name = network_names[i] + assert network['id'] == network_ids[expected_name] + if i == 2: + assert network['aliases'] == ['networkalias1'] + if i == 3: + assert network['aliases'] == ['networkalias2'] + if i == 3: + assert 'foo' in network['options'] + # Test missing name + with pytest.raises(TypeError): + docker_swarm_service.get_docker_networks([{'invalid': 'err'}], {'err': 1}) + # test for invalid aliases type + with pytest.raises(TypeError): + docker_swarm_service.get_docker_networks( + [{'name': 'test', 'aliases': 1}], + {'test': 1} + ) + # Test invalid aliases elements + with pytest.raises(TypeError): + docker_swarm_service.get_docker_networks( + [{'name': 'test', 'aliases': [1]}], + {'test': 1} + ) + # Test for invalid options type + with pytest.raises(TypeError): + docker_swarm_service.get_docker_networks( + [{'name': 'test', 'options': 1}], + {'test': 1} + ) + # Test for invalid networks type + with pytest.raises(TypeError): + docker_swarm_service.get_docker_networks( + 1, + {'test': 1} + ) + # Test for non existing networks + with pytest.raises(ValueError): + docker_swarm_service.get_docker_networks( + [{'name': 'idontexist'}], + {'test': 1} + ) + # Test empty values + assert docker_swarm_service.get_docker_networks([], {}) == [] + assert docker_swarm_service.get_docker_networks(None, {}) is None + # Test invalid options + with pytest.raises(TypeError): + docker_swarm_service.get_docker_networks( + [{'name': 'test', 'nonexisting_option': 'foo'}], + {'test': '1'} + ) diff --git a/tests/unit/plugins/modules/test_docker_volume.py b/tests/unit/plugins/modules/test_docker_volume.py new file mode 100644 index 00000000..6bce3f38 --- /dev/null +++ b/tests/unit/plugins/modules/test_docker_volume.py @@ -0,0 +1,36 @@ +# Copyright (c) 2018 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import json + +import pytest + +from ansible_collections.community.docker.plugins.modules import docker_volume +from ansible_collections.community.docker.plugins.module_utils import common + +pytestmark = pytest.mark.usefixtures('patch_ansible_module') + +TESTCASE_DOCKER_VOLUME = [ + { + 'name': 'daemon_config', + 'state': 'present' + } +] + + +@pytest.mark.parametrize('patch_ansible_module', TESTCASE_DOCKER_VOLUME, indirect=['patch_ansible_module']) +def test_create_volume_on_invalid_docker_version(mocker, capfd): + mocker.patch.object(common, 'HAS_DOCKER_PY', True) + mocker.patch.object(common, 'docker_version', '1.8.0') + + with pytest.raises(SystemExit): + docker_volume.main() + + out, dummy = capfd.readouterr() + results = json.loads(out) + assert results['failed'] + assert 'Error: Docker SDK for Python version is 1.8.0 ' in results['msg'] + assert 'Minimum version required is 1.10.0.' in results['msg']