mirror of
https://github.com/home-assistant/core.git
synced 2026-09-28 22:38:48 +01:00
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
150 lines
4.7 KiB
Python
150 lines
4.7 KiB
Python
"""Tests for the Freebox utility methods."""
|
|
|
|
import json
|
|
from pathlib import Path
|
|
from unittest.mock import Mock
|
|
|
|
from freebox_api.exceptions import AuthorizationError, HttpRequestError
|
|
import pytest
|
|
|
|
from homeassistant.components.freebox.const import STORAGE_KEY, STORAGE_VERSION
|
|
from homeassistant.components.freebox.router import (
|
|
async_forget_registration,
|
|
get_hosts_list_if_supported,
|
|
is_invalid_token_error,
|
|
is_json,
|
|
)
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.helpers.storage import Store
|
|
from homeassistant.util import slugify
|
|
|
|
from .const import (
|
|
DATA_LAN_GET_HOSTS_LIST_MODE_BRIDGE,
|
|
DATA_WIFI_GET_GLOBAL_CONFIG,
|
|
MOCK_HOST,
|
|
)
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def mock_path():
|
|
"""Use the real pathlib.Path in this module so file removal can be tested.
|
|
|
|
This overrides the autouse fixture of the same name in conftest.py,
|
|
which stubs out Path for the config flow / setup tests in this package.
|
|
"""
|
|
return
|
|
|
|
|
|
@pytest.fixture
|
|
def hass_config_dir(hass_tmp_config_dir: str) -> str:
|
|
"""Use a temporary config directory so the app token file is isolated.
|
|
|
|
This lets async_forget_registration's own Store lookup be exercised
|
|
for real, instead of mocking Store to point at a test-controlled path.
|
|
"""
|
|
return hass_tmp_config_dir
|
|
|
|
|
|
async def test_is_json() -> None:
|
|
"""Test is_json method."""
|
|
|
|
# Valid JSON values
|
|
assert is_json("{}")
|
|
assert is_json('{ "simple":"json" }')
|
|
assert is_json(json.dumps(DATA_WIFI_GET_GLOBAL_CONFIG))
|
|
assert is_json(json.dumps(DATA_LAN_GET_HOSTS_LIST_MODE_BRIDGE))
|
|
|
|
# Not valid JSON values
|
|
assert not is_json(None)
|
|
assert not is_json("")
|
|
assert not is_json("XXX")
|
|
assert not is_json("{XXX}")
|
|
|
|
|
|
async def test_get_hosts_list_if_supported(
|
|
router: Mock,
|
|
) -> None:
|
|
"""In router mode, get_hosts_list is supported and list is filled."""
|
|
supports_hosts, fbx_devices = await get_hosts_list_if_supported(router())
|
|
assert supports_hosts is True
|
|
# List must not be empty; but its content depends on
|
|
# how many unit tests are executed...
|
|
assert fbx_devices
|
|
# We expect 4 devices from lan_get_hosts_list.json
|
|
# and 1 from lan_get_hosts_list_guest.json
|
|
assert len(fbx_devices) == 5
|
|
assert "d633d0c8-958c-43cc-e807-d881b076924b" in str(fbx_devices)
|
|
assert "d633d0c8-958c-42cc-e807-d881b476924b" in str(fbx_devices)
|
|
|
|
|
|
async def test_get_hosts_list_if_supported_bridge(
|
|
router_bridge_mode: Mock,
|
|
) -> None:
|
|
"""In bridge mode, get_hosts_list is NOT supported and list is empty."""
|
|
supports_hosts, fbx_devices = await get_hosts_list_if_supported(
|
|
router_bridge_mode()
|
|
)
|
|
assert supports_hosts is False
|
|
assert fbx_devices == []
|
|
|
|
|
|
async def test_get_hosts_list_if_supported_bridge_error(
|
|
mock_router_bridge_mode_error: Mock,
|
|
) -> None:
|
|
"""Other exceptions must be propagated."""
|
|
with pytest.raises(HttpRequestError):
|
|
await get_hosts_list_if_supported(mock_router_bridge_mode_error())
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("error", "expected"),
|
|
[
|
|
pytest.param(
|
|
AuthorizationError(
|
|
'Starting session failed (APIResponse: {"success": false, '
|
|
'"error_code": "invalid_token"})'
|
|
),
|
|
True,
|
|
id="invalid_token",
|
|
),
|
|
pytest.param(
|
|
AuthorizationError(
|
|
'Starting session failed (APIResponse: {"success": false, '
|
|
'"error_code": "internal_error"})'
|
|
),
|
|
False,
|
|
id="other_error_code",
|
|
),
|
|
pytest.param(
|
|
AuthorizationError("Authorization timed out"),
|
|
False,
|
|
id="no_api_response",
|
|
),
|
|
pytest.param(
|
|
AuthorizationError("The app token is invalid or has been revoked"),
|
|
False,
|
|
id="denied_pairing_mentions_invalid_but_has_no_error_code",
|
|
),
|
|
],
|
|
)
|
|
def test_is_invalid_token_error(error: AuthorizationError, expected: bool) -> None:
|
|
"""Only a genuine invalid_token APIResponse must be treated as such."""
|
|
assert is_invalid_token_error(error) is expected
|
|
|
|
|
|
async def test_async_forget_registration(hass: HomeAssistant) -> None:
|
|
"""async_forget_registration must remove the stored app token, if any."""
|
|
token_file = (
|
|
Path(Store(hass, STORAGE_VERSION, STORAGE_KEY).path)
|
|
/ f"{slugify(MOCK_HOST)}.conf"
|
|
)
|
|
token_file.parent.mkdir(parents=True, exist_ok=True)
|
|
token_file.write_text('{"app_token": "stale"}')
|
|
assert token_file.exists()
|
|
|
|
await async_forget_registration(hass, MOCK_HOST)
|
|
assert not token_file.exists()
|
|
|
|
# Calling again with no stored file left must not raise.
|
|
await async_forget_registration(hass, MOCK_HOST)
|