Skip to content

Commit

Permalink
Add some unit tests for module_utils
Browse files Browse the repository at this point in the history
Signed-off-by: Alina Buzachis <[email protected]>
  • Loading branch information
alinabuzachis committed Aug 13, 2024
1 parent 3a75a16 commit 8195a9b
Show file tree
Hide file tree
Showing 2 changed files with 281 additions and 0 deletions.
101 changes: 101 additions & 0 deletions tests/unit/plugins/module_utils/test_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@

# -*- coding: utf-8 -*-

# Copyright: Contributors to the Ansible project
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)


import json
import pytest
from unittest.mock import Mock, patch
from urllib.error import HTTPError, URLError

from ansible_collections.ansible.eda.plugins.module_utils.errors import (
AuthError,
EDAHTTPError,
)
from ansible_collections.ansible.eda.plugins.module_utils.client import Client, Response # Replace 'your_module' with the actual module name


ENDPOINT = "/api/test_endpoint"
QUERY_PARAMS = {'param': 'value'}
ID = 1
DATA = {"key": "value"}
JSON_DATA = '{"key": "value"}'


@pytest.fixture
def mock_response():
response = Mock()
response.status = 200
response.read.return_value = JSON_DATA.encode('utf-8')
response.headers = {'content-type': 'application/json'}
return response

@pytest.fixture
def mock_error_response():
response = Mock()
response.status = 401
response.read.return_value = b"Unauthorized"
response.headers = {}
return response

@pytest.fixture
def mock_http_error():
return HTTPError(
url="http://example.com",
code=401,
msg="Unauthorized",
hdrs={},
fp=None
)

@pytest.fixture
def mock_url_error():
return URLError("URL error")

@pytest.fixture
def client():
with patch('ansible_collections.ansible.eda.plugins.module_utils.client.Request') as mock_request:
mock_request_instance = Mock()
mock_request.return_value = mock_request_instance
client_instance = Client(
host="http://mocked-url.com",
username="mocked_user",
password="mocked_pass",
timeout=10,
validate_certs=True
)
yield client_instance, mock_request_instance


@pytest.mark.parametrize("method, status_code, expected_response, exception_type, exception_message, headers, data", [
("get", 200, DATA, None, None, {}, None),
("post", 201, DATA, None, None, {"Content-Type": "application/json"}, DATA),
("patch", 200, DATA, None, None, {"Content-Type": "application/json"}, DATA),
("delete", 204, {}, None, None, {}, None),
("post", 401, None, AuthError, "Failed to authenticate with the instance: 401 Unauthorized", {"Content-Type": "application/json"}, DATA),
("get", 404, DATA, None, None, {}, None),
("get", None, None, EDAHTTPError, "URL error", {}, None)
])
def test_client_methods(method, status_code, expected_response, exception_type, exception_message, headers, data, client, mock_response, mock_http_error, mock_url_error):
client_instance, mock_request_instance = client
mock_request_instance.open = Mock()

if exception_type:
if exception_type == AuthError:
mock_request_instance.open.side_effect = mock_http_error
with pytest.raises(exception_type, match=exception_message):
getattr(client_instance, method)(ENDPOINT, data=data, headers=headers)
elif exception_type == EDAHTTPError:
mock_request_instance.open.side_effect = mock_url_error
with pytest.raises(exception_type, match=exception_message):
getattr(client_instance, method)(ENDPOINT, data=data, headers=headers)
else:
mock_response.status = status_code
mock_response.read.return_value = json.dumps(expected_response).encode('utf-8')
mock_request_instance.open.return_value = mock_response

response = getattr(client_instance, method)(ENDPOINT, data=data, headers=headers)
assert response.status == status_code
assert response.json == expected_response
180 changes: 180 additions & 0 deletions tests/unit/plugins/module_utils/test_controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
# -*- coding: utf-8 -*-

# Copyright: Contributors to the Ansible project
# Simplified BSD License (see licenses/simplified_bsd.txt or https://opensource.org/licenses/BSD-2-Clause)


import pytest
from unittest.mock import Mock, MagicMock
from ansible_collections.ansible.eda.plugins.module_utils.errors import EDAError
from ansible_collections.ansible.eda.plugins.module_utils.controller import Controller


ENDPOINT = "test_endpoint"


@pytest.fixture
def mock_client():
return Mock()


@pytest.fixture
def mock_module():
module = Mock()
module.params = {'update_secrets': True}
module.check_mode = False
return module


@pytest.fixture
def controller(mock_client, mock_module):
return Controller(client=mock_client, module=mock_module)


