Skip to content

Commit

Permalink
Add unit test for the Ansible module
Browse files Browse the repository at this point in the history
  • Loading branch information
WissemChb committed Sep 4, 2021
1 parent c9f0b9a commit 9c40159
Show file tree
Hide file tree
Showing 2 changed files with 273 additions and 28 deletions.
252 changes: 224 additions & 28 deletions tests/unit/modules/test_module_helm.py
Original file line number Diff line number Diff line change
@@ -1,40 +1,236 @@
# -*- coding: utf-8 -*-
# Copyright: (c) 2020, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

from __future__ import absolute_import, division, print_function

__metaclass__ = type

from ansible_collections.kubernetes.core.plugins.modules.helm import (
run_dep_update
import unittest

from unittest.mock import MagicMock, patch, call

from ansible.module_utils import basic
from ansible_collections.kubernetes.core.plugins.modules import helm
from ansible_collections.kubernetes.core.tests.unit.utils.ansible_module_mock import (
AnsibleFailJson,
AnsibleExitJson,
exit_json,
fail_json,
get_bin_path,
set_module_args
)


class MockedModule:
def __init__(self):
self.params = {
"api_key": None,
"ca_cert": None,
"host": None,
"kube_context": None,
"kubeconfig": None,
"release_namespace": None,
"validate_certs": None,
class TestDependencyUpdateWithoutChartRepoUrlOption(unittest.TestCase):

def setUp(self):
self.mock_module_helper = patch.multiple(basic.AnsibleModule,
exit_json=exit_json,
fail_json=fail_json,
get_bin_path=get_bin_path)
self.mock_module_helper.start()

# Stop the patch after test execution
# like tearDown but executed also when the setup failed
self.addCleanup(self.mock_module_helper.stop)

self.chart_info_without_dep = {
'apiVersion': 'v2',
'appVersion': 'default',
'description': 'A chart used in molecule tests',
'name': 'test-chart',
'type': 'application',
'version': '0.1.0'
}

self.chart_info_with_dep = {
'apiVersion': 'v2',
'appVersion': 'default',
'description': 'A chart used in molecule tests',
'name': 'test-chart',
'type': 'application',
'version': '0.1.0',
'dependencies': [
{
'name': 'test',
'version': '0.1.0',
'repository': 'file://../test-chart'
}
]
}

def test_module_fail_when_required_args_missing(self):
with self.assertRaises(AnsibleFailJson):
set_module_args({})
helm.main()

def test_dependency_update_option_not_defined(self):
set_module_args({
'release_name': 'test',
'release_namespace': 'test',
'chart_ref': '/tmp/path'
})
helm.get_release_status = MagicMock(return_value=None)
helm.fetch_chart_info = MagicMock(return_value=self.chart_info_without_dep)
helm.run_dep_update = MagicMock()
with patch.object(basic.AnsibleModule, 'run_command') as mock_run_command:
mock_run_command.return_value = 0, 'configuration updated', '' # successful execution
with self.assertRaises(AnsibleExitJson) as result:
helm.main()
helm.run_dep_update.assert_not_called()
mock_run_command.assert_called_once_with('/usr/bin/helm upgrade -i --reset-values test /tmp/path',
environ_update={'HELM_NAMESPACE': 'test'})
assert result.exception.args[0]['command'] == '/usr/bin/helm upgrade -i --reset-values test /tmp/path'

def test_dependency_update_option_false(self):
set_module_args({
'release_name': 'test',
'release_namespace': 'test',
'chart_ref': '/tmp/path',
'dependency_update': False
})
helm.get_release_status = MagicMock(return_value=None)
helm.fetch_chart_info = MagicMock(return_value=self.chart_info_without_dep)
helm.run_dep_update = MagicMock()
with patch.object(basic.AnsibleModule, 'run_command') as mock_run_command:
mock_run_command.return_value = 0, 'configuration updated', '' # successful execution
with self.assertRaises(AnsibleExitJson) as result:
helm.main()
helm.run_dep_update.assert_not_called()
mock_run_command.assert_called_once_with('/usr/bin/helm upgrade -i --reset-values test /tmp/path',
environ_update={'HELM_NAMESPACE': 'test'})
assert result.exception.args[0]['command'] == '/usr/bin/helm upgrade -i --reset-values test /tmp/path'

def test_dependency_update_option_true(self):
set_module_args({
'release_name': 'test',
'release_namespace': 'test',
'chart_ref': '/tmp/path',
'dependency_update': True
})
helm.get_release_status = MagicMock(return_value=None)
helm.fetch_chart_info = MagicMock(return_value=self.chart_info_with_dep)

with patch.object(basic.AnsibleModule, 'run_command') as mock_run_command:
mock_run_command.return_value = 0, 'configuration updated', ''
with patch.object(basic.AnsibleModule, 'warn') as mock_warn:
with self.assertRaises(AnsibleExitJson) as result:
helm.main()
mock_warn.assert_not_called()
mock_run_command.assert_has_calls([
call('/usr/bin/helm dependency update /tmp/path'), # Run commmand for dependency update
call('/usr/bin/helm upgrade -i --reset-values test /tmp/path', environ_update={'HELM_NAMESPACE': 'test'})])
assert result.exception.args[0]['command'] == '/usr/bin/helm upgrade -i --reset-values test /tmp/path'

def test_dependency_update_option_true_without_dependencies_block(self):
set_module_args({
'release_name': 'test',
'release_namespace': 'test',
'chart_ref': '/tmp/path',
'dependency_update': True
})
helm.get_release_status = MagicMock(return_value=None)
helm.fetch_chart_info = MagicMock(return_value=self.chart_info_without_dep)
with patch.object(basic.AnsibleModule, 'run_command') as mock_run_command:
mock_run_command.return_value = 0, 'configuration updated', '' # successful execution
with patch.object(basic.AnsibleModule, 'warn') as mock_warn:
with self.assertRaises(AnsibleExitJson) as result:
helm.main()
mock_warn.assert_called_once()
mock_run_command.assert_has_calls([
call('/usr/bin/helm dependency update /tmp/path'), # Run commmand for dependency update
call('/usr/bin/helm upgrade -i --reset-values test /tmp/path', environ_update={'HELM_NAMESPACE': 'test'})])
assert result.exception.args[0]['command'] == '/usr/bin/helm upgrade -i --reset-values test /tmp/path'


class TestDependencyUpdateWithChartRepoUrlOption(unittest.TestCase):
def setUp(self):
self.mock_module_helper = patch.multiple(basic.AnsibleModule,
exit_json=exit_json,
fail_json=fail_json,
get_bin_path=get_bin_path)
self.mock_module_helper.start()

# Stop the patch after test execution
# like tearDown but executed also when the setup failed
self.addCleanup(self.mock_module_helper.stop)

self.chart_info_without_dep = {
'apiVersion': 'v2',
'appVersion': 'default',
'description': 'A chart used in molecule tests',
'name': 'test-chart',
'type': 'application',
'version': '0.1.0'
}

self.r = {}
def test_dependency_update_option_not_defined(self):
set_module_args({
'release_name': 'test',
'release_namespace': 'test',
'chart_ref': 'chart1',
'chart_repo_url': 'http://repo.example/charts'
})
helm.get_release_status = MagicMock(return_value=None)
helm.fetch_chart_info = MagicMock(return_value=self.chart_info_without_dep)
with patch.object(basic.AnsibleModule, 'run_command') as mock_run_command:
mock_run_command.return_value = 0, 'configuration updated', '' # successful execution
with self.assertRaises(AnsibleExitJson) as result:
helm.main()
mock_run_command.assert_called_once_with('/usr/bin/helm --repo=http://repo.example/charts upgrade -i --reset-values test chart1',
environ_update={'HELM_NAMESPACE': 'test'})
assert result.exception.args[0]['command'] == '/usr/bin/helm --repo=http://repo.example/charts upgrade -i --reset-values test chart1'

def run_command(self, command, environ_update=None):
self.r = {"command": command, "environ_update": environ_update}
return 0, "", ""
def test_dependency_update_option_False(self):
set_module_args({
'release_name': 'test',
'release_namespace': 'test',
'chart_ref': 'chart1',
'chart_repo_url': 'http://repo.example/charts',
'dependency_update': False
})
helm.get_release_status = MagicMock(return_value=None)
helm.fetch_chart_info = MagicMock(return_value=self.chart_info_without_dep)
with patch.object(basic.AnsibleModule, 'run_command') as mock_run_command:
mock_run_command.return_value = 0, 'configuration updated', '' # successful execution
with self.assertRaises(AnsibleExitJson) as result:
helm.main()
mock_run_command.assert_called_once_with('/usr/bin/helm --repo=http://repo.example/charts upgrade -i --reset-values test chart1',
environ_update={'HELM_NAMESPACE': 'test'})
assert result.exception.args[0]['command'] == '/usr/bin/helm --repo=http://repo.example/charts upgrade -i --reset-values test chart1'

def test_dependency_update_option_True_and_replace_option_disabled(self):
set_module_args({
'release_name': 'test',
'release_namespace': 'test',
'chart_ref': 'chart1',
'chart_repo_url': 'http://repo.example/charts',
'dependency_update': True
})
helm.get_release_status = MagicMock(return_value=None)
helm.fetch_chart_info = MagicMock(return_value=self.chart_info_without_dep)
with patch.object(basic.AnsibleModule, 'run_command') as mock_run_command:
mock_run_command.return_value = 0, 'configuration updated', '' # successful execution
with self.assertRaises(AnsibleFailJson) as result:
helm.main()
# mock_run_command.assert_called_once_with('/usr/bin/helm --repo=http://repo.example/charts upgrade -i --reset-values test chart1',
# environ_update={'HELM_NAMESPACE': 'test'})
assert result.exception.args[0]['msg'] == ("'--dependency-update' hasn't been supported yet with 'helm upgrade'. "
"Please use 'helm install' instead by adding 'replace' option")
assert result.exception.args[0]['failed']

def test_dependency_update_naked():
module = MockedModule()
module.params = {}
common_cmd = "helm"
chart_ref = "/tmp/path/chart"
run_dep_update(module, common_cmd, chart_ref)
assert module.r["command"] == "helm dependency update /tmp/path/chart"
assert module.r["environ_update"] == {}
def test_dependency_update_option_True_and_replace_option_enabled(self):
set_module_args({
'release_name': "test",
'release_namespace': 'test',
'chart_ref': "chart1",
'chart_repo_url': 'http://repo.example/charts',
'dependency_update': True,
'replace': True
})
helm.get_release_status = MagicMock(return_value=None)
helm.fetch_chart_info = MagicMock(return_value=self.chart_info_without_dep)
with patch.object(basic.AnsibleModule, 'run_command') as mock_run_command:
mock_run_command.return_value = 0, 'configuration updated', '' # successful execution
with self.assertRaises(AnsibleExitJson) as result:
helm.main()
mock_run_command.assert_called_once_with('/usr/bin/helm --repo=http://repo.example/charts install --dependency-update --replace test chart1',
environ_update={'HELM_NAMESPACE': 'test'})
assert result.exception.args[0]['command'] == '/usr/bin/helm --repo=http://repo.example/charts install --dependency-update --replace test chart1'
49 changes: 49 additions & 0 deletions tests/unit/utils/ansible_module_mock.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from __future__ import absolute_import, division, print_function

__metaclass__ = type

import json

from ansible.module_utils import basic
from ansible.module_utils.common.text.converters import to_bytes


def set_module_args(args):
"""prepare arguments so that they will be picked up during module creation"""
args = json.dumps({'ANSIBLE_MODULE_ARGS': args})
basic._ANSIBLE_ARGS = to_bytes(args)


class AnsibleExitJson(Exception):
"""Exception class to be raised by module.exit_json and caught by the test case"""
pass


class AnsibleFailJson(Exception):
"""Exception class to be raised by module.fail_json and caught by the test case"""
pass


def exit_json(*args, **kwargs):
"""function to patch over exit_json; package return data into an exception"""
if 'changed' not in kwargs:
kwargs['changed'] = False
raise AnsibleExitJson(kwargs)


def fail_json(*args, **kwargs):
"""function to patch over fail_json; package return data into an exception"""
kwargs['failed'] = True
raise AnsibleFailJson(kwargs)


def get_bin_path(self, arg, required=False):
"""Mock AnsibleModule.get_bin_path"""
if arg.endswith('helm'):
return '/usr/bin/helm'
else:
if required:
fail_json(msg='%r not found !' % arg)

# def warn(self,msg):
# return msg

0 comments on commit 9c40159

Please sign in to comment.