diff --git a/tests/components/pterodactyl/__init__.py b/tests/components/pterodactyl/__init__.py index a5b28d67ae3..0142399ec42 100644 --- a/tests/components/pterodactyl/__init__.py +++ b/tests/components/pterodactyl/__init__.py @@ -1 +1,16 @@ """Tests for the Pterodactyl integration.""" + +from homeassistant.core import HomeAssistant + +from tests.common import MockConfigEntry + + +async def setup_integration( + hass: HomeAssistant, mock_config_entry: MockConfigEntry +) -> MockConfigEntry: + """Set up Pterodactyl mock config entry.""" + mock_config_entry.add_to_hass(hass) + await hass.config_entries.async_setup(mock_config_entry.entry_id) + await hass.async_block_till_done() + + return mock_config_entry diff --git a/tests/components/pterodactyl/conftest.py b/tests/components/pterodactyl/conftest.py index 62326e79207..c395410b6ae 100644 --- a/tests/components/pterodactyl/conftest.py +++ b/tests/components/pterodactyl/conftest.py @@ -9,108 +9,9 @@ import pytest from homeassistant.components.pterodactyl.const import DOMAIN from homeassistant.const import CONF_API_KEY, CONF_URL -from tests.common import MockConfigEntry +from .const import TEST_API_KEY, TEST_URL -TEST_URL = "https://192.168.0.1:8080/" -TEST_API_KEY = "TestClientApiKey" -TEST_USER_INPUT = { - CONF_URL: TEST_URL, - CONF_API_KEY: TEST_API_KEY, -} -TEST_SERVER_LIST_DATA = { - "meta": {"pagination": {"total": 2, "count": 2, "per_page": 50, "current_page": 1}}, - "data": [ - { - "object": "server", - "attributes": { - "server_owner": True, - "identifier": "1", - "internal_id": 1, - "uuid": "1-1-1-1-1", - "name": "Test Server 1", - "node": "default_node", - "description": "Description of Test Server 1", - "limits": { - "memory": 2048, - "swap": 1024, - "disk": 10240, - "io": 500, - "cpu": 100, - "threads": None, - "oom_disabled": True, - }, - "invocation": "java -jar test_server1.jar", - "docker_image": "test_docker_image_1", - "egg_features": ["java_version"], - }, - }, - { - "object": "server", - "attributes": { - "server_owner": True, - "identifier": "2", - "internal_id": 2, - "uuid": "2-2-2-2-2", - "name": "Test Server 2", - "node": "default_node", - "description": "Description of Test Server 2", - "limits": { - "memory": 2048, - "swap": 1024, - "disk": 10240, - "io": 500, - "cpu": 100, - "threads": None, - "oom_disabled": True, - }, - "invocation": "java -jar test_server_2.jar", - "docker_image": "test_docker_image2", - "egg_features": ["java_version"], - }, - }, - ], -} -TEST_SERVER = { - "server_owner": True, - "identifier": "1", - "internal_id": 1, - "uuid": "1-1-1-1-1", - "name": "Test Server 1", - "node": "default_node", - "is_node_under_maintenance": False, - "sftp_details": {"ip": "192.168.0.1", "port": 2022}, - "description": "", - "limits": { - "memory": 2048, - "swap": 1024, - "disk": 10240, - "io": 500, - "cpu": 100, - "threads": None, - "oom_disabled": True, - }, - "invocation": "java -jar test.jar", - "docker_image": "test_docker_image", - "egg_features": ["eula", "java_version", "pid_limit"], - "feature_limits": {"databases": 0, "allocations": 0, "backups": 3}, - "status": None, - "is_suspended": False, - "is_installing": False, - "is_transferring": False, - "relationships": {"allocations": {...}, "variables": {...}}, -} -TEST_SERVER_UTILIZATION = { - "current_state": "running", - "is_suspended": False, - "resources": { - "memory_bytes": 1111, - "cpu_absolute": 22, - "disk_bytes": 3333, - "network_rx_bytes": 44, - "network_tx_bytes": 55, - "uptime": 6666, - }, -} +from tests.common import MockConfigEntry, load_json_object_fixture @pytest.fixture @@ -139,17 +40,25 @@ def mock_config_entry() -> MockConfigEntry: @pytest.fixture -def mock_pterodactyl(): +def mock_pterodactyl() -> Generator[AsyncMock]: """Mock the Pterodactyl API.""" with patch( "homeassistant.components.pterodactyl.api.PterodactylClient", autospec=True ) as mock: + server_list_data = load_json_object_fixture("server_list_data.json", DOMAIN) + server_1_data = load_json_object_fixture("server_1_data.json", DOMAIN) + server_2_data = load_json_object_fixture("server_2_data.json", DOMAIN) + utilization_data = load_json_object_fixture("utilization_data.json", DOMAIN) + mock.return_value.client.servers.list_servers.return_value = PaginatedResponse( - mock.return_value, "client", TEST_SERVER_LIST_DATA + mock.return_value, "client", server_list_data ) - mock.return_value.client.servers.get_server.return_value = TEST_SERVER + mock.return_value.client.servers.get_server.side_effect = [ + server_1_data, + server_2_data, + ] mock.return_value.client.servers.get_server_utilization.return_value = ( - TEST_SERVER_UTILIZATION + utilization_data ) yield mock.return_value diff --git a/tests/components/pterodactyl/const.py b/tests/components/pterodactyl/const.py new file mode 100644 index 00000000000..f6684a82fc5 --- /dev/null +++ b/tests/components/pterodactyl/const.py @@ -0,0 +1,12 @@ +"""Constants for Pterodactyl tests.""" + +from homeassistant.const import CONF_API_KEY, CONF_URL + +TEST_URL = "https://192.168.0.1:8080/" + +TEST_API_KEY = "TestClientApiKey" + +TEST_USER_INPUT = { + CONF_URL: TEST_URL, + CONF_API_KEY: TEST_API_KEY, +} diff --git a/tests/components/pterodactyl/fixtures/server_1_data.json b/tests/components/pterodactyl/fixtures/server_1_data.json new file mode 100644 index 00000000000..c780d55b318 --- /dev/null +++ b/tests/components/pterodactyl/fixtures/server_1_data.json @@ -0,0 +1,39 @@ +{ + "server_owner": true, + "identifier": "1", + "internal_id": 1, + "uuid": "1-1-1-1-1", + "name": "Test Server 1", + "node": "default_node", + "is_node_under_maintenance": false, + "sftp_details": { + "ip": "192.168.0.1", + "port": 2022 + }, + "description": "", + "limits": { + "memory": 2048, + "swap": 1024, + "disk": 10240, + "io": 500, + "cpu": 100, + "threads": null, + "oom_disabled": true + }, + "invocation": "java -jar test1.jar", + "docker_image": "test_docker_image1", + "egg_features": ["eula", "java_version", "pid_limit"], + "feature_limits": { + "databases": 0, + "allocations": 0, + "backups": 3 + }, + "status": null, + "is_suspended": false, + "is_installing": false, + "is_transferring": false, + "relationships": { + "allocations": {}, + "variables": {} + } +} diff --git a/tests/components/pterodactyl/fixtures/server_2_data.json b/tests/components/pterodactyl/fixtures/server_2_data.json new file mode 100644 index 00000000000..b240ff62ced --- /dev/null +++ b/tests/components/pterodactyl/fixtures/server_2_data.json @@ -0,0 +1,39 @@ +{ + "server_owner": true, + "identifier": "2", + "internal_id": 2, + "uuid": "2-2-2-2-2", + "name": "Test Server 2", + "node": "default_node", + "is_node_under_maintenance": false, + "sftp_details": { + "ip": "192.168.0.2", + "port": 2022 + }, + "description": "", + "limits": { + "memory": 4096, + "swap": 2048, + "disk": 20480, + "io": 1000, + "cpu": 200, + "threads": null, + "oom_disabled": true + }, + "invocation": "java -jar test2.jar", + "docker_image": "test_docker_image2", + "egg_features": ["eula", "java_version", "pid_limit"], + "feature_limits": { + "databases": 1, + "allocations": 1, + "backups": 5 + }, + "status": null, + "is_suspended": false, + "is_installing": false, + "is_transferring": false, + "relationships": { + "allocations": {}, + "variables": {} + } +} diff --git a/tests/components/pterodactyl/fixtures/server_list_data.json b/tests/components/pterodactyl/fixtures/server_list_data.json new file mode 100644 index 00000000000..d8796ad533e --- /dev/null +++ b/tests/components/pterodactyl/fixtures/server_list_data.json @@ -0,0 +1,60 @@ +{ + "meta": { + "pagination": { + "total": 2, + "count": 2, + "per_page": 50, + "current_page": 1 + } + }, + "data": [ + { + "object": "server", + "attributes": { + "server_owner": true, + "identifier": "1", + "internal_id": 1, + "uuid": "1-1-1-1-1", + "name": "Test Server 1", + "node": "default_node", + "description": "Description of Test Server 1", + "limits": { + "memory": 2048, + "swap": 1024, + "disk": 10240, + "io": 500, + "cpu": 100, + "threads": null, + "oom_disabled": true + }, + "invocation": "java -jar test1.jar", + "docker_image": "test_docker_image_1", + "egg_features": ["java_version"] + } + }, + { + "object": "server", + "attributes": { + "server_owner": true, + "identifier": "2", + "internal_id": 2, + "uuid": "2-2-2-2-2", + "name": "Test Server 2", + "node": "default_node", + "description": "Description of Test Server 2", + "limits": { + "memory": 2048, + "swap": 1024, + "disk": 10240, + "io": 500, + "cpu": 100, + "threads": null, + "oom_disabled": true + }, + "invocation": "java -jar test2.jar", + "docker_image": "test_docker_image2", + "egg_features": ["java_version"] + } + } + ] +} diff --git a/tests/components/pterodactyl/fixtures/utilization_data.json b/tests/components/pterodactyl/fixtures/utilization_data.json new file mode 100644 index 00000000000..6b71cb44635 --- /dev/null +++ b/tests/components/pterodactyl/fixtures/utilization_data.json @@ -0,0 +1,12 @@ +{ + "current_state": "running", + "is_suspended": false, + "resources": { + "memory_bytes": 1111, + "cpu_absolute": 22, + "disk_bytes": 3333, + "network_rx_bytes": 44, + "network_tx_bytes": 55, + "uptime": 6666 + } +} diff --git a/tests/components/pterodactyl/snapshots/test_binary_sensor.ambr b/tests/components/pterodactyl/snapshots/test_binary_sensor.ambr new file mode 100644 index 00000000000..9bd7abc830b --- /dev/null +++ b/tests/components/pterodactyl/snapshots/test_binary_sensor.ambr @@ -0,0 +1,97 @@ +# serializer version: 1 +# name: test_binary_sensor[binary_sensor.test_server_1_status-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.test_server_1_status', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Status', + 'platform': 'pterodactyl', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'status', + 'unique_id': '1-1-1-1-1_status', + 'unit_of_measurement': None, + }) +# --- +# name: test_binary_sensor[binary_sensor.test_server_1_status-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'running', + 'friendly_name': 'Test Server 1 Status', + }), + 'context': , + 'entity_id': 'binary_sensor.test_server_1_status', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'on', + }) +# --- +# name: test_binary_sensor[binary_sensor.test_server_2_status-entry] + EntityRegistryEntrySnapshot({ + 'aliases': set({ + }), + 'area_id': None, + 'capabilities': None, + 'config_entry_id': , + 'config_subentry_id': , + 'device_class': None, + 'device_id': , + 'disabled_by': None, + 'domain': 'binary_sensor', + 'entity_category': None, + 'entity_id': 'binary_sensor.test_server_2_status', + 'has_entity_name': True, + 'hidden_by': None, + 'icon': None, + 'id': , + 'labels': set({ + }), + 'name': None, + 'options': dict({ + }), + 'original_device_class': , + 'original_icon': None, + 'original_name': 'Status', + 'platform': 'pterodactyl', + 'previous_unique_id': None, + 'supported_features': 0, + 'translation_key': 'status', + 'unique_id': '2-2-2-2-2_status', + 'unit_of_measurement': None, + }) +# --- +# name: test_binary_sensor[binary_sensor.test_server_2_status-state] + StateSnapshot({ + 'attributes': ReadOnlyDict({ + 'device_class': 'running', + 'friendly_name': 'Test Server 2 Status', + }), + 'context': , + 'entity_id': 'binary_sensor.test_server_2_status', + 'last_changed': , + 'last_reported': , + 'last_updated': , + 'state': 'on', + }) +# --- diff --git a/tests/components/pterodactyl/test_binary_sensor.py b/tests/components/pterodactyl/test_binary_sensor.py new file mode 100644 index 00000000000..4bacd30e011 --- /dev/null +++ b/tests/components/pterodactyl/test_binary_sensor.py @@ -0,0 +1,89 @@ +"""Tests for the binary sensor platform of the Pterodactyl integration.""" + +from collections.abc import Generator +from datetime import timedelta +from unittest.mock import AsyncMock, patch + +from freezegun.api import FrozenDateTimeFactory +import pytest +from requests.exceptions import ConnectionError +from syrupy.assertion import SnapshotAssertion + +from homeassistant.const import STATE_ON, STATE_UNAVAILABLE, Platform +from homeassistant.core import HomeAssistant +from homeassistant.helpers import entity_registry as er + +from . import setup_integration + +from tests.common import MockConfigEntry, async_fire_time_changed, snapshot_platform + + +@pytest.mark.usefixtures("mock_pterodactyl") +async def test_binary_sensor( + hass: HomeAssistant, + entity_registry: er.EntityRegistry, + mock_config_entry: MockConfigEntry, + snapshot: SnapshotAssertion, +) -> None: + """Test binary sensor.""" + with patch( + "homeassistant.components.pterodactyl._PLATFORMS", [Platform.BINARY_SENSOR] + ): + mock_config_entry = await setup_integration(hass, mock_config_entry) + + assert len(hass.states.async_all(Platform.BINARY_SENSOR)) == 2 + await snapshot_platform( + hass, entity_registry, snapshot, mock_config_entry.entry_id + ) + + +@pytest.mark.usefixtures("mock_pterodactyl") +async def test_binary_sensor_update( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + freezer: FrozenDateTimeFactory, +) -> None: + """Test binary sensor update.""" + await setup_integration(hass, mock_config_entry) + + freezer.tick(timedelta(seconds=90)) + async_fire_time_changed(hass) + await hass.async_block_till_done() + + assert len(hass.states.async_all(Platform.BINARY_SENSOR)) == 2 + assert ( + hass.states.get(f"{Platform.BINARY_SENSOR}.test_server_1_status").state + == STATE_ON + ) + assert ( + hass.states.get(f"{Platform.BINARY_SENSOR}.test_server_2_status").state + == STATE_ON + ) + + +async def test_binary_sensor_update_failure( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + mock_pterodactyl: Generator[AsyncMock], + freezer: FrozenDateTimeFactory, +) -> None: + """Test failed binary sensor update.""" + await setup_integration(hass, mock_config_entry) + + mock_pterodactyl.client.servers.get_server.side_effect = ConnectionError( + "Simulated connection error" + ) + + freezer.tick(timedelta(minutes=1)) + async_fire_time_changed(hass) + await hass.async_block_till_done(wait_background_tasks=True) + + assert len(hass.states.async_all(Platform.BINARY_SENSOR)) == 2 + assert ( + hass.states.get(f"{Platform.BINARY_SENSOR}.test_server_1_status").state + == STATE_UNAVAILABLE + ) + assert ( + hass.states.get(f"{Platform.BINARY_SENSOR}.test_server_2_status").state + == STATE_UNAVAILABLE + ) diff --git a/tests/components/pterodactyl/test_config_flow.py b/tests/components/pterodactyl/test_config_flow.py index 88247085083..8837fbe753b 100644 --- a/tests/components/pterodactyl/test_config_flow.py +++ b/tests/components/pterodactyl/test_config_flow.py @@ -1,6 +1,8 @@ """Test the Pterodactyl config flow.""" -from pydactyl import PterodactylClient +from collections.abc import Generator +from unittest.mock import AsyncMock + from pydactyl.exceptions import BadRequestError, PterodactylApiError import pytest from requests.exceptions import HTTPError @@ -12,7 +14,7 @@ from homeassistant.const import CONF_API_KEY, CONF_URL from homeassistant.core import HomeAssistant from homeassistant.data_entry_flow import FlowResultType -from .conftest import TEST_API_KEY, TEST_URL, TEST_USER_INPUT +from .const import TEST_API_KEY, TEST_URL, TEST_USER_INPUT from tests.common import MockConfigEntry @@ -59,7 +61,7 @@ async def test_recovery_after_error( hass: HomeAssistant, exception_type: Exception, expected_error: str, - mock_pterodactyl: PterodactylClient, + mock_pterodactyl: Generator[AsyncMock], ) -> None: """Test recovery after an error.""" result = await hass.config_entries.flow.async_init( @@ -143,7 +145,7 @@ async def test_reauth_recovery_after_error( exception_type: Exception, expected_error: str, mock_config_entry: MockConfigEntry, - mock_pterodactyl: PterodactylClient, + mock_pterodactyl: Generator[AsyncMock], ) -> None: """Test recovery after an error during re-authentication.""" mock_config_entry.add_to_hass(hass)