more tweaks

This commit is contained in:
Chris Talkington 2024-12-20 18:53:58 -06:00
parent 1122217326
commit dd2b092b70
5 changed files with 248 additions and 126 deletions

View File

@ -0,0 +1,41 @@
# serializer version: 1
# name: test_device_info
DeviceRegistryEntrySnapshot({
'area_id': None,
'config_entries': <ANY>,
'configuration_url': None,
'connections': set({
tuple(
'mac',
'b0:a7:37:96:4d:fa',
),
tuple(
'mac',
'b0:a7:37:96:4d:fb',
),
}),
'disabled_by': None,
'entry_type': None,
'hw_version': '4200X',
'id': <ANY>,
'identifiers': set({
tuple(
'roku',
'1GU48T017973',
),
}),
'is_new': False,
'labels': set({
}),
'manufacturer': 'Roku',
'model': 'Roku 3',
'model_id': None,
'name': 'My Roku 3',
'name_by_user': None,
'primary_config_entry': <ANY>,
'serial_number': None,
'suggested_area': None,
'sw_version': '7.5.0',
'via_device_id': None,
})
# ---

View File

@ -50,7 +50,7 @@
'app_id': '12',
'app_name': 'Netflix',
'device_class': 'receiver',
'entity_picture': '/api/media_player_proxy/media_player.my_roku_3?token=302b9654ec1a8c4fa9872fba3795edda5c07aa9c4d9a5231bf967376c91ceb1e&cache=2cab7007c60e13ef',
'entity_picture': '/api/media_player_proxy/media_player.my_roku_3?token=fa35b32224ca3c83235d621f684a24389bf26b4acd4f2daff53ae76433427864&cache=2cab7007c60e13ef',
'friendly_name': 'My Roku 3',
'media_content_type': <MediaType.APP: 'app'>,
'source': 'Netflix',
@ -199,7 +199,7 @@
'app_id': '74519',
'app_name': "Pluto TV - It's Free TV",
'device_class': 'receiver',
'entity_picture': '/api/media_player_proxy/media_player.my_roku_3?token=7c360550a5a5274ff9e9e79ebc73ae1044c37851e47a0ff58cd36cab59eda988&cache=be54f0f123f7d91f',
'entity_picture': '/api/media_player_proxy/media_player.my_roku_3?token=d91134eba0c155326d21ca70ef3c57b0e47cfb8da439b64a03016b40668ee3f7&cache=be54f0f123f7d91f',
'friendly_name': 'My Roku 3',
'media_content_type': <MediaType.APP: 'app'>,
'media_duration': 6496,
@ -278,7 +278,7 @@
'app_id': '74519',
'app_name': "Pluto TV - It's Free TV",
'device_class': 'receiver',
'entity_picture': '/api/media_player_proxy/media_player.my_roku_3?token=0b7d72bb6e62507f8efe49db9bbca12f4b803e52969cdd797cd8121b74f2de27&cache=be54f0f123f7d91f',
'entity_picture': '/api/media_player_proxy/media_player.my_roku_3?token=3961658cb01e3e6f9d3194a0487e1043616ede6c9ef18c7033650e09160226ec&cache=be54f0f123f7d91f',
'friendly_name': 'My Roku 3',
'media_content_type': <MediaType.APP: 'app'>,
'media_duration': 6496,
@ -506,7 +506,7 @@
'app_id': 'tvinput.dtv',
'app_name': 'Antenna TV',
'device_class': 'tv',
'entity_picture': '/api/media_player_proxy/media_player.58_onn_roku_tv?token=0304dee053f24675c326f5cda374eb4cebf7561bc8c3da18561632f5fda38ae6&cache=545b6ca11153d83a',
'entity_picture': '/api/media_player_proxy/media_player.58_onn_roku_tv?token=cc580699c8223d0f71d05c4de40776ae72df5981efeb24b5ee5725b19dd8b9b7&cache=545b6ca11153d83a',
'friendly_name': '58" Onn Roku TV',
'media_channel': 'getTV (14.3)',
'media_content_type': <MediaType.CHANNEL: 'channel'>,

View File

