From 06292566ec6bda36be05dd351c3635a744f21ad2 Mon Sep 17 00:00:00 2001 From: Mariusz Mazur Date: Tue, 25 Jun 2019 12:06:14 +0200 Subject: [PATCH] kubevirt: more unit tests (#57739) * Add tests for KubeAPIVersion * Legibility improvements for KubevirtVM tests * Create units.utils.kubevirt with common stuff * Add some VMIRS unit tests --- test/units/module_utils/test_kubevirt.py | 26 +++++ .../cloud/kubevirt/test_kubevirt_rs.py | 40 ++++++++ .../cloud/kubevirt/test_kubevirt_vm.py | 95 ++++--------------- test/units/utils/kubevirt_fixtures.py | 68 +++++++++++++ 4 files changed, 153 insertions(+), 76 deletions(-) create mode 100644 test/units/modules/cloud/kubevirt/test_kubevirt_rs.py create mode 100644 test/units/utils/kubevirt_fixtures.py diff --git a/test/units/module_utils/test_kubevirt.py b/test/units/module_utils/test_kubevirt.py index 75a711fa857..07c1a5424df 100644 --- a/test/units/module_utils/test_kubevirt.py +++ b/test/units/module_utils/test_kubevirt.py @@ -23,3 +23,29 @@ def test_double_nested_merge_dicts(): dict2 = {'metadata': {'labels': {'label2': 'value'}}} dict3 = json.dumps({'metadata': {'labels': {'label1': 'value', 'label2': 'value', 'label3': 'value'}}}, sort_keys=True) assert dict3 == json.dumps(dict(mymodule.KubeVirtRawModule.merge_dicts(dict1, dict2)), sort_keys=True) + + +@pytest.mark.parametrize("lval, operations, rval, result", [ + ('v1', ['<', '<='], 'v2', True), + ('v1', ['>', '>=', '=='], 'v2', False), + ('v1', ['>'], 'v1alpha1', True), + ('v1', ['==', '<', '<='], 'v1alpha1', False), + ('v1beta5', ['==', '<=', '>='], 'v1beta5', True), + ('v1beta5', ['<', '>', '!='], 'v1beta5', False), + +]) +def test_kubeapiversion_comparisons(lval, operations, rval, result): + KubeAPIVersion = mymodule.KubeAPIVersion + for op in operations: + test = '(KubeAPIVersion("{0}") {1} KubeAPIVersion("{2}")) == {3}'.format(lval, op, rval, result) + assert eval(test) + + +@pytest.mark.parametrize("ver", ('nope', 'v1delta7', '1.5', 'v1beta', 'v')) +def test_kubeapiversion_unsupported_versions(ver): + threw = False + try: + mymodule.KubeAPIVersion(ver) + except ValueError: + threw = True + assert threw diff --git a/test/units/modules/cloud/kubevirt/test_kubevirt_rs.py b/test/units/modules/cloud/kubevirt/test_kubevirt_rs.py new file mode 100644 index 00000000000..1df8b307826 --- /dev/null +++ b/test/units/modules/cloud/kubevirt/test_kubevirt_rs.py @@ -0,0 +1,40 @@ +import pytest + +openshiftdynamic = pytest.importorskip("openshift.dynamic") + +from units.modules.utils import set_module_args +from units.utils.kubevirt_fixtures import base_fixture, RESOURCE_DEFAULT_ARGS, AnsibleExitJson + +from ansible.module_utils.k8s.raw import KubernetesRawModule +from ansible.module_utils.kubevirt import KubeVirtRawModule +from ansible.modules.cloud.kubevirt import kubevirt_rs as mymodule + +KIND = 'VirtualMachineInstanceReplicaSet' + + +@pytest.mark.usefixtures("base_fixture") +@pytest.mark.parametrize("_replicas, _changed", ((1, True), + (3, True), + (2, False), + (5, True),)) +def test_scale_rs_nowait(_replicas, _changed): + _name = 'test-rs' + # Desired state: + args = dict(name=_name, namespace='vms', replicas=_replicas, wait=False) + set_module_args(args) + + # Mock pre-change state: + resource_args = dict(kind=KIND, **RESOURCE_DEFAULT_ARGS) + mymodule.KubeVirtVMIRS.find_supported_resource.return_value = openshiftdynamic.Resource(**resource_args) + res_inst = openshiftdynamic.ResourceInstance('', dict(kind=KIND, metadata={'name': _name}, spec={'replicas': 2})) + openshiftdynamic.Resource.get.return_value = res_inst + openshiftdynamic.Resource.search.return_value = [res_inst] + KubernetesRawModule.patch_resource.return_value = dict(kind=KIND, metadata={'name': _name}, + spec={'replicas': _replicas}), None + + # Run code: + with pytest.raises(AnsibleExitJson) as result: + mymodule.KubeVirtVMIRS().execute_module() + + # Verify result: + assert result.value['changed'] == _changed diff --git a/test/units/modules/cloud/kubevirt/test_kubevirt_vm.py b/test/units/modules/cloud/kubevirt/test_kubevirt_vm.py index 3622c11f9cc..6267d21e7ba 100644 --- a/test/units/modules/cloud/kubevirt/test_kubevirt_vm.py +++ b/test/units/modules/cloud/kubevirt/test_kubevirt_vm.py @@ -1,82 +1,18 @@ -import json import pytest -from units.compat.mock import patch, MagicMock +openshiftdynamic = pytest.importorskip("openshift.dynamic") + +from units.modules.utils import set_module_args +from units.utils.kubevirt_fixtures import base_fixture, RESOURCE_DEFAULT_ARGS, AnsibleExitJson -from ansible.module_utils import basic -from ansible.module_utils._text import to_bytes -from ansible.module_utils.k8s.common import K8sAnsibleMixin -from ansible.module_utils.k8s.raw import KubernetesRawModule from ansible.module_utils.kubevirt import KubeVirtRawModule - from ansible.modules.cloud.kubevirt import kubevirt_vm as mymodule -openshiftdynamic = pytest.importorskip("openshift.dynamic") -helpexceptions = pytest.importorskip("openshift.helper.exceptions") - KIND = 'VirtulMachine' -RESOURCE_DEFAULT_ARGS = {'api_version': 'v1alpha3', 'group': 'kubevirt.io', - 'prefix': 'apis', 'namespaced': True} -def set_module_args(args): - 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""" - def __init__(self, **kwargs): - for k in kwargs: - setattr(self, k, kwargs[k]) - - def __getitem__(self, attr): - return getattr(self, attr) - - -class AnsibleFailJson(Exception): - """Exception class to be raised by module.fail_json and caught - by the test case""" - def __init__(self, **kwargs): - for k in kwargs: - setattr(self, k, kwargs[k]) - - def __getitem__(self, attr): - return getattr(self, attr) - - -def exit_json(*args, **kwargs): - if 'changed' not in kwargs: - kwargs['changed'] = False - raise AnsibleExitJson(**kwargs) - - -def fail_json(*args, **kwargs): - raise AnsibleFailJson(**kwargs) - - -@pytest.fixture(autouse=True) -def setup_mixtures(monkeypatch): - monkeypatch.setattr( - KubernetesRawModule, "exit_json", exit_json) - monkeypatch.setattr( - KubernetesRawModule, "fail_json", fail_json) - # Create mock methods in Resource directly, otherwise dyn client - # tries binding those to corresponding methods in DynamicClient - # (with partial()), which is more problematic to intercept - openshiftdynamic.Resource.get = MagicMock() - openshiftdynamic.Resource.create = MagicMock() - openshiftdynamic.Resource.delete = MagicMock() - openshiftdynamic.Resource.patch = MagicMock() - # Globally mock some methods, since all tests will use this - K8sAnsibleMixin.get_api_client = MagicMock() - K8sAnsibleMixin.get_api_client.return_value = None - K8sAnsibleMixin.find_resource = MagicMock() - KubeVirtRawModule.find_supported_resource = MagicMock() - - -def test_vm_multus_creation(): +@pytest.mark.usefixtures("base_fixture") +def test_create_vm_with_multus(): # Desired state: args = dict( state='present', name='testvm', @@ -90,19 +26,22 @@ def test_vm_multus_creation(): set_module_args(args) # State as "returned" by the "k8s cluster": - openshiftdynamic.Resource.get.return_value = None resource_args = dict(kind=KIND, **RESOURCE_DEFAULT_ARGS) KubeVirtRawModule.find_supported_resource.return_value = openshiftdynamic.Resource(**resource_args) + openshiftdynamic.Resource.get.return_value = None # Object doesn't exist in the cluster - # Actual test: + # Run code: with pytest.raises(AnsibleExitJson) as result: mymodule.KubeVirtVM().execute_module() + + # Verify result: assert result.value['changed'] assert result.value['method'] == 'create' +@pytest.mark.usefixtures("base_fixture") @pytest.mark.parametrize("_wait", (False, True)) -def test_resource_absent(_wait): +def test_vm_is_absent(_wait): # Desired state: args = dict( state='absent', name='testvmi', @@ -112,12 +51,16 @@ def test_resource_absent(_wait): set_module_args(args) # State as "returned" by the "k8s cluster": - openshiftdynamic.Resource.get.return_value = None resource_args = dict(kind=KIND, **RESOURCE_DEFAULT_ARGS) KubeVirtRawModule.find_supported_resource.return_value = openshiftdynamic.Resource(**resource_args) + openshiftdynamic.Resource.get.return_value = None # Object doesn't exist in the cluster - # Actual test: + # Run code: with pytest.raises(AnsibleExitJson) as result: mymodule.KubeVirtVM().execute_module() - assert result.value['method'] == 'delete' + + # Verify result: assert not result.value['kubevirt_vm'] + assert result.value['method'] == 'delete' + # Note: nothing actually gets deleted, as we mock that there's not object in the cluster present, + # so if the method changes to something other than 'delete' at some point, that's fine diff --git a/test/units/utils/kubevirt_fixtures.py b/test/units/utils/kubevirt_fixtures.py new file mode 100644 index 00000000000..744b6ca9020 --- /dev/null +++ b/test/units/utils/kubevirt_fixtures.py @@ -0,0 +1,68 @@ +import json +import pytest + +from units.compat.mock import MagicMock + +from ansible.module_utils.k8s.common import K8sAnsibleMixin +from ansible.module_utils.k8s.raw import KubernetesRawModule +from ansible.module_utils.kubevirt import KubeVirtRawModule + +import openshift.dynamic + +RESOURCE_DEFAULT_ARGS = {'api_version': 'v1alpha3', 'group': 'kubevirt.io', + 'prefix': 'apis', 'namespaced': True} + + +class AnsibleExitJson(Exception): + """Exception class to be raised by module.exit_json and caught + by the test case""" + def __init__(self, **kwargs): + for k in kwargs: + setattr(self, k, kwargs[k]) + + def __getitem__(self, attr): + return getattr(self, attr) + + +class AnsibleFailJson(Exception): + """Exception class to be raised by module.fail_json and caught + by the test case""" + def __init__(self, **kwargs): + for k in kwargs: + setattr(self, k, kwargs[k]) + + def __getitem__(self, attr): + return getattr(self, attr) + + +def exit_json(*args, **kwargs): + if 'changed' not in kwargs: + kwargs['changed'] = False + raise AnsibleExitJson(**kwargs) + + +def fail_json(*args, **kwargs): + raise AnsibleFailJson(**kwargs) + + +@pytest.fixture() +def base_fixture(monkeypatch): + monkeypatch.setattr( + KubernetesRawModule, "exit_json", exit_json) + monkeypatch.setattr( + KubernetesRawModule, "fail_json", fail_json) + # Create mock methods in Resource directly, otherwise dyn client + # tries binding those to corresponding methods in DynamicClient + # (with partial()), which is more problematic to intercept + openshift.dynamic.Resource.get = MagicMock() + openshift.dynamic.Resource.create = MagicMock() + openshift.dynamic.Resource.delete = MagicMock() + openshift.dynamic.Resource.patch = MagicMock() + openshift.dynamic.Resource.search = MagicMock() + # Globally mock some methods, since all tests will use this + KubernetesRawModule.patch_resource = MagicMock() + KubernetesRawModule.patch_resource.return_value = ({}, None) + K8sAnsibleMixin.get_api_client = MagicMock() + K8sAnsibleMixin.get_api_client.return_value = None + K8sAnsibleMixin.find_resource = MagicMock() + KubeVirtRawModule.find_supported_resource = MagicMock()