Add init test to Freebox (#46998)

* Add init test to Freebox

* Review : more readable conftest

* Expect 2 blank lines between defs

* Review : Not I/O in the event loop

* Fix test_setup test

* remove useless const

* Review : mock setup methods

* Add service test

* Add import test
This commit is contained in:
Quentame 2021-03-02 13:37:33 +01:00 committed by GitHub
parent ab53b49d3f
commit 6b9abfc2c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 585 additions and 64 deletions

View File

@ -314,7 +314,6 @@ omit =
homeassistant/components/foscam/camera.py
homeassistant/components/foursquare/*
homeassistant/components/free_mobile/notify.py
homeassistant/components/freebox/__init__.py
homeassistant/components/freebox/device_tracker.py
homeassistant/components/freebox/router.py
homeassistant/components/freebox/sensor.py

View File

@ -9,7 +9,7 @@ from homeassistant.const import CONF_HOST, CONF_PORT, EVENT_HOMEASSISTANT_STOP
from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.typing import HomeAssistantType
from .const import DOMAIN, PLATFORMS
from .const import DOMAIN, PLATFORMS, SERVICE_REBOOT
from .router import FreeboxRouter
_LOGGER = logging.getLogger(__name__)
@ -55,7 +55,7 @@ async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry):
"""Handle reboot service call."""
await router.reboot()
hass.services.async_register(DOMAIN, "reboot", async_reboot)
hass.services.async_register(DOMAIN, SERVICE_REBOOT, async_reboot)
async def async_close_connection(event):
"""Close Freebox connection on HA Stop."""
@ -79,5 +79,6 @@ async def async_unload_entry(hass: HomeAssistantType, entry: ConfigEntry):
if unload_ok:
router = hass.data[DOMAIN].pop(entry.unique_id)
await router.close()
hass.services.async_remove(DOMAIN, SERVICE_REBOOT)
return unload_ok

View File

@ -8,6 +8,7 @@ from homeassistant.const import (
)
DOMAIN = "freebox"
SERVICE_REBOOT = "reboot"
APP_DESC = {
"app_id": "hass",

View File

@ -1,6 +1,7 @@
"""Represent the Freebox router and its devices and sensors."""
from datetime import datetime, timedelta
import logging
import os
from pathlib import Path
from typing import Any, Dict, List, Optional
@ -31,6 +32,18 @@ _LOGGER = logging.getLogger(__name__)
SCAN_INTERVAL = timedelta(seconds=30)
async def get_api(hass: HomeAssistantType, host: str) -> Freepybox:
"""Get the Freebox API."""
freebox_path = hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY).path
if not os.path.exists(freebox_path):
await hass.async_add_executor_job(os.makedirs, freebox_path)
token_file = Path(f"{freebox_path}/{slugify(host)}.conf")
return Freepybox(APP_DESC, token_file, API_VERSION)
class FreeboxRouter:
"""Representation of a Freebox router."""
@ -188,13 +201,3 @@ class FreeboxRouter:
def wifi(self) -> Wifi:
"""Return the wifi."""
return self._api.wifi
async def get_api(hass: HomeAssistantType, host: str) -> Freepybox:
"""Get the Freebox API."""
freebox_path = Path(hass.helpers.storage.Store(STORAGE_VERSION, STORAGE_KEY).path)
freebox_path.mkdir(exist_ok=True)
token_file = Path(f"{freebox_path}/{slugify(host)}.conf")
return Freepybox(APP_DESC, token_file, API_VERSION)

View File

@ -1,11 +1,41 @@
"""Test helpers for Freebox."""
from unittest.mock import patch
from unittest.mock import AsyncMock, patch
import pytest
from .const import (
DATA_CALL_GET_CALLS_LOG,
DATA_CONNECTION_GET_STATUS,
DATA_LAN_GET_HOSTS_LIST,
DATA_STORAGE_GET_DISKS,
DATA_SYSTEM_GET_CONFIG,
WIFI_GET_GLOBAL_CONFIG,
)
@pytest.fixture(autouse=True)
def mock_path():
"""Mock path lib."""
with patch("homeassistant.components.freebox.router.Path"):
yield
@pytest.fixture(name="router")
def mock_router():
"""Mock a successful connection."""
with patch("homeassistant.components.freebox.router.Freepybox") as service_mock:
instance = service_mock.return_value
instance.open = AsyncMock()
instance.system.get_config = AsyncMock(return_value=DATA_SYSTEM_GET_CONFIG)
# sensor
instance.call.get_calls_log = AsyncMock(return_value=DATA_CALL_GET_CALLS_LOG)
instance.storage.get_disks = AsyncMock(return_value=DATA_STORAGE_GET_DISKS)
instance.connection.get_status = AsyncMock(
return_value=DATA_CONNECTION_GET_STATUS
)
# switch
instance.wifi.get_global_config = AsyncMock(return_value=WIFI_GET_GLOBAL_CONFIG)
# device_tracker
instance.lan.get_hosts_list = AsyncMock(return_value=DATA_LAN_GET_HOSTS_LIST)
instance.close = AsyncMock()
yield service_mock

View File

@ -0,0 +1,376 @@
"""Test constants."""
MOCK_HOST = "myrouter.freeboxos.fr"
MOCK_PORT = 1234
# router
DATA_SYSTEM_GET_CONFIG = {
"mac": "68:A3:78:00:00:00",
"model_info": {
"has_ext_telephony": True,
"has_speakers_jack": True,
"wifi_type": "2d4_5g",
"pretty_name": "Freebox Server (r2)",
"customer_hdd_slots": 0,
"name": "fbxgw-r2/full",
"has_speakers": True,
"internal_hdd_size": 250,
"has_femtocell_exp": True,
"has_internal_hdd": True,
"has_dect": True,
},
"fans": [{"id": "fan0_speed", "name": "Ventilateur 1", "value": 2130}],
"sensors": [
{"id": "temp_hdd", "name": "Disque dur", "value": 40},
{"id": "temp_sw", "name": "Température Switch", "value": 50},
{"id": "temp_cpum", "name": "Température CPU M", "value": 60},
{"id": "temp_cpub", "name": "Température CPU B", "value": 56},
],
"board_name": "fbxgw2r",
"disk_status": "active",
"uptime": "156 jours 19 heures 56 minutes 16 secondes",
"uptime_val": 13550176,
"user_main_storage": "Disque dur",
"box_authenticated": True,
"serial": "762601T190510709",
"firmware_version": "4.2.5",
}
# sensors
DATA_CONNECTION_GET_STATUS = {
"type": "ethernet",
"rate_down": 198900,
"bytes_up": 12035728872949,
"ipv4_port_range": [0, 65535],
"rate_up": 1440000,
"bandwidth_up": 700000000,
"ipv6": "2a01:e35:ffff:ffff::1",
"bandwidth_down": 1000000000,
"media": "ftth",
"state": "up",
"bytes_down": 2355966141297,
"ipv4": "82.67.00.00",
}
DATA_CALL_GET_CALLS_LOG = [
{
"number": "0988290475",
"type": "missed",
"id": 94,
"duration": 15,
"datetime": 1613752718,
"contact_id": 0,
"line_id": 0,
"name": "0988290475",
"new": True,
},
{
"number": "0367250217",
"type": "missed",
"id": 93,
"duration": 25,
"datetime": 1613662328,
"contact_id": 0,
"line_id": 0,
"name": "0367250217",
"new": True,
},
{
"number": "0184726018",
"type": "missed",
"id": 92,
"duration": 25,
"datetime": 1613225098,
"contact_id": 0,
"line_id": 0,
"name": "0184726018",
"new": True,
},
]
DATA_STORAGE_GET_DISKS = [
{
"idle_duration": 0,
"read_error_requests": 0,
"read_requests": 110,
"spinning": True,
# "table_type": "ms-dos", API returns without dash, but codespell isn't agree
"firmware": "SC1D",
"type": "internal",
"idle": False,
"connector": 0,
"id": 0,
"write_error_requests": 0,
"state": "enabled",
"write_requests": 2708929,
"total_bytes": 250050000000,
"model": "ST9250311CS",
"active_duration": 0,
"temp": 40,
"serial": "6VCQY907",
"partitions": [
{
"fstype": "ext4",
"total_bytes": 244950000000,
"label": "Disque dur",
"id": 2,
"internal": True,
"fsck_result": "no_run_yet",
"state": "mounted",
"disk_id": 0,
"free_bytes": 227390000000,
"used_bytes": 5090000000,
"path": "L0Rpc3F1ZSBkdXI=",
}
],
}
]
# switch
WIFI_GET_GLOBAL_CONFIG = {"enabled": True, "mac_filter_state": "disabled"}
# device_tracker
DATA_LAN_GET_HOSTS_LIST = [
{
"l2ident": {"id": "8C:97:EA:00:00:00", "type": "mac_address"},
"active": True,
"persistent": False,
"names": [
{"name": "d633d0c8-958c-43cc-e807-d881b076924b", "source": "mdns"},
{"name": "Freebox Player POP", "source": "mdns_srv"},
],
"vendor_name": "Freebox SAS",
"host_type": "smartphone",
"interface": "pub",
"id": "ether-8c:97:ea:00:00:00",
"last_time_reachable": 1614107652,
"primary_name_manual": False,
"l3connectivities": [
{
"addr": "192.168.1.180",
"active": True,
"reachable": True,
"last_activity": 1614107614,
"af": "ipv4",
"last_time_reachable": 1614104242,
},
{
"addr": "fe80::dcef:dbba:6604:31d1",
"active": True,
"reachable": True,
"last_activity": 1614107645,
"af": "ipv6",
"last_time_reachable": 1614107645,
},
{
"addr": "2a01:e34:eda1:eb40:8102:4704:7ce0:2ace",
"active": False,
"reachable": False,
"last_activity": 1611574428,
"af": "ipv6",
"last_time_reachable": 1611574428,
},
{
"addr": "2a01:e34:eda1:eb40:c8e5:c524:c96d:5f5e",
"active": False,
"reachable": False,
"last_activity": 1612475101,
"af": "ipv6",
"last_time_reachable": 1612475101,
},
{
"addr": "2a01:e34:eda1:eb40:583a:49df:1df0:c2df",
"active": True,
"reachable": True,
"last_activity": 1614107652,
"af": "ipv6",
"last_time_reachable": 1614107652,
},
{
"addr": "2a01:e34:eda1:eb40:147e:3569:86ab:6aaa",
"active": False,
"reachable": False,
"last_activity": 1612486752,
"af": "ipv6",
"last_time_reachable": 1612486752,
},
],
"default_name": "Freebox Player POP",
"model": "fbx8am",
"reachable": True,
"last_activity": 1614107652,
"primary_name": "Freebox Player POP",
},
{
"l2ident": {"id": "DE:00:B0:00:00:00", "type": "mac_address"},
"active": False,
"persistent": False,
"vendor_name": "",
"host_type": "workstation",
"interface": "pub",
"id": "ether-de:00:b0:00:00:00",
"last_time_reachable": 1607125599,
"primary_name_manual": False,
"default_name": "",
"l3connectivities": [
{
"addr": "192.168.1.181",
"active": False,
"reachable": False,
"last_activity": 1607125599,
"af": "ipv4",
"last_time_reachable": 1607125599,
},
{
"addr": "192.168.1.182",
"active": False,
"reachable": False,
"last_activity": 1605958758,
"af": "ipv4",
"last_time_reachable": 1605958758,
},
{
"addr": "2a01:e34:eda1:eb40:dc00:b0ff:fedf:e30",
"active": False,
"reachable": False,
"last_activity": 1607125594,
"af": "ipv6",
"last_time_reachable": 1607125594,
},
],
"reachable": False,
"last_activity": 1607125599,
"primary_name": "",
},
{
"l2ident": {"id": "DC:00:B0:00:00:00", "type": "mac_address"},
"active": True,
"persistent": False,
"names": [
{"name": "Repeteur-Wifi-Freebox", "source": "mdns"},
{"name": "Repeteur Wifi Freebox", "source": "mdns_srv"},
],
"vendor_name": "",
"host_type": "freebox_wifi",
"interface": "pub",
"id": "ether-dc:00:b0:00:00:00",
"last_time_reachable": 1614107678,
"primary_name_manual": False,
"l3connectivities": [
{
"addr": "192.168.1.145",
"active": True,
"reachable": True,
"last_activity": 1614107678,
"af": "ipv4",
"last_time_reachable": 1614107678,
},
{
"addr": "fe80::de00:b0ff:fe52:6ef6",
"active": True,
"reachable": True,
"last_activity": 1614107608,
"af": "ipv6",
"last_time_reachable": 1614107603,
},
{
"addr": "2a01:e34:eda1:eb40:de00:b0ff:fe52:6ef6",
"active": True,
"reachable": True,
"last_activity": 1614107618,
"af": "ipv6",
"last_time_reachable": 1614107618,
},
],
"default_name": "Repeteur Wifi Freebox",
"model": "fbxwmr",
"reachable": True,
"last_activity": 1614107678,
"primary_name": "Repeteur Wifi Freebox",
},
{
"l2ident": {"id": "5E:65:55:00:00:00", "type": "mac_address"},
"active": False,
"persistent": False,
"names": [
{"name": "iPhoneofQuentin", "source": "dhcp"},
{"name": "iPhone-of-Quentin", "source": "mdns"},
],
"vendor_name": "",
"host_type": "smartphone",
"interface": "pub",
"id": "ether-5e:65:55:00:00:00",
"last_time_reachable": 1612611982,
"primary_name_manual": False,
"default_name": "iPhonedeQuentin",
"l3connectivities": [
{
"addr": "192.168.1.148",
"active": False,
"reachable": False,
"last_activity": 1612611973,
"af": "ipv4",
"last_time_reachable": 1612611973,
},
{
"addr": "fe80::14ca:6c30:938b:e281",
"active": False,
"reachable": False,
"last_activity": 1609693223,
"af": "ipv6",
"last_time_reachable": 1609693223,
},
{
"addr": "fe80::1c90:2b94:1ba2:bd8b",
"active": False,
"reachable": False,
"last_activity": 1610797303,
"af": "ipv6",
"last_time_reachable": 1610797303,
},
{
"addr": "fe80::8c8:e58b:838e:6785",
"active": False,
"reachable": False,
"last_activity": 1612611951,
"af": "ipv6",
"last_time_reachable": 1612611946,
},
{
"addr": "2a01:e34:eda1:eb40:f0e7:e198:3a69:58",
"active": False,
"reachable": False,
"last_activity": 1609693245,
"af": "ipv6",
"last_time_reachable": 1609693245,
},
{
"addr": "2a01:e34:eda1:eb40:1dc4:c6f8:aa20:c83b",
"active": False,
"reachable": False,
"last_activity": 1610797176,
"af": "ipv6",
"last_time_reachable": 1610797176,
},
{
"addr": "2a01:e34:eda1:eb40:6cf6:5811:1770:c662",
"active": False,
"reachable": False,
"last_activity": 1612611982,
"af": "ipv6",
"last_time_reachable": 1612611982,
},
{
"addr": "2a01:e34:eda1:eb40:438:9b2c:4f8f:f48a",
"active": False,
"reachable": False,
"last_activity": 1612611946,
"af": "ipv6",
"last_time_reachable": 1612611946,
},
],
"reachable": False,
"last_activity": 1612611982,
"primary_name": "iPhoneofQuentin",
},
]

View File

@ -1,23 +1,22 @@
"""Tests for the Freebox config flow."""
from unittest.mock import AsyncMock, patch
from unittest.mock import Mock, patch
from freebox_api.exceptions import (
AuthorizationError,
HttpRequestError,
InvalidTokenError,
)
import pytest
from homeassistant import data_entry_flow
from homeassistant.components.freebox.const import DOMAIN
from homeassistant.config_entries import SOURCE_IMPORT, SOURCE_USER, SOURCE_ZEROCONF
from homeassistant.const import CONF_HOST, CONF_PORT
from homeassistant.helpers.typing import HomeAssistantType
from .const import MOCK_HOST, MOCK_PORT
from tests.common import MockConfigEntry
HOST = "myrouter.freeboxos.fr"
PORT = 1234
MOCK_ZEROCONF_DATA = {
"host": "192.168.0.254",
"port": 80,
@ -30,33 +29,15 @@ MOCK_ZEROCONF_DATA = {
"api_base_url": "/api/",
"uid": "b15ab20debb399f95001a9ca207d2777",
"https_available": "1",
"https_port": f"{PORT}",
"https_port": f"{MOCK_PORT}",
"box_model": "fbxgw-r2/full",
"box_model_name": "Freebox Server (r2)",
"api_domain": HOST,
"api_domain": MOCK_HOST,
},
}
@pytest.fixture(name="connect")
def mock_controller_connect():
"""Mock a successful connection."""
with patch("homeassistant.components.freebox.router.Freepybox") as service_mock:
service_mock.return_value.open = AsyncMock()
service_mock.return_value.system.get_config = AsyncMock(
return_value={
"mac": "abcd",
"model_info": {"pretty_name": "Pretty Model"},
"firmware_version": "123",
}
)
service_mock.return_value.lan.get_hosts_list = AsyncMock()
service_mock.return_value.connection.get_status = AsyncMock()
service_mock.return_value.close = AsyncMock()
yield service_mock
async def test_user(hass):
async def test_user(hass: HomeAssistantType):
"""Test user config."""
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}
@ -68,24 +49,24 @@ async def test_user(hass):
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_USER},
data={CONF_HOST: HOST, CONF_PORT: PORT},
data={CONF_HOST: MOCK_HOST, CONF_PORT: MOCK_PORT},
)
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result["step_id"] == "link"
async def test_import(hass):
async def test_import(hass: HomeAssistantType):
"""Test import step."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_IMPORT},
data={CONF_HOST: HOST, CONF_PORT: PORT},
data={CONF_HOST: MOCK_HOST, CONF_PORT: MOCK_PORT},
)
assert result["type"] == data_entry_flow.RESULT_TYPE_FORM
assert result["step_id"] == "link"
async def test_zeroconf(hass):
async def test_zeroconf(hass: HomeAssistantType):
"""Test zeroconf step."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
@ -96,53 +77,64 @@ async def test_zeroconf(hass):
assert result["step_id"] == "link"
async def test_link(hass, connect):
async def test_link(hass: HomeAssistantType, router: Mock):
"""Test linking."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_USER},
data={CONF_HOST: HOST, CONF_PORT: PORT},
)
with patch(
"homeassistant.components.freebox.async_setup", return_value=True
) as mock_setup, patch(
"homeassistant.components.freebox.async_setup_entry",
return_value=True,
) as mock_setup_entry:
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_USER},
data={CONF_HOST: MOCK_HOST, CONF_PORT: MOCK_PORT},
)
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
assert result["result"].unique_id == HOST
assert result["title"] == HOST
assert result["data"][CONF_HOST] == HOST
assert result["data"][CONF_PORT] == PORT
result = await hass.config_entries.flow.async_configure(result["flow_id"], {})
assert result["type"] == data_entry_flow.RESULT_TYPE_CREATE_ENTRY
assert result["result"].unique_id == MOCK_HOST
assert result["title"] == MOCK_HOST
assert result["data"][CONF_HOST] == MOCK_HOST
assert result["data"][CONF_PORT] == MOCK_PORT
assert len(mock_setup.mock_calls) == 1
assert len(mock_setup_entry.mock_calls) == 1
async def test_abort_if_already_setup(hass):
async def test_abort_if_already_setup(hass: HomeAssistantType):
"""Test we abort if component is already setup."""
MockConfigEntry(
domain=DOMAIN, data={CONF_HOST: HOST, CONF_PORT: PORT}, unique_id=HOST
domain=DOMAIN,
data={CONF_HOST: MOCK_HOST, CONF_PORT: MOCK_PORT},
unique_id=MOCK_HOST,
).add_to_hass(hass)
# Should fail, same HOST (import)
# Should fail, same MOCK_HOST (import)
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_IMPORT},
data={CONF_HOST: HOST, CONF_PORT: PORT},
data={CONF_HOST: MOCK_HOST, CONF_PORT: MOCK_PORT},
)
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
assert result["reason"] == "already_configured"
# Should fail, same HOST (flow)
# Should fail, same MOCK_HOST (flow)
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_USER},
data={CONF_HOST: HOST, CONF_PORT: PORT},
data={CONF_HOST: MOCK_HOST, CONF_PORT: MOCK_PORT},
)
assert result["type"] == data_entry_flow.RESULT_TYPE_ABORT
assert result["reason"] == "already_configured"
async def test_on_link_failed(hass):
async def test_on_link_failed(hass: HomeAssistantType):
"""Test when we have errors during linking the router."""
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": SOURCE_USER},
data={CONF_HOST: HOST, CONF_PORT: PORT},
data={CONF_HOST: MOCK_HOST, CONF_PORT: MOCK_PORT},
)
with patch(

View File

@ -0,0 +1,119 @@
"""Tests for the Freebox config flow."""
from unittest.mock import Mock, patch
from homeassistant.components.device_tracker import DOMAIN as DT_DOMAIN
from homeassistant.components.freebox.const import DOMAIN as DOMAIN, SERVICE_REBOOT
from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN
from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN
from homeassistant.config_entries import ENTRY_STATE_LOADED, ENTRY_STATE_NOT_LOADED
from homeassistant.const import CONF_HOST, CONF_PORT, STATE_UNAVAILABLE
from homeassistant.helpers.typing import HomeAssistantType
from homeassistant.setup import async_setup_component
from .const import MOCK_HOST, MOCK_PORT
from tests.common import MockConfigEntry
async def test_setup(hass: HomeAssistantType, router: Mock):
"""Test setup of integration."""
entry = MockConfigEntry(
domain=DOMAIN,
data={CONF_HOST: MOCK_HOST, CONF_PORT: MOCK_PORT},
unique_id=MOCK_HOST,
)
entry.add_to_hass(hass)
assert await async_setup_component(hass, DOMAIN, {})
await hass.async_block_till_done()
assert hass.config_entries.async_entries() == [entry]
assert router.call_count == 1
assert router().open.call_count == 1
assert hass.services.has_service(DOMAIN, SERVICE_REBOOT)
with patch(
"homeassistant.components.freebox.router.FreeboxRouter.reboot"
) as mock_service:
await hass.services.async_call(
DOMAIN,
SERVICE_REBOOT,
blocking=True,
)
await hass.async_block_till_done()
mock_service.assert_called_once()
async def test_setup_import(hass: HomeAssistantType, router: Mock):
"""Test setup of integration from import."""
await async_setup_component(hass, "persistent_notification", {})
entry = MockConfigEntry(
domain=DOMAIN,
data={CONF_HOST: MOCK_HOST, CONF_PORT: MOCK_PORT},
unique_id=MOCK_HOST,
)
entry.add_to_hass(hass)
assert await async_setup_component(
hass, DOMAIN, {DOMAIN: {CONF_HOST: MOCK_HOST, CONF_PORT: MOCK_PORT}}
)
await hass.async_block_till_done()
assert hass.config_entries.async_entries() == [entry]
assert router.call_count == 1
assert router().open.call_count == 1
assert hass.services.has_service(DOMAIN, SERVICE_REBOOT)
async def test_unload_remove(hass: HomeAssistantType, router: Mock):
"""Test unload and remove of integration."""
entity_id_dt = f"{DT_DOMAIN}.freebox_server_r2"
entity_id_sensor = f"{SENSOR_DOMAIN}.freebox_download_speed"
entity_id_switch = f"{SWITCH_DOMAIN}.freebox_wifi"
entry = MockConfigEntry(
domain=DOMAIN,
data={CONF_HOST: MOCK_HOST, CONF_PORT: MOCK_PORT},
)
entry.add_to_hass(hass)
config_entries = hass.config_entries.async_entries(DOMAIN)
assert len(config_entries) == 1
assert entry is config_entries[0]
assert await async_setup_component(hass, DOMAIN, {}) is True
await hass.async_block_till_done()
assert entry.state == ENTRY_STATE_LOADED
state_dt = hass.states.get(entity_id_dt)
assert state_dt
state_sensor = hass.states.get(entity_id_sensor)
assert state_sensor
state_switch = hass.states.get(entity_id_switch)
assert state_switch
await hass.config_entries.async_unload(entry.entry_id)
assert entry.state == ENTRY_STATE_NOT_LOADED
state_dt = hass.states.get(entity_id_dt)
assert state_dt.state == STATE_UNAVAILABLE
state_sensor = hass.states.get(entity_id_sensor)
assert state_sensor.state == STATE_UNAVAILABLE
state_switch = hass.states.get(entity_id_switch)
assert state_switch.state == STATE_UNAVAILABLE
assert router().close.call_count == 1
assert not hass.services.has_service(DOMAIN, SERVICE_REBOOT)
await hass.config_entries.async_remove(entry.entry_id)
await hass.async_block_till_done()
assert router().close.call_count == 1
assert entry.state == ENTRY_STATE_NOT_LOADED
state_dt = hass.states.get(entity_id_dt)
assert state_dt is None
state_sensor = hass.states.get(entity_id_sensor)
assert state_sensor is None
state_switch = hass.states.get(entity_id_switch)
assert state_switch is None