import unittest from unittest.mock import patch, Mock import requests import json import os from components import ArchiveSynchronizer class TestArchiveSynchronizer(unittest.TestCase): def setUp(self): self.synchronizer = ArchiveSynchronizer(config=Mock(), name=None) self.synchronizer.archive_endpoint = "https://dev.r5portal.it/api/st-ten-save/" self.synchronizer.status_endpoint = "https://dev.r5portal.it/api/device-info-update/" self.synchronizer.download_endpoint = "https://dev.r5portal.it/media/uploads/" self.synchronizer.machine_id = "st-ten-1" self.synchronizer.log = Mock() @patch('requests.Session') @patch('os.makedirs') @patch('builtins.open', new_callable=unittest.mock.mock_open) def test_remote_fetch_success(self, mock_open, mock_makedirs, mock_session): remote_path = "img-001.png" local_path = "config/warning_images" mock_response = Mock() mock_response.status_code = 200 mock_response.content = b'Test content' mock_session.return_value.__enter__.return_value.get.return_value = mock_response result = self.synchronizer.remote_fetch(remote_path, local_path) self.assertEqual(result, {"downloaded_file": f"{local_path}/{remote_path}"}) mock_open.assert_called_with(f"{local_path}/{remote_path}", "wb") mock_open().write.assert_called_with(b'Test content') @patch('requests.Session') def test_remote_fetch_not_found(self, mock_session): remote_path = "img-001.png" local_path = "config/warning_images" mock_response = Mock() mock_response.status_code = 404 mock_session.return_value.__enter__.return_value.get.return_value = mock_response result = self.synchronizer.remote_fetch(remote_path, local_path) self.assertEqual(result, {"error": "File not found"}) @patch('requests.Session') def test_remote_fetch_forbidden(self, mock_session): remote_path = "img-001.png" local_path = "config/warning_images" mock_response = Mock() mock_response.status_code = 403 mock_session.return_value.__enter__.return_value.get.return_value = mock_response result = self.synchronizer.remote_fetch(remote_path, local_path) self.assertEqual(result, {"error": "Access forbidden or not logged in"}) @patch('requests.Session') @patch('requests.Session.get', side_effect=requests.ConnectionError("Connection error")) def test_remote_fetch_connection_error(self, mock_get, mock_session): remote_path = "img-001.png" local_path = "config/warning_images" result = self.synchronizer.remote_fetch(remote_path, local_path) self.assertEqual(result, {"error": "Connection error"}) self.synchronizer.log.error.assert_called_with( "Connection error occurred while fetching the file: Connection error") @patch('requests.Session') @patch('requests.Session.get', side_effect=requests.Timeout("Timeout error")) def test_remote_fetch_timeout_error(self, mock_get, mock_session): remote_path = "img-001.png" local_path = "config/warning_images" result = self.synchronizer.remote_fetch(remote_path, local_path) self.assertEqual(result, {"error": "Timeout error"}) self.synchronizer.log.error.assert_called_with("Timeout error occurred while fetching the file: Timeout error") @patch('requests.Session') def test_parse_response_and_execute_success(self, mock_session): mock_response = Mock() mock_response.json.return_value = { "STATUS": "OK", "ACTIONS_TO_DO": [ {"remote_path": "img-001.png", "local_path": "config/warning_images"} ] } with patch.object(self.synchronizer, 'remote_fetch', return_value={"downloaded_file": "config/warning_images/img-001.png"}) as mock_rf: self.synchronizer.parse_response_and_execute(mock_response) mock_rf.assert_called_once_with(remote_path="img-001.png", local_path="config/warning_images") def test_parse_response_and_execute_json_error(self): mock_response = Mock() mock_response.json.side_effect = json.JSONDecodeError("Expecting value", "doc", 0) self.synchronizer.parse_response_and_execute(mock_response) self.synchronizer.log.error.assert_called_with("Failed to decode JSON response") @patch('requests.Session') @patch.object(ArchiveSynchronizer, 'parse_response_and_execute') def test_update_machine_status_success(self, mock_parse_response, mock_session): mock_response = Mock() mock_response.status_code = 200 mock_session.return_value.__enter__.return_value.post.return_value = mock_response self.synchronizer.machine_status = "running" result = self.synchronizer.update_machine_status() self.assertTrue(result) mock_parse_response.assert_called_once_with(mock_response) self.synchronizer.log.info.assert_called_with("Status: running: Machine Status Updated Successfully") @patch('requests.Session') def test_update_machine_status_bad_status_response(self, mock_session): mock_response = Mock() mock_response.status_code = 500 mock_session.return_value.__enter__.return_value.post.return_value = mock_response self.synchronizer.machine_status = "idle" result = self.synchronizer.update_machine_status() self.assertFalse(result) self.synchronizer.log.warning.assert_called_with( "Status: idle: failed to update machine status: bad status response: 500: no response" ) @patch('requests.Session', side_effect=requests.ConnectionError("Connection error")) def test_update_machine_status_connection_error(self, mock_session): self.synchronizer.machine_status = "offline" result = self.synchronizer.update_machine_status() self.assertFalse(result) self.synchronizer.log.warning.assert_called_with( "Status: offline: failed to update machine status, archive_endpoint might be unreachable: Connection error" ) @patch('requests.Session', side_effect=requests.Timeout("Timeout error")) def test_update_machine_status_timeout_error(self, mock_session): self.synchronizer.machine_status = "offline" result = self.synchronizer.update_machine_status() self.assertFalse(result) self.synchronizer.log.warning.assert_called_with( "Status: offline: failed to update machine status, archive_endpoint might be unreachable: Timeout error" ) if __name__ == '__main__': unittest.main()