From 590f0ce61ffc0e622479bc7dbff9654bf32d2945 Mon Sep 17 00:00:00 2001 From: Joost Lekkerkerker Date: Sat, 28 Dec 2024 12:37:21 +0100 Subject: [PATCH] Refactor Tile tests (#134130) --- tests/components/tile/__init__.py | 14 +- tests/components/tile/conftest.py | 104 +++++++------- tests/components/tile/const.py | 4 + .../tile/snapshots/test_diagnostics.ambr | 2 +- tests/components/tile/test_config_flow.py | 129 +++++++++++++++--- tests/components/tile/test_diagnostics.py | 12 +- 6 files changed, 187 insertions(+), 78 deletions(-) create mode 100644 tests/components/tile/const.py diff --git a/tests/components/tile/__init__.py b/tests/components/tile/__init__.py index 5f26eb01ce0..d459dbad187 100644 --- a/tests/components/tile/__init__.py +++ b/tests/components/tile/__init__.py @@ -1 +1,13 @@ -"""Define tests for the Tile component.""" +"""Tests for the Tile integration.""" + +from homeassistant.core import HomeAssistant + +from tests.common import MockConfigEntry + + +async def setup_integration(hass: HomeAssistant, config_entry: MockConfigEntry) -> None: + """Fixture for setting up the component.""" + config_entry.add_to_hass(hass) + + await hass.config_entries.async_setup(config_entry.entry_id) + await hass.async_block_till_done() diff --git a/tests/components/tile/conftest.py b/tests/components/tile/conftest.py index 01a711d9261..006f932b06b 100644 --- a/tests/components/tile/conftest.py +++ b/tests/components/tile/conftest.py @@ -1,77 +1,77 @@ """Define test fixtures for Tile.""" from collections.abc import Generator -import json -from typing import Any -from unittest.mock import AsyncMock, Mock, patch +from datetime import datetime +from unittest.mock import AsyncMock, patch import pytest +from pytile.api import API from pytile.tile import Tile from homeassistant.components.tile.const import DOMAIN from homeassistant.const import CONF_PASSWORD, CONF_USERNAME -from homeassistant.core import HomeAssistant -from tests.common import MockConfigEntry, load_fixture +from .const import TEST_PASSWORD, TEST_USERNAME -TEST_PASSWORD = "123abc" -TEST_USERNAME = "user@host.com" +from tests.common import MockConfigEntry -@pytest.fixture(name="api") -def api_fixture(data_tile_details: dict[str, Any]) -> Mock: - """Define a pytile API object.""" - tile = Tile(None, data_tile_details) - tile.async_update = AsyncMock() +@pytest.fixture +def tile() -> AsyncMock: + """Define a Tile object.""" + mock = AsyncMock(spec=Tile) + mock.uuid = "19264d2dffdbca32" + mock.name = "Wallet" + mock.as_dict.return_value = { + "accuracy": 13.496111, + "altitude": 0, + "archetype": "WALLET", + "dead": False, + "firmware_version": "01.12.14.0", + "hardware_version": "02.09", + "kind": "TILE", + "last_timestamp": datetime(2020, 8, 12, 17, 55, 26), + "latitude": 0, + "longitude": 0, + "lost": False, + "lost_timestamp": datetime(1969, 12, 31, 19, 0, 0), + "name": "Wallet", + "ring_state": "STOPPED", + "uuid": "19264d2dffdbca32", + "visible": True, + "voip_state": "OFFLINE", + } + return mock - return Mock( - async_get_tiles=AsyncMock( - return_value={data_tile_details["result"]["tile_uuid"]: tile} - ) + +@pytest.fixture +def mock_config_entry() -> MockConfigEntry: + """Define a config entry fixture.""" + return MockConfigEntry( + domain=DOMAIN, + unique_id=TEST_USERNAME, + data={CONF_USERNAME: TEST_USERNAME, CONF_PASSWORD: TEST_PASSWORD}, ) -@pytest.fixture(name="config_entry") -def config_entry_fixture( - hass: HomeAssistant, config: dict[str, Any] -) -> MockConfigEntry: - """Define a config entry fixture.""" - entry = MockConfigEntry(domain=DOMAIN, unique_id=config[CONF_USERNAME], data=config) - entry.add_to_hass(hass) - return entry - - -@pytest.fixture(name="config") -def config_fixture() -> dict[str, Any]: - """Define a config entry data fixture.""" - return { - CONF_USERNAME: TEST_USERNAME, - CONF_PASSWORD: TEST_PASSWORD, - } - - -@pytest.fixture(name="data_tile_details", scope="package") -def data_tile_details_fixture(): - """Define a Tile details data payload.""" - return json.loads(load_fixture("tile_details_data.json", "tile")) - - -@pytest.fixture(name="mock_pytile") -def mock_pytile_fixture(api: Mock) -> Generator[None]: +@pytest.fixture +def mock_pytile(tile: AsyncMock) -> Generator[None]: """Define a fixture to patch pytile.""" + client = AsyncMock(spec=API) + client.async_get_tiles = AsyncMock(return_value={"19264d2dffdbca32": tile}) with ( patch( - "homeassistant.components.tile.config_flow.async_login", return_value=api + "homeassistant.components.tile.config_flow.async_login", return_value=client ), - patch("homeassistant.components.tile.async_login", return_value=api), + patch("homeassistant.components.tile.async_login", return_value=client), ): yield -@pytest.fixture(name="setup_config_entry") -async def setup_config_entry_fixture( - hass: HomeAssistant, config_entry: MockConfigEntry, mock_pytile: None -) -> None: - """Define a fixture to set up tile.""" - assert await hass.config_entries.async_setup(config_entry.entry_id) - await hass.async_block_till_done() +@pytest.fixture +def mock_setup_entry(): + """Mock async_setup_entry.""" + with patch( + "homeassistant.components.tile.async_setup_entry", return_value=True + ) as mock_setup_entry: + yield mock_setup_entry diff --git a/tests/components/tile/const.py b/tests/components/tile/const.py new file mode 100644 index 00000000000..c48a0e73dab --- /dev/null +++ b/tests/components/tile/const.py @@ -0,0 +1,4 @@ +"""Constants for the Tile component tests.""" + +TEST_PASSWORD = "123abc" +TEST_USERNAME = "user@host.com" diff --git a/tests/components/tile/snapshots/test_diagnostics.ambr b/tests/components/tile/snapshots/test_diagnostics.ambr index c04bd93315f..2876b058408 100644 --- a/tests/components/tile/snapshots/test_diagnostics.ambr +++ b/tests/components/tile/snapshots/test_diagnostics.ambr @@ -14,7 +14,7 @@ 'latitude': '**REDACTED**', 'longitude': '**REDACTED**', 'lost': False, - 'lost_timestamp': '1969-12-31T23:59:59.999000', + 'lost_timestamp': '1969-12-31T19:00:00', 'name': 'Wallet', 'ring_state': 'STOPPED', 'uuid': '**REDACTED**', diff --git a/tests/components/tile/test_config_flow.py b/tests/components/tile/test_config_flow.py index 70090713e47..2ebfaa5c583 100644 --- a/tests/components/tile/test_config_flow.py +++ b/tests/components/tile/test_config_flow.py @@ -16,15 +16,45 @@ from .conftest import TEST_PASSWORD, TEST_USERNAME from tests.common import MockConfigEntry +async def test_full_flow( + hass: HomeAssistant, mock_pytile: AsyncMock, mock_setup_entry: AsyncMock +) -> None: + """Test a full flow.""" + result = await hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_USER} + ) + assert result["type"] is FlowResultType.FORM + assert result["step_id"] == "user" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={ + CONF_USERNAME: TEST_USERNAME, + CONF_PASSWORD: TEST_PASSWORD, + }, + ) + assert result["type"] is FlowResultType.CREATE_ENTRY + assert result["title"] == TEST_USERNAME + assert result["data"] == { + CONF_USERNAME: TEST_USERNAME, + CONF_PASSWORD: TEST_PASSWORD, + } + assert result["result"].unique_id == TEST_USERNAME + + @pytest.mark.parametrize( - ("mock_login_response", "errors"), + ("exception", "errors"), [ - (AsyncMock(side_effect=InvalidAuthError), {"base": "invalid_auth"}), - (AsyncMock(side_effect=TileError), {"base": "unknown"}), + (InvalidAuthError, {"base": "invalid_auth"}), + (TileError, {"base": "unknown"}), ], ) async def test_create_entry( - hass: HomeAssistant, api, config, errors, mock_login_response, mock_pytile + hass: HomeAssistant, + mock_pytile: AsyncMock, + mock_setup_entry: AsyncMock, + exception: Exception, + errors: dict[str, str], ) -> None: """Test creating an entry.""" result = await hass.config_entries.flow.async_init( @@ -33,46 +63,61 @@ async def test_create_entry( assert result["type"] is FlowResultType.FORM assert result["step_id"] == "user" - # Test errors that can arise: with patch( - "homeassistant.components.tile.config_flow.async_login", mock_login_response + "homeassistant.components.tile.config_flow.async_login", side_effect=exception ): result = await hass.config_entries.flow.async_configure( - result["flow_id"], user_input=config + result["flow_id"], + user_input={ + CONF_USERNAME: TEST_USERNAME, + CONF_PASSWORD: TEST_PASSWORD, + }, ) assert result["type"] is FlowResultType.FORM assert result["step_id"] == "user" assert result["errors"] == errors - # Test that we can recover from login errors: result = await hass.config_entries.flow.async_configure( - result["flow_id"], user_input=config + result["flow_id"], + user_input={ + CONF_USERNAME: TEST_USERNAME, + CONF_PASSWORD: TEST_PASSWORD, + }, ) assert result["type"] is FlowResultType.CREATE_ENTRY - assert result["title"] == TEST_USERNAME - assert result["data"] == { - CONF_USERNAME: TEST_USERNAME, - CONF_PASSWORD: TEST_PASSWORD, - } -async def test_duplicate_error(hass: HomeAssistant, config, setup_config_entry) -> None: +async def test_duplicate_error( + hass: HomeAssistant, mock_config_entry: MockConfigEntry +) -> None: """Test that errors are shown when duplicates are added.""" + mock_config_entry.add_to_hass(hass) result = await hass.config_entries.flow.async_init( - DOMAIN, context={"source": SOURCE_USER}, data=config + DOMAIN, context={"source": SOURCE_USER} + ) + assert result["type"] is FlowResultType.FORM + assert result["step_id"] == "user" + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={ + CONF_USERNAME: TEST_USERNAME, + CONF_PASSWORD: TEST_PASSWORD, + }, ) assert result["type"] is FlowResultType.ABORT assert result["reason"] == "already_configured" async def test_step_reauth( - hass: HomeAssistant, config, config_entry: MockConfigEntry, setup_config_entry + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + mock_setup_entry: AsyncMock, + mock_pytile: AsyncMock, ) -> None: """Test that the reauth step works.""" - result = await config_entry.start_reauth_flow(hass) - assert result["step_id"] == "reauth_confirm" - - result = await hass.config_entries.flow.async_configure(result["flow_id"]) + mock_config_entry.add_to_hass(hass) + result = await mock_config_entry.start_reauth_flow(hass) assert result["type"] is FlowResultType.FORM assert result["step_id"] == "reauth_confirm" @@ -82,3 +127,45 @@ async def test_step_reauth( assert result["type"] is FlowResultType.ABORT assert result["reason"] == "reauth_successful" assert len(hass.config_entries.async_entries()) == 1 + + +@pytest.mark.parametrize( + ("exception", "errors"), + [ + (InvalidAuthError, {"base": "invalid_auth"}), + (TileError, {"base": "unknown"}), + ], +) +async def test_step_reauth_errors( + hass: HomeAssistant, + mock_config_entry: MockConfigEntry, + mock_setup_entry: AsyncMock, + mock_pytile: AsyncMock, + exception: Exception, + errors: dict[str, str], +) -> None: + """Test that the reauth step can recover from an error.""" + mock_config_entry.add_to_hass(hass) + result = await mock_config_entry.start_reauth_flow(hass) + assert result["type"] is FlowResultType.FORM + assert result["step_id"] == "reauth_confirm" + + with patch( + "homeassistant.components.tile.config_flow.async_login", side_effect=exception + ): + result = await hass.config_entries.flow.async_configure( + result["flow_id"], + user_input={ + CONF_PASSWORD: TEST_PASSWORD, + }, + ) + assert result["type"] is FlowResultType.FORM + assert result["step_id"] == "reauth_confirm" + assert result["errors"] == errors + + result = await hass.config_entries.flow.async_configure( + result["flow_id"], user_input={CONF_PASSWORD: "password"} + ) + assert result["type"] is FlowResultType.ABORT + assert result["reason"] == "reauth_successful" + assert len(hass.config_entries.async_entries()) == 1 diff --git a/tests/components/tile/test_diagnostics.py b/tests/components/tile/test_diagnostics.py index 2ac3e06ccd8..87bc670d604 100644 --- a/tests/components/tile/test_diagnostics.py +++ b/tests/components/tile/test_diagnostics.py @@ -1,22 +1,28 @@ """Test Tile diagnostics.""" +from unittest.mock import AsyncMock + from syrupy import SnapshotAssertion from homeassistant.core import HomeAssistant +from . import setup_integration + +from tests.common import MockConfigEntry from tests.components.diagnostics import get_diagnostics_for_config_entry from tests.typing import ClientSessionGenerator async def test_entry_diagnostics( hass: HomeAssistant, - config_entry, hass_client: ClientSessionGenerator, - setup_config_entry, snapshot: SnapshotAssertion, + mock_config_entry: MockConfigEntry, + mock_pytile: AsyncMock, ) -> None: """Test config entry diagnostics.""" + await setup_integration(hass, mock_config_entry) assert ( - await get_diagnostics_for_config_entry(hass, hass_client, config_entry) + await get_diagnostics_for_config_entry(hass, hass_client, mock_config_entry) == snapshot )