Skip to content

Commit

Permalink
Refactor Tile tests (#134130)
Browse files Browse the repository at this point in the history
  • Loading branch information
joostlek authored Dec 28, 2024
1 parent 14059c6 commit 590f0ce
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 78 deletions.
14 changes: 13 additions & 1 deletion tests/components/tile/__init__.py
Original file line number Diff line number Diff line change
@@ -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()
108 changes: 54 additions & 54 deletions tests/components/tile/conftest.py
Original file line number Diff line number Diff line change
@@ -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

TEST_PASSWORD = "123abc"
TEST_USERNAME = "user@host.com"


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

return Mock(
async_get_tiles=AsyncMock(
return_value={data_tile_details["result"]["tile_uuid"]: tile}
)
)


@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,
from .const import TEST_PASSWORD, TEST_USERNAME

from tests.common import MockConfigEntry


@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


@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
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="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
4 changes: 4 additions & 0 deletions tests/components/tile/const.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
"""Constants for the Tile component tests."""

TEST_PASSWORD = "123abc"
TEST_USERNAME = "user@host.com"
2 changes: 1 addition & 1 deletion tests/components/tile/snapshots/test_diagnostics.ambr
Original file line number Diff line number Diff line change
Expand Up @@ -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**',
Expand Down
125 changes: 106 additions & 19 deletions tests/components/tile/test_config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,45 @@
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(
Expand All @@ -33,49 +63,106 @@ 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)
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"

result = await hass.config_entries.flow.async_configure(result["flow_id"])
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


@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"}
)
Expand Down
12 changes: 9 additions & 3 deletions tests/components/tile/test_diagnostics.py
Original file line number Diff line number Diff line change
@@ -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
)

0 comments on commit 590f0ce

Please sign in to comment.