import os import tempfile import unittest from unittest.mock import patch from modules import app_device_config, app_device_sync, app_remote_config class RemoteConfigTest(unittest.TestCase): def setUp(self): self.tmpdir = tempfile.TemporaryDirectory() self.creds_path = os.path.join(self.tmpdir.name, "upload_creds.env") def tearDown(self): self.tmpdir.cleanup() def test_update_and_read_remote_config(self): with patch.object(app_remote_config, "_CREDS_PATH", self.creds_path): errors = {} app_remote_config.update_remote_config( { "server_url": "https://example.test/", "device_name": "yaris-2013", "api_key": "secret-key", }, errors, ) self.assertEqual(errors, {}) config = app_remote_config.get_remote_config() self.assertEqual(config["server_url"], "https://example.test") self.assertEqual(config["device_name"], "yaris-2013") self.assertTrue(config["api_key_configured"]) def test_blank_api_key_preserves_existing_until_cleared(self): with patch.object(app_remote_config, "_CREDS_PATH", self.creds_path): app_remote_config.update_remote_config( { "server_url": "https://example.test", "device_name": "yaris-2013", "api_key": "secret-key", }, {}, ) errors = {} app_remote_config.update_remote_config( { "server_url": "https://example.test", "device_name": "yaris-2013", "api_key": "", "clear_api_key": False, }, errors, ) self.assertEqual(errors, {}) self.assertTrue(app_remote_config.get_remote_config()["api_key_configured"]) app_remote_config.update_remote_config( {"clear_api_key": True}, {}, ) self.assertFalse(app_remote_config.get_remote_config()["api_key_configured"]) @patch("modules.app_remote_config.requests.get") def test_test_connection_checks_health_and_device_auth(self, requests_get): class Response: def __init__(self, status_code): self.status_code = status_code def raise_for_status(self): if self.status_code >= 400: raise RuntimeError("bad status") requests_get.side_effect = [Response(200), Response(200)] with patch.object(app_remote_config, "_CREDS_PATH", self.creds_path): app_remote_config.update_remote_config( { "server_url": "https://example.test", "device_name": "yaris-2013", "api_key": "secret-key", }, {}, ) result = app_remote_config.test_connection() self.assertTrue(result["ok"]) self.assertTrue(result["authenticated"]) self.assertEqual(requests_get.call_count, 2) class DeviceConfigSyncPrereqTest(unittest.TestCase): @patch("modules.app_remote_config.get_remote_config") def test_sync_enable_requires_remote_credentials(self, get_remote_config): get_remote_config.return_value = { "server_url": "", "device_name": "", "api_key_configured": False, } errors = {} with patch("modules.app_device_config._get_sync_enabled", return_value=False): app_device_config.update_device_config({"sync_enabled": True}, errors) self.assertIn("sync_enabled", errors) class DeviceSyncNowTest(unittest.TestCase): @patch("modules.app_device_sync._has_internet", return_value=True) @patch("modules.app_device_sync._acknowledge_sync") @patch("modules.app_device_sync._on_config_changed") @patch("modules.app_device_sync._store_synced_config") @patch("modules.app_device_sync.requests.get") @patch("modules.app_remote_config.server_credentials") def test_sync_now_fetches_and_applies_config( self, server_credentials, requests_get, store_synced_config, on_config_changed, acknowledge_sync, _has_internet, ): class Response: status_code = 200 def raise_for_status(self): return None def json(self): return {"owner_name": "alice", "config_version": 4} server_credentials.return_value = { "server_url": "https://example.test", "device_name": "yaris-2013", "api_key": "secret-key", } requests_get.return_value = Response() original_version = app_device_sync._SYNC_CONFIG_VERSION app_device_sync._SYNC_CONFIG_VERSION = 0 try: result = app_device_sync.sync_now() finally: app_device_sync._SYNC_CONFIG_VERSION = original_version self.assertEqual(result["status"], "synced") store_synced_config.assert_called_once() on_config_changed.assert_called_once() acknowledge_sync.assert_called_once_with( "https://example.test", "yaris-2013", "secret-key", 4, ) if __name__ == "__main__": unittest.main()