@pytest.mark.parametrize(
"existing_item, new_item, mock_response, expected_result, expected_calls",
[
# create_if_needed without existing item
(
None,
{'name': 'Test'},
Mock(status=201, json={"id": 1}),
{'changed': True, 'id': 1},
1, # Expected number of post calls
),
# create_if_needed with existing item
(
{'id': 1, 'url': 'http://test.com/api/item/1'},
{'name': 'Test'},
None,
None,
0, # Expected number of post calls
),
],
)
def test_create_if_needed(
mock_client, controller, existing_item, new_item, mock_response, expected_result, expected_calls
):
if mock_response:
mock_client.post.return_value = mock_response
result = controller.create_if_needed(existing_item, new_item, ENDPOINT)
assert result == expected_result
assert mock_client.post.call_count == expected_calls
if expected_calls > 0:
mock_client.post.assert_called_with(ENDPOINT, **{"data": new_item})


@pytest.mark.parametrize(
"existing_item, mock_response, expected_result, expected_calls",
[
# delete_if_needed with an existing item
(
{'id': 1, 'name': 'test_item'},
Mock(status=204, json={}),
{'changed': True, 'id': 1},
1, # Expected number of delete calls
),
# delete_if_needed without an existing item
(
None,
None,
{'changed': False},
0, # Expected number of delete calls
),
],
)
def test_delete_if_needed(
mock_client, controller, existing_item, mock_response, expected_result, expected_calls
):
if mock_response:
mock_client.delete.return_value = mock_response

result = controller.delete_if_needed(existing_item, ENDPOINT)
assert result == expected_result
assert mock_client.delete.call_count == expected_calls
if expected_calls > 0:
mock_client.delete.assert_called_with(ENDPOINT, **{"id": existing_item['id']})


def test_update_if_needed_with_existing_item(mock_client, controller):
existing_item = {'id': 1, 'name': 'Test1'}
new_item = {'name': 'Test2'}
response = Mock(status=200, json={"id": 1, "name": "Test2"})
mock_client.patch.return_value = response
result = controller.update_if_needed(existing_item, new_item, ENDPOINT, 'resource type')
mock_client.patch.assert_called_with(ENDPOINT, **{"data": new_item, "id": 1})
assert result['changed'] is True
assert result['id'] == 1


def test_get_endpoint(mock_client, controller):
response = Mock(status=200, json={"count": 1, "results": [{"id": 1}]})
mock_client.get.return_value = response
result = controller.get_endpoint(ENDPOINT)
mock_client.get.assert_called_with(ENDPOINT)
assert result == response


def test_post_endpoint(mock_client, controller):
response = Mock(status=201, json={"id": 1})
mock_client.post.return_value = response
result = controller.post_endpoint(ENDPOINT)
mock_client.post.assert_called_with(ENDPOINT)
assert result == response


def test_patch_endpoint_check_mode(controller):
controller.module.check_mode = True
result = controller.patch_endpoint(ENDPOINT)
assert result['changed'] is True


def test_get_name_field_from_endpoint():
assert Controller.get_name_field_from_endpoint('users') == 'username'
assert Controller.get_name_field_from_endpoint('unknown') == 'name'


@pytest.mark.parametrize(
"item, expected_name, should_raise",
[
({"name": "test_item"}, "test_item", False),
({"username": "test_user"}, "test_user", False),
({}, None, True),
],
)
def test_get_item_name(controller, item, expected_name, should_raise):
if should_raise:
with pytest.raises(EDAError):
controller.get_item_name(item)
else:
assert controller.get_item_name(item) == expected_name


def test_has_encrypted_values():
assert Controller.has_encrypted_values({'key': '$encrypted$'}) is True
assert Controller.has_encrypted_values({'key': 'value'}) is False


def test_fail_wanted_one(mock_client, controller):
response = MagicMock()
response.json.return_value = {'count': 2, 'results': [{'id': 1}, {'id': 2}]}
mock_client.build_url.return_value.geturl.return_value = 'http://example.com/api'
mock_client.host = 'http://example.com'
with pytest.raises(EDAError, match='expected 1'):
controller.fail_wanted_one(response, 'endpoint', {})


def test_fields_could_be_same():
assert Controller.fields_could_be_same({'key': '$encrypted$'}, {'key': 'value'}) is True
assert Controller.fields_could_be_same({'key1': 'value1'}, {'key2': 'value2'}) is False


@pytest.mark.parametrize(
"old, new, expected",
[
({"key": "$encrypted$"}, {"key": "value"}, True),
({"key": "value"}, {"key": "value"}, False),
]
)
def test_objects_could_be_different(controller, old, new, expected):
assert controller.objects_could_be_different(old, new) is expected

0 comments on commit 8195a9b

Please sign in to comment.