supervisor/tests/test_updater.py
Mike Degatano 52cc17fa3f
Delay initial version fetch until there is connectivity (#5603)
* Delay inital version fetch until there is connectivity

* Add test

* Only mock get not whole websession object

* drive delayed fetch off of supervisor connectivity not host

* Fix test to not rely on sleep guessing to track tasks

* Use fixture to remove job throttle temporarily
2025-02-11 13:22:33 +01:00

132 lines
4.7 KiB
Python

"""Test updater files."""
import asyncio
from unittest.mock import AsyncMock, MagicMock, patch
from awesomeversion import AwesomeVersion
import pytest
from supervisor.const import BusEvent
from supervisor.coresys import CoreSys
from supervisor.dbus.const import ConnectivityState
from supervisor.jobs import SupervisorJob
from tests.common import load_binary_fixture
from tests.dbus_service_mocks.network_manager import (
NetworkManager as NetworkManagerService,
)
URL_TEST = "https://version.home-assistant.io/stable.json"
@pytest.mark.usefixtures("no_job_throttle")
async def test_fetch_versions(coresys: CoreSys) -> None:
"""Test download and sync version."""
coresys.security.force = True
await coresys.updater.fetch_data()
async with coresys.websession.get(URL_TEST) as request:
data = await request.json()
assert coresys.updater.version_supervisor == data["supervisor"]
assert coresys.updater.version_homeassistant == data["homeassistant"]["default"]
assert coresys.updater.version_audio == data["audio"]
assert coresys.updater.version_cli == data["cli"]
assert coresys.updater.version_dns == data["dns"]
assert coresys.updater.version_multicast == data["multicast"]
assert coresys.updater.version_observer == data["observer"]
assert coresys.updater.image_homeassistant == data["images"]["core"].format(
machine=coresys.machine
)
assert coresys.updater.image_supervisor == data["images"]["supervisor"].format(
arch=coresys.arch.supervisor
)
assert coresys.updater.image_cli == data["images"]["cli"].format(
arch=coresys.arch.supervisor
)
assert coresys.updater.image_audio == data["images"]["audio"].format(
arch=coresys.arch.supervisor
)
assert coresys.updater.image_dns == data["images"]["dns"].format(
arch=coresys.arch.supervisor
)
assert coresys.updater.image_observer == data["images"]["observer"].format(
arch=coresys.arch.supervisor
)
assert coresys.updater.image_multicast == data["images"]["multicast"].format(
arch=coresys.arch.supervisor
)
@pytest.mark.usefixtures("no_job_throttle")
@pytest.mark.parametrize(
"version, expected",
[
("3.1", "3.13"),
("4.5", "4.20"),
("5.0", "5.13"),
("6.4", "6.6"),
("4.20", "5.13"),
],
)
async def test_os_update_path(coresys: CoreSys, version: str, expected: str):
"""Test OS upgrade path across major versions."""
coresys.os._board = "rpi4" # pylint: disable=protected-access
coresys.os._version = AwesomeVersion(version) # pylint: disable=protected-access
with patch.object(type(coresys.security), "verify_own_content"):
await coresys.updater.fetch_data()
assert coresys.updater.version_hassos == AwesomeVersion(expected)
@pytest.mark.usefixtures("no_job_throttle")
async def test_delayed_fetch_for_connectivity(
coresys: CoreSys, network_manager_service: NetworkManagerService
):
"""Test initial version fetch waits for connectivity on load."""
coresys.websession.get = MagicMock()
coresys.websession.get.return_value.__aenter__.return_value.status = 200
coresys.websession.get.return_value.__aenter__.return_value.read.return_value = (
load_binary_fixture("version_stable.json")
)
coresys.websession.head = AsyncMock()
coresys.security.verify_own_content = AsyncMock()
# Network connectivity change causes a series of async tasks to eventually do a version fetch
# Rather then use some kind of sleep loop, set up listener for start of fetch data job
event = asyncio.Event()
async def find_fetch_data_job_start(job: SupervisorJob):
if job.name == "updater_fetch_data":
event.set()
coresys.bus.register_event(BusEvent.SUPERVISOR_JOB_START, find_fetch_data_job_start)
# Start with no connectivity and confirm there is no version fetch on load
coresys.supervisor.connectivity = False
network_manager_service.connectivity = ConnectivityState.CONNECTIVITY_NONE.value
await coresys.host.network.load()
await coresys.host.network.check_connectivity()
await coresys.updater.load()
coresys.websession.get.assert_not_called()
# Now signal host has connectivity and wait for fetch data to complete to assert
network_manager_service.emit_properties_changed(
{"Connectivity": ConnectivityState.CONNECTIVITY_FULL}
)
await network_manager_service.ping()
async with asyncio.timeout(5):
await event.wait()
await asyncio.sleep(0)
coresys.websession.get.assert_called_once()
assert (
coresys.websession.get.call_args[0][0]
== "https://version.home-assistant.io/stable.json"
)