diff --git a/.gitea/workflows/pull-request-open.yml b/.gitea/workflows/pull-request-open.yml index 4ab33eb..fcc53ca 100644 --- a/.gitea/workflows/pull-request-open.yml +++ b/.gitea/workflows/pull-request-open.yml @@ -46,6 +46,13 @@ jobs: shell: bash working-directory: ${{ gitea.workspace }} + - name: Setup testing environment + run: | + mkdir -p /tmp/ansible_collections/ednz_cloud + ln -s ${{ gitea.workspace }} /tmp/ansible_collections/ednz_cloud/infomaniak + shell: bash + working-directory: ${{ gitea.workspace }} + - name: Run ansible unit tests run: ansible-test units --coverage shell: bash diff --git a/tests/unit/plugins/modules/test_public_cloud_config_info.py b/tests/unit/plugins/modules/test_public_cloud_config_info.py index 1e401c8..89db23a 100644 --- a/tests/unit/plugins/modules/test_public_cloud_config_info.py +++ b/tests/unit/plugins/modules/test_public_cloud_config_info.py @@ -1,11 +1,40 @@ import unittest +import json +import pytest from unittest.mock import patch, MagicMock from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils import basic +from ansible.module_utils.common.text.converters import to_bytes from ansible_collections.ednz_cloud.infomaniak.plugins.modules import ( public_cloud_config_info, ) +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): + def __init__(self, kwargs): + self.kwargs = kwargs + + +def exit_json(*args, **kwargs): + if "changed" not in kwargs: + kwargs["changed"] = False + raise AnsibleExitJson(kwargs) + + +def fail_json(*args, **kwargs): + raise SystemExit(json.dumps(kwargs)) + + class TestPublicCloudConfigInfoModule(unittest.TestCase): """ Unit tests for the public_cloud_config_info Ansible module. @@ -21,7 +50,6 @@ class TestPublicCloudConfigInfoModule(unittest.TestCase): """ Test a successful retrieval of public cloud configuration. """ - # Mock API response data api_response_data = { "result": "success", "data": { @@ -34,209 +62,139 @@ class TestPublicCloudConfigInfoModule(unittest.TestCase): }, } - # Mock the InfomaniakAPIClient instance mock_client_instance = MagicMock() mock_client_instance.get.return_value = api_response_data mock_client_class.return_value = mock_client_instance - # Mock AnsibleModule parameters mock_module_instance = MagicMock() mock_module_instance.params = { "api_token": "mock_api_token", "account_id": "mock_account_id", } - mock_module_instance.check_mode = False # Simulate non-check mode + mock_module_instance.check_mode = False mock_ansible_module.return_value = mock_module_instance - # Mock exit_json to prevent actual exit mock_module_instance.exit_json = MagicMock() - # Execute the run_module function public_cloud_config_info.run_module() - # Assert that the InfomaniakAPIClient was initialized with the correct token mock_client_class.assert_called_with( api_version="1", api_token="mock_api_token" ) - # Assert that the correct API call was made mock_client_instance.get.assert_called_once_with( "/public_clouds/config", params={"account_id": "mock_account_id"} ) - # Assert exit_json is called once with the expected data mock_module_instance.exit_json.assert_called_once_with( changed=False, config=api_response_data["data"] ) - @patch( - "ansible_collections.ednz_cloud.infomaniak.plugins.modules.public_cloud_config_info.AnsibleModule" - ) - @patch( - "ansible_collections.ednz_cloud.infomaniak.plugins.modules.public_cloud_config_info.InfomaniakAPIClient" - ) - def test_api_failure(self, mock_client_class, mock_ansible_module): - """ - Test failure scenario where API returns an error result. - """ + # def test_successful_retrieval(self): + # """ + # Test a successful retrieval of public cloud configuration. + # """ + # set_module_args( + # { + # "api_token": "mock_api_token", + # "account_id": "mock_account_id", + # } + # ) + # + # api_response_data = { + # "result": "success", + # "data": { + # "free_tier": 300, + # "free_tier_used": 24.34, + # "account_resource_level": 2, + # "valid_from": 1707584356, + # "valid_to": 1717192799, + # "project_count": 2, + # }, + # } + # + # self.mock_client_instance.get.return_value = api_response_data + # + # with patch.object( + # AnsibleModule, "exit_json", side_effect=exit_json + # ) as mock_exit_json: + # with patch.object( + # AnsibleModule, "fail_json", side_effect=fail_json + # ) as mock_fail_json: + # with pytest.raises(AnsibleExitJson) as e: + # public_cloud_config_info.main() + # mock_fail_json.assert_not_called() + # result = e.value.kwargs + # assert result["changed"] is False + # assert "config" in result + # assert result["config"] == api_response_data["data"] + # mock_exit_json.assert_called_once() + # self.mock_client_class.assert_called_with( + # api_version="1", api_token="mock_api_token" + # ) + # self.mock_client_instance.get.assert_called_once_with( + # "/public_clouds/config", + # params={"account_id": "mock_account_id"}, + # ) - # Mock API failure response - mock_client_instance = MagicMock() - mock_client_instance.get.side_effect = Exception( - "API request failed with result: error" - ) - mock_client_class.return_value = mock_client_instance - - # Mock AnsibleModule parameters - mock_module_instance = MagicMock() - mock_module_instance.params = { - "api_token": "mock_api_token", - "account_id": "invalid_account_id", - } - mock_module_instance.check_mode = False # Simulate non-check mode - mock_ansible_module.return_value = mock_module_instance - - # Mock fail_json to prevent actual exit - mock_module_instance.fail_json = MagicMock() - - # Execute the run_module function - public_cloud_config_info.run_module() - - # Assert fail_json was called with the expected error message - mock_module_instance.fail_json.assert_called_once_with( - msg="API request failed with result: error" - ) - - @patch( - "ansible_collections.ednz_cloud.infomaniak.plugins.modules.public_cloud_config_info.AnsibleModule" - ) - def test_missing_required_params(self, mock_ansible_module): + def test_missing_required_params(self): """ Test behavior when required parameters are missing. """ - # Mock the AnsibleModule instance - mock_module_instance = MagicMock() - - # Simulate missing 'api_token' in the parameters - mock_module_instance.params = { - "account_id": "mock_account_id", # Missing api_token - } - - # Simulate Ansible behavior: fail_json should be called when 'api_token' is missing - mock_ansible_module.return_value = mock_module_instance - - # Execute the run_module function and expect KeyError - with self.assertRaises(KeyError): - public_cloud_config_info.run_module() - - @patch( - "ansible_collections.ednz_cloud.infomaniak.plugins.modules.public_cloud_config_info.AnsibleModule" - ) - @patch( - "ansible_collections.ednz_cloud.infomaniak.plugins.modules.public_cloud_config_info.InfomaniakAPIClient" - ) - def test_empty_api_response(self, mock_client_class, mock_ansible_module): - """ - Test behavior when the API returns an empty response. - """ - # Mock API response data - api_response_data = {"result": "success", "data": {}} # Empty data - - # Mock the InfomaniakAPIClient instance - mock_client_instance = MagicMock() - mock_client_instance.get.return_value = api_response_data - mock_client_class.return_value = mock_client_instance - - # Mock AnsibleModule parameters - mock_module_instance = MagicMock() - mock_module_instance.params = { - "api_token": "mock_api_token", - "account_id": "mock_account_id", - } - mock_module_instance.check_mode = False # Simulate non-check mode - mock_ansible_module.return_value = mock_module_instance - - # Execute the run_module function - public_cloud_config_info.run_module() - - # Assert exit_json is called once with empty config data - mock_module_instance.exit_json.assert_called_once_with(changed=False, config={}) - - @patch( - "ansible_collections.ednz_cloud.infomaniak.plugins.modules.public_cloud_config_info.AnsibleModule" - ) - @patch( - "ansible_collections.ednz_cloud.infomaniak.plugins.modules.public_cloud_config_info.InfomaniakAPIClient" - ) - def test_unexpected_api_response_format( - self, mock_client_class, mock_ansible_module - ): - """ - Test behavior when the API response format is unexpected. - """ - # Mock an unexpected API response (missing 'result') - api_response_data = { - "data": { - "free_tier": 300, + set_module_args( + { + "api_token": "mock_api_token", + # 'account_id' is missing } - } - - # Mock the InfomaniakAPIClient instance - mock_client_instance = MagicMock() - mock_client_instance.get.return_value = api_response_data - mock_client_class.return_value = mock_client_instance - - # Mock AnsibleModule parameters - mock_module_instance = MagicMock() - mock_module_instance.params = { - "api_token": "mock_api_token", - "account_id": "mock_account_id", - } - mock_ansible_module.return_value = mock_module_instance - - # Execute the run_module function - public_cloud_config_info.run_module() - - # Assert fail_json is called with an appropriate error - mock_module_instance.fail_json.assert_called_once_with( - msg="API request failed with result: None" ) - @patch( - "ansible_collections.ednz_cloud.infomaniak.plugins.modules.public_cloud_config_info.AnsibleModule" - ) - def test_check_mode(self, mock_ansible_module): - """ - Test behavior when check_mode is enabled. - """ - # Mock AnsibleModule parameters - mock_module_instance = MagicMock() - mock_module_instance.params = { - "api_token": "mock_api_token", - "account_id": "mock_account_id", - } - mock_module_instance.check_mode = True # Simulate check mode - mock_ansible_module.return_value = mock_module_instance + with patch.object(AnsibleModule, "fail_json", side_effect=fail_json): + with pytest.raises(SystemExit) as e: + public_cloud_config_info.main() - # Execute the run_module function - public_cloud_config_info.run_module() + captured_output = json.loads(e.value.args[0]) - # Assert exit_json is called with the check_mode message - mock_module_instance.exit_json.assert_called_once_with( - changed=False, - msg="Check mode: No changes made, would retrieve public cloud config.", + assert "msg" in captured_output + assert "missing required arguments: account_id" in captured_output["msg"] + + def test_check_mode(self): + """ + Test the behavior in check mode, where no changes should be made. + """ + set_module_args( + { + "api_token": "mock_api_token", + "account_id": "mock_account_id", + "_ansible_check_mode": True, + } ) - @patch( - "ansible_collections.ednz_cloud.infomaniak.plugins.modules.public_cloud_config_info.AnsibleModule" - ) - @patch( - "ansible_collections.ednz_cloud.infomaniak.plugins.modules.public_cloud_config_info.InfomaniakAPIClient" - ) - def test_network_timeout(self, mock_client_class, mock_ansible_module): + with patch.object( + AnsibleModule, "exit_json", side_effect=exit_json + ) as mock_exit_json: + with pytest.raises(AnsibleExitJson) as e: + public_cloud_config_info.main() + + mock_exit_json.assert_called_once_with( + changed=False, + msg="Check mode: No changes made, would retrieve public cloud config.", + ) + + def test_network_timeout(self): """ Test behavior when a network timeout occurs. """ + + set_module_args( + { + "api_token": "mock_api_token", + "account_id": "mock_account_id", + } + ) + + with patch.object(AnsibleModule, "fail_json", side_effect=fail_json): + with pytest.raises(SystemExit) as e: + public_cloud_config_info.main() # Simulate a network timeout error mock_client_instance = MagicMock() mock_client_instance.get.side_effect = Exception("Request timeout") @@ -257,36 +215,61 @@ class TestPublicCloudConfigInfoModule(unittest.TestCase): mock_module_instance.fail_json.assert_called_once_with(msg="Request timeout") @patch( - "ansible_collections.ednz_cloud.infomaniak.plugins.modules.public_cloud_config_info.AnsibleModule" + "ansible_collections.ednz_cloud.infomaniak.plugins.modules.public_cloud_config_info.InfomaniakAPIClient" ) + def test_network_timeout(self, mock_client_class): + """ + Test behavior when a network timeout occurs. + """ + # Simulate a network timeout error + mock_client_instance = MagicMock() + mock_client_instance.get.side_effect = Exception("Request timeout") + mock_client_class.return_value = mock_client_instance + + set_module_args( + { + "api_token": "mock_api_token", + "account_id": "mock_account_id", + } + ) + + with patch.object( + AnsibleModule, "fail_json", side_effect=fail_json + ) as mock_fail_json: + with pytest.raises(SystemExit) as e: + public_cloud_config_info.main() + + captured_output = json.loads(e.value.args[0]) + assert "msg" in captured_output + assert captured_output["msg"] == "Request timeout" + mock_fail_json.assert_called_once() + @patch( "ansible_collections.ednz_cloud.infomaniak.plugins.modules.public_cloud_config_info.InfomaniakAPIClient" ) - def test_invalid_json_response(self, mock_client_class, mock_ansible_module): + def test_invalid_json_response(self, mock_client_class): """ Test behavior when the API returns an invalid JSON response. """ - # Mock the InfomaniakAPIClient instance to raise a JSONDecodeError mock_client_instance = MagicMock() mock_client_instance.get.side_effect = Exception("Invalid JSON response") mock_client_class.return_value = mock_client_instance - # Mock AnsibleModule parameters - mock_module_instance = MagicMock() - mock_module_instance.params = { - "api_token": "mock_api_token", - "account_id": "mock_account_id", - } - mock_ansible_module.return_value = mock_module_instance - - # Execute the run_module function - public_cloud_config_info.run_module() - - # Assert fail_json is called with the appropriate error message - mock_module_instance.fail_json.assert_called_once_with( - msg="Invalid JSON response" + # Set module args with the required parameters + set_module_args( + { + "api_token": "mock_api_token", + "account_id": "mock_account_id", + } ) + with patch.object( + AnsibleModule, "fail_json", side_effect=fail_json + ) as mock_fail_json: + with pytest.raises(SystemExit) as e: + public_cloud_config_info.main() -if __name__ == "__main__": - unittest.main() + captured_output = json.loads(e.value.args[0]) + assert "msg" in captured_output + assert captured_output["msg"] == "Invalid JSON response" + mock_fail_json.assert_called_once()