Merge pull request #79 from 0xFelix/unit-pytest

Migrate existing unit tests to pytest and add basic unit tests for inventory plugin
This commit is contained in:
kubevirt-bot
2024-04-16 16:38:36 +02:00
committed by GitHub
8 changed files with 332 additions and 362 deletions

View File

@@ -1,4 +1,5 @@
pytest
pytest-ansible
pytest-mock
pytest-xdist
tox-ansible

View File

@@ -1,44 +0,0 @@
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 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"))
mocker.patch("ansible.module_utils.basic.sys.stdin", mocker.MagicMock())
mocker.patch("ansible.module_utils.basic.sys.stdin.buffer", fake_stdin)
yield fake_stdin
ansible.module_utils.basic._ANSIBLE_ARGS = old_args
sys.argv = old_argv

View File

@@ -1,217 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2021, 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 unittest
from unittest.mock import patch, ANY
from ansible.module_utils import basic
from ansible_collections.kubernetes.core.plugins.module_utils.k8s import runner
from ansible_collections.kubevirt.core.plugins.modules import kubevirt_vm
from ansible_collections.kubevirt.core.tests.unit.utils.ansible_module_mock import (
AnsibleFailJson,
AnsibleExitJson,
exit_json,
fail_json,
set_module_args,
get_api_client,
)
FIXTURE1 = {
"apiVersion": "kubevirt.io/v1",
"kind": "VirtualMachine",
"metadata": {
"name": "testvm",
"namespace": "default",
"labels": {"environment": "staging", "service": "loadbalancer"},
},
"spec": {
"running": True,
"instancetype": {"name": "u1.medium"},
"preference": {"name": "fedora"},
"dataVolumeTemplates": [
{
"metadata": {"name": "testdv"},
"spec": {
"source": {
"registry": {
"url": "docker://quay.io/containerdisks/fedora:latest"
},
},
"storage": {
"accessModes": ["ReadWriteOnce"],
"resources": {"requests": {"storage": "5Gi"}},
},
},
}
],
"template": {
"metadata": {
"labels": {"environment": "staging", "service": "loadbalancer"}
},
"spec": {"domain": {"devices": {}}, "terminationGracePeriodSeconds": 180},
},
},
}
METADATA = """apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
name: "testvm"
namespace: "default"
labels:
environment: staging
service: loadbalancer
spec:
running: True
instancetype:
name: u1.medium
preference:
name: fedora
dataVolumeTemplates:
- metadata:
name: testdv
spec:
source:
registry:
url: docker://quay.io/containerdisks/fedora:latest
storage:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
template:
metadata:
labels:
environment: staging
service: loadbalancer
spec:
domain:
devices: {}
terminationGracePeriodSeconds: 180
"""
FIXTURE2 = {
"name": "testvm",
"namespace": "default",
"state": "present",
"labels": {"service": "loadbalancer", "environment": "staging"},
"instancetype": {"name": "u1.medium"},
"preference": {"name": "fedora"},
"data_volume_templates": [
{
"metadata": {"name": "testdv"},
"spec": {
"source": {
"registry": {
"url": "docker://quay.io/containerdisks/fedora:latest"
},
},
"storage": {
"accessModes": ["ReadWriteOnce"],
"resources": {"requests": {"storage": "5Gi"}},
},
},
}
],
"spec": {"domain": {"devices": {}}, "terminationGracePeriodSeconds": 180},
"api_version": "kubevirt.io/v1",
"running": True,
"wait": False,
"wait_sleep": 5,
"wait_timeout": 120,
"force": False,
"generate_name": None,
"annotations": None,
"kubeconfig": None,
"context": None,
"host": None,
"api_key": None,
"username": None,
"password": None,
"validate_certs": None,
"ca_cert": None,
"client_cert": None,
"client_key": None,
"proxy": None,
"no_proxy": None,
"proxy_headers": None,
"persist_config": None,
"impersonate_user": None,
"impersonate_groups": None,
"delete_options": None,
"resource_definition": METADATA,
"wait_condition": {"type": "Ready", "status": True},
}
class TestCreateVM(unittest.TestCase):
def setUp(self):
self.mock_module_helper = patch.multiple(
basic.AnsibleModule, exit_json=exit_json, fail_json=fail_json
)
self.mock_module_helper.start()
self.mock_runner = patch.multiple(runner, get_api_client=get_api_client)
self.mock_runner.start()
# Stop the patch after test execution
# like tearDown but executed also when the setup failed
self.addCleanup(self.mock_module_helper.stop)
self.addCleanup(self.mock_runner.stop)
def test_module_fail_when_required_args_missing(self):
with self.assertRaises(AnsibleFailJson):
set_module_args({})
kubevirt_vm.main()
def test_create(self):
set_module_args(
{
"name": "testvm",
"namespace": "default",
"state": "present",
"labels": {"service": "loadbalancer", "environment": "staging"},
"instancetype": {"name": "u1.medium"},
"preference": {"name": "fedora"},
"data_volume_templates": [
{
"metadata": {"name": "testdv"},
"spec": {
"source": {
"registry": {
"url": "docker://quay.io/containerdisks/fedora:latest"
},
},
"storage": {
"accessModes": ["ReadWriteOnce"],
"resources": {"requests": {"storage": "5Gi"}},
},
},
}
],
"spec": {
"domain": {"devices": {}},
"terminationGracePeriodSeconds": 180,
},
}
)
with patch.object(runner, "perform_action") as mock_run_command:
mock_run_command.return_value = {
"method": "create",
"changed": True,
"result": "success",
} # successful execution
with self.assertRaises(AnsibleExitJson) as result:
kubevirt_vm.main()
mock_run_command.assert_called_once_with(
ANY,
FIXTURE1,
FIXTURE2,
)

