From 48ffb31390b8802eaeb3e23e60bfc7b3b29e2389 Mon Sep 17 00:00:00 2001 From: Natalie Gaston Date: Mon, 30 Sep 2024 17:35:52 -0700 Subject: [PATCH] Increase Cloudadapter unit test coverage back to 90 percent --- .../unit/cloud/adapters/test_azure_adapter.py | 93 ++++++++++++++++++- .../tests/unit/cloud/test_adapter_factory.py | 19 ++++ .../dispatcher/dispatcher_class.py | 2 +- inbm/dockerfiles/Dockerfile-check.m4 | 2 +- 4 files changed, 113 insertions(+), 3 deletions(-) diff --git a/inbm/cloudadapter-agent/tests/unit/cloud/adapters/test_azure_adapter.py b/inbm/cloudadapter-agent/tests/unit/cloud/adapters/test_azure_adapter.py index 457c024f0..9465e6c47 100644 --- a/inbm/cloudadapter-agent/tests/unit/cloud/adapters/test_azure_adapter.py +++ b/inbm/cloudadapter-agent/tests/unit/cloud/adapters/test_azure_adapter.py @@ -3,6 +3,7 @@ """ +import json import unittest import mock from unittest.mock import MagicMock @@ -11,6 +12,36 @@ from cloudadapter.cloud.adapters.azure_adapter import AzureAdapter from time import time +#class TestAzureAdapterFailures(unittest.TestCase): + # @mock.patch('cloudadapter.cloud.adapters.azure_adapter.requests.put') + # @mock.patch('cloudadapter.cloud.adapters.azure_adapter.requests.get') + # def test_configure_json_decode_error(self, mock_get, mock_put): + # # Set up the AzureAdapter with some dummy configs + # configs = { + # "scope_id": "test_scope_id", + # "device_id": "test_device_id", + # "device_sas_key": "test_device_sas_key" + # } + # adapter = AzureAdapter(configs) + + # # Mock the put request to return a response with invalid JSON content + # mock_put_response = MagicMock() + # mock_put_response.ok = True + # mock_put_response.text = "Invalid JSON" + # mock_put.return_value = mock_put_response + + # # Mock the get request to return a response with valid JSON content + # # This is necessary because the configure method may call get after put + # mock_get_response = MagicMock() + # mock_get_response.ok = True + # mock_get_response.text = json.dumps({"status": "assigned", "registrationState": {"assignedHub": "test_hub"}}) + # mock_get.return_value = mock_get_response + + # with self.assertRaises(AdapterConfigureError) as context: + # adapter.configure(configs) + + # # Check that the exception message contains the expected text + # self.assertIn("Error retrieving hostname", str(context.exception)) class TestAzureAdapter(unittest.TestCase): @@ -50,6 +81,66 @@ def setUp(self, mock_build_client_with_config, MockCloudClient, _mock_retrieve_h self.azure_adapter = AzureAdapter(self.CONFIG) self.azure_adapter.configure(self.CONFIG) + @mock.patch('cloudadapter.cloud.adapters.azure_adapter.requests.put') + @mock.patch('cloudadapter.cloud.adapters.azure_adapter.requests.get') + def test_configure_json_decode_error(self, mock_get, mock_put): + # Mock the put request to return a response with invalid JSON content + mock_put_response = MagicMock() + mock_put_response.ok = True + mock_put_response.text = "Invalid JSON" + mock_put.return_value = mock_put_response + + # Mock the get request to return a response with valid JSON content + # This is necessary because the configure method may call get after put + mock_get_response = MagicMock() + mock_get_response.ok = True + mock_get_response.text = json.dumps({"status": "assigned", "registrationState": {"assignedHub": "test_hub"}}) + mock_get.return_value = mock_get_response + + with self.assertRaises(AdapterConfigureError) as context: + self.azure_adapter.configure(self.CONFIG) + + # Check that the exception message contains the expected text + self.assertIn("Error retrieving hostname", str(context.exception)) + + @mock.patch('cloudadapter.cloud.adapters.azure_adapter.requests.put') + @mock.patch('cloudadapter.cloud.adapters.azure_adapter.requests.get') + def test_retrieve_hostname_while_loop(self, mock_get, mock_put): + # Mock the put request to return a response indicating the device is being assigned + assigning_response = { + "status": "assigning", + "operationId": "test_operation_id" + } + mock_put_response = MagicMock() + mock_put_response.ok = True + mock_put_response.text = json.dumps(assigning_response) + mock_put.return_value = mock_put_response + + # Mock the get request to return "assigning" status twice, then "assigned" + assigning_get_response = MagicMock() + assigning_get_response.ok = True + assigning_get_response.text = json.dumps(assigning_response) + + assigned_response = { + "status": "assigned", + "registrationState": {"assignedHub": "test_hub"} + } + assigned_get_response = MagicMock() + assigned_get_response.ok = True + assigned_get_response.text = json.dumps(assigned_response) + + # Set up the side_effect for the get mock to return "assigning" twice, then "assigned" + mock_get.side_effect = [assigning_get_response, assigning_get_response, assigned_get_response] + + # Call the _retrieve_hostname method + hostname = self.azure_adapter._retrieve_hostname(self.CONFIG['scope_id'], self.CONFIG['device_id'], {'sas_key': self.CONFIG['device_sas_key']}, None) + + # Assert that the hostname is as expected + self.assertEqual(hostname, "test_hub") + + # Assert that the get request was called three times + self.assertEqual(mock_get.call_count, 3) + @mock.patch.object(AzureAdapter, '_retrieve_hostname', autospec=True) @mock.patch('cloudadapter.cloud.client.cloud_client.CloudClient', autospec=True) @mock.patch( @@ -71,7 +162,7 @@ def test_sas_configure_succeeds( self.azure_adapter.configure(self.SAS_CONFIG) assert mock_get_sas_token.call_count == 1 assert mock_build_client_with_config.call_count == 1 - + @mock.patch('base64.b64encode', autospec=True) @mock.patch('future.moves.urllib.request.quote', autospec=True) def test_generate_sas_token(self, mock_quote, mock_base64encode) -> None: diff --git a/inbm/cloudadapter-agent/tests/unit/cloud/test_adapter_factory.py b/inbm/cloudadapter-agent/tests/unit/cloud/test_adapter_factory.py index 61da11d88..100429215 100644 --- a/inbm/cloudadapter-agent/tests/unit/cloud/test_adapter_factory.py +++ b/inbm/cloudadapter-agent/tests/unit/cloud/test_adapter_factory.py @@ -5,6 +5,7 @@ """ +import json import unittest import mock @@ -35,6 +36,24 @@ def test_islink_error(self, mock_islink) -> None: str(context.exception), ) + @mock.patch('cloudadapter.cloud.adapter_factory.load_adapter_config') + def test_get_adapter_config_filepaths_with_auxiliary_files(self, mock_load): + mock_load.return_value = { + "auxiliary_files": ["aux_file_1.json", "aux_file_2.json"] + } + filepaths = adapter_factory.get_adapter_config_filepaths() + self.assertIn("aux_file_1.json", filepaths) + self.assertIn("aux_file_2.json", filepaths) + self.assertIn(ADAPTER_CONFIG_PATH, filepaths) + + @mock.patch("os.path.islink", return_value=False) + @mock.patch("builtins.open") + def test_load_adapter_config_success(self, mock_open, mock_islink) -> None: + mock_data = json.dumps({'key': 'value'}) + mock_open.return_value.__enter__.return_value.read.return_value = mock_data + result = load_adapter_config() + self.assertEqual(result, {'key': 'value'}) + @mock.patch('cloudadapter.cloud.adapter_factory.AzureAdapter') @mock.patch('cloudadapter.cloud.adapter_factory.load_adapter_config', autospec=True) def test_get_adapter_azure_succeeds(self, mock_load_adapter_config, MockAzureAdapter) -> None: diff --git a/inbm/dispatcher-agent/dispatcher/dispatcher_class.py b/inbm/dispatcher-agent/dispatcher/dispatcher_class.py index ab8410c91..265a06e35 100644 --- a/inbm/dispatcher-agent/dispatcher/dispatcher_class.py +++ b/inbm/dispatcher-agent/dispatcher/dispatcher_class.py @@ -496,7 +496,7 @@ def _on_cloud_request(self, topic: str, payload: str, qos: int) -> None: request_type = topic.split('/')[2] request_id = topic.split('/')[3] if len(topic.split('/')) > 3 else None manifest = payload - self._add_cmd_to_queue(request_type, manifest, request_id) + self._add_request_to_queue(request_type, manifest, request_id) def _on_message(self, topic: str, payload: Any, qos: int) -> None: """Called when a message is received from _telemetry-agent diff --git a/inbm/dockerfiles/Dockerfile-check.m4 b/inbm/dockerfiles/Dockerfile-check.m4 index 0245ecb2a..9d3b222c4 100644 --- a/inbm/dockerfiles/Dockerfile-check.m4 +++ b/inbm/dockerfiles/Dockerfile-check.m4 @@ -175,7 +175,7 @@ RUN source /venv-py3/bin/activate && \ mkdir -p /output/coverage && \ set -o pipefail && \ export PYTHONPATH=$PYTHONPATH:$(pwd) && \ - pytest --timeout=10 -n 10 --cov=cloudadapter --cov-report=term-missing --cov-fail-under=89 tests/unit 2>&1 | tee /output/coverage/cloudadapter-coverage.txt + pytest --timeout=10 -n 10 --cov=cloudadapter --cov-report=term-missing --cov-fail-under=90 tests/unit 2>&1 | tee /output/coverage/cloudadapter-coverage.txt # ---telemetry agent---