diff options
Diffstat (limited to 'tests/unit')
36 files changed, 1817 insertions, 0 deletions
| diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/unit/__init__.py diff --git a/tests/unit/compat/__init__.py b/tests/unit/compat/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/unit/compat/__init__.py diff --git a/tests/unit/compat/builtins.py b/tests/unit/compat/builtins.py new file mode 100644 index 00000000..bfc8adfb --- /dev/null +++ b/tests/unit/compat/builtins.py @@ -0,0 +1,34 @@ +# (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 for python2.7 +# + +# One unittest needs to import builtins via __import__() so we need to have +# the string that represents it +try: +    import __builtin__ +except ImportError: +    BUILTINS = "builtins" +else: +    BUILTINS = "__builtin__" diff --git a/tests/unit/compat/mock.py b/tests/unit/compat/mock.py new file mode 100644 index 00000000..b45d6b5c --- /dev/null +++ b/tests/unit/compat/mock.py @@ -0,0 +1,127 @@ +# (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 + +# 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: +            import _io + +            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/tests/unit/compat/unittest.py b/tests/unit/compat/unittest.py new file mode 100644 index 00000000..df3379b8 --- /dev/null +++ b/tests/unit/compat/unittest.py @@ -0,0 +1,39 @@ +# (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/tests/unit/mock/__init__.py b/tests/unit/mock/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/unit/mock/__init__.py diff --git a/tests/unit/mock/loader.py b/tests/unit/mock/loader.py new file mode 100644 index 00000000..c21188ee --- /dev/null +++ b/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, path): +        path = to_text(path) +        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/tests/unit/mock/path.py b/tests/unit/mock/path.py new file mode 100644 index 00000000..aea8ba1e --- /dev/null +++ b/tests/unit/mock/path.py @@ -0,0 +1,7 @@ +from ansible_collections.vyos.vyos.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/tests/unit/mock/procenv.py b/tests/unit/mock/procenv.py new file mode 100644 index 00000000..1587949e --- /dev/null +++ b/tests/unit/mock/procenv.py @@ -0,0 +1,94 @@ +# (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.vyos.vyos.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/tests/unit/mock/vault_helper.py b/tests/unit/mock/vault_helper.py new file mode 100644 index 00000000..b34ae134 --- /dev/null +++ b/tests/unit/mock/vault_helper.py @@ -0,0 +1,42 @@ +# 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 + +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/tests/unit/mock/yaml_helper.py b/tests/unit/mock/yaml_helper.py new file mode 100644 index 00000000..1a945f1b --- /dev/null +++ b/tests/unit/mock/yaml_helper.py @@ -0,0 +1,164 @@ +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/tests/unit/modules/__init__.py b/tests/unit/modules/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/unit/modules/__init__.py diff --git a/tests/unit/modules/conftest.py b/tests/unit/modules/conftest.py new file mode 100644 index 00000000..ac56c9c7 --- /dev/null +++ b/tests/unit/modules/conftest.py @@ -0,0 +1,37 @@ +# Copyright (c) 2017 Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +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/tests/unit/modules/network/__init__.py b/tests/unit/modules/network/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/unit/modules/network/__init__.py diff --git a/tests/unit/modules/network/vyos/__init__.py b/tests/unit/modules/network/vyos/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/unit/modules/network/vyos/__init__.py diff --git a/tests/unit/modules/network/vyos/fixtures/__init__.py b/tests/unit/modules/network/vyos/fixtures/__init__.py new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/unit/modules/network/vyos/fixtures/__init__.py diff --git a/tests/unit/modules/network/vyos/fixtures/show_host_name b/tests/unit/modules/network/vyos/fixtures/show_host_name new file mode 100644 index 00000000..e89bc064 --- /dev/null +++ b/tests/unit/modules/network/vyos/fixtures/show_host_name @@ -0,0 +1 @@ +vyos01 diff --git a/tests/unit/modules/network/vyos/fixtures/show_version b/tests/unit/modules/network/vyos/fixtures/show_version new file mode 100644 index 00000000..a015d554 --- /dev/null +++ b/tests/unit/modules/network/vyos/fixtures/show_version @@ -0,0 +1,14 @@ +Version:      VyOS 1.1.7 +Description:  VyOS 1.1.7 (helium) +Copyright:    2016 VyOS maintainers and contributors +Built by:     maintainers@vyos.net +Built on:     Wed Feb 17 09:57:31 UTC 2016 +Build ID:     1602170957-4459750 +System type:  x86 64-bit +Boot via:     image +Hypervisor:   VMware +HW model:     VMware Virtual Platform +HW S/N:       VMware-42 3c 26 25 44 c5 0a 91-cf 2c 97 2b fe 9b 25 be +HW UUID:      423C2625-44C5-0A91-CF2C-972BFE9B25BE +Uptime:       01:08:20 up 52 days,  2:13,  1 user,  load average: 0.00, 0.01, 0.05 + diff --git a/tests/unit/modules/network/vyos/fixtures/vyos_config_config.cfg b/tests/unit/modules/network/vyos/fixtures/vyos_config_config.cfg new file mode 100644 index 00000000..fcef8ebd --- /dev/null +++ b/tests/unit/modules/network/vyos/fixtures/vyos_config_config.cfg @@ -0,0 +1,10 @@ +set system host-name 'router' +set system domain-name 'example.com' +set system domain-search domain 'example.com' +set system name-server '8.8.8.8' +set system name-server '8.8.4.4' +set interfaces ethernet eth0 address '1.2.3.4/24' +set interfaces ethernet eth0 description 'test string' +set interfaces ethernet eth1 address '6.7.8.9/24' +set interfaces ethernet eth1 description 'test string' +set interfaces ethernet eth1 disable diff --git a/tests/unit/modules/network/vyos/fixtures/vyos_config_src.cfg b/tests/unit/modules/network/vyos/fixtures/vyos_config_src.cfg new file mode 100644 index 00000000..410f6115 --- /dev/null +++ b/tests/unit/modules/network/vyos/fixtures/vyos_config_src.cfg @@ -0,0 +1,6 @@ +set system host-name foo + +delete interfaces ethernet eth0 address +set interfaces ethernet eth1 address '6.7.8.9/24' +     set interfaces ethernet eth1 description 'test string' +set interfaces ethernet eth1 disable diff --git a/tests/unit/modules/network/vyos/fixtures/vyos_config_src_brackets.cfg b/tests/unit/modules/network/vyos/fixtures/vyos_config_src_brackets.cfg new file mode 100644 index 00000000..468b32c2 --- /dev/null +++ b/tests/unit/modules/network/vyos/fixtures/vyos_config_src_brackets.cfg @@ -0,0 +1,13 @@ +interfaces { +    ethernet eth0 { +        address 10.10.10.10/24 +    } +    ethernet eth1 { +        address 6.7.8.9/24 +        description test string +        disable +    } +} +system { +    host-name foo +} diff --git a/tests/unit/modules/network/vyos/fixtures/vyos_ping_ping_10.10.10.10_count_2 b/tests/unit/modules/network/vyos/fixtures/vyos_ping_ping_10.10.10.10_count_2 new file mode 100644 index 00000000..c28fba15 --- /dev/null +++ b/tests/unit/modules/network/vyos/fixtures/vyos_ping_ping_10.10.10.10_count_2 @@ -0,0 +1,7 @@ +PING 10.10.10.10 (10.10.10.10) 56(84) bytes of data. +64 bytes from 10.10.10.10: icmp_req=1 ttl=255 time=1.27 ms +64 bytes from 10.10.10.10: icmp_req=2 ttl=255 time=2.28 ms + +--- 10.8.38.66 ping statistics --- +2 packets transmitted, 2 received, 0% packet loss, time 1001ms +rtt min/avg/max/mdev = 12.1222/17.124/22.225/10.143 ms diff --git a/tests/unit/modules/network/vyos/fixtures/vyos_ping_ping_10.10.10.11_count_10_ttl_128_size_512 b/tests/unit/modules/network/vyos/fixtures/vyos_ping_ping_10.10.10.11_count_10_ttl_128_size_512 new file mode 100644 index 00000000..54e026c2 --- /dev/null +++ b/tests/unit/modules/network/vyos/fixtures/vyos_ping_ping_10.10.10.11_count_10_ttl_128_size_512 @@ -0,0 +1,15 @@ +PING 10.10.10.11 (10.8.38.65) 512(540) bytes of data. +520 bytes from 10.10.10.11: icmp_req=1 ttl=255 time=1.17 ms +520 bytes from 10.10.10.11: icmp_req=2 ttl=255 time=1.32 ms +520 bytes from 10.10.10.11: icmp_req=3 ttl=255 time=1.21 ms +520 bytes from 10.10.10.11: icmp_req=4 ttl=255 time=1.46 ms +520 bytes from 10.10.10.11: icmp_req=5 ttl=255 time=1.32 ms +520 bytes from 10.10.10.11: icmp_req=6 ttl=255 time=1.28 ms +520 bytes from 10.10.10.11: icmp_req=7 ttl=255 time=1.25 ms +520 bytes from 10.10.10.11: icmp_req=8 ttl=255 time=1.23 ms +520 bytes from 10.10.10.11: icmp_req=9 ttl=255 time=1.34 ms +520 bytes from 10.10.10.11: icmp_req=10 ttl=255 time=21.0 ms + +--- 10.10.10.11 ping statistics --- +10 packets transmitted, 10 received, 0% packet loss, time 9012ms +rtt min/avg/max/mdev = 1.170/3.262/21.002/5.913 ms diff --git a/tests/unit/modules/network/vyos/fixtures/vyos_ping_ping_10.10.10.20_count_4 b/tests/unit/modules/network/vyos/fixtures/vyos_ping_ping_10.10.10.20_count_4 new file mode 100644 index 00000000..08e61817 --- /dev/null +++ b/tests/unit/modules/network/vyos/fixtures/vyos_ping_ping_10.10.10.20_count_4 @@ -0,0 +1,9 @@ +PING 10.10.10.20 (10.10.10.20) 56(84) bytes of data. +From 10.10.10.20 icmp_seq=1 Destination Host Unreachable +From 10.10.10.20 icmp_seq=2 Destination Host Unreachable +From 10.10.10.20 icmp_seq=3 Destination Host Unreachable +From 10.10.10.20 icmp_seq=4 Destination Host Unreachable + +--- 10.10.10.20 ping statistics --- +4 packets transmitted, 0 received, +4 errors, 100% packet loss, time 3053ms +pipe 3 diff --git a/tests/unit/modules/network/vyos/fixtures/vyos_user_config.cfg b/tests/unit/modules/network/vyos/fixtures/vyos_user_config.cfg new file mode 100644 index 00000000..81cd1a48 --- /dev/null +++ b/tests/unit/modules/network/vyos/fixtures/vyos_user_config.cfg @@ -0,0 +1,2 @@ +set system login user admin level operator authentication encrypted-password '$6$V5oWW3JM9NFAwOG$P2L4raFvIrZjjs3g0qmH4Ns5ti7flRpSs6aEqy4TrGZYXGeBiYzwi2A6jy' +set system login user ansible level operator authentication encrypted-password '$6$ZfvSv6A50W6yNPYX$4HP5eg2sywcXYxTqhApQ7zvUvx0HsQHrI9xuJoFLy2gM/' diff --git a/tests/unit/modules/network/vyos/test_vyos_banner.py b/tests/unit/modules/network/vyos/test_vyos_banner.py new file mode 100644 index 00000000..c575409c --- /dev/null +++ b/tests/unit/modules/network/vyos/test_vyos_banner.py @@ -0,0 +1,63 @@ +# 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 + +from ansible_collections.vyos.vyos.tests.unit.compat.mock import patch +from ansible_collections.vyos.vyos.plugins.modules import vyos_banner +from ansible_collections.vyos.vyos.tests.unit.modules.utils import ( +    set_module_args, +) +from .vyos_module import TestVyosModule + + +class TestVyosBannerModule(TestVyosModule): + +    module = vyos_banner + +    def setUp(self): +        super(TestVyosBannerModule, self).setUp() + +        self.mock_get_config = patch( +            "ansible_collections.vyos.vyos.plugins.modules.vyos_banner.get_config" +        ) +        self.get_config = self.mock_get_config.start() + +        self.mock_load_config = patch( +            "ansible_collections.vyos.vyos.plugins.modules.vyos_banner.load_config" +        ) +        self.load_config = self.mock_load_config.start() + +    def tearDown(self): +        super(TestVyosBannerModule, self).tearDown() +        self.mock_get_config.stop() +        self.mock_load_config.stop() + +    def load_fixtures(self, commands=None): +        self.load_config.return_value = dict(diff=None, session="session") + +    def test_vyos_banner_create(self): +        set_module_args(dict(banner="pre-login", text="test\nbanner\nstring")) +        commands = [ +            "set system login banner pre-login 'test\\nbanner\\nstring'" +        ] +        self.execute_module(changed=True, commands=commands) + +    def test_vyos_banner_remove(self): +        set_module_args(dict(banner="pre-login", state="absent")) +        self.execute_module(changed=False, commands=[]) diff --git a/tests/unit/modules/network/vyos/test_vyos_command.py b/tests/unit/modules/network/vyos/test_vyos_command.py new file mode 100644 index 00000000..820c6c44 --- /dev/null +++ b/tests/unit/modules/network/vyos/test_vyos_command.py @@ -0,0 +1,122 @@ +# (c) 2016 Red Hat Inc. +# +# 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 + +from ansible_collections.vyos.vyos.tests.unit.compat.mock import patch +from ansible_collections.vyos.vyos.plugins.modules import vyos_command +from ansible_collections.vyos.vyos.tests.unit.modules.utils import ( +    set_module_args, +) +from .vyos_module import TestVyosModule, load_fixture + + +class TestVyosCommandModule(TestVyosModule): + +    module = vyos_command + +    def setUp(self): +        super(TestVyosCommandModule, self).setUp() +        self.mock_run_commands = patch( +            "ansible_collections.vyos.vyos.plugins.modules.vyos_command.run_commands" +        ) +        self.run_commands = self.mock_run_commands.start() + +    def tearDown(self): +        super(TestVyosCommandModule, self).tearDown() +        self.mock_run_commands.stop() + +    def load_fixtures(self, commands=None): +        def load_from_file(*args, **kwargs): +            module, commands = args +            output = list() + +            for item in commands: +                try: +                    command = item["command"] +                except ValueError: +                    command = item +                filename = str(command).replace(" ", "_") +                output.append(load_fixture(filename)) +            return output + +        self.run_commands.side_effect = load_from_file + +    def test_vyos_command_simple(self): +        set_module_args(dict(commands=["show version"])) +        result = self.execute_module() +        self.assertEqual(len(result["stdout"]), 1) +        self.assertTrue(result["stdout"][0].startswith("Version:      VyOS")) + +    def test_vyos_command_multiple(self): +        set_module_args(dict(commands=["show version", "show version"])) +        result = self.execute_module() +        self.assertEqual(len(result["stdout"]), 2) +        self.assertTrue(result["stdout"][0].startswith("Version:      VyOS")) + +    def test_vyos_command_wait_for(self): +        wait_for = 'result[0] contains "VyOS maintainers"' +        set_module_args(dict(commands=["show version"], wait_for=wait_for)) +        self.execute_module() + +    def test_vyos_command_wait_for_fails(self): +        wait_for = 'result[0] contains "test string"' +        set_module_args(dict(commands=["show version"], wait_for=wait_for)) +        self.execute_module(failed=True) +        self.assertEqual(self.run_commands.call_count, 10) + +    def test_vyos_command_retries(self): +        wait_for = 'result[0] contains "test string"' +        set_module_args( +            dict(commands=["show version"], wait_for=wait_for, retries=2) +        ) +        self.execute_module(failed=True) +        self.assertEqual(self.run_commands.call_count, 2) + +    def test_vyos_command_match_any(self): +        wait_for = [ +            'result[0] contains "VyOS maintainers"', +            'result[0] contains "test string"', +        ] +        set_module_args( +            dict(commands=["show version"], wait_for=wait_for, match="any") +        ) +        self.execute_module() + +    def test_vyos_command_match_all(self): +        wait_for = [ +            'result[0] contains "VyOS maintainers"', +            'result[0] contains "maintainers@vyos.net"', +        ] +        set_module_args( +            dict(commands=["show version"], wait_for=wait_for, match="all") +        ) +        self.execute_module() + +    def test_vyos_command_match_all_failure(self): +        wait_for = [ +            'result[0] contains "VyOS maintainers"', +            'result[0] contains "test string"', +        ] +        commands = ["show version", "show version"] +        set_module_args( +            dict(commands=commands, wait_for=wait_for, match="all") +        ) +        self.execute_module(failed=True) diff --git a/tests/unit/modules/network/vyos/test_vyos_config.py b/tests/unit/modules/network/vyos/test_vyos_config.py new file mode 100644 index 00000000..a471edd4 --- /dev/null +++ b/tests/unit/modules/network/vyos/test_vyos_config.py @@ -0,0 +1,159 @@ +# +# (c) 2016 Red Hat Inc. +# +# 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 + +from ansible_collections.vyos.vyos.tests.unit.compat.mock import ( +    patch, +    MagicMock, +) +from ansible_collections.vyos.vyos.plugins.modules import vyos_config +from ansible_collections.vyos.vyos.plugins.cliconf.vyos import Cliconf +from ansible_collections.vyos.vyos.tests.unit.modules.utils import ( +    set_module_args, +) +from .vyos_module import TestVyosModule, load_fixture + + +class TestVyosConfigModule(TestVyosModule): + +    module = vyos_config + +    def setUp(self): +        super(TestVyosConfigModule, self).setUp() + +        self.mock_get_config = patch( +            "ansible_collections.vyos.vyos.plugins.modules.vyos_config.get_config" +        ) +        self.get_config = self.mock_get_config.start() + +        self.mock_load_config = patch( +            "ansible_collections.vyos.vyos.plugins.modules.vyos_config.load_config" +        ) +        self.load_config = self.mock_load_config.start() + +        self.mock_run_commands = patch( +            "ansible_collections.vyos.vyos.plugins.modules.vyos_config.run_commands" +        ) +        self.run_commands = self.mock_run_commands.start() + +        self.mock_get_connection = patch( +            "ansible_collections.vyos.vyos.plugins.modules.vyos_config.get_connection" +        ) +        self.get_connection = self.mock_get_connection.start() + +        self.cliconf_obj = Cliconf(MagicMock()) +        self.running_config = load_fixture("vyos_config_config.cfg") + +        self.conn = self.get_connection() +        self.conn.edit_config = MagicMock() +        self.running_config = load_fixture("vyos_config_config.cfg") + +    def tearDown(self): +        super(TestVyosConfigModule, self).tearDown() + +        self.mock_get_config.stop() +        self.mock_load_config.stop() +        self.mock_run_commands.stop() +        self.mock_get_connection.stop() + +    def load_fixtures(self, commands=None): +        config_file = "vyos_config_config.cfg" +        self.get_config.return_value = load_fixture(config_file) +        self.load_config.return_value = None + +    def test_vyos_config_unchanged(self): +        src = load_fixture("vyos_config_config.cfg") +        self.conn.get_diff = MagicMock( +            return_value=self.cliconf_obj.get_diff(src, src) +        ) +        set_module_args(dict(src=src)) +        self.execute_module() + +    def test_vyos_config_src(self): +        src = load_fixture("vyos_config_src.cfg") +        set_module_args(dict(src=src)) +        candidate = "\n".join(self.module.format_commands(src.splitlines())) +        commands = [ +            "set system host-name foo", +            "delete interfaces ethernet eth0 address", +        ] +        self.conn.get_diff = MagicMock( +            return_value=self.cliconf_obj.get_diff( +                candidate, self.running_config +            ) +        ) +        self.execute_module(changed=True, commands=commands) + +    def test_vyos_config_src_brackets(self): +        src = load_fixture("vyos_config_src_brackets.cfg") +        set_module_args(dict(src=src)) +        candidate = "\n".join(self.module.format_commands(src.splitlines())) +        commands = [ +            "set interfaces ethernet eth0 address 10.10.10.10/24", +            "set system host-name foo", +        ] +        self.conn.get_diff = MagicMock( +            return_value=self.cliconf_obj.get_diff( +                candidate, self.running_config +            ) +        ) +        self.execute_module(changed=True, commands=commands) + +    def test_vyos_config_backup(self): +        set_module_args(dict(backup=True)) +        result = self.execute_module() +        self.assertIn("__backup__", result) + +    def test_vyos_config_lines(self): +        commands = ["set system host-name foo"] +        set_module_args(dict(lines=commands)) +        candidate = "\n".join(commands) +        self.conn.get_diff = MagicMock( +            return_value=self.cliconf_obj.get_diff( +                candidate, self.running_config +            ) +        ) +        self.execute_module(changed=True, commands=commands) + +    def test_vyos_config_config(self): +        config = "set system host-name localhost" +        new_config = ["set system host-name router"] +        set_module_args(dict(lines=new_config, config=config)) +        candidate = "\n".join(new_config) +        self.conn.get_diff = MagicMock( +            return_value=self.cliconf_obj.get_diff(candidate, config) +        ) +        self.execute_module(changed=True, commands=new_config) + +    def test_vyos_config_match_none(self): +        lines = [ +            "set system interfaces ethernet eth0 address 1.2.3.4/24", +            "set system interfaces ethernet eth0 description test string", +        ] +        set_module_args(dict(lines=lines, match="none")) +        candidate = "\n".join(lines) +        self.conn.get_diff = MagicMock( +            return_value=self.cliconf_obj.get_diff( +                candidate, None, diff_match="none" +            ) +        ) +        self.execute_module(changed=True, commands=lines, sort=False) diff --git a/tests/unit/modules/network/vyos/test_vyos_facts.py b/tests/unit/modules/network/vyos/test_vyos_facts.py new file mode 100644 index 00000000..b22a5232 --- /dev/null +++ b/tests/unit/modules/network/vyos/test_vyos_facts.py @@ -0,0 +1,109 @@ +# (c) 2016 Red Hat Inc. +# +# 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 json +from ansible_collections.vyos.vyos.tests.unit.compat.mock import patch +from ansible_collections.vyos.vyos.plugins.modules import vyos_facts +from ansible_collections.vyos.vyos.tests.unit.modules.utils import ( +    set_module_args, +) +from .vyos_module import TestVyosModule, load_fixture + + +class TestVyosFactsModule(TestVyosModule): +    module = vyos_facts + +    def setUp(self): +        super(TestVyosFactsModule, self).setUp() +        self.mock_run_commands = patch( +            "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.legacy.base.run_commands" +        ) +        self.run_commands = self.mock_run_commands.start() + +        self.mock_get_resource_connection = patch( +            "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.facts.facts.get_resource_connection" +        ) +        self.get_resource_connection = ( +            self.mock_get_resource_connection.start() +        ) + +        self.mock_get_capabilities = patch( +            "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.legacy.base.get_capabilities" +        ) +        self.get_capabilities = self.mock_get_capabilities.start() +        self.get_capabilities.return_value = { +            "device_info": { +                "network_os": "vyos", +                "network_os_hostname": "vyos01", +                "network_os_model": "VMware", +                "network_os_version": "VyOS 1.1.7", +            }, +            "network_api": "cliconf", +        } + +    def tearDown(self): +        super(TestVyosFactsModule, self).tearDown() +        self.mock_run_commands.stop() +        self.mock_get_capabilities.stop() +        self.mock_get_resource_connection.stop() + +    def load_fixtures(self, commands=None): +        def load_from_file(*args, **kwargs): +            module, commands = args +            output = list() +            for item in commands: +                try: +                    obj = json.loads(item) +                    command = obj["command"] +                except ValueError: +                    command = item +                filename = str(command).replace(" ", "_") +                output.append(load_fixture(filename)) +            return output + +        self.run_commands.side_effect = load_from_file + +    def test_vyos_facts_default(self): +        set_module_args(dict(gather_subset="default")) +        result = self.execute_module() +        facts = result.get("ansible_facts") +        self.assertEqual(len(facts), 10) +        self.assertEqual(facts["ansible_net_hostname"].strip(), "vyos01") +        self.assertEqual(facts["ansible_net_version"], "VyOS 1.1.7") + +    def test_vyos_facts_not_all(self): +        set_module_args(dict(gather_subset="!all")) +        result = self.execute_module() +        facts = result.get("ansible_facts") +        self.assertEqual(len(facts), 10) +        self.assertEqual(facts["ansible_net_hostname"].strip(), "vyos01") +        self.assertEqual(facts["ansible_net_version"], "VyOS 1.1.7") + +    def test_vyos_facts_exclude_most(self): +        set_module_args(dict(gather_subset=["!neighbors", "!config"])) +        result = self.execute_module() +        facts = result.get("ansible_facts") +        self.assertEqual(len(facts), 10) +        self.assertEqual(facts["ansible_net_hostname"].strip(), "vyos01") +        self.assertEqual(facts["ansible_net_version"], "VyOS 1.1.7") + +    def test_vyos_facts_invalid_subset(self): +        set_module_args(dict(gather_subset="cereal")) +        self.execute_module(failed=True) diff --git a/tests/unit/modules/network/vyos/test_vyos_ping.py b/tests/unit/modules/network/vyos/test_vyos_ping.py new file mode 100644 index 00000000..e3076103 --- /dev/null +++ b/tests/unit/modules/network/vyos/test_vyos_ping.py @@ -0,0 +1,107 @@ +# (c) 2016 Red Hat Inc. +# +# 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 + +from ansible_collections.vyos.vyos.tests.unit.compat.mock import patch +from ansible_collections.vyos.vyos.plugins.modules import vyos_ping +from ansible_collections.vyos.vyos.tests.unit.modules.utils import ( +    set_module_args, +) +from .vyos_module import TestVyosModule, load_fixture + + +class TestVyosPingModule(TestVyosModule): + +    module = vyos_ping + +    def setUp(self): +        super(TestVyosPingModule, self).setUp() +        self.mock_run_commands = patch( +            "ansible_collections.vyos.vyos.plugins.modules.vyos_ping.run_commands" +        ) +        self.run_commands = self.mock_run_commands.start() + +    def tearDown(self): +        super(TestVyosPingModule, self).tearDown() +        self.mock_run_commands.stop() + +    def load_fixtures(self, commands=None): +        def load_from_file(*args, **kwargs): +            commands = kwargs["commands"] +            output = list() + +            for command in commands: +                filename = str(command).split(" | ")[0].replace(" ", "_") +                output.append(load_fixture("vyos_ping_%s" % filename)) +            return output + +        self.run_commands.side_effect = load_from_file + +    def test_vyos_ping_expected_success(self): +        """ Test for successful pings when destination should be reachable """ +        set_module_args(dict(count=2, dest="10.10.10.10")) +        self.execute_module() + +    def test_vyos_ping_expected_failure(self): +        """ Test for unsuccessful pings when destination should not be reachable """ +        set_module_args(dict(count=4, dest="10.10.10.20", state="absent")) +        self.execute_module() + +    def test_vyos_ping_unexpected_success(self): +        """ Test for successful pings when destination should not be reachable - FAIL. """ +        set_module_args(dict(count=2, dest="10.10.10.10", state="absent")) +        self.execute_module(failed=True) + +    def test_vyos_ping_unexpected_failure(self): +        """ Test for unsuccessful pings when destination should be reachable - FAIL. """ +        set_module_args(dict(count=4, dest="10.10.10.20")) +        self.execute_module(failed=True) + +    def test_vyos_ping_failure_stats(self): +        """Test for asserting stats when ping fails""" +        set_module_args(dict(count=4, dest="10.10.10.20")) +        result = self.execute_module(failed=True) +        self.assertEqual(result["packet_loss"], "100%") +        self.assertEqual(result["packets_rx"], 0) +        self.assertEqual(result["packets_tx"], 4) + +    def test_vyos_ping_success_stats(self): +        """Test for asserting stats when ping passes""" +        set_module_args(dict(count=2, dest="10.10.10.10")) +        result = self.execute_module() +        self.assertEqual(result["packet_loss"], "0%") +        self.assertEqual(result["packets_rx"], 2) +        self.assertEqual(result["packets_tx"], 2) +        self.assertEqual(result["rtt"]["min"], 12) +        self.assertEqual(result["rtt"]["avg"], 17) +        self.assertEqual(result["rtt"]["max"], 22) +        self.assertEqual(result["rtt"]["mdev"], 10) + +    def test_vyos_ping_success_stats_with_options(self): +        set_module_args(dict(count=10, ttl=128, size=512, dest="10.10.10.11")) +        result = self.execute_module() +        self.assertEqual(result["packet_loss"], "0%") +        self.assertEqual(result["packets_rx"], 10) +        self.assertEqual(result["packets_tx"], 10) +        self.assertEqual(result["rtt"]["min"], 1) +        self.assertEqual(result["rtt"]["avg"], 3) +        self.assertEqual(result["rtt"]["max"], 21) +        self.assertEqual(result["rtt"]["mdev"], 5) diff --git a/tests/unit/modules/network/vyos/test_vyos_static_route.py b/tests/unit/modules/network/vyos/test_vyos_static_route.py new file mode 100644 index 00000000..e020ca51 --- /dev/null +++ b/tests/unit/modules/network/vyos/test_vyos_static_route.py @@ -0,0 +1,71 @@ +# (c) 2016 Red Hat Inc. +# +# 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 + +from ansible_collections.vyos.vyos.tests.unit.compat.mock import patch +from ansible_collections.vyos.vyos.plugins.modules import vyos_static_route +from ansible_collections.vyos.vyos.tests.unit.modules.utils import ( +    set_module_args, +) +from .vyos_module import TestVyosModule + + +class TestVyosStaticRouteModule(TestVyosModule): + +    module = vyos_static_route + +    def setUp(self): +        super(TestVyosStaticRouteModule, self).setUp() + +        self.mock_get_config = patch( +            "ansible_collections.vyos.vyos.plugins.modules.vyos_static_route.get_config" +        ) +        self.get_config = self.mock_get_config.start() + +        self.mock_load_config = patch( +            "ansible_collections.vyos.vyos.plugins.modules.vyos_static_route.load_config" +        ) +        self.load_config = self.mock_load_config.start() + +    def tearDown(self): +        super(TestVyosStaticRouteModule, self).tearDown() + +        self.mock_get_config.stop() +        self.mock_load_config.stop() + +    def load_fixtures(self, commands=None, transport="cli"): +        self.load_config.return_value = dict(diff=None, session="session") + +    def test_vyos_static_route_present(self): +        set_module_args( +            dict( +                prefix="172.26.0.0/16", +                next_hop="172.26.4.1", +                admin_distance="1", +            ) +        ) +        result = self.execute_module(changed=True) +        self.assertEqual( +            result["commands"], +            [ +                "set protocols static route 172.26.0.0/16 next-hop 172.26.4.1 distance 1" +            ], +        ) diff --git a/tests/unit/modules/network/vyos/test_vyos_system.py b/tests/unit/modules/network/vyos/test_vyos_system.py new file mode 100644 index 00000000..c22f7c18 --- /dev/null +++ b/tests/unit/modules/network/vyos/test_vyos_system.py @@ -0,0 +1,116 @@ +# (c) 2016 Red Hat Inc. +# +# 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 + +from ansible_collections.vyos.vyos.tests.unit.compat.mock import patch +from ansible_collections.vyos.vyos.plugins.modules import vyos_system +from ansible_collections.vyos.vyos.tests.unit.modules.utils import ( +    set_module_args, +) +from .vyos_module import TestVyosModule, load_fixture + + +class TestVyosSystemModule(TestVyosModule): + +    module = vyos_system + +    def setUp(self): +        super(TestVyosSystemModule, self).setUp() + +        self.mock_get_config = patch( +            "ansible_collections.vyos.vyos.plugins.modules.vyos_system.get_config" +        ) +        self.get_config = self.mock_get_config.start() + +        self.mock_load_config = patch( +            "ansible_collections.vyos.vyos.plugins.modules.vyos_system.load_config" +        ) +        self.load_config = self.mock_load_config.start() + +    def tearDown(self): +        super(TestVyosSystemModule, self).tearDown() + +        self.mock_get_config.stop() +        self.mock_load_config.stop() + +    def load_fixtures(self, commands=None): +        self.get_config.return_value = load_fixture("vyos_config_config.cfg") + +    def test_vyos_system_hostname(self): +        set_module_args(dict(host_name="foo")) +        commands = ["set system host-name 'foo'"] +        self.execute_module(changed=True, commands=commands) + +    def test_vyos_system_clear_hostname(self): +        set_module_args(dict(host_name="foo", state="absent")) +        commands = ["delete system host-name"] +        self.execute_module(changed=True, commands=commands) + +    def test_vyos_remove_single_name_server(self): +        set_module_args(dict(name_server=["8.8.4.4"], state="absent")) +        commands = ["delete system name-server '8.8.4.4'"] +        self.execute_module(changed=True, commands=commands) + +    def test_vyos_system_domain_name(self): +        set_module_args(dict(domain_name="example2.com")) +        commands = ["set system domain-name 'example2.com'"] +        self.execute_module(changed=True, commands=commands) + +    def test_vyos_system_clear_domain_name(self): +        set_module_args(dict(domain_name="example.com", state="absent")) +        commands = ["delete system domain-name"] +        self.execute_module(changed=True, commands=commands) + +    def test_vyos_system_domain_search(self): +        set_module_args( +            dict(domain_search=["foo.example.com", "bar.example.com"]) +        ) +        commands = [ +            "set system domain-search domain 'foo.example.com'", +            "set system domain-search domain 'bar.example.com'", +        ] +        self.execute_module(changed=True, commands=commands) + +    def test_vyos_system_clear_domain_search(self): +        set_module_args(dict(domain_search=[])) +        commands = ["delete system domain-search domain"] +        self.execute_module(changed=True, commands=commands) + +    def test_vyos_system_no_change(self): +        set_module_args( +            dict( +                host_name="router", +                domain_name="example.com", +                name_server=["8.8.8.8", "8.8.4.4"], +            ) +        ) +        result = self.execute_module() +        self.assertEqual([], result["commands"]) + +    def test_vyos_system_clear_all(self): +        set_module_args(dict(state="absent")) +        commands = [ +            "delete system host-name", +            "delete system domain-search domain", +            "delete system domain-name", +            "delete system name-server", +        ] +        self.execute_module(changed=True, commands=commands) diff --git a/tests/unit/modules/network/vyos/test_vyos_user.py b/tests/unit/modules/network/vyos/test_vyos_user.py new file mode 100644 index 00000000..d4c2dbe6 --- /dev/null +++ b/tests/unit/modules/network/vyos/test_vyos_user.py @@ -0,0 +1,139 @@ +# (c) 2016 Red Hat Inc. +# +# 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 + +from ansible_collections.vyos.vyos.tests.unit.compat.mock import patch +from ansible_collections.vyos.vyos.plugins.modules import vyos_user +from ansible_collections.vyos.vyos.tests.unit.modules.utils import ( +    set_module_args, +) +from .vyos_module import TestVyosModule, load_fixture + + +class TestVyosUserModule(TestVyosModule): + +    module = vyos_user + +    def setUp(self): +        super(TestVyosUserModule, self).setUp() + +        self.mock_get_config = patch( +            "ansible_collections.vyos.vyos.plugins.modules.vyos_user.get_config" +        ) +        self.get_config = self.mock_get_config.start() + +        self.mock_load_config = patch( +            "ansible_collections.vyos.vyos.plugins.modules.vyos_user.load_config" +        ) +        self.load_config = self.mock_load_config.start() + +    def tearDown(self): +        super(TestVyosUserModule, self).tearDown() +        self.mock_get_config.stop() +        self.mock_load_config.stop() + +    def load_fixtures(self, commands=None, transport="cli"): +        self.get_config.return_value = load_fixture("vyos_user_config.cfg") +        self.load_config.return_value = dict(diff=None, session="session") + +    def test_vyos_user_password(self): +        set_module_args(dict(name="ansible", configured_password="test")) +        result = self.execute_module(changed=True) +        self.assertEqual( +            result["commands"], +            [ +                "set system login user ansible authentication plaintext-password test" +            ], +        ) + +    def test_vyos_user_delete(self): +        set_module_args(dict(name="ansible", state="absent")) +        result = self.execute_module(changed=True) +        self.assertEqual( +            result["commands"], ["delete system login user ansible"] +        ) + +    def test_vyos_user_level(self): +        set_module_args(dict(name="ansible", level="operator")) +        result = self.execute_module(changed=True) +        self.assertEqual( +            result["commands"], +            ["set system login user ansible level operator"], +        ) + +    def test_vyos_user_level_invalid(self): +        set_module_args(dict(name="ansible", level="sysadmin")) +        self.execute_module(failed=True) + +    def test_vyos_user_purge(self): +        set_module_args(dict(purge=True)) +        result = self.execute_module(changed=True) +        self.assertEqual( +            sorted(result["commands"]), +            sorted( +                [ +                    "delete system login user ansible", +                    "delete system login user admin", +                ] +            ), +        ) + +    def test_vyos_user_update_password_changed(self): +        set_module_args( +            dict( +                name="test", +                configured_password="test", +                update_password="on_create", +            ) +        ) +        result = self.execute_module(changed=True) +        self.assertEqual( +            result["commands"], +            [ +                "set system login user test authentication plaintext-password test" +            ], +        ) + +    def test_vyos_user_update_password_on_create_ok(self): +        set_module_args( +            dict( +                name="ansible", +                configured_password="test", +                update_password="on_create", +            ) +        ) +        self.execute_module() + +    def test_vyos_user_update_password_always(self): +        set_module_args( +            dict( +                name="ansible", +                configured_password="test", +                update_password="always", +            ) +        ) +        result = self.execute_module(changed=True) +        self.assertEqual( +            result["commands"], +            [ +                "set system login user ansible authentication plaintext-password test" +            ], +        ) diff --git a/tests/unit/modules/network/vyos/vyos_module.py b/tests/unit/modules/network/vyos/vyos_module.py new file mode 100644 index 00000000..fb15c715 --- /dev/null +++ b/tests/unit/modules/network/vyos/vyos_module.py @@ -0,0 +1,104 @@ +# (c) 2016 Red Hat Inc. +# +# 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 +import json + +from ansible_collections.vyos.vyos.tests.unit.modules.utils import ( +    AnsibleExitJson, +    AnsibleFailJson, +    ModuleTestCase, +) + + +fixture_path = os.path.join(os.path.dirname(__file__), "fixtures") +fixture_data = {} + + +def load_fixture(name): +    path = os.path.join(fixture_path, name) + +    if path in fixture_data: +        return fixture_data[path] + +    with open(path) as f: +        data = f.read() + +    try: +        data = json.loads(data) +    except Exception: +        pass + +    fixture_data[path] = data +    return data + + +class TestVyosModule(ModuleTestCase): +    def execute_module( +        self, +        failed=False, +        changed=False, +        commands=None, +        sort=True, +        defaults=False, +    ): +        self.load_fixtures(commands) + +        if failed: +            result = self.failed() +            self.assertTrue(result["failed"], result) +        else: +            result = self.changed(changed) +            self.assertEqual(result["changed"], changed, result) + +        if commands is not None: +            if sort: +                self.assertEqual( +                    sorted(commands), +                    sorted(result["commands"]), +                    result["commands"], +                ) +            else: +                self.assertEqual( +                    commands, result["commands"], result["commands"] +                ) + +        return result + +    def failed(self): +        with self.assertRaises(AnsibleFailJson) as exc: +            self.module.main() + +        result = exc.exception.args[0] +        self.assertTrue(result["failed"], result) +        return result + +    def changed(self, changed=False): +        with self.assertRaises(AnsibleExitJson) as exc: +            self.module.main() + +        result = exc.exception.args[0] +        self.assertEqual(result["changed"], changed, result) +        return result + +    def load_fixtures(self, commands=None): +        pass diff --git a/tests/unit/modules/utils.py b/tests/unit/modules/utils.py new file mode 100644 index 00000000..2c9c602e --- /dev/null +++ b/tests/unit/modules/utils.py @@ -0,0 +1,48 @@ +import json + +from ansible_collections.vyos.vyos.tests.unit.compat import unittest +from ansible_collections.vyos.vyos.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/tests/unit/requirements.txt b/tests/unit/requirements.txt new file mode 100644 index 00000000..a9772bea --- /dev/null +++ b/tests/unit/requirements.txt @@ -0,0 +1,42 @@ +boto3 +placebo +pycrypto +passlib +pypsrp +python-memcached +pytz +pyvmomi +redis +requests +setuptools > 0.6 # pytest-xdist installed via requirements does not work with very old setuptools (sanity_ok) +unittest2 ; python_version < '2.7' +importlib ; python_version < '2.7' +netaddr +ipaddress +netapp-lib +solidfire-sdk-python + +# requirements for F5 specific modules +f5-sdk ; python_version >= '2.7' +f5-icontrol-rest ; python_version >= '2.7' +deepdiff + +# requirement for Fortinet specific modules +pyFMG + +# requirement for aci_rest module +xmljson + +# requirement for winrm connection plugin tests +pexpect + +# requirement for the linode module +linode-python # APIv3 +linode_api4 ; python_version > '2.6' # APIv4 + +# requirement for the gitlab module +python-gitlab +httmock + +# requirment for kubevirt modules +openshift ; python_version >= '2.7' | 
