Refactor Frontier Silicon tests (#180264)

This commit is contained in:
Joost Lekkerkerker
2026-08-27 16:03:03 +02:00
committed by GitHub
parent bff05e9c1b
commit 935e17a222
4 changed files with 260 additions and 361 deletions
@@ -1 +1,13 @@
"""Tests for the Frontier Silicon 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()
+66 -169
View File
@@ -3,8 +3,7 @@
from collections.abc import Generator
from unittest.mock import AsyncMock, patch
from afsapi import Equaliser, FSConnectionError, PlayCaps, PlayerMode, Preset
from afsapi.nodes import PresetsListItem
from afsapi import Equaliser, PlayCaps, PlayerMode, PlayState, Preset
import pytest
from homeassistant.components.frontier_silicon.const import CONF_WEBFSAPI_URL, DOMAIN
@@ -13,140 +12,73 @@ from homeassistant.const import CONF_PIN
from tests.common import MockConfigEntry
class FakeAFSAPIDevice:
"""A fake Frontier Silicon Device."""
# registry of fake AFSAPI devices
afsapi_device_map = {}
def __new__(cls, webfsapi_endpoint: str, pin: str | int, timeout: int = 2):
"""Create or reuse a fake device for the endpoint."""
if webfsapi_endpoint not in cls.afsapi_device_map:
cls.afsapi_device_map[webfsapi_endpoint] = super().__new__(cls)
return cls.afsapi_device_map[webfsapi_endpoint]
def __init__(
self, webfsapi_endpoint: str, pin: str | int, timeout: int = 2
) -> None:
"""Constructor."""
if not hasattr(self, "init_done"):
self.webfsapi_endpoint = webfsapi_endpoint
self.reset()
self.init_done = True
def reset(self):
"""Reset the state of the device, ready for the next test."""
self.fail_get_power = False
self.has_power = False
async def get_radio_id(self) -> str:
"""Mock get_radio_id AFSAPI function."""
return "FakeID"
async def get_power(self) -> bool:
"""Mock get_power AFSAPI function."""
if self.fail_get_power:
raise FSConnectionError
return self.has_power
async def get_play_name(self) -> str:
"""Mock get_play_name AFSAPI function."""
return "Something Playing"
async def get_play_text(self) -> str:
"""Mock get_play_text AFSAPI function."""
return "Something Playing Extra Text"
async def get_play_artist(self) -> str:
"""Mock get_play_artist AFSAPI function."""
return "Artist Name"
async def get_play_album(self) -> str:
"""Mock get_play_album AFSAPI function."""
return "Album Name"
async def get_play_status(self) -> int:
"""Mock get_play_status AFSAPI function."""
return 0
async def get_mode(self) -> PlayerMode:
"""Mock get_mode AFSAPI function."""
available_modes = await self.get_modes()
return available_modes[0]
async def get_mute(self) -> bool:
"""Mock get_mute AFSAPI function."""
return False
async def get_volume(self) -> int:
"""Mock get_volume AFSAPI function."""
return 3
async def get_play_graphic(self) -> str:
"""Mock get_play_graphic AFSAPI function."""
return "https://1.1.1.1/graphic_url"
async def get_modes(self) -> list[PlayerMode]:
"""Mock get_modes AFSAPI function."""
valid_modes = [(0, {"id": "mocked_mode0", "label": "MockedMode"})]
return [
PlayerMode(
id=v["id"],
key=int(k),
label=v.get("label"),
selectable=v.get("selectable"),
streamable=v.get("streamable"),
modetype=v.get("modeType"),
)
for k, v in valid_modes
]
async def get_play_caps(self) -> PlayCaps | None:
"""Mock get_play_caps AFSAPI function."""
return PlayCaps(0)
async def get_presets(self) -> list[Preset]:
"""Mock get_presets AFSAPI function."""
def _to_preset(
key: str,
preset_fields: PresetsListItem,
) -> Preset:
"""Internal helper function to convert data to Preset."""
return Preset(int(key), preset_fields.get("type"), preset_fields["name"])
presets_data = [(0, {"type": "mocked_eqpreset_type0", "name": "MockedPreset"})]
return [_to_preset(key, preset_fields) for key, preset_fields in presets_data]
async def get_eq_preset(self) -> Equaliser:
"""Mock get_eq_preset AFSAPI function."""
available_equalisers = await self.get_equalisers()
return available_equalisers[0]
async def get_equalisers(self) -> list[Equaliser]:
"""Mock get_equalisers AFSAPI function."""
equalisers_data = [(0, {"label": "MockedEq"})]
return [
Equaliser(key=int(key), label=eqinfo["label"])
for key, eqinfo in equalisers_data
]
async def get_volume_steps(self) -> int:
"""Mock get_volume_steps AFSAPI function."""
return 2
@pytest.fixture
def mock_setup_entry() -> Generator[AsyncMock]:
"""Override async_setup_entry."""
with patch(
"homeassistant.components.frontier_silicon.async_setup_entry",
return_value=True,
) as mock_setup_entry:
yield mock_setup_entry
@pytest.fixture
def fake_afsapi_dev(config_entry: MockConfigEntry):
"""Return a test FakeAFSAPIDevice, creating it for an endpoint if needed."""
webfsapi_endpoint = config_entry.data[CONF_WEBFSAPI_URL]
pin = config_entry.data[CONF_PIN]
fake_dev = FakeAFSAPIDevice(webfsapi_endpoint, pin)
yield fake_dev
fake_dev.reset()
def mock_afsapi() -> Generator[AsyncMock]:
"""Mock a Frontier Silicon AFSAPI client."""
with (
patch(
"homeassistant.components.frontier_silicon.AFSAPI",
autospec=True,
) as mock_client,
patch(
"homeassistant.components.frontier_silicon.config_flow.AFSAPI",
new=mock_client,
),
):
client = mock_client.return_value
client.webfsapi_endpoint = "http://1.1.1.1:80/webfsapi"
# get_webfsapi_endpoint is a staticmethod on the class; expose it on the
# instance mock too so tests can configure it via the yielded client.
mock_client.get_webfsapi_endpoint.return_value = "http://1.1.1.1:80/webfsapi"
client.get_webfsapi_endpoint = mock_client.get_webfsapi_endpoint
client.get_friendly_name.return_value = "Name of the device"
client.get_radio_id.return_value = "mock_radio_id"
client.get_power.return_value = False
client.get_play_status.return_value = PlayState.IDLE
client.get_play_name.return_value = "Something Playing"
client.get_play_text.return_value = "Something Playing Extra Text"
client.get_play_artist.return_value = "Artist Name"
client.get_play_album.return_value = "Album Name"
client.get_play_graphic.return_value = "https://1.1.1.1/graphic_url"
client.get_mute.return_value = False
client.get_volume.return_value = 3
client.get_volume_steps.return_value = 2
client.get_play_caps.return_value = PlayCaps(0)
modes = [
PlayerMode(
id="mocked_mode0",
key=0,
label="MockedMode",
selectable=True,
streamable=None,
modetype=None,
)
]
client.get_modes.return_value = modes
client.get_mode.return_value = modes[0]
equalisers = [Equaliser(key=0, label="MockedEq")]
client.get_equalisers.return_value = equalisers
client.get_eq_preset.return_value = equalisers[0]
client.get_presets.return_value = [
Preset(0, "mocked_eqpreset_type0", "MockedPreset")
]
yield client
@pytest.fixture
@@ -154,42 +86,7 @@ def config_entry() -> MockConfigEntry:
"""Create a mock Frontier Silicon config entry."""
return MockConfigEntry(
domain=DOMAIN,
title="Name of the device",
unique_id="mock_radio_id",
data={CONF_WEBFSAPI_URL: "http://1.1.1.1:80/webfsapi", CONF_PIN: "1234"},
)
@pytest.fixture(autouse=True)
def mock_valid_device_url() -> Generator[None]:
"""Return a valid webfsapi endpoint."""
with patch(
"afsapi.AFSAPI.get_webfsapi_endpoint",
return_value="http://1.1.1.1:80/webfsapi",
):
yield
@pytest.fixture(autouse=True)
def mock_valid_pin() -> Generator[None]:
"""Make get_friendly_name return a value, indicating a valid pin."""
with patch(
"afsapi.AFSAPI.get_friendly_name",
return_value="Name of the device",
):
yield
@pytest.fixture(autouse=True)
def mock_radio_id() -> Generator[None]:
"""Return a valid radio_id."""
with patch("afsapi.AFSAPI.get_radio_id", return_value="mock_radio_id"):
yield
@pytest.fixture
def mock_setup_entry() -> Generator[AsyncMock]:
"""Override async_setup_entry."""
with patch(
"homeassistant.components.frontier_silicon.async_setup_entry", return_value=True
) as mock_setup_entry:
yield mock_setup_entry
@@ -1,6 +1,6 @@
"""Test the Frontier Silicon config flow."""
from unittest.mock import AsyncMock, patch
from unittest.mock import AsyncMock
from afsapi import FSConnectionError, FSNotImplementedError, InvalidPinError
import pytest
@@ -18,7 +18,7 @@ from homeassistant.helpers.service_info.ssdp import SsdpServiceInfo
from tests.common import MockConfigEntry
pytestmark = pytest.mark.usefixtures("mock_setup_entry")
pytestmark = pytest.mark.usefixtures("mock_afsapi", "mock_setup_entry")
MOCK_DISCOVERY = SsdpServiceInfo(
@@ -46,6 +46,7 @@ INVALID_MOCK_DISCOVERY = SsdpServiceInfo(
)
async def test_form_default_pin(
hass: HomeAssistant,
mock_afsapi: AsyncMock,
mock_setup_entry: AsyncMock,
radio_id_return_value: str | None,
radio_id_side_effect: Exception | None,
@@ -58,15 +59,12 @@ async def test_form_default_pin(
assert result["step_id"] == "user"
assert result["errors"] == {}
with patch(
"homeassistant.components.frontier_silicon.config_flow.AFSAPI.get_radio_id",
return_value=radio_id_return_value,
side_effect=radio_id_side_effect,
):
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_HOST: "1.1.1.1", CONF_PORT: 80},
)
mock_afsapi.get_radio_id.return_value = radio_id_return_value
mock_afsapi.get_radio_id.side_effect = radio_id_side_effect
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_HOST: "1.1.1.1", CONF_PORT: 80},
)
await hass.async_block_till_done()
assert result2["type"] is FlowResultType.CREATE_ENTRY
@@ -84,6 +82,7 @@ async def test_form_default_pin(
)
async def test_form_nondefault_pin(
hass: HomeAssistant,
mock_afsapi: AsyncMock,
mock_setup_entry: AsyncMock,
radio_id_return_value: str | None,
radio_id_side_effect: Exception | None,
@@ -96,29 +95,24 @@ async def test_form_nondefault_pin(
assert result["step_id"] == "user"
assert result["errors"] == {}
with patch(
"homeassistant.components.frontier_silicon.config_flow.AFSAPI.get_friendly_name",
side_effect=InvalidPinError,
):
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_HOST: "1.1.1.1", CONF_PORT: 80},
)
await hass.async_block_till_done()
mock_afsapi.get_friendly_name.side_effect = InvalidPinError
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_HOST: "1.1.1.1", CONF_PORT: 80},
)
await hass.async_block_till_done()
assert result2["type"] is FlowResultType.FORM
assert result2["step_id"] == "device_config"
assert result2["errors"] is None
with patch(
"homeassistant.components.frontier_silicon.config_flow.AFSAPI.get_radio_id",
return_value=radio_id_return_value,
side_effect=radio_id_side_effect,
):
result3 = await hass.config_entries.flow.async_configure(
result2["flow_id"],
{CONF_PIN: "4321"},
)
mock_afsapi.get_friendly_name.side_effect = None
mock_afsapi.get_radio_id.return_value = radio_id_return_value
mock_afsapi.get_radio_id.side_effect = radio_id_side_effect
result3 = await hass.config_entries.flow.async_configure(
result2["flow_id"],
{CONF_PIN: "4321"},
)
await hass.async_block_till_done()
assert result3["type"] is FlowResultType.CREATE_ENTRY
@@ -140,6 +134,7 @@ async def test_form_nondefault_pin(
)
async def test_form_nondefault_pin_invalid(
hass: HomeAssistant,
mock_afsapi: AsyncMock,
friendly_name_error: Exception,
result_error: str,
mock_setup_entry: AsyncMock,
@@ -152,34 +147,29 @@ async def test_form_nondefault_pin_invalid(
assert result["step_id"] == "user"
assert result["errors"] == {}
with patch(
"homeassistant.components.frontier_silicon.config_flow.AFSAPI.get_friendly_name",
side_effect=InvalidPinError,
):
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_HOST: "1.1.1.1", CONF_PORT: 80},
)
await hass.async_block_till_done()
mock_afsapi.get_friendly_name.side_effect = InvalidPinError
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_HOST: "1.1.1.1", CONF_PORT: 80},
)
await hass.async_block_till_done()
assert result2["type"] is FlowResultType.FORM
assert result2["step_id"] == "device_config"
assert result2["errors"] is None
with patch(
"homeassistant.components.frontier_silicon.config_flow.AFSAPI.get_friendly_name",
side_effect=friendly_name_error,
):
result3 = await hass.config_entries.flow.async_configure(
result2["flow_id"],
{CONF_PIN: "4321"},
)
await hass.async_block_till_done()
mock_afsapi.get_friendly_name.side_effect = friendly_name_error
result3 = await hass.config_entries.flow.async_configure(
result2["flow_id"],
{CONF_PIN: "4321"},
)
await hass.async_block_till_done()
assert result3["type"] is FlowResultType.FORM
assert result2["step_id"] == "device_config"
assert result3["errors"] == {"base": result_error}
mock_afsapi.get_friendly_name.side_effect = None
result4 = await hass.config_entries.flow.async_configure(
result3["flow_id"],
{CONF_PIN: "4321"},
@@ -204,6 +194,7 @@ async def test_form_nondefault_pin_invalid(
)
async def test_invalid_device_url(
hass: HomeAssistant,
mock_afsapi: AsyncMock,
webfsapi_endpoint_error: Exception,
result_error: str,
mock_setup_entry: AsyncMock,
@@ -216,20 +207,18 @@ async def test_invalid_device_url(
assert result["step_id"] == "user"
assert result["errors"] == {}
with patch(
"homeassistant.components.frontier_silicon.config_flow.AFSAPI.get_webfsapi_endpoint",
side_effect=webfsapi_endpoint_error,
):
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_HOST: "1.1.1.1", CONF_PORT: 80},
)
await hass.async_block_till_done()
mock_afsapi.get_webfsapi_endpoint.side_effect = webfsapi_endpoint_error
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_HOST: "1.1.1.1", CONF_PORT: 80},
)
await hass.async_block_till_done()
assert result2["type"] is FlowResultType.FORM
assert result2["step_id"] == "user"
assert result2["errors"] == {"base": result_error}
mock_afsapi.get_webfsapi_endpoint.side_effect = None
result3 = await hass.config_entries.flow.async_configure(
result2["flow_id"],
{CONF_HOST: "1.1.1.1", CONF_PORT: 80},
@@ -247,6 +236,7 @@ async def test_invalid_device_url(
async def test_user_already_configured_without_unique_id(
hass: HomeAssistant,
mock_afsapi: AsyncMock,
) -> None:
"""Test manual setup aborts when an entry with the same endpoint already exists."""
entry = MockConfigEntry(
@@ -256,17 +246,14 @@ async def test_user_already_configured_without_unique_id(
)
entry.add_to_hass(hass)
with patch(
"homeassistant.components.frontier_silicon.config_flow.AFSAPI.get_radio_id",
side_effect=FSNotImplementedError,
):
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_HOST: "1.1.1.1", CONF_PORT: 80},
)
mock_afsapi.get_radio_id.side_effect = FSNotImplementedError
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_USER}
)
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_HOST: "1.1.1.1", CONF_PORT: 80},
)
assert result2["type"] is FlowResultType.ABORT
assert result2["reason"] == "already_configured"
@@ -278,21 +265,19 @@ async def test_user_already_configured_without_unique_id(
)
async def test_ssdp(
hass: HomeAssistant,
mock_setup_entry: MockConfigEntry,
mock_afsapi: AsyncMock,
mock_setup_entry: AsyncMock,
radio_id_return_value: str | None,
radio_id_side_effect: Exception | None,
) -> None:
"""Test a device being discovered."""
with patch(
"homeassistant.components.frontier_silicon.config_flow.AFSAPI.get_radio_id",
return_value=radio_id_return_value,
side_effect=radio_id_side_effect,
):
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data=MOCK_DISCOVERY,
)
mock_afsapi.get_radio_id.return_value = radio_id_return_value
mock_afsapi.get_radio_id.side_effect = radio_id_side_effect
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data=MOCK_DISCOVERY,
)
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "confirm"
@@ -351,35 +336,32 @@ async def test_ssdp_already_configured(
[(ValueError, "unknown"), (FSConnectionError, "cannot_connect")],
)
async def test_ssdp_fail(
hass: HomeAssistant, webfsapi_endpoint_error: Exception, result_error: str
hass: HomeAssistant,
mock_afsapi: AsyncMock,
webfsapi_endpoint_error: Exception,
result_error: str,
) -> None:
"""Test a device being discovered but failing to reply."""
with patch(
"homeassistant.components.frontier_silicon.config_flow.AFSAPI.get_webfsapi_endpoint",
side_effect=webfsapi_endpoint_error,
):
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data=MOCK_DISCOVERY,
)
mock_afsapi.get_webfsapi_endpoint.side_effect = webfsapi_endpoint_error
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data=MOCK_DISCOVERY,
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == result_error
async def test_ssdp_nondefault_pin(hass: HomeAssistant) -> None:
async def test_ssdp_nondefault_pin(hass: HomeAssistant, mock_afsapi: AsyncMock) -> None:
"""Test a device being discovered."""
with patch(
"homeassistant.components.frontier_silicon.config_flow.AFSAPI.get_friendly_name",
side_effect=InvalidPinError,
):
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data=MOCK_DISCOVERY,
)
mock_afsapi.get_friendly_name.side_effect = InvalidPinError
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={"source": config_entries.SOURCE_SSDP},
data=MOCK_DISCOVERY,
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "invalid_auth"
@@ -413,6 +395,7 @@ async def test_reauth_flow(hass: HomeAssistant, config_entry: MockConfigEntry) -
)
async def test_reauth_flow_friendly_name_error(
hass: HomeAssistant,
mock_afsapi: AsyncMock,
exception: Exception,
reason: str,
config_entry: MockConfigEntry,
@@ -425,20 +408,18 @@ async def test_reauth_flow_friendly_name_error(
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "device_config"
with patch(
"homeassistant.components.frontier_silicon.config_flow.AFSAPI.get_friendly_name",
side_effect=exception,
):
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_PIN: "4321"},
)
await hass.async_block_till_done()
mock_afsapi.get_friendly_name.side_effect = exception
result2 = await hass.config_entries.flow.async_configure(
result["flow_id"],
{CONF_PIN: "4321"},
)
await hass.async_block_till_done()
assert result2["type"] is FlowResultType.FORM
assert result2["step_id"] == "device_config"
assert result2["errors"] == {"base": reason}
mock_afsapi.get_friendly_name.side_effect = None
result3 = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input={CONF_PIN: "4242"},
@@ -1,24 +1,54 @@
"""Test the Frontier Silicon media player entity."""
import logging
from unittest.mock import AsyncMock, patch
from datetime import timedelta
from unittest.mock import AsyncMock
from afsapi import FSConnectionError, FSNotImplementedError, PlayCaps
from freezegun.api import FrozenDateTimeFactory
import pytest
from homeassistant.components.frontier_silicon.media_player import AFSAPIMediaPlayer
from homeassistant.components.media_player import MediaPlayerEntityFeature
from homeassistant.const import STATE_IDLE, STATE_OFF, STATE_UNAVAILABLE
from homeassistant.components.media_player import (
DOMAIN as MEDIA_PLAYER_DOMAIN,
MediaPlayerEntityFeature,
)
from homeassistant.const import (
ATTR_ENTITY_ID,
ATTR_SUPPORTED_FEATURES,
SERVICE_MEDIA_PREVIOUS_TRACK,
STATE_IDLE,
STATE_OFF,
STATE_UNAVAILABLE,
)
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import device_registry as dr, entity_registry as er
from homeassistant.helpers.entity_component import async_update_entity
from .conftest import FakeAFSAPIDevice
from . import setup_integration
from tests.common import MockConfigEntry
from tests.common import MockConfigEntry, async_fire_time_changed
_LOGGER = logging.getLogger(__name__)
ENTITY_ID = "media_player.name_of_the_device"
_FULL_PLAY_CAPS = (
PlayCaps.PAUSE
| PlayCaps.STOP
| PlayCaps.SKIP_NEXT
| PlayCaps.SKIP_PREVIOUS
| PlayCaps.FAST_FORWARD
| PlayCaps.REWIND
| PlayCaps.SHUFFLE
| PlayCaps.REPEAT
| PlayCaps.SEEK
| PlayCaps.APPLY_FEEDBACK
| PlayCaps.SCROBBLING
| PlayCaps.ADD_PRESET
| PlayCaps.THUMBS_UP
| PlayCaps.THUMBS_DOWN
| PlayCaps.SKIP_FORWARD
| PlayCaps.SKIP_BACKWARD
| PlayCaps.REPEAT_ONE
)
@pytest.mark.parametrize(
@@ -33,16 +63,27 @@ _LOGGER = logging.getLogger(__name__)
],
)
async def test_async_media_previous_track_maps_errors(
error: Exception, translation_key: str, message: str | None
hass: HomeAssistant,
config_entry: MockConfigEntry,
mock_afsapi: AsyncMock,
error: Exception,
translation_key: str,
message: str | None,
) -> None:
"""Test previous track maps API failures to Home Assistant errors."""
fs_device = AsyncMock()
fs_device.rewind.side_effect = error
mock_config_entry = MockConfigEntry()
entity = AFSAPIMediaPlayer(mock_config_entry, fs_device)
mock_afsapi.get_power.return_value = True
mock_afsapi.get_play_caps.return_value = _FULL_PLAY_CAPS
mock_afsapi.rewind.side_effect = error
await setup_integration(hass, config_entry)
with pytest.raises(HomeAssistantError) as exc_info:
await entity.async_media_previous_track()
await hass.services.async_call(
MEDIA_PLAYER_DOMAIN,
SERVICE_MEDIA_PREVIOUS_TRACK,
{ATTR_ENTITY_ID: ENTITY_ID},
blocking=True,
)
assert exc_info.value.translation_key == translation_key
assert exc_info.value.translation_placeholders["command"] == "media_previous_track"
@@ -52,33 +93,18 @@ async def test_async_media_previous_track_maps_errors(
)
async def test_async_media_caps() -> None:
async def test_async_media_caps(
hass: HomeAssistant,
config_entry: MockConfigEntry,
mock_afsapi: AsyncMock,
) -> None:
"""Test AFSAPI play caps translation to MediaPlayerEntityFeatures."""
fs_device = AsyncMock()
fs_device.get_power.return_value = False
fs_device.get_play_caps.return_value = (
PlayCaps.PAUSE
| PlayCaps.STOP
| PlayCaps.SKIP_NEXT
| PlayCaps.SKIP_PREVIOUS
| PlayCaps.FAST_FORWARD
| PlayCaps.REWIND
| PlayCaps.SHUFFLE
| PlayCaps.REPEAT
| PlayCaps.SEEK
| PlayCaps.APPLY_FEEDBACK
| PlayCaps.SCROBBLING
| PlayCaps.ADD_PRESET
| PlayCaps.THUMBS_UP
| PlayCaps.THUMBS_DOWN
| PlayCaps.SKIP_FORWARD
| PlayCaps.SKIP_BACKWARD
| PlayCaps.REPEAT_ONE
)
mock_config_entry = MockConfigEntry()
entity = AFSAPIMediaPlayer(mock_config_entry, fs_device)
await entity.async_update()
assert entity.supported_features == (
mock_afsapi.get_play_caps.return_value = _FULL_PLAY_CAPS
await setup_integration(hass, config_entry)
state = hass.states.get(ENTITY_ID)
assert state.attributes[ATTR_SUPPORTED_FEATURES] == (
AFSAPIMediaPlayer._BASE_SUPPORTED_FEATURES
| MediaPlayerEntityFeature.PLAY
| MediaPlayerEntityFeature.PAUSE
@@ -97,32 +123,25 @@ async def test_media_player_on(
config_entry: MockConfigEntry,
device_registry: dr.DeviceRegistry,
entity_registry: er.EntityRegistry,
fake_afsapi_dev: FakeAFSAPIDevice,
mock_afsapi: AsyncMock,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test update of a device which is powered on."""
# Connect device
with patch(
"homeassistant.components.frontier_silicon.AFSAPI",
FakeAFSAPIDevice,
):
config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
await setup_integration(hass, config_entry)
# Verify device exists
devices = dr.async_entries_for_config_entry(device_registry, config_entry.entry_id)
assert len(devices) == 1
device_entry = devices[0]
# Verify device has the expected number of entities
expected_num_entities = 1
entities = er.async_entries_for_device(entity_registry, device_entry.id)
assert len(entities) == expected_num_entities
assert len(entities) == 1
# Power on the device and advance time to trigger a poll
mock_afsapi.get_power.return_value = True
freezer.tick(timedelta(seconds=10))
async_fire_time_changed(hass)
await hass.async_block_till_done()
# Power on the fake device
fake_afsapi_dev.has_power = True
# get hass to do an update
await async_update_entity(hass, entities[0].entity_id)
assert hass.states.get(entities[0].entity_id).state == STATE_IDLE
@@ -131,43 +150,33 @@ async def test_async_update_disconnect(
config_entry: MockConfigEntry,
device_registry: dr.DeviceRegistry,
entity_registry: er.EntityRegistry,
fake_afsapi_dev: FakeAFSAPIDevice,
mock_afsapi: AsyncMock,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test that an update with a disconnect can change device availability."""
await setup_integration(hass, config_entry)
# Connect device
with patch(
"homeassistant.components.frontier_silicon.AFSAPI",
FakeAFSAPIDevice,
):
config_entry.add_to_hass(hass)
await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
# Verify device exists
devices = dr.async_entries_for_config_entry(device_registry, config_entry.entry_id)
assert len(devices) == 1
device_entry = devices[0]
# Verify device has the expected number of entities
expected_num_entities = 1
entities = er.async_entries_for_device(entity_registry, device_entry.id)
assert len(entities) == expected_num_entities
assert len(entities) == 1
entity_id = entities[0].entity_id
# Get hass to do an update
await async_update_entity(hass, entities[0].entity_id)
# Fake device starts in off state
assert hass.states.get(entities[0].entity_id).state == STATE_OFF
# Device starts in off state
assert hass.states.get(entity_id).state == STATE_OFF
# Make the fake device raise a connection error next time get_power is called
fake_afsapi_dev.fail_get_power = True
# get hass to do an update
await async_update_entity(hass, entities[0].entity_id)
# Check device availability, should now be offline
assert hass.states.get(entities[0].entity_id).state == STATE_UNAVAILABLE
# Make the device raise a connection error on the next poll
mock_afsapi.get_power.side_effect = FSConnectionError
freezer.tick(timedelta(seconds=10))
async_fire_time_changed(hass)
await hass.async_block_till_done()
assert hass.states.get(entity_id).state == STATE_UNAVAILABLE
# Reset device error state
fake_afsapi_dev.fail_get_power = False
await async_update_entity(hass, entities[0].entity_id)
# Fake device should be back in off state
assert hass.states.get(entities[0].entity_id).state == STATE_OFF
mock_afsapi.get_power.side_effect = None
freezer.tick(timedelta(seconds=10))
async_fire_time_changed(hass)
await hass.async_block_till_done()
assert hass.states.get(entity_id).state == STATE_OFF