diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 16:03:42 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 16:03:42 +0000 |
commit | 66cec45960ce1d9c794e9399de15c138acb18aed (patch) | |
tree | 59cd19d69e9d56b7989b080da7c20ef1a3fe2a5a /ansible_collections/community/vmware/tests/unit | |
parent | Initial commit. (diff) | |
download | ansible-upstream.tar.xz ansible-upstream.zip |
Adding upstream version 7.3.0+dfsg.upstream/7.3.0+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ansible_collections/community/vmware/tests/unit')
22 files changed, 1560 insertions, 0 deletions
diff --git a/ansible_collections/community/vmware/tests/unit/__init__.py b/ansible_collections/community/vmware/tests/unit/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/ansible_collections/community/vmware/tests/unit/__init__.py diff --git a/ansible_collections/community/vmware/tests/unit/compat/__init__.py b/ansible_collections/community/vmware/tests/unit/compat/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/ansible_collections/community/vmware/tests/unit/compat/__init__.py diff --git a/ansible_collections/community/vmware/tests/unit/compat/mock.py b/ansible_collections/community/vmware/tests/unit/compat/mock.py new file mode 100644 index 00000000..53b6048d --- /dev/null +++ b/ansible_collections/community/vmware/tests/unit/compat/mock.py @@ -0,0 +1,122 @@ +# (c) 2014, Toshio Kuratomi <tkuratomi@ansible.com> +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +''' +Compat module for Python3.x's unittest.mock module +''' +import sys +import _io + +# Python 2.7 + +# Note: Could use the pypi mock library on python3.x as well as python2.x. It +# is the same as the python3 stdlib mock library + +try: + # Allow wildcard import because we really do want to import all of mock's + # symbols into this compat shim + # pylint: disable=wildcard-import,unused-wildcard-import + from unittest.mock import * +except ImportError: + # Python 2 + # pylint: disable=wildcard-import,unused-wildcard-import + try: + from mock import * + except ImportError: + print('You need the mock library installed on python2.x to run tests') + + +# Prior to 3.4.4, mock_open cannot handle binary read_data +if sys.version_info >= (3,) and sys.version_info < (3, 4, 4): + file_spec = None + + def _iterate_read_data(read_data): + # Helper for mock_open: + # Retrieve lines from read_data via a generator so that separate calls to + # readline, read, and readlines are properly interleaved + sep = b'\n' if isinstance(read_data, bytes) else '\n' + data_as_list = [l + sep for l in read_data.split(sep)] + + if data_as_list[-1] == sep: + # If the last line ended in a newline, the list comprehension will have an + # extra entry that's just a newline. Remove this. + data_as_list = data_as_list[:-1] + else: + # If there wasn't an extra newline by itself, then the file being + # emulated doesn't have a newline to end the last line remove the + # newline that our naive format() added + data_as_list[-1] = data_as_list[-1][:-1] + + for line in data_as_list: + yield line + + def mock_open(mock=None, read_data=''): + """ + A helper function to create a mock to replace the use of `open`. It works + for `open` called directly or used as a context manager. + + The `mock` argument is the mock object to configure. If `None` (the + default) then a `MagicMock` will be created for you, with the API limited + to methods or attributes available on standard file handles. + + `read_data` is a string for the `read` methoddline`, and `readlines` of the + file handle to return. This is an empty string by default. + """ + def _readlines_side_effect(*args, **kwargs): + if handle.readlines.return_value is not None: + return handle.readlines.return_value + return list(_data) + + def _read_side_effect(*args, **kwargs): + if handle.read.return_value is not None: + return handle.read.return_value + return type(read_data)().join(_data) + + def _readline_side_effect(): + if handle.readline.return_value is not None: + while True: + yield handle.readline.return_value + for line in _data: + yield line + + global file_spec + if file_spec is None: + file_spec = list(set(dir(_io.TextIOWrapper)).union(set(dir(_io.BytesIO)))) + + if mock is None: + mock = MagicMock(name='open', spec=open) + + handle = MagicMock(spec=file_spec) + handle.__enter__.return_value = handle + + _data = _iterate_read_data(read_data) + + handle.write.return_value = None + handle.read.return_value = None + handle.readline.return_value = None + handle.readlines.return_value = None + + handle.read.side_effect = _read_side_effect + handle.readline.side_effect = _readline_side_effect() + handle.readlines.side_effect = _readlines_side_effect + + mock.return_value = handle + return mock diff --git a/ansible_collections/community/vmware/tests/unit/compat/unittest.py b/ansible_collections/community/vmware/tests/unit/compat/unittest.py new file mode 100644 index 00000000..98f08ad6 --- /dev/null +++ b/ansible_collections/community/vmware/tests/unit/compat/unittest.py @@ -0,0 +1,38 @@ +# (c) 2014, Toshio Kuratomi <tkuratomi@ansible.com> +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +''' +Compat module for Python2.7's unittest module +''' + +import sys + +# Allow wildcard import because we really do want to import all of +# unittests's symbols into this compat shim +# pylint: disable=wildcard-import,unused-wildcard-import +if sys.version_info < (2, 7): + try: + # Need unittest2 on python2.6 + from unittest2 import * + except ImportError: + print('You need unittest2 installed on python2.6.x to run tests') +else: + from unittest import * diff --git a/ansible_collections/community/vmware/tests/unit/mock/__init__.py b/ansible_collections/community/vmware/tests/unit/mock/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/ansible_collections/community/vmware/tests/unit/mock/__init__.py diff --git a/ansible_collections/community/vmware/tests/unit/mock/loader.py b/ansible_collections/community/vmware/tests/unit/mock/loader.py new file mode 100644 index 00000000..e5dff78c --- /dev/null +++ b/ansible_collections/community/vmware/tests/unit/mock/loader.py @@ -0,0 +1,116 @@ +# (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com> +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os + +from ansible.errors import AnsibleParserError +from ansible.parsing.dataloader import DataLoader +from ansible.module_utils._text import to_bytes, to_text + + +class DictDataLoader(DataLoader): + + def __init__(self, file_mapping=None): + file_mapping = {} if file_mapping is None else file_mapping + assert type(file_mapping) == dict + + super(DictDataLoader, self).__init__() + + self._file_mapping = file_mapping + self._build_known_directories() + self._vault_secrets = None + + def load_from_file(self, path, cache=True, unsafe=False): + path = to_text(path) + if path in self._file_mapping: + return self.load(self._file_mapping[path], path) + return None + + # TODO: the real _get_file_contents returns a bytestring, so we actually convert the + # unicode/text it's created with to utf-8 + def _get_file_contents(self, file_name): + path = to_text(file_name) + if path in self._file_mapping: + return (to_bytes(self._file_mapping[path]), False) + else: + raise AnsibleParserError("file not found: %s" % path) + + def path_exists(self, path): + path = to_text(path) + return path in self._file_mapping or path in self._known_directories + + def is_file(self, path): + path = to_text(path) + return path in self._file_mapping + + def is_directory(self, path): + path = to_text(path) + return path in self._known_directories + + def list_directory(self, path): + ret = [] + path = to_text(path) + for x in (list(self._file_mapping.keys()) + self._known_directories): + if x.startswith(path): + if os.path.dirname(x) == path: + ret.append(os.path.basename(x)) + return ret + + def is_executable(self, path): + # FIXME: figure out a way to make paths return true for this + return False + + def _add_known_directory(self, directory): + if directory not in self._known_directories: + self._known_directories.append(directory) + + def _build_known_directories(self): + self._known_directories = [] + for path in self._file_mapping: + dirname = os.path.dirname(path) + while dirname not in ('/', ''): + self._add_known_directory(dirname) + dirname = os.path.dirname(dirname) + + def push(self, path, content): + rebuild_dirs = False + if path not in self._file_mapping: + rebuild_dirs = True + + self._file_mapping[path] = content + + if rebuild_dirs: + self._build_known_directories() + + def pop(self, path): + if path in self._file_mapping: + del self._file_mapping[path] + self._build_known_directories() + + def clear(self): + self._file_mapping = dict() + self._known_directories = [] + + def get_basedir(self): + return os.getcwd() + + def set_vault_secrets(self, vault_secrets): + self._vault_secrets = vault_secrets diff --git a/ansible_collections/community/vmware/tests/unit/mock/path.py b/ansible_collections/community/vmware/tests/unit/mock/path.py new file mode 100644 index 00000000..37959e5c --- /dev/null +++ b/ansible_collections/community/vmware/tests/unit/mock/path.py @@ -0,0 +1,11 @@ +# Copyright: (c) 2017 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 + +from ansible_collections.community.vmware.tests.unit.compat.mock import MagicMock +from ansible.utils.path import unfrackpath + + +mock_unfrackpath_noop = MagicMock(spec_set=unfrackpath, side_effect=lambda x, *args, **kwargs: x) diff --git a/ansible_collections/community/vmware/tests/unit/mock/procenv.py b/ansible_collections/community/vmware/tests/unit/mock/procenv.py new file mode 100644 index 00000000..30f3b2bf --- /dev/null +++ b/ansible_collections/community/vmware/tests/unit/mock/procenv.py @@ -0,0 +1,90 @@ +# (c) 2016, Matt Davis <mdavis@ansible.com> +# (c) 2016, Toshio Kuratomi <tkuratomi@ansible.com> +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see <http://www.gnu.org/licenses/>. + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import sys +import json + +from contextlib import contextmanager +from io import BytesIO, StringIO +from ansible_collections.community.vmware.tests.unit.compat import unittest +from ansible.module_utils.six import PY3 +from ansible.module_utils._text import to_bytes + + +@contextmanager +def swap_stdin_and_argv(stdin_data='', argv_data=tuple()): + """ + context manager that temporarily masks the test runner's values for stdin and argv + """ + real_stdin = sys.stdin + real_argv = sys.argv + + if PY3: + fake_stream = StringIO(stdin_data) + fake_stream.buffer = BytesIO(to_bytes(stdin_data)) + else: + fake_stream = BytesIO(to_bytes(stdin_data)) + + try: + sys.stdin = fake_stream + sys.argv = argv_data + + yield + finally: + sys.stdin = real_stdin + sys.argv = real_argv + + +@contextmanager +def swap_stdout(): + """ + context manager that temporarily replaces stdout for tests that need to verify output + """ + old_stdout = sys.stdout + + if PY3: + fake_stream = StringIO() + else: + fake_stream = BytesIO() + + try: + sys.stdout = fake_stream + + yield fake_stream + finally: + sys.stdout = old_stdout + + +class ModuleTestCase(unittest.TestCase): + def setUp(self, module_args=None): + if module_args is None: + module_args = {'_ansible_remote_tmp': '/tmp', '_ansible_keep_remote_files': False} + + args = json.dumps(dict(ANSIBLE_MODULE_ARGS=module_args)) + + # unittest doesn't have a clean place to use a context manager, so we have to enter/exit manually + self.stdin_swap = swap_stdin_and_argv(stdin_data=args) + self.stdin_swap.__enter__() + + def tearDown(self): + # unittest doesn't have a clean place to use a context manager, so we have to enter/exit manually + self.stdin_swap.__exit__(None, None, None) diff --git a/ansible_collections/community/vmware/tests/unit/mock/vault_helper.py b/ansible_collections/community/vmware/tests/unit/mock/vault_helper.py new file mode 100644 index 00000000..10afb639 --- /dev/null +++ b/ansible_collections/community/vmware/tests/unit/mock/vault_helper.py @@ -0,0 +1,28 @@ +# Copyright: (c) 2017 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 + +from ansible.module_utils._text import to_bytes + +from ansible.parsing.vault import VaultSecret + + +class TextVaultSecret(VaultSecret): + '''A secret piece of text. ie, a password. Tracks text encoding. + + The text encoding of the text may not be the default text encoding so + we keep track of the encoding so we encode it to the same bytes.''' + + def __init__(self, text, encoding=None, errors=None, _bytes=None): + super(TextVaultSecret, self).__init__() + self.text = text + self.encoding = encoding or 'utf-8' + self._bytes = _bytes + self.errors = errors or 'strict' + + @property + def bytes(self): + '''The text encoded with encoding, unless we specifically set _bytes.''' + return self._bytes or to_bytes(self.text, encoding=self.encoding, errors=self.errors) diff --git a/ansible_collections/community/vmware/tests/unit/mock/yaml_helper.py b/ansible_collections/community/vmware/tests/unit/mock/yaml_helper.py new file mode 100644 index 00000000..7250393b --- /dev/null +++ b/ansible_collections/community/vmware/tests/unit/mock/yaml_helper.py @@ -0,0 +1,127 @@ +# Copyright: (c) 2017 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 io +import yaml + +from ansible.module_utils.six import PY3 +from ansible.parsing.yaml.loader import AnsibleLoader +from ansible.parsing.yaml.dumper import AnsibleDumper + + +class YamlTestUtils(object): + """Mixin class to combine with a unittest.TestCase subclass.""" + def _loader(self, stream): + """Vault related tests will want to override this. + + Vault cases should setup a AnsibleLoader that has the vault password.""" + return AnsibleLoader(stream) + + def _dump_stream(self, obj, stream, dumper=None): + """Dump to a py2-unicode or py3-string stream.""" + if PY3: + return yaml.dump(obj, stream, Dumper=dumper) + else: + return yaml.dump(obj, stream, Dumper=dumper, encoding=None) + + def _dump_string(self, obj, dumper=None): + """Dump to a py2-unicode or py3-string""" + if PY3: + return yaml.dump(obj, Dumper=dumper) + else: + return yaml.dump(obj, Dumper=dumper, encoding=None) + + def _dump_load_cycle(self, obj): + # Each pass though a dump or load revs the 'generation' + # obj to yaml string + string_from_object_dump = self._dump_string(obj, dumper=AnsibleDumper) + + # wrap a stream/file like StringIO around that yaml + stream_from_object_dump = io.StringIO(string_from_object_dump) + loader = self._loader(stream_from_object_dump) + # load the yaml stream to create a new instance of the object (gen 2) + obj_2 = loader.get_data() + + # dump the gen 2 objects directory to strings + string_from_object_dump_2 = self._dump_string(obj_2, + dumper=AnsibleDumper) + + # The gen 1 and gen 2 yaml strings + self.assertEqual(string_from_object_dump, string_from_object_dump_2) + # the gen 1 (orig) and gen 2 py object + self.assertEqual(obj, obj_2) + + # again! gen 3... load strings into py objects + stream_3 = io.StringIO(string_from_object_dump_2) + loader_3 = self._loader(stream_3) + obj_3 = loader_3.get_data() + + string_from_object_dump_3 = self._dump_string(obj_3, dumper=AnsibleDumper) + + self.assertEqual(obj, obj_3) + # should be transitive, but... + self.assertEqual(obj_2, obj_3) + self.assertEqual(string_from_object_dump, string_from_object_dump_3) + + def _old_dump_load_cycle(self, obj): + '''Dump the passed in object to yaml, load it back up, dump again, compare.''' + stream = io.StringIO() + + yaml_string = self._dump_string(obj, dumper=AnsibleDumper) + self._dump_stream(obj, stream, dumper=AnsibleDumper) + + yaml_string_from_stream = stream.getvalue() + + # reset stream + stream.seek(0) + + loader = self._loader(stream) + # loader = AnsibleLoader(stream, vault_password=self.vault_password) + obj_from_stream = loader.get_data() + + stream_from_string = io.StringIO(yaml_string) + loader2 = self._loader(stream_from_string) + # loader2 = AnsibleLoader(stream_from_string, vault_password=self.vault_password) + obj_from_string = loader2.get_data() + + stream_obj_from_stream = io.StringIO() + stream_obj_from_string = io.StringIO() + + if PY3: + yaml.dump(obj_from_stream, stream_obj_from_stream, Dumper=AnsibleDumper) + yaml.dump(obj_from_stream, stream_obj_from_string, Dumper=AnsibleDumper) + else: + yaml.dump(obj_from_stream, stream_obj_from_stream, Dumper=AnsibleDumper, encoding=None) + yaml.dump(obj_from_stream, stream_obj_from_string, Dumper=AnsibleDumper, encoding=None) + + yaml_string_stream_obj_from_stream = stream_obj_from_stream.getvalue() + yaml_string_stream_obj_from_string = stream_obj_from_string.getvalue() + + stream_obj_from_stream.seek(0) + stream_obj_from_string.seek(0) + + if PY3: + yaml_string_obj_from_stream = yaml.dump(obj_from_stream, Dumper=AnsibleDumper) + yaml_string_obj_from_string = yaml.dump(obj_from_string, Dumper=AnsibleDumper) + else: + yaml_string_obj_from_stream = yaml.dump(obj_from_stream, Dumper=AnsibleDumper, encoding=None) + yaml_string_obj_from_string = yaml.dump(obj_from_string, Dumper=AnsibleDumper, encoding=None) + + assert yaml_string == yaml_string_obj_from_stream + assert yaml_string == yaml_string_obj_from_stream == yaml_string_obj_from_string + assert (yaml_string == yaml_string_obj_from_stream == yaml_string_obj_from_string == yaml_string_stream_obj_from_stream == + yaml_string_stream_obj_from_string) + assert obj == obj_from_stream + assert obj == obj_from_string + assert obj == yaml_string_obj_from_stream + assert obj == yaml_string_obj_from_string + assert obj == obj_from_stream == obj_from_string == yaml_string_obj_from_stream == yaml_string_obj_from_string + return {'obj': obj, + 'yaml_string': yaml_string, + 'yaml_string_from_stream': yaml_string_from_stream, + 'obj_from_stream': obj_from_stream, + 'obj_from_string': obj_from_string, + 'yaml_string_obj_from_string': yaml_string_obj_from_string} diff --git a/ansible_collections/community/vmware/tests/unit/module_utils/__init__.py b/ansible_collections/community/vmware/tests/unit/module_utils/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/ansible_collections/community/vmware/tests/unit/module_utils/__init__.py diff --git a/ansible_collections/community/vmware/tests/unit/module_utils/conftest.py b/ansible_collections/community/vmware/tests/unit/module_utils/conftest.py new file mode 100644 index 00000000..89aecf85 --- /dev/null +++ b/ansible_collections/community/vmware/tests/unit/module_utils/conftest.py @@ -0,0 +1,72 @@ +# Copyright (c) 2017 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 sys +from io import BytesIO + +import pytest + +import ansible.module_utils.basic +from ansible.module_utils.six import PY3, string_types +from ansible.module_utils._text import to_bytes +from ansible.module_utils.common._collections_compat import MutableMapping + + +@pytest.fixture +def stdin(mocker, request): + old_args = ansible.module_utils.basic._ANSIBLE_ARGS + ansible.module_utils.basic._ANSIBLE_ARGS = None + old_argv = sys.argv + sys.argv = ['ansible_unittest'] + + if isinstance(request.param, string_types): + args = request.param + elif isinstance(request.param, MutableMapping): + if 'ANSIBLE_MODULE_ARGS' not in request.param: + request.param = {'ANSIBLE_MODULE_ARGS': request.param} + if '_ansible_remote_tmp' not in request.param['ANSIBLE_MODULE_ARGS']: + request.param['ANSIBLE_MODULE_ARGS']['_ansible_remote_tmp'] = '/tmp' + if '_ansible_keep_remote_files' not in request.param['ANSIBLE_MODULE_ARGS']: + request.param['ANSIBLE_MODULE_ARGS']['_ansible_keep_remote_files'] = False + args = json.dumps(request.param) + else: + raise Exception('Malformed data to the stdin pytest fixture') + + fake_stdin = BytesIO(to_bytes(args, errors='surrogate_or_strict')) + if PY3: + mocker.patch('ansible.module_utils.basic.sys.stdin', mocker.MagicMock()) + mocker.patch('ansible.module_utils.basic.sys.stdin.buffer', fake_stdin) + else: + mocker.patch('ansible.module_utils.basic.sys.stdin', fake_stdin) + + yield fake_stdin + + ansible.module_utils.basic._ANSIBLE_ARGS = old_args + sys.argv = old_argv + + +@pytest.fixture +def am(stdin, request): + old_args = ansible.module_utils.basic._ANSIBLE_ARGS + ansible.module_utils.basic._ANSIBLE_ARGS = None + old_argv = sys.argv + sys.argv = ['ansible_unittest'] + + argspec = {} + if hasattr(request, 'param'): + if isinstance(request.param, dict): + argspec = request.param + + am = ansible.module_utils.basic.AnsibleModule( + argument_spec=argspec, + ) + am._name = 'ansible_unittest' + + yield am + + ansible.module_utils.basic._ANSIBLE_ARGS = old_args + sys.argv = old_argv diff --git a/ansible_collections/community/vmware/tests/unit/module_utils/test_vmware.py b/ansible_collections/community/vmware/tests/unit/module_utils/test_vmware.py new file mode 100644 index 00000000..11483d34 --- /dev/null +++ b/ansible_collections/community/vmware/tests/unit/module_utils/test_vmware.py @@ -0,0 +1,244 @@ +# -*- coding: utf-8 -*- +# Copyright: (c) 2018, Ansible Project +# Copyright: (c) 2018, Abhijeet Kasurde <akasurde@redhat.com> +# 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 ssl +import sys +import pytest + +pyvmomi = pytest.importorskip('pyVmomi') + +from ansible_collections.community.vmware.tests.unit.compat import mock +from ansible_collections.community.vmware.plugins.module_utils.vmware import option_diff + +import ansible_collections.community.vmware.plugins.module_utils.vmware as vmware_module_utils + + +test_data = [ + ( + dict( + username='Administrator@vsphere.local', + password='Esxi@123$%', + hostname=False, + validate_certs=False, + ), + "Hostname parameter is missing. Please specify this parameter in task or" + " export environment variable like 'export VMWARE_HOST=ESXI_HOSTNAME'" + ), + ( + dict( + username=False, + password='Esxi@123$%', + hostname='esxi1', + validate_certs=False, + ), + "Username parameter is missing. Please specify this parameter in task or" + " export environment variable like 'export VMWARE_USER=ESXI_USERNAME'" + ), + ( + dict( + username='Administrator@vsphere.local', + password=False, + hostname='esxi1', + validate_certs=False, + ), + "Password parameter is missing. Please specify this parameter in task or" + " export environment variable like 'export VMWARE_PASSWORD=ESXI_PASSWORD'" + ), + ( + dict( + username='Administrator@vsphere.local', + password='Esxi@123$%', + hostname='esxi1', + validate_certs=True, + ), + "certificate verify failed" + ), + ( + dict( + username='Administrator@vsphere.local', + password='Esxi@123$%', + hostname='esxi1', + proxy_host='myproxyserver.com', + proxy_port=80, + validate_certs=False, + ), + " [proxy: myproxyserver.com:80]" + ), +] + +test_ids = [ + 'hostname', + 'username', + 'password', + 'validate_certs', + 'valid_http_proxy', +] + + +class FailJsonException(BaseException): + pass + + +@pytest.fixture +def fake_ansible_module(): + ret = mock.Mock() + ret.params = test_data[3][0] + ret.tmpdir = None + ret.fail_json.side_effect = FailJsonException() + return ret + + +def fake_connect_to_api(module, return_si=None): + return None, mock.Mock() + + +testdata = [ + ('HAS_PYVMOMI', 'PyVmomi'), + ('HAS_REQUESTS', 'requests'), +] + + +@pytest.mark.parametrize("key,libname", testdata) +def test_lib_loading_failure(monkeypatch, fake_ansible_module, key, libname): + """ Test if Pyvmomi is present or not""" + monkeypatch.setattr(vmware_module_utils, key, False) + with pytest.raises(FailJsonException): + vmware_module_utils.PyVmomi(fake_ansible_module) + error_str = 'Failed to import the required Python library (%s)' % libname + assert fake_ansible_module.fail_json.called_once() + assert error_str in fake_ansible_module.fail_json.call_args[1]['msg'] + + +@pytest.mark.skipif(sys.version_info < (2, 7), reason="requires python2.7 and greater") +@pytest.mark.parametrize("params, msg", test_data, ids=test_ids) +def test_required_params(request, params, msg, fake_ansible_module): + """ Test if required params are correct or not""" + fake_ansible_module.params = params + with pytest.raises(FailJsonException): + vmware_module_utils.connect_to_api(fake_ansible_module) + assert fake_ansible_module.fail_json.called_once() + # TODO: assert msg in fake_ansible_module.fail_json.call_args[1]['msg'] + + +def test_validate_certs(monkeypatch, fake_ansible_module): + """ Test if SSL is required or not""" + fake_ansible_module.params = test_data[3][0] + + monkeypatch.setattr(vmware_module_utils, 'ssl', mock.Mock()) + del vmware_module_utils.ssl.SSLContext + with pytest.raises(FailJsonException): + vmware_module_utils.PyVmomi(fake_ansible_module) + msg = 'pyVim does not support changing verification mode with python < 2.7.9.' \ + ' Either update python or use validate_certs=false.' + assert fake_ansible_module.fail_json.called_once() + assert msg == fake_ansible_module.fail_json.call_args[1]['msg'] + + +def test_vmdk_disk_path_split(monkeypatch, fake_ansible_module): + """ Test vmdk_disk_path_split function""" + fake_ansible_module.params = test_data[0][0] + + monkeypatch.setattr(vmware_module_utils, 'connect_to_api', fake_connect_to_api) + pyv = vmware_module_utils.PyVmomi(fake_ansible_module) + v = pyv.vmdk_disk_path_split('[ds1] VM_0001/VM0001_0.vmdk') + assert v == ('ds1', 'VM_0001/VM0001_0.vmdk', 'VM0001_0.vmdk', 'VM_0001') + + +def test_vmdk_disk_path_split_negative(monkeypatch, fake_ansible_module): + """ Test vmdk_disk_path_split function""" + fake_ansible_module.params = test_data[0][0] + + monkeypatch.setattr(vmware_module_utils, 'connect_to_api', fake_connect_to_api) + with pytest.raises(FailJsonException): + pyv = vmware_module_utils.PyVmomi(fake_ansible_module) + pyv.vmdk_disk_path_split('[ds1]') + assert fake_ansible_module.fail_json.called_once() + assert 'Bad path' in fake_ansible_module.fail_json.call_args[1]['msg'] + + +@pytest.mark.skipif(sys.version_info < (2, 7), reason="requires python2.7 and greater") +def test_connect_to_api_validate_certs(monkeypatch, fake_ansible_module): + monkeypatch.setattr(vmware_module_utils, 'connect', mock.Mock()) + + def MockSSLContext(proto): + ssl_context.proto = proto + return ssl_context + + # New Python with SSLContext + validate_certs=True + vmware_module_utils.connect.reset_mock() + ssl_context = mock.Mock() + monkeypatch.setattr(vmware_module_utils.ssl, 'SSLContext', MockSSLContext) + fake_ansible_module.params['validate_certs'] = True + vmware_module_utils.connect_to_api(fake_ansible_module) + assert ssl_context.proto == ssl.PROTOCOL_SSLv23 + assert ssl_context.verify_mode == ssl.CERT_REQUIRED + assert ssl_context.check_hostname is True + vmware_module_utils.connect.SmartConnect.assert_called_once_with( + host='esxi1', + port=443, + pwd='Esxi@123$%', + user='Administrator@vsphere.local', + sslContext=ssl_context) + + # New Python with SSLContext + validate_certs=False + vmware_module_utils.connect.reset_mock() + ssl_context = mock.Mock() + monkeypatch.setattr(vmware_module_utils.ssl, 'SSLContext', MockSSLContext) + fake_ansible_module.params['validate_certs'] = False + vmware_module_utils.connect_to_api(fake_ansible_module) + assert ssl_context.proto == ssl.PROTOCOL_SSLv23 + assert ssl_context.verify_mode == ssl.CERT_NONE + assert ssl_context.check_hostname is False + vmware_module_utils.connect.SmartConnect.assert_called_once_with( + host='esxi1', + port=443, + pwd='Esxi@123$%', + user='Administrator@vsphere.local', + sslContext=ssl_context) + + # Old Python with no SSLContext + validate_certs=True + vmware_module_utils.connect.reset_mock() + ssl_context = mock.Mock() + ssl_context.proto = None + monkeypatch.delattr(vmware_module_utils.ssl, 'SSLContext') + fake_ansible_module.params['validate_certs'] = True + with pytest.raises(FailJsonException): + vmware_module_utils.connect_to_api(fake_ansible_module) + assert ssl_context.proto is None + fake_ansible_module.fail_json.assert_called_once_with(msg=( + 'pyVim does not support changing verification mode with python ' + '< 2.7.9. Either update python or use validate_certs=false.')) + assert not vmware_module_utils.connect.SmartConnect.called + + # Old Python with no SSLContext + validate_certs=False + vmware_module_utils.connect.reset_mock() + ssl_context = mock.Mock() + ssl_context.proto = None + monkeypatch.delattr(vmware_module_utils.ssl, 'SSLContext', raising=False) + fake_ansible_module.params['validate_certs'] = False + vmware_module_utils.connect_to_api(fake_ansible_module) + assert ssl_context.proto is None + vmware_module_utils.connect.SmartConnect.assert_called_once_with( + host='esxi1', + port=443, + pwd='Esxi@123$%', + user='Administrator@vsphere.local') + + +@pytest.mark.parametrize("test_options, test_current_options, test_truthy_strings_as_bool", [ + ({"data": True}, [], True), + ({"data": 1}, [], True), + ({"data": 1.2}, [], True), + ({"data": 'string'}, [], True), + ({"data": True}, [], False), + ({"data": 1}, [], False), + ({"data": 1.2}, [], False), + ({"data": 'string'}, [], False), +]) +def test_option_diff(test_options, test_current_options, test_truthy_strings_as_bool): + assert option_diff(test_options, test_current_options, test_truthy_strings_as_bool)[0].value == test_options["data"] diff --git a/ansible_collections/community/vmware/tests/unit/modules/__init__.py b/ansible_collections/community/vmware/tests/unit/modules/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/ansible_collections/community/vmware/tests/unit/modules/__init__.py diff --git a/ansible_collections/community/vmware/tests/unit/modules/cloud/__init__.py b/ansible_collections/community/vmware/tests/unit/modules/cloud/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/ansible_collections/community/vmware/tests/unit/modules/cloud/__init__.py diff --git a/ansible_collections/community/vmware/tests/unit/modules/cloud/vmware/__init__.py b/ansible_collections/community/vmware/tests/unit/modules/cloud/vmware/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/ansible_collections/community/vmware/tests/unit/modules/cloud/vmware/__init__.py diff --git a/ansible_collections/community/vmware/tests/unit/modules/cloud/vmware/test_data/test_vmware_guest_with_parameters.json b/ansible_collections/community/vmware/tests/unit/modules/cloud/vmware/test_data/test_vmware_guest_with_parameters.json new file mode 100644 index 00000000..ce5e27c7 --- /dev/null +++ b/ansible_collections/community/vmware/tests/unit/modules/cloud/vmware/test_data/test_vmware_guest_with_parameters.json @@ -0,0 +1,106 @@ +[ + [ + { + "name": "sample_vm_001", + "hostname": "esxi01.example.com", + "username": "administrator@vsphere.local", + "password": "Secret@123$%", + "validate_certs": "True" + }, + { + "failed": "True", + "msg": "Unknown error while connecting to vCenter or ESXi API" + } + ], + [ + { + "name": "sample_vm_001" + }, + { + "failed": "True", + "msg": "Hostname parameter is missing." + } + ], + [ + { + "uuid": "52f7b088-357e-bb81-59ec-9d9389c7d89e" + }, + { + "failed": "True", + "msg": "Hostname parameter is missing." + } + ], + [ + { + "name": "sample_vm_001", + "hostname": "esxi01.example.com" + }, + { + "failed": "True", + "msg": "Username parameter is missing." + } + ], + [ + { + "name": "sample_vm_001", + "hostname": "esxi01.example.com", + "username": "administrator@vsphere.local" + }, + { + "failed": "True", + "msg": "Password parameter is missing." + } + ], + [ + { + "name": "sample_vm_001", + "hostname": "esxi01.example.com", + "username": "administrator@vsphere.local", + "password": "Secret@123$%" + }, + { + "failed": "True", + "msg": "Unknown error while connecting to vCenter or ESXi API" + } + ], + [ + { + "name": "sample_vm_001", + "hostname": "esxi01.example.com", + "username": "administrator@vsphere.local", + "password": "Secret@123$%", + "validate_certs": "False" + }, + { + "failed": "True", + "msg": "Unknown error while connecting to vCenter or ESXi API" + } + ], + [ + { + "name": "sample_vm_001", + "hostname": "esxi01.example.com", + "username": "administrator@vsphere.local", + "password": "Secret@123$%", + "port": "8443" + }, + { + "failed": "True", + "msg": "8443" + } + ], + [ + { + "name": "sample_vm_001", + "hostname": "esxi01.example.com", + "username": "administrator@vsphere.local", + "password": "Secret@123$%", + "validate_certs": "True" + }, + { + "test_ssl_context": "True", + "failed": "True", + "msg": "pyVim does not support changing verification mode with python < 2.7.9. Either update python or use validate_certs=false." + } + ] +] diff --git a/ansible_collections/community/vmware/tests/unit/modules/cloud/vmware/test_vmware_guest.py b/ansible_collections/community/vmware/tests/unit/modules/cloud/vmware/test_vmware_guest.py new file mode 100644 index 00000000..29f31b68 --- /dev/null +++ b/ansible_collections/community/vmware/tests/unit/modules/cloud/vmware/test_vmware_guest.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2018, Ansible Project +# Copyright: (c) 2018, Abhijeet Kasurde <akasurde@redhat.com> +# 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 os +import sys +import pytest +import json + +pyvmomi = pytest.importorskip('pyVmomi') + +if sys.version_info < (2, 7): + pytestmark = pytest.mark.skip("vmware_guest Ansible modules require Python >= 2.7") + + +from ansible_collections.community.vmware.plugins.modules import vmware_guest + +curr_dir = os.path.dirname(__file__) +test_data_file = open(os.path.join(curr_dir, 'test_data', 'test_vmware_guest_with_parameters.json'), 'r') +TEST_CASES = json.loads(test_data_file.read()) +test_data_file.close() + + +@pytest.mark.parametrize('patch_ansible_module', [{}], indirect=['patch_ansible_module']) +@pytest.mark.usefixtures('patch_ansible_module') +def test_vmware_guest_wo_parameters(capfd): + with pytest.raises(SystemExit): + vmware_guest.main() + out, err = capfd.readouterr() + results = json.loads(out) + assert results['failed'] + assert "one of the following is required: name, uuid" in results['msg'] + + +@pytest.mark.parametrize('patch_ansible_module, testcase', TEST_CASES, indirect=['patch_ansible_module']) +@pytest.mark.usefixtures('patch_ansible_module') +def test_vmware_guest_with_parameters(mocker, capfd, testcase): + if testcase.get('test_ssl_context', None): + class mocked_ssl: + pass + mocker.patch('ansible_collections.community.vmware.plugins.module_utils.vmware.ssl', new=mocked_ssl) + + with pytest.raises(SystemExit): + vmware_guest.main() + out, err = capfd.readouterr() + results = json.loads(out) + assert str(results['failed']) == testcase['failed'] + assert testcase['msg'] in results['msg'] diff --git a/ansible_collections/community/vmware/tests/unit/modules/cloud/vmware/test_vmware_host_sriov.py b/ansible_collections/community/vmware/tests/unit/modules/cloud/vmware/test_vmware_host_sriov.py new file mode 100644 index 00000000..1d2190db --- /dev/null +++ b/ansible_collections/community/vmware/tests/unit/modules/cloud/vmware/test_vmware_host_sriov.py @@ -0,0 +1,469 @@ +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +import json + + +try: + from unittest import mock +except ImportError: + import mock +import unittest + +from ansible.module_utils import basic +from ansible.module_utils._text import to_bytes + +from ansible_collections.community.vmware.plugins.modules import vmware_host_sriov + + +def gen_mock_attrs(user_input): + mock_attrs = dict(user_input) + mock_attrs["results"] = {"before": {}, "after": {}, "changes": {}} + mock_host = mock.Mock() + mock_attrs["hosts"] = [mock_host] + return mock_attrs + + +class AnsibleFailJson(Exception): + """Exception class to be raised by module.fail_json and caught by the test case""" + pass + + +def set_module_args(args): + """prepare arguments so that they will be picked up during module creation""" + args = json.dumps({"ANSIBLE_MODULE_ARGS": args}) + basic._ANSIBLE_ARGS = to_bytes(args) + + +class AnsibleExitJson(Exception): + """Exception class to be raised by module.exit_json and caught by the test case""" + pass + + +def exit_json(*args, **kwargs): + """function to patch over exit_json; package return data into an exception""" + if "changed" not in kwargs: + kwargs["changed"] = False + raise AnsibleExitJson(kwargs) + + +def fail_json(*args, **kwargs): + """function to patch over fail_json; package return data into an exception""" + kwargs["failed"] = True + raise AnsibleFailJson(kwargs) + + +def get_bin_path(self, arg, required=False): + """Mock AnsibleModule.get_bin_path""" + if arg.endswith("my_command"): + return "/usr/bin/my_command" + else: + if required: + fail_json(msg="%r not found !" % arg) + + +data_sanitize_params = [ + # 0. num_virt_func < 0 + { + "user_input": { + "hostname": "test_vCenter", + "esxi_host_name": "test_host", + "sriov_on": True, + "num_virt_func": -4, + "vmnic": "vmnic0", + }, + "before": { + "sriovActive": False, + "sriovEnabled": False, + "maxVirtualFunctionSupported": 63, + "numVirtualFunction": 0, + "numVirtualFunctionRequested": 0, + "rebootRequired": False, + "sriovCapable": True, + }, + "expected": AnsibleFailJson( + {"msg": "allowed value for num_virt_func >= 0", "failed": True} + ), + }, + # 1. num_virt_func == 0, sriov_on == True + { + "user_input": { + "hostname": "test_vCenter", + "esxi_host_name": "test_host", + "sriov_on": True, + "num_virt_func": 0, + "vmnic": "vmnic0", + }, + "before": { + "sriovActive": False, + "sriovEnabled": False, + "maxVirtualFunctionSupported": 63, + "numVirtualFunction": 0, + "numVirtualFunctionRequested": 0, + "rebootRequired": False, + "sriovCapable": True, + }, + "expected": AnsibleFailJson( + { + "msg": "with sriov_on == true, allowed value for num_virt_func > 0", + "failed": True, + } + ), + }, + # 2. num_virt_func > 0, sriov_on == False + { + "user_input": { + "hostname": "test_vCenter", + "esxi_host_name": "test_host", + "sriov_on": False, + "num_virt_func": 5, + "vmnic": "vmnic0", + }, + "before": { + "sriovActive": False, + "sriovEnabled": False, + "maxVirtualFunctionSupported": 63, + "numVirtualFunction": 0, + "numVirtualFunctionRequested": 0, + "rebootRequired": False, + "sriovCapable": True, + }, + "expected": AnsibleFailJson( + { + "msg": "with sriov_on == false, allowed value for num_virt_func is 0", + "failed": True, + } + ), + }, + # 3. input OK + { + "user_input": { + "hostname": "test_vCenter", + "esxi_host_name": "test_host", + "sriov_on": True, + "num_virt_func": 8, + "vmnic": "vmnic0", + }, + "before": { + "sriovActive": False, + "sriovEnabled": False, + "maxVirtualFunctionSupported": 63, + "numVirtualFunction": 0, + "numVirtualFunctionRequested": 0, + "rebootRequired": False, + "sriovCapable": True, + }, + "expected": None, + }, +] + +data_check_compatibility = [ + # 0. not supported sriov + { + "user_input": { + "hostname": "test_vCenter", + "esxi_host_name": "test_host", + "sriov_on": True, + "num_virt_func": 8, + "vmnic": "vmnic0", + }, + "before": { + "sriovActive": False, + "sriovEnabled": False, + "maxVirtualFunctionSupported": 0, + "numVirtualFunction": 0, + "numVirtualFunctionRequested": 0, + "rebootRequired": False, + "sriovCapable": False, + }, + "expected": AnsibleFailJson( + { + "msg": "sriov not supported on host= test_host, nic= vmnic0", + "failed": True, + } + ), + }, + # 1. not supported num_virt_func + { + "user_input": { + "hostname": "test_vCenter", + "esxi_host_name": "test_host", + "sriov_on": True, + "num_virt_func": 8, + "vmnic": "vmnic0", + }, + "before": { + "sriovActive": False, + "sriovEnabled": False, + "maxVirtualFunctionSupported": 4, + "numVirtualFunction": 0, + "numVirtualFunctionRequested": 0, + "rebootRequired": False, + "sriovCapable": True, + }, + "expected": AnsibleFailJson( + {"msg": "maxVirtualFunctionSupported= 4 on vmnic0", "failed": True} + ), + }, + # 2. normal enabling + { + "user_input": { + "hostname": "test_vCenter", + "esxi_host_name": "test_host", + "sriov_on": True, + "num_virt_func": 8, + "vmnic": "vmnic0", + }, + "before": { + "sriovActive": False, + "sriovEnabled": False, + "maxVirtualFunctionSupported": 63, + "numVirtualFunction": 0, + "numVirtualFunctionRequested": 0, + "rebootRequired": False, + "sriovCapable": True, + }, + "expected": None, + }, + # 3. disable sriov + { + "user_input": { + "hostname": "test_vCenter", + "esxi_host_name": "test_host", + "sriov_on": False, + "num_virt_func": 0, + "vmnic": "vmnic0", + }, + "before": { + "sriovActive": True, + "sriovEnabled": True, + "maxVirtualFunctionSupported": 63, + "numVirtualFunction": 8, + "numVirtualFunctionRequested": 8, + "rebootRequired": False, + "sriovCapable": True, + }, + "expected": None, + }, +] + +data_make_diff = [ + # 0. normal enabling + { + "user_input": { + "hostname": "test_vCenter", + "esxi_host_name": "test_host", + "sriov_on": True, + "num_virt_func": 8, + "vmnic": "vmnic0", + }, + "before": { + "sriovActive": False, + "sriovEnabled": False, + "maxVirtualFunctionSupported": 63, + "numVirtualFunction": 0, + "numVirtualFunctionRequested": 0, + "rebootRequired": False, + "sriovCapable": True, + }, + "expected": { + "sriovEnabled": True, + "numVirtualFunction": 8, + "msg": "", + "change": True, + }, + }, + # 1. already enabled, not active + { + "user_input": { + "hostname": "test_vCenter", + "esxi_host_name": "test_host", + "sriov_on": True, + "num_virt_func": 8, + "vmnic": "vmnic0", + }, + "before": { + "sriovActive": False, + "sriovEnabled": True, + "maxVirtualFunctionSupported": 63, + "numVirtualFunction": 0, + "numVirtualFunctionRequested": 8, + "rebootRequired": True, + "sriovCapable": True, + }, + "expected": { + "msg": "Not active (looks like not rebooted) No any changes, already configured ", + "change": False, + }, + }, + # 2. already enabled and active + { + "user_input": { + "hostname": "test_vCenter", + "esxi_host_name": "test_host", + "sriov_on": True, + "num_virt_func": 8, + "vmnic": "vmnic0", + }, + "before": { + "sriovActive": True, + "sriovEnabled": True, + "maxVirtualFunctionSupported": 63, + "numVirtualFunction": 8, + "numVirtualFunctionRequested": 8, + "rebootRequired": False, + "sriovCapable": True, + }, + "expected": {"msg": "No any changes, already configured ", "change": False}, + }, + # 3. already enabled diff in numVirtualFunction + { + "user_input": { + "hostname": "test_vCenter", + "esxi_host_name": "test_host", + "sriov_on": True, + "num_virt_func": 8, + "vmnic": "vmnic0", + }, + "before": { + "sriovActive": True, + "sriovEnabled": True, + "maxVirtualFunctionSupported": 63, + "numVirtualFunction": 4, + "numVirtualFunctionRequested": 4, + "rebootRequired": False, + "sriovCapable": True, + }, + "expected": {"numVirtualFunction": 8, "msg": "", "change": True}, + }, + # 4. disable sriov + { + "user_input": { + "hostname": "test_vCenter", + "esxi_host_name": "test_host", + "sriov_on": False, + "num_virt_func": 0, + "vmnic": "vmnic0", + }, + "before": { + "sriovActive": True, + "sriovEnabled": True, + "maxVirtualFunctionSupported": 63, + "numVirtualFunction": 8, + "numVirtualFunctionRequested": 8, + "rebootRequired": False, + "sriovCapable": True, + }, + "expected": { + "sriovEnabled": False, + "numVirtualFunction": 0, + "msg": "", + "change": True, + }, + }, +] + + +class TestAdapterMethods(unittest.TestCase): + def setUp(self): + pass + + def tearDown(self): + pass + + @mock.patch.object( + vmware_host_sriov.VmwareAdapterConfigManager, "__init__", return_value=None + ) + def test_sanitize_params(self, mock__init__): + for num, case in enumerate(data_sanitize_params): + config = vmware_host_sriov.VmwareAdapterConfigManager() + config.module = mock.Mock() + config.module.check_mode = False + config.module.fail_json.side_effect = fail_json + + config.__dict__.update(gen_mock_attrs(case["user_input"])) + + try: + result = config.sanitize_params() + self.assertIsInstance( + result, type(case["expected"]), "test=" + str(num) + ) + self.assertEqual(result, case["expected"], "test=" + str(num)) + + except Exception as e: + self.assertIsInstance(e, type(case["expected"]), "test=" + str(num)) + self.assertEqual( + e.args[0], case["expected"].args[0], "test=" + str(num) + ) + + @mock.patch.object( + vmware_host_sriov.VmwareAdapterConfigManager, "__init__", return_value=None + ) + def test_check_compatibility(self, mock__init__): + for num, case in enumerate(data_check_compatibility): + config = vmware_host_sriov.VmwareAdapterConfigManager() + config.module = mock.Mock() + config.module.check_mode = False + config.module.fail_json.side_effect = fail_json + + config.__dict__.update(gen_mock_attrs(case["user_input"])) + + try: + result = config.check_compatibility( + case["before"], case["user_input"]["esxi_host_name"] + ) + self.assertIsInstance( + result, type(case["expected"]), "test=" + str(num) + ) + self.assertEqual(result, case["expected"], "test=" + str(num)) + + except Exception as e: + self.assertIsInstance(e, type(case["expected"]), "test=" + str(num)) + self.assertEqual( + e.args[0], case["expected"].args[0], "test=" + str(num) + ) + + for num, case in enumerate(data_check_compatibility): + config = vmware_host_sriov.VmwareAdapterConfigManager() + config.module = mock.Mock() + config.module.check_mode = False + config.module.fail_json.side_effect = fail_json + + config.__dict__.update(gen_mock_attrs(case["user_input"])) + + try: + result = config.check_compatibility( + case["before"], case["user_input"]["esxi_host_name"] + ) + self.assertIsInstance( + result, type(case["expected"]), "test=" + str(num) + ) + self.assertEqual(result, case["expected"], "test=" + str(num)) + + except Exception as e: + self.assertIsInstance(e, type(case["expected"]), "test=" + str(num)) + self.assertEqual( + e.args[0], case["expected"].args[0], "test=" + str(num) + ) + + @mock.patch.object( + vmware_host_sriov.VmwareAdapterConfigManager, "__init__", return_value=None + ) + def test_make_diff(self, mock__init__): + for num, case in enumerate(data_make_diff): + config = vmware_host_sriov.VmwareAdapterConfigManager() + config.module = mock.Mock() + config.module.check_mode = False + config.module.fail_json.side_effect = fail_json + + config.__dict__.update(gen_mock_attrs(case["user_input"])) + + result = config.make_diff( + case["before"], case["user_input"]["esxi_host_name"] + ) + self.assertIsInstance(result, type(case["expected"]), "test=" + str(num)) + self.assertEqual(result, case["expected"], "test=" + str(num)) + + +if __name__ == "__main__": + unittest.main() diff --git a/ansible_collections/community/vmware/tests/unit/modules/conftest.py b/ansible_collections/community/vmware/tests/unit/modules/conftest.py new file mode 100644 index 00000000..d2f7f4b5 --- /dev/null +++ b/ansible_collections/community/vmware/tests/unit/modules/conftest.py @@ -0,0 +1,31 @@ +# Copyright (c) 2017 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.module_utils.six import string_types +from ansible.module_utils._text import to_bytes +from ansible.module_utils.common._collections_compat import MutableMapping + + +@pytest.fixture +def patch_ansible_module(request, mocker): + if isinstance(request.param, string_types): + args = request.param + elif isinstance(request.param, MutableMapping): + if 'ANSIBLE_MODULE_ARGS' not in request.param: + request.param = {'ANSIBLE_MODULE_ARGS': request.param} + if '_ansible_remote_tmp' not in request.param['ANSIBLE_MODULE_ARGS']: + request.param['ANSIBLE_MODULE_ARGS']['_ansible_remote_tmp'] = '/tmp' + if '_ansible_keep_remote_files' not in request.param['ANSIBLE_MODULE_ARGS']: + request.param['ANSIBLE_MODULE_ARGS']['_ansible_keep_remote_files'] = False + args = json.dumps(request.param) + else: + raise Exception('Malformed data to the patch_ansible_module pytest fixture') + + mocker.patch('ansible.module_utils.basic._ANSIBLE_ARGS', to_bytes(args)) diff --git a/ansible_collections/community/vmware/tests/unit/modules/utils.py b/ansible_collections/community/vmware/tests/unit/modules/utils.py new file mode 100644 index 00000000..001d6c16 --- /dev/null +++ b/ansible_collections/community/vmware/tests/unit/modules/utils.py @@ -0,0 +1,53 @@ +# Copyright: (c) 2020 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 + +from ansible_collections.community.vmware.tests.unit.compat import unittest +from ansible_collections.community.vmware.tests.unit.compat.mock import patch +from ansible.module_utils import basic +from ansible.module_utils._text import to_bytes + + +def set_module_args(args): + if '_ansible_remote_tmp' not in args: + args['_ansible_remote_tmp'] = '/tmp' + if '_ansible_keep_remote_files' not in args: + args['_ansible_keep_remote_files'] = False + + args = json.dumps({'ANSIBLE_MODULE_ARGS': args}) + basic._ANSIBLE_ARGS = to_bytes(args) + + +class AnsibleExitJson(Exception): + pass + + +class AnsibleFailJson(Exception): + pass + + +def exit_json(*args, **kwargs): + if 'changed' not in kwargs: + kwargs['changed'] = False + raise AnsibleExitJson(kwargs) + + +def fail_json(*args, **kwargs): + kwargs['failed'] = True + raise AnsibleFailJson(kwargs) + + +class ModuleTestCase(unittest.TestCase): + + def setUp(self): + self.mock_module = patch.multiple(basic.AnsibleModule, exit_json=exit_json, fail_json=fail_json) + self.mock_module.start() + self.mock_sleep = patch('time.sleep') + self.mock_sleep.start() + set_module_args({}) + self.addCleanup(self.mock_module.stop) + self.addCleanup(self.mock_sleep.stop) diff --git a/ansible_collections/community/vmware/tests/unit/requirements.txt b/ansible_collections/community/vmware/tests/unit/requirements.txt new file mode 100644 index 00000000..6f750903 --- /dev/null +++ b/ansible_collections/community/vmware/tests/unit/requirements.txt @@ -0,0 +1 @@ +pyvmomi |