diff --git a/supervisor/core.py b/supervisor/core.py index 6f3cfd79cca..6cb299ee60d 100644 --- a/supervisor/core.py +++ b/supervisor/core.py @@ -28,7 +28,7 @@ from .resolution.const import ContextType, IssueType, SuggestionType, UnhealthyReason from .utils.dt import utcnow from .utils.sentry import capture_exception -from .utils.whoami import retrieve_whoami +from .utils.whoami import WhoamiData, retrieve_whoami _LOGGER: logging.Logger = logging.getLogger(__name__) @@ -363,6 +363,13 @@ def _update_last_boot(self): self.sys_config.last_boot = self.sys_hardware.helper.last_boot self.sys_config.save_data() + async def _retrieve_whoami(self, with_ssl: bool) -> WhoamiData | None: + try: + return await retrieve_whoami(self.sys_websession, with_ssl) + except WhoamiSSLError: + _LOGGER.info("Whoami service SSL error") + return None + async def _adjust_system_datetime(self): """Adjust system time/date on startup.""" # If no timezone is detect or set @@ -375,21 +382,15 @@ async def _adjust_system_datetime(self): # Get Timezone data try: - data = await retrieve_whoami(self.sys_websession) - except WhoamiSSLError: - pass + data = await self._retrieve_whoami(True) + + # SSL Date Issue & possible time drift + if not data: + data = await self._retrieve_whoami(False) except WhoamiError as err: _LOGGER.warning("Can't adjust Time/Date settings: %s", err) return - # SSL Date Issue & possible time drift - if not data: - try: - data = await retrieve_whoami(self.sys_websession, with_ssl=False) - except WhoamiError as err: - _LOGGER.error("Can't adjust Time/Date settings: %s", err) - return - self.sys_config.timezone = self.sys_config.timezone or data.timezone # Calculate if system time is out of sync diff --git a/tests/test_core.py b/tests/test_core.py index eaf55957942..08a044b6d5c 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1,7 +1,15 @@ """Testing handling with CoreState.""" +# pylint: disable=W0212 +import datetime +from unittest.mock import AsyncMock, PropertyMock, patch from supervisor.const import CoreState from supervisor.coresys import CoreSys +from supervisor.exceptions import WhoamiSSLError +from supervisor.host.control import SystemControl +from supervisor.host.info import InfoCenter +from supervisor.supervisor import Supervisor +from supervisor.utils.whoami import WhoamiData def test_write_state(run_dir, coresys: CoreSys): @@ -14,3 +22,58 @@ def test_write_state(run_dir, coresys: CoreSys): coresys.core.state = CoreState.SHUTDOWN assert run_dir.read_text() == CoreState.SHUTDOWN + + +async def test_adjust_system_datetime(coresys: CoreSys): + """Test _adjust_system_datetime method with successful retrieve_whoami.""" + utc_ts = datetime.datetime.now().replace(tzinfo=datetime.UTC) + with patch( + "supervisor.core.retrieve_whoami", + new_callable=AsyncMock, + side_effect=[WhoamiData("Europe/Zurich", utc_ts)], + ) as mock_retrieve_whoami: + await coresys.core._adjust_system_datetime() + mock_retrieve_whoami.assert_called_once() + assert coresys.core.sys_config.timezone == "Europe/Zurich" + + # Validate we don't retrieve whoami once timezone has been set + mock_retrieve_whoami.reset_mock() + await coresys.core._adjust_system_datetime() + mock_retrieve_whoami.assert_not_called() + + +async def test_adjust_system_datetime_without_ssl(coresys: CoreSys): + """Test _adjust_system_datetime method when retrieve_whoami raises WhoamiSSLError.""" + utc_ts = datetime.datetime.now().replace(tzinfo=datetime.UTC) + with patch( + "supervisor.core.retrieve_whoami", + new_callable=AsyncMock, + side_effect=[WhoamiSSLError("SSL error"), WhoamiData("Europe/Zurich", utc_ts)], + ) as mock_retrieve_whoami: + await coresys.core._adjust_system_datetime() + assert mock_retrieve_whoami.call_count == 2 + assert mock_retrieve_whoami.call_args_list[0].args[1] + assert not mock_retrieve_whoami.call_args_list[1].args[1] + assert coresys.core.sys_config.timezone == "Europe/Zurich" + + +async def test_adjust_system_datetime_if_time_behind(coresys: CoreSys): + """Test _adjust_system_datetime method when current time is ahead more than 3 days.""" + utc_ts = datetime.datetime.now().replace(tzinfo=datetime.UTC) + datetime.timedelta( + days=4 + ) + with patch( + "supervisor.core.retrieve_whoami", + new_callable=AsyncMock, + side_effect=[WhoamiData("Europe/Zurich", utc_ts)], + ) as mock_retrieve_whoami, patch.object( + SystemControl, "set_datetime" + ) as mock_set_datetime, patch.object( + InfoCenter, "dt_synchronized", new=PropertyMock(return_value=False) + ), patch.object( + Supervisor, "check_connectivity" + ) as mock_check_connectivity: + await coresys.core._adjust_system_datetime() + mock_retrieve_whoami.assert_called_once() + mock_set_datetime.assert_called_once() + mock_check_connectivity.assert_called_once()