Refactor Tile tests (#134130)

This commit is contained in:
Joost Lekkerkerker 2024-12-28 12:37:21 +01:00 committed by GitHub
parent 14059c6df8
commit 590f0ce61f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 187 additions and 78 deletions

View File

@ -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()

View File

@ -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

View File

@ -0,0 +1,4 @@
"""Constants for the Tile component tests."""
TEST_PASSWORD = "123abc"
TEST_USERNAME = "user@host.com"

View File

@ -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**',

View File

@ -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

View File

@ -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
)