View File

@@ -1,93 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2021, 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 unittest import TestCase
from unittest.mock import patch
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.service import (
K8sService,
)
from ansible_collections.kubevirt.core.plugins.modules import (
kubevirt_vm_info,
)
from ansible_collections.kubevirt.core.tests.unit.utils.ansible_module_mock import (
AnsibleExitJson,
exit_json,
fail_json,
set_module_args,
get_api_client,
)
FIXTURE1 = {
"kind": "VirtualMachine",
"api_version": "kubevirt.io/v1",
"name": None,
"namespace": None,
"label_selectors": [],
"field_selectors": [],
"wait": False,
"wait_sleep": 5,
"wait_timeout": 120,
"condition": {"type": "Ready", "status": True},
}
FIXTURE2 = {
"kind": "VirtualMachine",
"api_version": "kubevirt.io/v1",
"name": "testvm",
"namespace": "default",
"label_selectors": [],
"field_selectors": [],
"wait": False,
"wait_sleep": 5,
"wait_timeout": 120,
"condition": {"type": "Ready", "status": True},
}
class TestDescribeVM(TestCase):
def setUp(self):
self.mock_module_helper = patch.multiple(
AnsibleModule, exit_json=exit_json, fail_json=fail_json
)
self.mock_module_helper.start()
self.mock_main = patch.multiple(kubevirt_vm_info, get_api_client=get_api_client)
self.mock_main.start()
# Stop the patch after test execution
# like tearDown but executed also when the setup failed
self.addCleanup(self.mock_module_helper.stop)
self.addCleanup(self.mock_main.stop)
def run_module(self, fixture):
with patch.object(K8sService, "find") as mock_find_command:
mock_find_command.return_value = {
"api_found": True,
"failed": False,
"resources": [],
} # successful execution
with self.assertRaises(AnsibleExitJson):
kubevirt_vm_info.main()
mock_find_command.assert_called_once_with(
**fixture,
)
def test_describe_without_args(self):
set_module_args({})
self.run_module(FIXTURE1)
def test_describe_with_args(self):
set_module_args(
{
"name": "testvm",
"namespace": "default",
}
)
self.run_module(FIXTURE2)

View File

