Improve fyta tests (#117661)

* Add test for init

* update tests

* split common.py into const.py and __init__.py

* Update tests/components/fyta/__init__.py

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>

* add autospec, tidy up

* adjust len-test

---------

Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
This commit is contained in:
dontinelli 2024-05-23 10:51:30 +02:00 committed by GitHub
parent e8f544d216
commit 6682244abf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 158 additions and 50 deletions

View File

@ -471,7 +471,6 @@ omit =
homeassistant/components/frontier_silicon/browse_media.py homeassistant/components/frontier_silicon/browse_media.py
homeassistant/components/frontier_silicon/media_player.py homeassistant/components/frontier_silicon/media_player.py
homeassistant/components/futurenow/light.py homeassistant/components/futurenow/light.py
homeassistant/components/fyta/__init__.py
homeassistant/components/fyta/coordinator.py homeassistant/components/fyta/coordinator.py
homeassistant/components/fyta/entity.py homeassistant/components/fyta/entity.py
homeassistant/components/fyta/sensor.py homeassistant/components/fyta/sensor.py

View File

@ -1 +1,19 @@
"""Tests for the Fyta integration.""" """Tests for the Fyta integration."""
from unittest.mock import patch
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry
async def setup_platform(
hass: HomeAssistant, config_entry: MockConfigEntry, platforms: list[Platform]
) -> MockConfigEntry:
"""Set up the Fyta platform."""
config_entry.add_to_hass(hass)
with patch("homeassistant.components.fyta.PLATFORMS", platforms):
await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()

View File

@ -1,50 +1,63 @@
"""Test helpers.""" """Test helpers for FYTA."""
from collections.abc import Generator from collections.abc import Generator
from datetime import UTC, datetime, timedelta from datetime import UTC, datetime
from unittest.mock import AsyncMock, patch from unittest.mock import AsyncMock, patch
import pytest import pytest
from homeassistant.components.fyta.const import CONF_EXPIRATION from homeassistant.components.fyta.const import CONF_EXPIRATION, DOMAIN as FYTA_DOMAIN
from homeassistant.const import CONF_ACCESS_TOKEN from homeassistant.const import CONF_ACCESS_TOKEN, CONF_PASSWORD, CONF_USERNAME
from .test_config_flow import ACCESS_TOKEN, EXPIRATION from .const import ACCESS_TOKEN, EXPIRATION, PASSWORD, USERNAME
from tests.common import MockConfigEntry
@pytest.fixture @pytest.fixture
def mock_fyta(): def mock_config_entry() -> MockConfigEntry:
"""Build a fixture for the Fyta API that connects successfully and returns one device.""" """Mock a config entry."""
return MockConfigEntry(
mock_fyta_api = AsyncMock() domain=FYTA_DOMAIN,
with patch( title="fyta_user",
"homeassistant.components.fyta.config_flow.FytaConnector", data={
return_value=mock_fyta_api, CONF_USERNAME: USERNAME,
) as mock_fyta_api: CONF_PASSWORD: PASSWORD,
mock_fyta_api.return_value.login.return_value = {
CONF_ACCESS_TOKEN: ACCESS_TOKEN, CONF_ACCESS_TOKEN: ACCESS_TOKEN,
CONF_EXPIRATION: EXPIRATION, CONF_EXPIRATION: EXPIRATION,
} },
yield mock_fyta_api minor_version=2,
)
@pytest.fixture @pytest.fixture
def mock_fyta_init(): def mock_fyta_connector():
"""Build a fixture for the Fyta API that connects successfully and returns one device.""" """Build a fixture for the Fyta API that connects successfully and returns one device."""
mock_fyta_api = AsyncMock() mock_fyta_connector = AsyncMock()
mock_fyta_api.expiration = datetime.now(tz=UTC) + timedelta(days=1) mock_fyta_connector.expiration = datetime.fromisoformat(EXPIRATION).replace(
mock_fyta_api.login = AsyncMock( tzinfo=UTC
)
mock_fyta_connector.client = AsyncMock(autospec=True)
mock_fyta_connector.login = AsyncMock(
return_value={ return_value={
CONF_ACCESS_TOKEN: ACCESS_TOKEN, CONF_ACCESS_TOKEN: ACCESS_TOKEN,
CONF_EXPIRATION: EXPIRATION, CONF_EXPIRATION: datetime.fromisoformat(EXPIRATION).replace(tzinfo=UTC),
} }
) )
with patch( with (
"homeassistant.components.fyta.FytaConnector.__new__", patch(
return_value=mock_fyta_api, "homeassistant.components.fyta.FytaConnector",
autospec=True,
return_value=mock_fyta_connector,
),
patch(
"homeassistant.components.fyta.config_flow.FytaConnector",
autospec=True,
return_value=mock_fyta_connector,
),
): ):
yield mock_fyta_api yield mock_fyta_connector
@pytest.fixture @pytest.fixture

View File

@ -0,0 +1,7 @@
"""Common methods and const used across tests for FYTA."""
USERNAME = "fyta_user"
PASSWORD = "fyta_pass"
ACCESS_TOKEN = "123xyz"
EXPIRATION = "2030-12-31T10:00:00+00:00"
EXPIRATION_OLD = "2020-01-01T00:00:00+00:00"

View File

@ -1,6 +1,5 @@
"""Test the fyta config flow.""" """Test the fyta config flow."""
from datetime import UTC, datetime
from unittest.mock import AsyncMock from unittest.mock import AsyncMock
from fyta_cli.fyta_exceptions import ( from fyta_cli.fyta_exceptions import (
@ -16,16 +15,13 @@ from homeassistant.const import CONF_ACCESS_TOKEN, CONF_PASSWORD, CONF_USERNAME
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType from homeassistant.data_entry_flow import FlowResultType
from tests.common import MockConfigEntry from .const import ACCESS_TOKEN, EXPIRATION, PASSWORD, USERNAME
USERNAME = "fyta_user" from tests.common import MockConfigEntry
PASSWORD = "fyta_pass"
ACCESS_TOKEN = "123xyz"
EXPIRATION = datetime.fromisoformat("2024-12-31T10:00:00").replace(tzinfo=UTC)
async def test_user_flow( async def test_user_flow(
hass: HomeAssistant, mock_fyta: AsyncMock, mock_setup_entry: AsyncMock hass: HomeAssistant, mock_fyta_connector: AsyncMock, mock_setup_entry: AsyncMock
) -> None: ) -> None:
"""Test we get the form.""" """Test we get the form."""
@ -46,7 +42,7 @@ async def test_user_flow(
CONF_USERNAME: USERNAME, CONF_USERNAME: USERNAME,
CONF_PASSWORD: PASSWORD, CONF_PASSWORD: PASSWORD,
CONF_ACCESS_TOKEN: ACCESS_TOKEN, CONF_ACCESS_TOKEN: ACCESS_TOKEN,
CONF_EXPIRATION: "2024-12-31T10:00:00+00:00", CONF_EXPIRATION: EXPIRATION,
} }
assert len(mock_setup_entry.mock_calls) == 1 assert len(mock_setup_entry.mock_calls) == 1
@ -64,7 +60,7 @@ async def test_form_exceptions(
hass: HomeAssistant, hass: HomeAssistant,
exception: Exception, exception: Exception,
error: dict[str, str], error: dict[str, str],
mock_fyta: AsyncMock, mock_fyta_connector: AsyncMock,
mock_setup_entry: AsyncMock, mock_setup_entry: AsyncMock,
) -> None: ) -> None:
"""Test we can handle Form exceptions.""" """Test we can handle Form exceptions."""
@ -73,7 +69,7 @@ async def test_form_exceptions(
DOMAIN, context={"source": config_entries.SOURCE_USER} DOMAIN, context={"source": config_entries.SOURCE_USER}
) )
mock_fyta.return_value.login.side_effect = exception mock_fyta_connector.login.side_effect = exception
# tests with connection error # tests with connection error
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
@ -85,7 +81,7 @@ async def test_form_exceptions(
assert result["step_id"] == "user" assert result["step_id"] == "user"
assert result["errors"] == error assert result["errors"] == error
mock_fyta.return_value.login.side_effect = None mock_fyta_connector.login.side_effect = None
# tests with all information provided # tests with all information provided
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
@ -98,12 +94,14 @@ async def test_form_exceptions(
assert result["data"][CONF_USERNAME] == USERNAME assert result["data"][CONF_USERNAME] == USERNAME
assert result["data"][CONF_PASSWORD] == PASSWORD assert result["data"][CONF_PASSWORD] == PASSWORD
assert result["data"][CONF_ACCESS_TOKEN] == ACCESS_TOKEN assert result["data"][CONF_ACCESS_TOKEN] == ACCESS_TOKEN
assert result["data"][CONF_EXPIRATION] == "2024-12-31T10:00:00+00:00" assert result["data"][CONF_EXPIRATION] == EXPIRATION
assert len(mock_setup_entry.mock_calls) == 1 assert len(mock_setup_entry.mock_calls) == 1
async def test_duplicate_entry(hass: HomeAssistant, mock_fyta: AsyncMock) -> None: async def test_duplicate_entry(
hass: HomeAssistant, mock_fyta_connector: AsyncMock
) -> None:
"""Test duplicate setup handling.""" """Test duplicate setup handling."""
entry = MockConfigEntry( entry = MockConfigEntry(
domain=DOMAIN, domain=DOMAIN,
@ -143,7 +141,7 @@ async def test_reauth(
hass: HomeAssistant, hass: HomeAssistant,
exception: Exception, exception: Exception,
error: dict[str, str], error: dict[str, str],
mock_fyta: AsyncMock, mock_fyta_connector: AsyncMock,
mock_setup_entry: AsyncMock, mock_setup_entry: AsyncMock,
) -> None: ) -> None:
"""Test reauth-flow works.""" """Test reauth-flow works."""
@ -155,7 +153,7 @@ async def test_reauth(
CONF_USERNAME: USERNAME, CONF_USERNAME: USERNAME,
CONF_PASSWORD: PASSWORD, CONF_PASSWORD: PASSWORD,
CONF_ACCESS_TOKEN: ACCESS_TOKEN, CONF_ACCESS_TOKEN: ACCESS_TOKEN,
CONF_EXPIRATION: "2024-06-30T10:00:00+00:00", CONF_EXPIRATION: EXPIRATION,
}, },
) )
entry.add_to_hass(hass) entry.add_to_hass(hass)
@ -168,7 +166,7 @@ async def test_reauth(
assert result["type"] is FlowResultType.FORM assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "reauth_confirm" assert result["step_id"] == "reauth_confirm"
mock_fyta.return_value.login.side_effect = exception mock_fyta_connector.login.side_effect = exception
# tests with connection error # tests with connection error
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
@ -181,7 +179,7 @@ async def test_reauth(
assert result["step_id"] == "reauth_confirm" assert result["step_id"] == "reauth_confirm"
assert result["errors"] == error assert result["errors"] == error
mock_fyta.return_value.login.side_effect = None mock_fyta_connector.login.side_effect = None
# tests with all information provided # tests with all information provided
result = await hass.config_entries.flow.async_configure( result = await hass.config_entries.flow.async_configure(
@ -195,4 +193,4 @@ async def test_reauth(
assert entry.data[CONF_USERNAME] == "other_username" assert entry.data[CONF_USERNAME] == "other_username"
assert entry.data[CONF_PASSWORD] == "other_password" assert entry.data[CONF_PASSWORD] == "other_password"
assert entry.data[CONF_ACCESS_TOKEN] == ACCESS_TOKEN assert entry.data[CONF_ACCESS_TOKEN] == ACCESS_TOKEN
assert entry.data[CONF_EXPIRATION] == "2024-12-31T10:00:00+00:00" assert entry.data[CONF_EXPIRATION] == EXPIRATION

View File

@ -1,23 +1,96 @@
"""Test the initialization.""" """Test the initialization."""
from datetime import UTC, datetime
from unittest.mock import AsyncMock from unittest.mock import AsyncMock
from homeassistant.components.fyta.const import CONF_EXPIRATION, DOMAIN from fyta_cli.fyta_exceptions import (
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_PASSWORD, CONF_USERNAME FytaAuthentificationError,
FytaConnectionError,
FytaPasswordError,
)
import pytest
from homeassistant.components.fyta.const import CONF_EXPIRATION, DOMAIN as FYTA_DOMAIN
from homeassistant.config_entries import ConfigEntryState
from homeassistant.const import (
CONF_ACCESS_TOKEN,
CONF_PASSWORD,
CONF_USERNAME,
Platform,
)
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from .test_config_flow import ACCESS_TOKEN, PASSWORD, USERNAME from . import setup_platform
from .const import ACCESS_TOKEN, EXPIRATION, EXPIRATION_OLD, PASSWORD, USERNAME
from tests.common import MockConfigEntry from tests.common import MockConfigEntry
async def test_load_unload(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_fyta_connector: AsyncMock,
) -> None:
"""Test load and unload."""
await setup_platform(hass, mock_config_entry, [Platform.SENSOR])
assert mock_config_entry.state is ConfigEntryState.LOADED
assert await hass.config_entries.async_unload(mock_config_entry.entry_id)
await hass.async_block_till_done()
assert mock_config_entry.state is ConfigEntryState.NOT_LOADED
@pytest.mark.parametrize(
"exception",
[
FytaAuthentificationError,
FytaPasswordError,
],
)
async def test_invalid_credentials(
hass: HomeAssistant,
exception: Exception,
mock_config_entry: MockConfigEntry,
mock_fyta_connector: AsyncMock,
) -> None:
"""Test FYTA credentials changing."""
mock_fyta_connector.expiration = datetime.fromisoformat(EXPIRATION_OLD).replace(
tzinfo=UTC
)
mock_fyta_connector.login.side_effect = exception
await setup_platform(hass, mock_config_entry, [Platform.SENSOR])
await hass.async_block_till_done()
assert mock_config_entry.state is ConfigEntryState.SETUP_ERROR
async def test_raise_config_entry_not_ready_when_offline(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_fyta_connector: AsyncMock,
) -> None:
"""Config entry state is SETUP_RETRY when FYTA is offline."""
mock_fyta_connector.update_all_plants.side_effect = FytaConnectionError
await setup_platform(hass, mock_config_entry, [Platform.SENSOR])
await hass.async_block_till_done()
assert mock_config_entry.state is ConfigEntryState.SETUP_RETRY
assert len(hass.config_entries.flow.async_progress()) == 0
async def test_migrate_config_entry( async def test_migrate_config_entry(
hass: HomeAssistant, hass: HomeAssistant,
mock_fyta_init: AsyncMock, mock_fyta_connector: AsyncMock,
) -> None: ) -> None:
"""Test successful migration of entry data.""" """Test successful migration of entry data."""
entry = MockConfigEntry( entry = MockConfigEntry(
domain=DOMAIN, domain=FYTA_DOMAIN,
title=USERNAME, title=USERNAME,
data={ data={
CONF_USERNAME: USERNAME, CONF_USERNAME: USERNAME,
@ -39,4 +112,4 @@ async def test_migrate_config_entry(
assert entry.data[CONF_USERNAME] == USERNAME assert entry.data[CONF_USERNAME] == USERNAME
assert entry.data[CONF_PASSWORD] == PASSWORD assert entry.data[CONF_PASSWORD] == PASSWORD
assert entry.data[CONF_ACCESS_TOKEN] == ACCESS_TOKEN assert entry.data[CONF_ACCESS_TOKEN] == ACCESS_TOKEN
assert entry.data[CONF_EXPIRATION] == "2024-12-31T10:00:00+00:00" assert entry.data[CONF_EXPIRATION] == EXPIRATION