diff --git a/tests/components/minecraft_server/const.py b/tests/components/minecraft_server/const.py new file mode 100644 index 00000000000..3f635fbe333 --- /dev/null +++ b/tests/components/minecraft_server/const.py @@ -0,0 +1,32 @@ +"""Constants for Minecraft Server integration tests.""" +from mcstatus.motd import Motd +from mcstatus.status_response import ( + JavaStatusPlayers, + JavaStatusResponse, + JavaStatusVersion, +) + +TEST_HOST = "mc.dummyserver.com" + +TEST_JAVA_STATUS_RESPONSE_RAW = { + "description": {"text": "Dummy Description"}, + "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 = JavaStatusResponse( + raw=TEST_JAVA_STATUS_RESPONSE_RAW, + players=JavaStatusPlayers.build(TEST_JAVA_STATUS_RESPONSE_RAW["players"]), + version=JavaStatusVersion.build(TEST_JAVA_STATUS_RESPONSE_RAW["version"]), + motd=Motd.parse(TEST_JAVA_STATUS_RESPONSE_RAW["description"], bedrock=False), + icon=None, + latency=5, +) diff --git a/tests/components/minecraft_server/test_config_flow.py b/tests/components/minecraft_server/test_config_flow.py index d9e7d46a88c..c4d8c72e32d 100644 --- a/tests/components/minecraft_server/test_config_flow.py +++ b/tests/components/minecraft_server/test_config_flow.py @@ -1,9 +1,8 @@ -"""Test the Minecraft Server config flow.""" +"""Tests for the Minecraft Server config flow.""" from unittest.mock import AsyncMock, patch import aiodns -from mcstatus.status_response import JavaStatusResponse from homeassistant.components.minecraft_server.const import ( DEFAULT_NAME, @@ -15,39 +14,27 @@ from homeassistant.const import CONF_HOST, CONF_NAME from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType +from .const import TEST_HOST, TEST_JAVA_STATUS_RESPONSE + class QueryMock: """Mock for result of aiodns.DNSResolver.query.""" def __init__(self) -> None: """Set up query result mock.""" - self.host = "mc.dummyserver.com" + self.host = TEST_HOST self.port = 23456 self.priority = 1 self.weight = 1 self.ttl = None -JAVA_STATUS_RESPONSE_RAW = { - "description": {"text": "Dummy Description"}, - "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"}, - ], - }, -} - USER_INPUT = { CONF_NAME: DEFAULT_NAME, - CONF_HOST: f"mc.dummyserver.com:{DEFAULT_PORT}", + CONF_HOST: f"{TEST_HOST}:{DEFAULT_PORT}", } -USER_INPUT_SRV = {CONF_NAME: DEFAULT_NAME, CONF_HOST: "dummyserver.com"} +USER_INPUT_SRV = {CONF_NAME: DEFAULT_NAME, CONF_HOST: TEST_HOST} USER_INPUT_IPV4 = { CONF_NAME: DEFAULT_NAME, @@ -61,12 +48,12 @@ USER_INPUT_IPV6 = { USER_INPUT_PORT_TOO_SMALL = { CONF_NAME: DEFAULT_NAME, - CONF_HOST: "mc.dummyserver.com:1023", + CONF_HOST: f"{TEST_HOST}:1023", } USER_INPUT_PORT_TOO_LARGE = { CONF_NAME: DEFAULT_NAME, - CONF_HOST: "mc.dummyserver.com:65536", + CONF_HOST: f"{TEST_HOST}:65536", } @@ -129,9 +116,7 @@ async def test_connection_succeeded_with_srv_record(hass: HomeAssistant) -> None side_effect=AsyncMock(return_value=[QueryMock()]), ), patch( "mcstatus.server.JavaServer.async_status", - return_value=JavaStatusResponse( - None, None, None, None, JAVA_STATUS_RESPONSE_RAW, None - ), + return_value=TEST_JAVA_STATUS_RESPONSE, ): result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER}, data=USER_INPUT_SRV @@ -150,9 +135,7 @@ async def test_connection_succeeded_with_host(hass: HomeAssistant) -> None: side_effect=aiodns.error.DNSError, ), patch( "mcstatus.server.JavaServer.async_status", - return_value=JavaStatusResponse( - None, None, None, None, JAVA_STATUS_RESPONSE_RAW, None - ), + return_value=TEST_JAVA_STATUS_RESPONSE, ): result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER}, data=USER_INPUT @@ -161,7 +144,7 @@ async def test_connection_succeeded_with_host(hass: HomeAssistant) -> None: assert result["type"] == FlowResultType.CREATE_ENTRY assert result["title"] == USER_INPUT[CONF_HOST] assert result["data"][CONF_NAME] == USER_INPUT[CONF_NAME] - assert result["data"][CONF_HOST] == "mc.dummyserver.com" + assert result["data"][CONF_HOST] == TEST_HOST async def test_connection_succeeded_with_ip4(hass: HomeAssistant) -> None: @@ -171,9 +154,7 @@ async def test_connection_succeeded_with_ip4(hass: HomeAssistant) -> None: side_effect=aiodns.error.DNSError, ), patch( "mcstatus.server.JavaServer.async_status", - return_value=JavaStatusResponse( - None, None, None, None, JAVA_STATUS_RESPONSE_RAW, None - ), + return_value=TEST_JAVA_STATUS_RESPONSE, ): result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER}, data=USER_INPUT_IPV4 @@ -192,9 +173,7 @@ async def test_connection_succeeded_with_ip6(hass: HomeAssistant) -> None: side_effect=aiodns.error.DNSError, ), patch( "mcstatus.server.JavaServer.async_status", - return_value=JavaStatusResponse( - None, None, None, None, JAVA_STATUS_RESPONSE_RAW, None - ), + return_value=TEST_JAVA_STATUS_RESPONSE, ): result = await hass.config_entries.flow.async_init( DOMAIN, context={"source": SOURCE_USER}, data=USER_INPUT_IPV6 diff --git a/tests/components/minecraft_server/test_init.py b/tests/components/minecraft_server/test_init.py new file mode 100644 index 00000000000..5bdce5ed9b7 --- /dev/null +++ b/tests/components/minecraft_server/test_init.py @@ -0,0 +1,131 @@ +"""Tests for the Minecraft Server integration.""" +from unittest.mock import patch + +import aiodns + +from homeassistant.components.binary_sensor import DOMAIN as BINARY_SENSOR_DOMAIN +from homeassistant.components.minecraft_server.const import ( + DEFAULT_NAME, + DEFAULT_PORT, + DOMAIN, +) +from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN +from homeassistant.const import 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_HOST, TEST_JAVA_STATUS_RESPONSE + +from tests.common import MockConfigEntry + +TEST_UNIQUE_ID = f"{TEST_HOST}-{DEFAULT_PORT}" + +SENSOR_KEYS = [ + {"v1": "Latency Time", "v2": "latency"}, + {"v1": "Players Max", "v2": "players_max"}, + {"v1": "Players Online", "v2": "players_online"}, + {"v1": "Protocol Version", "v2": "protocol_version"}, + {"v1": "Version", "v2": "version"}, + {"v1": "World Message", "v2": "motd"}, +] + +BINARY_SENSOR_KEYS = {"v1": "Status", "v2": "status"} + + +async def test_entry_migration_v1_to_v2(hass: HomeAssistant) -> None: + """Test entry migratiion from version 1 to 2.""" + + # Create mock config entry. + config_entry_v1 = MockConfigEntry( + domain=DOMAIN, + unique_id=TEST_UNIQUE_ID, + data={ + CONF_NAME: DEFAULT_NAME, + CONF_HOST: TEST_HOST, + CONF_PORT: DEFAULT_PORT, + }, + version=1, + ) + config_entry_id = config_entry_v1.entry_id + config_entry_v1.add_to_hass(hass) + + # Create mock device entry. + device_registry = dr.async_get(hass) + device_entry_v1 = device_registry.async_get_or_create( + config_entry_id=config_entry_id, + identifiers={(DOMAIN, TEST_UNIQUE_ID)}, + ) + device_entry_id = device_entry_v1.id + assert device_entry_v1 + assert device_entry_id + + # Create mock sensor entity entries. + sensor_entity_id_key_mapping_list = [] + entity_registry = er.async_get(hass) + for sensor_key in SENSOR_KEYS: + entity_unique_id = f"{TEST_UNIQUE_ID}-{sensor_key['v1']}" + entity_entry_v1 = entity_registry.async_get_or_create( + SENSOR_DOMAIN, + DOMAIN, + unique_id=entity_unique_id, + config_entry=config_entry_v1, + device_id=device_entry_id, + ) + assert entity_entry_v1.unique_id == entity_unique_id + sensor_entity_id_key_mapping_list.append( + {"entity_id": entity_entry_v1.entity_id, "key": sensor_key["v2"]} + ) + + # Create mock binary sensor entity entry. + entity_unique_id = f"{TEST_UNIQUE_ID}-{BINARY_SENSOR_KEYS['v1']}" + entity_entry_v1 = entity_registry.async_get_or_create( + BINARY_SENSOR_DOMAIN, + DOMAIN, + unique_id=entity_unique_id, + config_entry=config_entry_v1, + device_id=device_entry_id, + ) + assert entity_entry_v1.unique_id == entity_unique_id + binary_sensor_entity_id_key_mapping = { + "entity_id": entity_entry_v1.entity_id, + "key": BINARY_SENSOR_KEYS["v2"], + } + + # Trigger migration. + with patch( + "aiodns.DNSResolver.query", + side_effect=aiodns.error.DNSError, + ), patch( + "mcstatus.server.JavaServer.async_status", + return_value=TEST_JAVA_STATUS_RESPONSE, + ): + assert await hass.config_entries.async_setup(config_entry_id) + await hass.async_block_till_done() + + # Test migrated config entry. + config_entry_v2 = hass.config_entries.async_get_entry(config_entry_id) + assert config_entry_v2.unique_id is None + assert config_entry_v2.data == { + CONF_NAME: DEFAULT_NAME, + CONF_HOST: TEST_HOST, + CONF_PORT: DEFAULT_PORT, + } + assert config_entry_v2.version == 2 + + # Test migrated device entry. + device_entry_v2 = device_registry.async_get(device_entry_id) + assert device_entry_v2.identifiers == {(DOMAIN, config_entry_id)} + + # Test migrated sensor entity entries. + for mapping in sensor_entity_id_key_mapping_list: + entity_entry_v2 = entity_registry.async_get(mapping["entity_id"]) + assert entity_entry_v2.unique_id == f"{config_entry_id}-{mapping['key']}" + + # Test migrated binary sensor entity entry. + entity_entry_v2 = entity_registry.async_get( + binary_sensor_entity_id_key_mapping["entity_id"] + ) + assert ( + entity_entry_v2.unique_id + == f"{config_entry_id}-{binary_sensor_entity_id_key_mapping['key']}" + )