diff --git a/.coveragerc b/.coveragerc index 74729b059f178..e12222594ac72 100644 --- a/.coveragerc +++ b/.coveragerc @@ -744,7 +744,6 @@ omit = homeassistant/components/mikrotik/hub.py homeassistant/components/mill/climate.py homeassistant/components/mill/sensor.py - homeassistant/components/minecraft_server/__init__.py homeassistant/components/minecraft_server/api.py homeassistant/components/minecraft_server/binary_sensor.py homeassistant/components/minecraft_server/coordinator.py diff --git a/tests/components/minecraft_server/conftest.py b/tests/components/minecraft_server/conftest.py index 8e166fbb2daef..b118b15d08a89 100644 --- a/tests/components/minecraft_server/conftest.py +++ b/tests/components/minecraft_server/conftest.py @@ -5,7 +5,7 @@ from homeassistant.components.minecraft_server.const import DEFAULT_NAME, DOMAIN from homeassistant.const import CONF_ADDRESS, CONF_NAME, CONF_TYPE -from .const import TEST_ADDRESS +from .const import TEST_ADDRESS, TEST_CONFIG_ENTRY_ID from tests.common import MockConfigEntry @@ -16,7 +16,7 @@ def java_mock_config_entry() -> MockConfigEntry: return MockConfigEntry( domain=DOMAIN, unique_id=None, - entry_id="01234567890123456789012345678901", + entry_id=TEST_CONFIG_ENTRY_ID, data={ CONF_NAME: DEFAULT_NAME, CONF_ADDRESS: TEST_ADDRESS, @@ -32,7 +32,7 @@ def bedrock_mock_config_entry() -> MockConfigEntry: return MockConfigEntry( domain=DOMAIN, unique_id=None, - entry_id="01234567890123456789012345678901", + entry_id=TEST_CONFIG_ENTRY_ID, data={ CONF_NAME: DEFAULT_NAME, CONF_ADDRESS: TEST_ADDRESS, diff --git a/tests/components/minecraft_server/const.py b/tests/components/minecraft_server/const.py index f299dd8efb860..56be9132f19a1 100644 --- a/tests/components/minecraft_server/const.py +++ b/tests/components/minecraft_server/const.py @@ -7,27 +7,33 @@ JavaStatusPlayers, JavaStatusResponse, JavaStatusVersion, + RawJavaResponse, + RawJavaResponsePlayer, + RawJavaResponsePlayers, + RawJavaResponseVersion, ) from homeassistant.components.minecraft_server.api import MinecraftServerData +TEST_CONFIG_ENTRY_ID: str = "01234567890123456789012345678901" TEST_HOST = "mc.dummyserver.com" TEST_PORT = 25566 TEST_ADDRESS = f"{TEST_HOST}:{TEST_PORT}" -TEST_JAVA_STATUS_RESPONSE_RAW = { - "description": {"text": "Dummy MOTD"}, - "version": {"name": "Dummy Version", "protocol": 123}, - "players": { - "online": 3, - "max": 10, - "sample": [ - {"name": "Player 1", "id": "1"}, - {"name": "Player 2", "id": "2"}, - {"name": "Player 3", "id": "3"}, +TEST_JAVA_STATUS_RESPONSE_RAW = RawJavaResponse( + description="Dummy MOTD", + players=RawJavaResponsePlayers( + online=3, + max=10, + sample=[ + RawJavaResponsePlayer(id="1", name="Player 1"), + RawJavaResponsePlayer(id="2", name="Player 2"), + RawJavaResponsePlayer(id="3", name="Player 3"), ], - }, -} + ), + version=RawJavaResponseVersion(name="Dummy Version", protocol=123), + favicon="Dummy Icon", +) TEST_JAVA_STATUS_RESPONSE = JavaStatusResponse( raw=TEST_JAVA_STATUS_RESPONSE_RAW, diff --git a/tests/components/minecraft_server/test_init.py b/tests/components/minecraft_server/test_init.py index cc9730ef3dfaf..09e411f0b62e3 100644 --- a/tests/components/minecraft_server/test_init.py +++ b/tests/components/minecraft_server/test_init.py @@ -1,15 +1,24 @@ """Tests for the Minecraft Server integration.""" from unittest.mock import patch +from mcstatus import JavaServer +import pytest + from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN -from homeassistant.components.minecraft_server.api import MinecraftServerAddressError from homeassistant.components.minecraft_server.const import DEFAULT_NAME, DOMAIN from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN +from homeassistant.config_entries import ConfigEntryState from homeassistant.const import CONF_ADDRESS, CONF_HOST, CONF_NAME, CONF_PORT from homeassistant.core import HomeAssistant from homeassistant.helpers import device_registry as dr, entity_registry as er -from .const import TEST_ADDRESS, TEST_HOST, TEST_JAVA_DATA, TEST_PORT +from .const import ( + TEST_ADDRESS, + TEST_CONFIG_ENTRY_ID, + TEST_HOST, + TEST_JAVA_STATUS_RESPONSE, + TEST_PORT, +) from tests.common import MockConfigEntry @@ -27,11 +36,13 @@ BINARY_SENSOR_KEYS = {"v1": "Status", "v2": "status"} -def create_v1_mock_config_entry(hass: HomeAssistant) -> int: - """Create mock config entry.""" - config_entry_v1 = MockConfigEntry( +@pytest.fixture +def v1_mock_config_entry() -> MockConfigEntry: + """Create mock config entry with version 1.""" + return MockConfigEntry( domain=DOMAIN, unique_id=TEST_UNIQUE_ID, + entry_id=TEST_CONFIG_ENTRY_ID, data={ CONF_NAME: DEFAULT_NAME, CONF_HOST: TEST_HOST, @@ -39,14 +50,10 @@ def create_v1_mock_config_entry(hass: HomeAssistant) -> int: }, version=1, ) - config_entry_id = config_entry_v1.entry_id - config_entry_v1.add_to_hass(hass) - - return config_entry_id -def create_v1_mock_device_entry(hass: HomeAssistant, config_entry_id: int) -> int: - """Create mock device entry.""" +def create_v1_mock_device_entry(hass: HomeAssistant, config_entry_id: str) -> str: + """Create mock device entry with version 1.""" device_registry = dr.async_get(hass) device_entry_v1 = device_registry.async_get_or_create( config_entry_id=config_entry_id, @@ -61,9 +68,9 @@ def create_v1_mock_device_entry(hass: HomeAssistant, config_entry_id: int) -> in def create_v1_mock_sensor_entity_entries( - hass: HomeAssistant, config_entry_id: int, device_entry_id: int + hass: HomeAssistant, config_entry_id: str, device_entry_id: str ) -> list[dict]: - """Create mock sensor entity entries.""" + """Create mock sensor entity entries with version 1.""" sensor_entity_id_key_mapping_list = [] config_entry = hass.config_entries.async_get_entry(config_entry_id) entity_registry = er.async_get(hass) @@ -86,9 +93,9 @@ def create_v1_mock_sensor_entity_entries( def create_v1_mock_binary_sensor_entity_entry( - hass: HomeAssistant, config_entry_id: int, device_entry_id: int + hass: HomeAssistant, config_entry_id: str, device_entry_id: str ) -> dict: - """Create mock binary sensor entity entry.""" + """Create mock binary sensor entity entry with version 1.""" config_entry = hass.config_entries.async_get_entry(config_entry_id) entity_registry = er.async_get(hass) entity_unique_id = f"{TEST_UNIQUE_ID}-{BINARY_SENSOR_KEYS['v1']}" @@ -108,53 +115,121 @@ def create_v1_mock_binary_sensor_entity_entry( return binary_sensor_entity_id_key_mapping -async def test_entry_migration(hass: HomeAssistant) -> None: +async def test_setup_and_unload_entry( + hass: HomeAssistant, java_mock_config_entry: MockConfigEntry +) -> None: + """Test successful entry setup and unload.""" + java_mock_config_entry.add_to_hass(hass) + + with patch( + "homeassistant.components.minecraft_server.api.JavaServer.lookup", + return_value=JavaServer(host=TEST_HOST, port=TEST_PORT), + ), patch( + "homeassistant.components.minecraft_server.api.JavaServer.async_status", + return_value=TEST_JAVA_STATUS_RESPONSE, + ): + assert await hass.config_entries.async_setup(java_mock_config_entry.entry_id) + await hass.async_block_till_done() + assert java_mock_config_entry.state == ConfigEntryState.LOADED + + assert await hass.config_entries.async_unload(java_mock_config_entry.entry_id) + await hass.async_block_till_done() + assert not hass.data.get(DOMAIN) + assert java_mock_config_entry.state == ConfigEntryState.NOT_LOADED + + +async def test_setup_entry_failure( + hass: HomeAssistant, java_mock_config_entry: MockConfigEntry +) -> None: + """Test failed entry setup.""" + java_mock_config_entry.add_to_hass(hass) + + with patch( + "homeassistant.components.minecraft_server.api.JavaServer.lookup", + side_effect=ValueError, + ): + assert not await hass.config_entries.async_setup( + java_mock_config_entry.entry_id + ) + + await hass.async_block_till_done() + assert java_mock_config_entry.state == ConfigEntryState.SETUP_ERROR + + +async def test_setup_entry_not_ready( + hass: HomeAssistant, java_mock_config_entry: MockConfigEntry +) -> None: + """Test entry setup not ready.""" + java_mock_config_entry.add_to_hass(hass) + + with patch( + "homeassistant.components.minecraft_server.api.JavaServer.lookup", + return_value=JavaServer(host=TEST_HOST, port=TEST_PORT), + ), patch( + "homeassistant.components.minecraft_server.api.JavaServer.async_status", + return_value=OSError, + ): + assert not await hass.config_entries.async_setup( + java_mock_config_entry.entry_id + ) + + await hass.async_block_till_done() + assert java_mock_config_entry.state == ConfigEntryState.SETUP_RETRY + + +async def test_entry_migration( + hass: HomeAssistant, v1_mock_config_entry: MockConfigEntry +) -> None: """Test entry migration from version 1 to 3, where host and port is required for the connection to the server.""" - config_entry_id = create_v1_mock_config_entry(hass) - device_entry_id = create_v1_mock_device_entry(hass, config_entry_id) + v1_mock_config_entry.add_to_hass(hass) + + device_entry_id = create_v1_mock_device_entry(hass, v1_mock_config_entry.entry_id) sensor_entity_id_key_mapping_list = create_v1_mock_sensor_entity_entries( - hass, config_entry_id, device_entry_id + hass, v1_mock_config_entry.entry_id, device_entry_id ) binary_sensor_entity_id_key_mapping = create_v1_mock_binary_sensor_entity_entry( - hass, config_entry_id, device_entry_id + hass, v1_mock_config_entry.entry_id, device_entry_id ) # Trigger migration. with patch( - "homeassistant.components.minecraft_server.api.MinecraftServer.__init__", + "homeassistant.components.minecraft_server.api.JavaServer.lookup", side_effect=[ - MinecraftServerAddressError, # async_migrate_entry - None, # async_migrate_entry - None, # async_setup_entry + ValueError, # async_migrate_entry + JavaServer(host=TEST_HOST, port=TEST_PORT), # async_migrate_entry + JavaServer(host=TEST_HOST, port=TEST_PORT), # async_setup_entry ], - return_value=None, ), patch( - "homeassistant.components.minecraft_server.api.MinecraftServer.async_get_data", - return_value=TEST_JAVA_DATA, + "homeassistant.components.minecraft_server.api.JavaServer.async_status", + return_value=TEST_JAVA_STATUS_RESPONSE, ): - assert await hass.config_entries.async_setup(config_entry_id) + assert await hass.config_entries.async_setup(v1_mock_config_entry.entry_id) await hass.async_block_till_done() + migrated_config_entry = v1_mock_config_entry + # Test migrated config entry. - config_entry = hass.config_entries.async_get_entry(config_entry_id) - assert config_entry.unique_id is None - assert config_entry.data == { + assert migrated_config_entry.unique_id is None + assert migrated_config_entry.data == { CONF_NAME: DEFAULT_NAME, CONF_ADDRESS: TEST_ADDRESS, } - - assert config_entry.version == 3 + assert migrated_config_entry.version == 3 + assert migrated_config_entry.state == ConfigEntryState.LOADED # Test migrated device entry. device_registry = dr.async_get(hass) device_entry = device_registry.async_get(device_entry_id) - assert device_entry.identifiers == {(DOMAIN, config_entry_id)} + assert device_entry.identifiers == {(DOMAIN, migrated_config_entry.entry_id)} # Test migrated sensor entity entries. entity_registry = er.async_get(hass) for mapping in sensor_entity_id_key_mapping_list: entity_entry = entity_registry.async_get(mapping["entity_id"]) - assert entity_entry.unique_id == f"{config_entry_id}-{mapping['key']}" + assert ( + entity_entry.unique_id + == f"{migrated_config_entry.entry_id}-{mapping['key']}" + ) # Test migrated binary sensor entity entry. entity_entry = entity_registry.async_get( @@ -162,61 +237,70 @@ async def test_entry_migration(hass: HomeAssistant) -> None: ) assert ( entity_entry.unique_id - == f"{config_entry_id}-{binary_sensor_entity_id_key_mapping['key']}" + == f"{migrated_config_entry.entry_id}-{binary_sensor_entity_id_key_mapping['key']}" ) -async def test_entry_migration_host_only(hass: HomeAssistant) -> None: +async def test_entry_migration_host_only( + hass: HomeAssistant, v1_mock_config_entry: MockConfigEntry +) -> None: """Test entry migration from version 1 to 3, where host alone is sufficient for the connection to the server.""" - config_entry_id = create_v1_mock_config_entry(hass) - device_entry_id = create_v1_mock_device_entry(hass, config_entry_id) - create_v1_mock_sensor_entity_entries(hass, config_entry_id, device_entry_id) - create_v1_mock_binary_sensor_entity_entry(hass, config_entry_id, device_entry_id) + v1_mock_config_entry.add_to_hass(hass) + + device_entry_id = create_v1_mock_device_entry(hass, v1_mock_config_entry.entry_id) + create_v1_mock_sensor_entity_entries( + hass, v1_mock_config_entry.entry_id, device_entry_id + ) + create_v1_mock_binary_sensor_entity_entry( + hass, v1_mock_config_entry.entry_id, device_entry_id + ) # Trigger migration. with patch( - "homeassistant.components.minecraft_server.api.MinecraftServer.__init__", - side_effect=[ - None, # async_migrate_entry - None, # async_setup_entry - ], - return_value=None, + "homeassistant.components.minecraft_server.api.JavaServer.lookup", + return_value=JavaServer(host=TEST_HOST, port=TEST_PORT), ), patch( - "homeassistant.components.minecraft_server.api.MinecraftServer.async_get_data", - return_value=TEST_JAVA_DATA, + "homeassistant.components.minecraft_server.api.JavaServer.async_status", + return_value=TEST_JAVA_STATUS_RESPONSE, ): - assert await hass.config_entries.async_setup(config_entry_id) + assert await hass.config_entries.async_setup(v1_mock_config_entry.entry_id) await hass.async_block_till_done() # Test migrated config entry. - config_entry = hass.config_entries.async_get_entry(config_entry_id) - assert config_entry.unique_id is None - assert config_entry.data == { + assert v1_mock_config_entry.unique_id is None + assert v1_mock_config_entry.data == { CONF_NAME: DEFAULT_NAME, CONF_ADDRESS: TEST_HOST, } - assert config_entry.version == 3 + assert v1_mock_config_entry.version == 3 + assert v1_mock_config_entry.state == ConfigEntryState.LOADED -async def test_entry_migration_v3_failure(hass: HomeAssistant) -> None: +async def test_entry_migration_v3_failure( + hass: HomeAssistant, v1_mock_config_entry: MockConfigEntry +) -> None: """Test failed entry migration from version 2 to 3.""" - config_entry_id = create_v1_mock_config_entry(hass) - device_entry_id = create_v1_mock_device_entry(hass, config_entry_id) - create_v1_mock_sensor_entity_entries(hass, config_entry_id, device_entry_id) - create_v1_mock_binary_sensor_entity_entry(hass, config_entry_id, device_entry_id) + v1_mock_config_entry.add_to_hass(hass) + + device_entry_id = create_v1_mock_device_entry(hass, v1_mock_config_entry.entry_id) + create_v1_mock_sensor_entity_entries( + hass, v1_mock_config_entry.entry_id, device_entry_id + ) + create_v1_mock_binary_sensor_entity_entry( + hass, v1_mock_config_entry.entry_id, device_entry_id + ) # Trigger migration. with patch( - "homeassistant.components.minecraft_server.api.MinecraftServer.__init__", + "homeassistant.components.minecraft_server.api.JavaServer.lookup", side_effect=[ - MinecraftServerAddressError, # async_migrate_entry - MinecraftServerAddressError, # async_migrate_entry + ValueError, # async_migrate_entry + ValueError, # async_migrate_entry ], - return_value=None, ): - assert not await hass.config_entries.async_setup(config_entry_id) + assert not await hass.config_entries.async_setup(v1_mock_config_entry.entry_id) await hass.async_block_till_done() # Test config entry. - config_entry = hass.config_entries.async_get_entry(config_entry_id) - assert config_entry.version == 2 + assert v1_mock_config_entry.version == 2 + assert v1_mock_config_entry.state == ConfigEntryState.MIGRATION_ERROR