"""Setup/unload ConfigEntry: клиент стартует, останавливается, ошибки порта.""" from __future__ import annotations import asyncio import socket from helpers import free_port from homeassistant.core import HomeAssistant from pytest_homeassistant_custom_component.common import MockConfigEntry from custom_components.fglair.const import CONF_LISTEN_PORT, DOMAIN from pyfglair import Prop, State async def test_setup_unload(hass: HomeAssistant, config_entry, socket_enabled): config_entry.add_to_hass(hass) assert await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() client = config_entry.runtime_data assert client is not None assert isinstance(client.state, State) assert await hass.config_entries.async_unload(config_entry.entry_id) await hass.async_block_till_done() assert client.cached(Prop.OPERATION_MODE) is None async def test_two_entries_default_port(hass: HomeAssistant, entry_data, socket_enabled): """Две записи без явного listen_port не конфликтуют (порт 0 → эфемерный).""" first = MockConfigEntry(domain=DOMAIN, data=entry_data, unique_id="DSN-1") second = MockConfigEntry( domain=DOMAIN, data={**entry_data, "dsn": "DSN-2"}, unique_id="DSN-2", ) first.add_to_hass(hass) assert await hass.config_entries.async_setup(first.entry_id) await hass.async_block_till_done() second.add_to_hass(hass) assert await hass.config_entries.async_setup(second.entry_id) await hass.async_block_till_done() assert first.state.name == "LOADED" assert second.state.name == "LOADED" first_client = first.runtime_data assert await hass.config_entries.async_unload(first.entry_id) assert first_client.cached(Prop.OPERATION_MODE) is None assert second.state.name == "LOADED" assert await hass.config_entries.async_unload(second.entry_id) async def test_two_entries_same_explicit_port( hass: HomeAssistant, entry_data, socket_enabled ): """Явно заданный один порт на две записи — вторая SETUP_RETRY.""" port = free_port() first = MockConfigEntry( domain=DOMAIN, data={**entry_data, CONF_LISTEN_PORT: port}, unique_id="DSN-1", ) first.add_to_hass(hass) assert await hass.config_entries.async_setup(first.entry_id) await hass.async_block_till_done() second = MockConfigEntry( domain=DOMAIN, data={**entry_data, "dsn": "DSN-2", CONF_LISTEN_PORT: port}, unique_id="DSN-2", ) second.add_to_hass(hass) assert not await hass.config_entries.async_setup(second.entry_id) await hass.async_block_till_done() assert second.state.name == "SETUP_RETRY" assert await hass.config_entries.async_unload(first.entry_id) async def test_unload_does_not_block_loop( hass: HomeAssistant, config_entry, socket_enabled ): config_entry.add_to_hass(hass) assert await hass.config_entries.async_setup(config_entry.entry_id) await hass.async_block_till_done() ticks = 0 async def ticker() -> None: nonlocal ticks while True: await asyncio.sleep(0.01) ticks += 1 task = asyncio.create_task(ticker()) await asyncio.sleep(0.05) assert await hass.config_entries.async_unload(config_entry.entry_id) await hass.async_block_till_done() task.cancel() assert ticks >= 3 async def test_setup_port_busy(hass: HomeAssistant, entry_data, socket_enabled): port = free_port() sock = socket.socket() sock.bind(("127.0.0.1", port)) sock.listen(1) try: entry = MockConfigEntry( domain=DOMAIN, data={**entry_data, CONF_LISTEN_PORT: port}, unique_id=entry_data["dsn"], ) entry.add_to_hass(hass) assert not await hass.config_entries.async_setup(entry.entry_id) await hass.async_block_till_done() assert entry.state.name == "SETUP_RETRY" finally: sock.close()