@@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
# Copyright 2024 Red Hat, Inc.
# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import pytest
from ansible_collections.kubevirt.core.plugins.inventory.kubevirt import InventoryModule
@pytest.fixture
def inventory():
return InventoryModule()
@pytest.mark.parametrize(
"file_name,expected",
[
("inventory.kubevirt.yml", True),
("inventory.kubevirt.yaml", True),
("something.kubevirt.yml", True),
("something.kubevirt.yaml", True),
("inventory.somethingelse.yml", False),
("inventory.somethingelse.yaml", False),
("something.somethingelse.yml", False),
("something.somethingelse.yaml", False),
],
)
def test_verify_file(tmp_path, inventory, file_name, expected):
file = tmp_path / file_name
file.touch()
assert inventory.verify_file(str(file)) is expected
@pytest.mark.parametrize(
"file_name",
[
"inventory.kubevirt.yml",
"inventory.kubevirt.yaml",
"something.kubevirt.yml",
"something.kubevirt.yaml",
"inventory.somethingelse.yml",
"inventory.somethingelse.yaml",
"something.somethingelse.yml",
"something.somethingelse.yaml",
],
)
def test_verify_file_bad_config(inventory, file_name):
assert inventory.verify_file(file_name) is False

View File

@@ -0,0 +1,198 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2021, 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 pytest
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.kubernetes.core.plugins.module_utils.k8s import runner
from ansible_collections.kubevirt.core.plugins.modules import kubevirt_vm
from ansible_collections.kubevirt.core.tests.unit.utils.ansible_module_mock import (
AnsibleFailJson,
AnsibleExitJson,
exit_json,
fail_json,
set_module_args,
)
@pytest.fixture(scope="module")
def vm_definition():
return {
"apiVersion": "kubevirt.io/v1",
"kind": "VirtualMachine",
"metadata": {
"name": "testvm",
"namespace": "default",
"labels": {"environment": "staging", "service": "loadbalancer"},
},
"spec": {
"running": True,
"instancetype": {"name": "u1.medium"},
"preference": {"name": "fedora"},
"dataVolumeTemplates": [
{
"metadata": {"name": "testdv"},
"spec": {
"source": {
"registry": {
"url": "docker://quay.io/containerdisks/fedora:latest"
},
},
"storage": {
"accessModes": ["ReadWriteOnce"],
"resources": {"requests": {"storage": "5Gi"}},
},
},
}
],
"template": {
"metadata": {
"labels": {"environment": "staging", "service": "loadbalancer"}
},
"spec": {
"domain": {"devices": {}},
"terminationGracePeriodSeconds": 180,
},
},
},
}
@pytest.fixture(scope="module")
def vm_manifest():
return """apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
name: "testvm"
namespace: "default"
labels:
environment: staging
service: loadbalancer
spec:
running: True
instancetype:
name: u1.medium
preference:
name: fedora
dataVolumeTemplates:
- metadata:
name: testdv
spec:
source:
registry:
url: docker://quay.io/containerdisks/fedora:latest
storage:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
template:
metadata:
labels:
environment: staging
service: loadbalancer
spec:
domain:
devices: {}
terminationGracePeriodSeconds: 180
"""
@pytest.fixture(scope="module")
def module_params_create():
return {
"name": "testvm",
"namespace": "default",
"state": "present",
"labels": {"service": "loadbalancer", "environment": "staging"},
"instancetype": {"name": "u1.medium"},
"preference": {"name": "fedora"},
"data_volume_templates": [
{
"metadata": {"name": "testdv"},
"spec": {
"source": {
"registry": {
"url": "docker://quay.io/containerdisks/fedora:latest"
},
},
"storage": {
"accessModes": ["ReadWriteOnce"],
"resources": {"requests": {"storage": "5Gi"}},
},
},
}
],
"spec": {
"domain": {"devices": {}},
"terminationGracePeriodSeconds": 180,
},
}
@pytest.fixture(scope="module")
def k8s_module_params_create(module_params_create, vm_manifest):
return module_params_create | {
"api_version": "kubevirt.io/v1",
"running": True,
"wait": False,
"wait_sleep": 5,
"wait_timeout": 120,
"force": False,
"generate_name": None,
"annotations": None,
"kubeconfig": None,
"context": None,
"host": None,
"api_key": None,
"username": None,
"password": None,
"validate_certs": None,
"ca_cert": None,
"client_cert": None,
"client_key": None,
"proxy": None,
"no_proxy": None,
"proxy_headers": None,
"persist_config": None,
"impersonate_user": None,
"impersonate_groups": None,
"delete_options": None,
"resource_definition": vm_manifest,
"wait_condition": {"type": "Ready", "status": True},
}
def test_module_fails_when_required_args_missing(monkeypatch):
monkeypatch.setattr(AnsibleModule, "fail_json", fail_json)
with pytest.raises(AnsibleFailJson):
set_module_args({})
kubevirt_vm.main()
def test_module_create(
monkeypatch, mocker, module_params_create, k8s_module_params_create, vm_definition
):
monkeypatch.setattr(AnsibleModule, "exit_json", exit_json)
monkeypatch.setattr(runner, "get_api_client", lambda _: None)
set_module_args(module_params_create)
perform_action = mocker.patch.object(runner, "perform_action")
perform_action.return_value = {
"method": "create",
"changed": True,
"result": "success",
}
with pytest.raises(AnsibleExitJson):
kubevirt_vm.main()
perform_action.assert_called_once_with(
mocker.ANY, vm_definition, k8s_module_params_create
)