@ -1,10 +1,13 @@
"""Test the Roku config flow."""
import dataclasses
from unittest.mock import AsyncMock, MagicMock
import pytest
from rokuecp import Device as RokuDevice, RokuConnectionError
from rokuecp import (
Device as RokuDevice,
RokuConnectionError,
RokuConnectionTimeoutError,
)
from homeassistant.components.roku.const import CONF_PLAY_MEDIA_APP_ID, DOMAIN
from homeassistant.config_entries import (
@ -31,39 +34,6 @@ from tests.common import MockConfigEntry
RECONFIGURE_HOST = "192.168.1.190"
async def test_duplicate_error(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_roku_config_flow: MagicMock,
) -> None:
"""Test that errors are shown when duplicates are added."""
mock_config_entry.add_to_hass(hass)
user_input = {CONF_HOST: mock_config_entry.data[CONF_HOST]}
result = await hass.config_entries.flow.async_init(
DOMAIN, context={CONF_SOURCE: SOURCE_USER}, data=user_input
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"
user_input = {CONF_HOST: mock_config_entry.data[CONF_HOST]}
result = await hass.config_entries.flow.async_init(
DOMAIN, context={CONF_SOURCE: SOURCE_USER}, data=user_input
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"
discovery_info = dataclasses.replace(MOCK_SSDP_DISCOVERY_INFO)
result = await hass.config_entries.flow.async_init(
DOMAIN, context={CONF_SOURCE: SOURCE_SSDP}, data=discovery_info
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"
async def test_form(
hass: HomeAssistant,
mock_roku_config_flow: MagicMock,
@ -73,6 +43,9 @@ async def test_form(
result = await hass.config_entries.flow.async_init(
DOMAIN, context={CONF_SOURCE: SOURCE_USER}
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.FORM
assert result["errors"] == {}
@ -80,6 +53,7 @@ async def test_form(
result = await hass.config_entries.flow.async_configure(
flow_id=result["flow_id"], user_input=user_input
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY
@ -92,77 +66,100 @@ async def test_form(
assert result["result"].unique_id == "1GU48T017973"
async def test_form_cannot_connect(
hass: HomeAssistant, mock_roku_config_flow: MagicMock
@pytest.mark.parametrize(
("error", "reason"),
[
(RokuConnectionError, "cannot_connect"),
(RokuConnectionTimeoutError, "cannot_connect"),
],
)
async def test_form_error(
hass: HomeAssistant,
mock_roku_config_flow: MagicMock,
error: Exception,
reason: str,
) -> None:
"""Test we handle cannot connect roku error."""
mock_roku_config_flow.update.side_effect = RokuConnectionError
"""Test we handle usrr flow on error."""
mock_roku_config_flow.update.side_effect = error
result = await hass.config_entries.flow.async_init(
DOMAIN, context={CONF_SOURCE: SOURCE_USER}
)
await hass.async_block_till_done()
result = await hass.config_entries.flow.async_configure(
flow_id=result["flow_id"], user_input={CONF_HOST: HOST}
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.FORM
assert result["errors"] == {"base": "cannot_connect"}
assert result["errors"] == {"base": reason}
mock_roku_config_flow.update.side_effect = None
result = await hass.config_entries.flow.async_configure(
flow_id=result["flow_id"], user_input={CONF_HOST: HOST}
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY
async def test_form_unknown_error(
hass: HomeAssistant, mock_roku_config_flow: MagicMock
) -> None:
"""Test we handle unknown error."""
"""Test we handle user flow on unknown error."""
mock_roku_config_flow.update.side_effect = Exception
result = await hass.config_entries.flow.async_init(
DOMAIN, context={CONF_SOURCE: SOURCE_USER}
)
await hass.async_block_till_done()
user_input = {CONF_HOST: HOST}
result = await hass.config_entries.flow.async_configure(
flow_id=result["flow_id"], user_input=user_input
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "unknown"
async def test_homekit_cannot_connect(
hass: HomeAssistant, mock_roku_config_flow: MagicMock
) -> None:
"""Test we abort homekit flow on connection error."""
mock_roku_config_flow.update.side_effect = RokuConnectionError
discovery_info = dataclasses.replace(MOCK_HOMEKIT_DISCOVERY_INFO)
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={CONF_SOURCE: SOURCE_HOMEKIT},
data=discovery_info,
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "cannot_connect"
async def test_homekit_unknown_error(
hass: HomeAssistant, mock_roku_config_flow: MagicMock
) -> None:
"""Test we abort homekit flow on unknown error."""
mock_roku_config_flow.update.side_effect = Exception
discovery_info = dataclasses.replace(MOCK_HOMEKIT_DISCOVERY_INFO)
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={CONF_SOURCE: SOURCE_HOMEKIT},
data=discovery_info,
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "unknown"
async def test_form_duplicate_error(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_roku_config_flow: MagicMock,
) -> None:
"""Test that we handle user flow on duplicates."""
mock_config_entry.add_to_hass(hass)
user_input = {CONF_HOST: mock_config_entry.data[CONF_HOST]}
result = await hass.config_entries.flow.async_init(
DOMAIN, context={CONF_SOURCE: SOURCE_USER}, data=user_input
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"
user_input = {CONF_HOST: mock_config_entry.data[CONF_HOST]}
result = await hass.config_entries.flow.async_init(
DOMAIN, context={CONF_SOURCE: SOURCE_USER}, data=user_input
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"
@pytest.mark.parametrize("mock_device", ["rokutv-7820x"], indirect=True)
async def test_homekit_discovery(
hass: HomeAssistant,
@ -170,11 +167,13 @@ async def test_homekit_discovery(
mock_setup_entry: None,
) -> None:
"""Test the homekit discovery flow."""
discovery_info = dataclasses.replace(MOCK_HOMEKIT_DISCOVERY_INFO)
discovery_info = MOCK_HOMEKIT_DISCOVERY_INFO
result = await hass.config_entries.flow.async_init(
DOMAIN, context={CONF_SOURCE: SOURCE_HOMEKIT}, data=discovery_info
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "discovery_confirm"
assert result["description_placeholders"] == {CONF_NAME: NAME_ROKUTV}
@ -182,70 +181,80 @@ async def test_homekit_discovery(
result = await hass.config_entries.flow.async_configure(
flow_id=result["flow_id"], user_input={}
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == NAME_ROKUTV
assert "result" in result
assert result["result"].unique_id == "YN00H5555555"
assert "data" in result
assert result["data"][CONF_HOST] == HOMEKIT_HOST
assert result["data"][CONF_NAME] == NAME_ROKUTV
# test abort on existing host
discovery_info = dataclasses.replace(MOCK_HOMEKIT_DISCOVERY_INFO)
@pytest.mark.parametrize(
("error", "reason"),
[
(RokuConnectionError, "cannot_connect"),
(RokuConnectionTimeoutError, "cannot_connect"),
(Exception, "unknown"),
],
)
async def test_homekit_error(
hass: HomeAssistant,
mock_roku_config_flow: MagicMock,
error: Exception,
reason: str,
) -> None:
"""Test we abort Homekit flow on error."""
mock_roku_config_flow.update.side_effect = error
discovery_info = MOCK_HOMEKIT_DISCOVERY_INFO
result = await hass.config_entries.flow.async_init(
DOMAIN, context={CONF_SOURCE: SOURCE_HOMEKIT}, data=discovery_info
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == reason
async def test_homekit_duplicate_error(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_roku_config_flow: MagicMock,
) -> None:
"""Test that we handle Homekit flow on duplicates."""
mock_config_entry.add_to_hass(hass)
discovery_info = MOCK_HOMEKIT_DISCOVERY_INFO
result = await hass.config_entries.flow.async_init(
DOMAIN, context={CONF_SOURCE: SOURCE_HOMEKIT}, data=discovery_info
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"
async def test_ssdp_cannot_connect(
hass: HomeAssistant, mock_roku_config_flow: MagicMock
) -> None:
"""Test we abort SSDP flow on connection error."""
mock_roku_config_flow.update.side_effect = RokuConnectionError
discovery_info = dataclasses.replace(MOCK_SSDP_DISCOVERY_INFO)
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={CONF_SOURCE: SOURCE_SSDP},
data=discovery_info,
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "cannot_connect"
async def test_ssdp_unknown_error(
hass: HomeAssistant, mock_roku_config_flow: MagicMock
) -> None:
"""Test we abort SSDP flow on unknown error."""
mock_roku_config_flow.update.side_effect = Exception
discovery_info = dataclasses.replace(MOCK_SSDP_DISCOVERY_INFO)
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={CONF_SOURCE: SOURCE_SSDP},
data=discovery_info,
)
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "unknown"
async def test_ssdp_discovery(
hass: HomeAssistant,
mock_roku_config_flow: MagicMock,
mock_setup_entry: None,
) -> None:
"""Test the SSDP discovery flow."""
discovery_info = dataclasses.replace(MOCK_SSDP_DISCOVERY_INFO)
discovery_info = MOCK_SSDP_DISCOVERY_INFO
result = await hass.config_entries.flow.async_init(
DOMAIN, context={CONF_SOURCE: SOURCE_SSDP}, data=discovery_info
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.FORM
assert result["step_id"] == "discovery_confirm"
assert result["description_placeholders"] == {CONF_NAME: UPNP_FRIENDLY_NAME}
@ -253,16 +262,69 @@ async def test_ssdp_discovery(
result = await hass.config_entries.flow.async_configure(
flow_id=result["flow_id"], user_input={}
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.CREATE_ENTRY
assert result["title"] == UPNP_FRIENDLY_NAME
assert "result" in result
assert result["result"].unique_id == "1GU48T017973"
assert result["data"]
assert result["data"][CONF_HOST] == HOST
assert result["data"][CONF_NAME] == UPNP_FRIENDLY_NAME
@pytest.mark.parametrize(
("error", "reason"),
[
(RokuConnectionError, "cannot_connect"),
(RokuConnectionTimeoutError, "cannot_connect"),
(Exception, "unknown"),
],
)
async def test_ssdp_error(
hass: HomeAssistant,
mock_roku_config_flow: MagicMock,
error: Exception,
reason: str,
) -> None:
"""Test we abort SSDP flow on error."""
mock_roku_config_flow.update.side_effect = error
discovery_info = MOCK_SSDP_DISCOVERY_INFO
result = await hass.config_entries.flow.async_init(
DOMAIN,
context={CONF_SOURCE: SOURCE_SSDP},
data=discovery_info,
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == reason
async def test_ssdp_duplicate_error(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_roku_config_flow: MagicMock,
) -> None:
"""Test that we handle SSDP flow on duplicates."""
mock_config_entry.add_to_hass(hass)
discovery_info = MOCK_SSDP_DISCOVERY_INFO
result = await hass.config_entries.flow.async_init(
DOMAIN, context={CONF_SOURCE: SOURCE_SSDP}, data=discovery_info
)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "already_configured"
async def test_options_flow(
hass: HomeAssistant, mock_config_entry: MockConfigEntry
) -> None:
@ -271,6 +333,8 @@ async def test_options_flow(
result = await hass.config_entries.options.async_init(mock_config_entry.entry_id)
await hass.async_block_till_done()
assert result.get("type") is FlowResultType.FORM
assert result.get("step_id") == "init"
@ -279,6 +343,8 @@ async def test_options_flow(
user_input={CONF_PLAY_MEDIA_APP_ID: "782875"},
)
await hass.async_block_till_done()
assert result2.get("type") is FlowResultType.CREATE_ENTRY
assert result2.get("data") == {
CONF_PLAY_MEDIA_APP_ID: "782875",
@ -312,6 +378,8 @@ async def test_reconfigure_flow(
"""Test reconfigure flow."""
result = await _start_reconfigure_flow(hass, mock_config_entry)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "reconfigure_successful"
@ -334,5 +402,7 @@ async def test_reconfigure_unique_id_mismatch(
result = await _start_reconfigure_flow(hass, mock_config_entry)
await hass.async_block_till_done()
assert result["type"] is FlowResultType.ABORT
assert result["reason"] == "wrong_device"

View File

@ -3,13 +3,30 @@
from unittest.mock import AsyncMock, MagicMock, patch
from rokuecp import RokuConnectionError
from syrupy import SnapshotAssertion
from homeassistant.components.roku.const import DOMAIN
from homeassistant.config_entries import ConfigEntryState
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr
from tests.common import MockConfigEntry
async def test_device_info(
hass: HomeAssistant,
snapshot: SnapshotAssertion,
device_registry: dr.DeviceRegistry,
init_integration: MockConfigEntry,
) -> None:
"""Test device registry integration."""
device_entry = device_registry.async_get_device(
identifiers={(DOMAIN, init_integration.unique_id)}
)
assert device_entry is not None
assert device_entry == snapshot
@patch(
"homeassistant.components.roku.coordinator.Roku._request",
side_effect=RokuConnectionError,

View File

@ -57,7 +57,6 @@ from homeassistant.core import HomeAssistant
from homeassistant.core_config import async_process_ha_core_config
from homeassistant.helpers import entity_registry as er
from homeassistant.setup import async_setup_component
from homeassistant.util import dt as dt_util
from . import setup_integration
@ -109,24 +108,19 @@ async def test_availability(
error: RokuError,
) -> None:
"""Test entity availability."""
now = dt_util.utcnow()
future = now + timedelta(minutes=1)
mock_config_entry.add_to_hass(hass)
freezer.move_to(now)
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
freezer.move_to(future)
freezer.tick(timedelta(minutes=1))
mock_roku.update.side_effect = error
async_fire_time_changed(hass, future)
async_fire_time_changed(hass)
await hass.async_block_till_done()
assert hass.states.get(MAIN_ENTITY_ID).state == STATE_UNAVAILABLE
future += timedelta(minutes=1)
freezer.move_to(future)
freezer.tick(timedelta(minutes=1))
mock_roku.update.side_effect = None
async_fire_time_changed(hass, future)
async_fire_time_changed(hass)
await hass.async_block_till_done()
assert hass.states.get(MAIN_ENTITY_ID).state == STATE_IDLE