View File

@@ -0,0 +1,79 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2021, 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 pytest
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.kubernetes.core.plugins.module_utils.k8s.service import (
K8sService,
)
from ansible_collections.kubevirt.core.plugins.modules import (
kubevirt_vm_info,
)
from ansible_collections.kubevirt.core.tests.unit.utils.ansible_module_mock import (
AnsibleExitJson,
exit_json,
set_module_args,
)
@pytest.fixture(scope="module")
def find_args_default():
return {
"kind": "VirtualMachine",
"api_version": "kubevirt.io/v1",
"name": None,
"namespace": None,
"label_selectors": [],
"field_selectors": [],
"wait": False,
"wait_sleep": 5,
"wait_timeout": 120,
"condition": {"type": "Ready", "status": True},
}
@pytest.fixture(scope="module")
def find_args_name_namespace():
return {
"kind": "VirtualMachine",
"api_version": "kubevirt.io/v1",
"name": "testvm",
"namespace": "default",
"label_selectors": [],
"field_selectors": [],
"wait": False,
"wait_sleep": 5,
"wait_timeout": 120,
"condition": {"type": "Ready", "status": True},
}
@pytest.mark.parametrize(
"module_args,find_args",
[
({}, "find_args_default"),
({"name": "testvm", "namespace": "default"}, "find_args_name_namespace"),
],
)
def test_module(request, monkeypatch, mocker, module_args, find_args):
monkeypatch.setattr(AnsibleModule, "exit_json", exit_json)
monkeypatch.setattr(kubevirt_vm_info, "get_api_client", lambda _: None)
set_module_args(module_args)
find = mocker.patch.object(K8sService, "find")
find.return_value = {
"api_found": True,
"failed": False,
"resources": [],
}
with pytest.raises(AnsibleExitJson):
kubevirt_vm_info.main()
find.assert_called_once_with(**request.getfixturevalue(find_args))

View File

@@ -2,7 +2,8 @@
# Copyright: (c) 2021, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# This module maock the AnsibleModule class for more information please visite
# This file allows to run modules in unit tests.
# It was taken from:
# https://docs.ansible.com/ansible/latest/dev_guide/testing_units_modules.html#module-argument-processing
from __future__ import absolute_import, division, print_function
@@ -23,13 +24,11 @@ def set_module_args(args):
class AnsibleExitJson(Exception):
"""Exception class to be raised by module.exit_json and caught by the test case"""
pass
class AnsibleFailJson(Exception):
"""Exception class to be raised by module.fail_json and caught by the test case"""
pass
@@ -44,8 +43,3 @@ 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_api_client(*args, **kwargs):
"""function to patch over get_api_client"""
pass