mirror of
https://github.com/home-assistant/core.git
synced 2025-07-09 22:37:11 +00:00
Update HEOS tests to not patch internals (#136136)
This commit is contained in:
parent
f6b444b24b
commit
79a43b8a50
@ -38,15 +38,8 @@ rules:
|
|||||||
comment: Needs to be set to 0. The underlying library handles parallel updates.
|
comment: Needs to be set to 0. The underlying library handles parallel updates.
|
||||||
reauthentication-flow: done
|
reauthentication-flow: done
|
||||||
test-coverage:
|
test-coverage:
|
||||||
status: todo
|
status: done
|
||||||
comment: |
|
comment: 99% test coverage
|
||||||
1. Integration has >95% coverage, however tests need to be updated to not patch internals.
|
|
||||||
2. test_async_setup_entry_connect_failure and test_async_setup_entry_player_failure -> Instead of
|
|
||||||
calling async_setup_entry directly, rather use hass.config_entries.async_setup and then assert
|
|
||||||
the config_entry.state is what we expect.
|
|
||||||
3. test_unload_entry -> We should use hass.config_entries.async_unload and assert the entry state
|
|
||||||
4. Recommend using snapshot in test_state_attributes.
|
|
||||||
5. Find a way to avoid using internal dispatcher in test_updates_from_connection_event.
|
|
||||||
# Gold
|
# Gold
|
||||||
devices: done
|
devices: done
|
||||||
diagnostics: todo
|
diagnostics: todo
|
||||||
|
34
tests/components/heos/snapshots/test_media_player.ambr
Normal file
34
tests/components/heos/snapshots/test_media_player.ambr
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# serializer version: 1
|
||||||
|
# name: test_state_attributes
|
||||||
|
StateSnapshot({
|
||||||
|
'attributes': ReadOnlyDict({
|
||||||
|
'entity_picture': 'http://',
|
||||||
|
'friendly_name': 'Test Player',
|
||||||
|
'group_members': list([
|
||||||
|
'media_player.test_player',
|
||||||
|
'media_player.test_player_2',
|
||||||
|
]),
|
||||||
|
'is_volume_muted': False,
|
||||||
|
'media_album_id': 1,
|
||||||
|
'media_album_name': 'Album',
|
||||||
|
'media_artist': 'Artist',
|
||||||
|
'media_content_id': '1',
|
||||||
|
'media_content_type': <MediaType.MUSIC: 'music'>,
|
||||||
|
'media_queue_id': 1,
|
||||||
|
'media_source_id': 1,
|
||||||
|
'media_station': 'Station Name',
|
||||||
|
'media_title': 'Song',
|
||||||
|
'media_type': 'Station',
|
||||||
|
'shuffle': False,
|
||||||
|
'source_list': list([
|
||||||
|
"Today's Hits Radio",
|
||||||
|
'Classical MPR (Classical Music)',
|
||||||
|
'HEOS Drive - Line In 1',
|
||||||
|
]),
|
||||||
|
'supported_features': <MediaPlayerEntityFeature: 2817597>,
|
||||||
|
'volume_level': 0.25,
|
||||||
|
}),
|
||||||
|
'entity_id': 'media_player.test_player',
|
||||||
|
'state': 'idle',
|
||||||
|
})
|
||||||
|
# ---
|
@ -3,7 +3,6 @@
|
|||||||
from pyheos import CommandAuthenticationError, CommandFailedError, Heos, HeosError
|
from pyheos import CommandAuthenticationError, CommandFailedError, Heos, HeosError
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components import heos
|
|
||||||
from homeassistant.components.heos.const import DOMAIN
|
from homeassistant.components.heos.const import DOMAIN
|
||||||
from homeassistant.config_entries import SOURCE_SSDP, SOURCE_USER
|
from homeassistant.config_entries import SOURCE_SSDP, SOURCE_USER
|
||||||
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
|
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
|
||||||
@ -44,7 +43,7 @@ async def test_cannot_connect_shows_error_form(
|
|||||||
"""Test form is shown with error when cannot connect."""
|
"""Test form is shown with error when cannot connect."""
|
||||||
controller.connect.side_effect = HeosError()
|
controller.connect.side_effect = HeosError()
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
heos.DOMAIN, context={"source": SOURCE_USER}, data={CONF_HOST: "127.0.0.1"}
|
DOMAIN, context={"source": SOURCE_USER}, data={CONF_HOST: "127.0.0.1"}
|
||||||
)
|
)
|
||||||
assert result["type"] is FlowResultType.FORM
|
assert result["type"] is FlowResultType.FORM
|
||||||
assert result["step_id"] == "user"
|
assert result["step_id"] == "user"
|
||||||
@ -60,7 +59,7 @@ async def test_create_entry_when_host_valid(
|
|||||||
data = {CONF_HOST: "127.0.0.1"}
|
data = {CONF_HOST: "127.0.0.1"}
|
||||||
|
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
heos.DOMAIN, context={"source": SOURCE_USER}, data=data
|
DOMAIN, context={"source": SOURCE_USER}, data=data
|
||||||
)
|
)
|
||||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||||
assert result["result"].unique_id == DOMAIN
|
assert result["result"].unique_id == DOMAIN
|
||||||
@ -78,7 +77,7 @@ async def test_create_entry_when_friendly_name_valid(
|
|||||||
data = {CONF_HOST: "Office (127.0.0.1)"}
|
data = {CONF_HOST: "Office (127.0.0.1)"}
|
||||||
|
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
heos.DOMAIN, context={"source": SOURCE_USER}, data=data
|
DOMAIN, context={"source": SOURCE_USER}, data=data
|
||||||
)
|
)
|
||||||
|
|
||||||
assert result["type"] is FlowResultType.CREATE_ENTRY
|
assert result["type"] is FlowResultType.CREATE_ENTRY
|
||||||
@ -99,7 +98,7 @@ async def test_discovery_shows_create_form(
|
|||||||
|
|
||||||
# Single discovered host shows form for user to finish setup.
|
# Single discovered host shows form for user to finish setup.
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
heos.DOMAIN, context={"source": SOURCE_SSDP}, data=discovery_data
|
DOMAIN, context={"source": SOURCE_SSDP}, data=discovery_data
|
||||||
)
|
)
|
||||||
assert hass.data[DOMAIN] == {"Office (127.0.0.1)": "127.0.0.1"}
|
assert hass.data[DOMAIN] == {"Office (127.0.0.1)": "127.0.0.1"}
|
||||||
assert result["type"] is FlowResultType.FORM
|
assert result["type"] is FlowResultType.FORM
|
||||||
@ -107,7 +106,7 @@ async def test_discovery_shows_create_form(
|
|||||||
|
|
||||||
# Subsequent discovered hosts append to discovered hosts and abort.
|
# Subsequent discovered hosts append to discovered hosts and abort.
|
||||||
result = await hass.config_entries.flow.async_init(
|
result = await hass.config_entries.flow.async_init(
|
||||||
heos.DOMAIN, context={"source": SOURCE_SSDP}, data=discovery_data_bedroom
|
DOMAIN, context={"source": SOURCE_SSDP}, data=discovery_data_bedroom
|
||||||
)
|
)
|
||||||
assert hass.data[DOMAIN] == {
|
assert hass.data[DOMAIN] == {
|
||||||
"Office (127.0.0.1)": "127.0.0.1",
|
"Office (127.0.0.1)": "127.0.0.1",
|
||||||
|
@ -2,30 +2,22 @@
|
|||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from typing import cast
|
from typing import cast
|
||||||
from unittest.mock import Mock, patch
|
|
||||||
|
|
||||||
from pyheos import (
|
from pyheos import (
|
||||||
CommandFailedError,
|
CommandFailedError,
|
||||||
Heos,
|
Heos,
|
||||||
HeosError,
|
HeosError,
|
||||||
|
HeosOptions,
|
||||||
SignalHeosEvent,
|
SignalHeosEvent,
|
||||||
SignalType,
|
SignalType,
|
||||||
const,
|
const,
|
||||||
)
|
)
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components.heos import (
|
|
||||||
ControllerManager,
|
|
||||||
HeosOptions,
|
|
||||||
HeosRuntimeData,
|
|
||||||
async_setup_entry,
|
|
||||||
async_unload_entry,
|
|
||||||
)
|
|
||||||
from homeassistant.components.heos.const import DOMAIN
|
from homeassistant.components.heos.const import DOMAIN
|
||||||
from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntryState
|
from homeassistant.config_entries import SOURCE_REAUTH, ConfigEntryState
|
||||||
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
|
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
from homeassistant.exceptions import ConfigEntryNotReady
|
|
||||||
from homeassistant.helpers import device_registry as dr
|
from homeassistant.helpers import device_registry as dr
|
||||||
|
|
||||||
from tests.common import MockConfigEntry
|
from tests.common import MockConfigEntry
|
||||||
@ -38,13 +30,9 @@ async def test_async_setup_entry_loads_platforms(
|
|||||||
) -> None:
|
) -> None:
|
||||||
"""Test load connects to heos, retrieves players, and loads platforms."""
|
"""Test load connects to heos, retrieves players, and loads platforms."""
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
with patch.object(
|
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
hass.config_entries, "async_forward_entry_setups"
|
assert config_entry.state == ConfigEntryState.LOADED
|
||||||
) as forward_mock:
|
assert hass.states.get("media_player.test_player") is not None
|
||||||
assert await async_setup_entry(hass, config_entry)
|
|
||||||
# Assert platforms loaded
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
assert forward_mock.call_count == 1
|
|
||||||
assert controller.connect.call_count == 1
|
assert controller.connect.call_count == 1
|
||||||
assert controller.get_players.call_count == 1
|
assert controller.get_players.call_count == 1
|
||||||
assert controller.get_favorites.call_count == 1
|
assert controller.get_favorites.call_count == 1
|
||||||
@ -75,7 +63,7 @@ async def test_async_setup_entry_with_options_loads_platforms(
|
|||||||
async def test_async_setup_entry_auth_failure_starts_reauth(
|
async def test_async_setup_entry_auth_failure_starts_reauth(
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
config_entry_options: MockConfigEntry,
|
config_entry_options: MockConfigEntry,
|
||||||
controller: Mock,
|
controller: Heos,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Test load with auth failure starts reauth, loads platforms."""
|
"""Test load with auth failure starts reauth, loads platforms."""
|
||||||
config_entry_options.add_to_hass(hass)
|
config_entry_options.add_to_hass(hass)
|
||||||
@ -110,13 +98,7 @@ async def test_async_setup_entry_not_signed_in_loads_platforms(
|
|||||||
"""Test setup does not retrieve favorites when not logged in."""
|
"""Test setup does not retrieve favorites when not logged in."""
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
controller._signed_in_username = None
|
controller._signed_in_username = None
|
||||||
with patch.object(
|
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
hass.config_entries, "async_forward_entry_setups"
|
|
||||||
) as forward_mock:
|
|
||||||
assert await async_setup_entry(hass, config_entry)
|
|
||||||
# Assert platforms loaded
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
assert forward_mock.call_count == 1
|
|
||||||
assert controller.connect.call_count == 1
|
assert controller.connect.call_count == 1
|
||||||
assert controller.get_players.call_count == 1
|
assert controller.get_players.call_count == 1
|
||||||
assert controller.get_favorites.call_count == 0
|
assert controller.get_favorites.call_count == 0
|
||||||
@ -134,8 +116,8 @@ async def test_async_setup_entry_connect_failure(
|
|||||||
"""Connection failure raises ConfigEntryNotReady."""
|
"""Connection failure raises ConfigEntryNotReady."""
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
controller.connect.side_effect = HeosError()
|
controller.connect.side_effect = HeosError()
|
||||||
with pytest.raises(ConfigEntryNotReady):
|
assert not await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
await async_setup_entry(hass, config_entry)
|
assert config_entry.state == ConfigEntryState.SETUP_RETRY
|
||||||
assert controller.connect.call_count == 1
|
assert controller.connect.call_count == 1
|
||||||
assert controller.disconnect.call_count == 1
|
assert controller.disconnect.call_count == 1
|
||||||
controller.connect.reset_mock()
|
controller.connect.reset_mock()
|
||||||
@ -148,27 +130,21 @@ async def test_async_setup_entry_player_failure(
|
|||||||
"""Failure to retrieve players/sources raises ConfigEntryNotReady."""
|
"""Failure to retrieve players/sources raises ConfigEntryNotReady."""
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
controller.get_players.side_effect = HeosError()
|
controller.get_players.side_effect = HeosError()
|
||||||
with pytest.raises(ConfigEntryNotReady):
|
assert not await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
await async_setup_entry(hass, config_entry)
|
|
||||||
assert controller.connect.call_count == 1
|
assert controller.connect.call_count == 1
|
||||||
assert controller.disconnect.call_count == 1
|
assert controller.disconnect.call_count == 1
|
||||||
controller.connect.reset_mock()
|
controller.connect.reset_mock()
|
||||||
controller.disconnect.reset_mock()
|
controller.disconnect.reset_mock()
|
||||||
|
|
||||||
|
|
||||||
async def test_unload_entry(hass: HomeAssistant, config_entry: MockConfigEntry) -> None:
|
async def test_unload_entry(
|
||||||
|
hass: HomeAssistant, config_entry: MockConfigEntry, controller: Heos
|
||||||
|
) -> None:
|
||||||
"""Test entries are unloaded correctly."""
|
"""Test entries are unloaded correctly."""
|
||||||
controller_manager = Mock(ControllerManager)
|
config_entry.add_to_hass(hass)
|
||||||
config_entry.runtime_data = HeosRuntimeData(controller_manager, None, None, {})
|
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
|
assert await hass.config_entries.async_unload(config_entry.entry_id)
|
||||||
with patch.object(
|
assert controller.disconnect.call_count == 1
|
||||||
hass.config_entries, "async_forward_entry_unload", return_value=True
|
|
||||||
) as unload:
|
|
||||||
assert await async_unload_entry(hass, config_entry)
|
|
||||||
await hass.async_block_till_done()
|
|
||||||
assert controller_manager.disconnect.call_count == 1
|
|
||||||
assert unload.call_count == 1
|
|
||||||
assert DOMAIN not in hass.data
|
|
||||||
|
|
||||||
|
|
||||||
async def test_update_sources_retry(
|
async def test_update_sources_retry(
|
||||||
|
@ -18,15 +18,14 @@ from pyheos import (
|
|||||||
const,
|
const,
|
||||||
)
|
)
|
||||||
import pytest
|
import pytest
|
||||||
|
from syrupy.assertion import SnapshotAssertion
|
||||||
|
from syrupy.filters import props
|
||||||
|
|
||||||
from homeassistant.components.heos import media_player
|
|
||||||
from homeassistant.components.heos.const import DOMAIN, SIGNAL_HEOS_UPDATED
|
from homeassistant.components.heos.const import DOMAIN, SIGNAL_HEOS_UPDATED
|
||||||
from homeassistant.components.media_player import (
|
from homeassistant.components.media_player import (
|
||||||
ATTR_GROUP_MEMBERS,
|
ATTR_GROUP_MEMBERS,
|
||||||
ATTR_INPUT_SOURCE,
|
ATTR_INPUT_SOURCE,
|
||||||
ATTR_INPUT_SOURCE_LIST,
|
ATTR_INPUT_SOURCE_LIST,
|
||||||
ATTR_MEDIA_ALBUM_NAME,
|
|
||||||
ATTR_MEDIA_ARTIST,
|
|
||||||
ATTR_MEDIA_CONTENT_ID,
|
ATTR_MEDIA_CONTENT_ID,
|
||||||
ATTR_MEDIA_CONTENT_TYPE,
|
ATTR_MEDIA_CONTENT_TYPE,
|
||||||
ATTR_MEDIA_DURATION,
|
ATTR_MEDIA_DURATION,
|
||||||
@ -34,7 +33,6 @@ from homeassistant.components.media_player import (
|
|||||||
ATTR_MEDIA_POSITION,
|
ATTR_MEDIA_POSITION,
|
||||||
ATTR_MEDIA_POSITION_UPDATED_AT,
|
ATTR_MEDIA_POSITION_UPDATED_AT,
|
||||||
ATTR_MEDIA_SHUFFLE,
|
ATTR_MEDIA_SHUFFLE,
|
||||||
ATTR_MEDIA_TITLE,
|
|
||||||
ATTR_MEDIA_VOLUME_LEVEL,
|
ATTR_MEDIA_VOLUME_LEVEL,
|
||||||
ATTR_MEDIA_VOLUME_MUTED,
|
ATTR_MEDIA_VOLUME_MUTED,
|
||||||
DOMAIN as MEDIA_PLAYER_DOMAIN,
|
DOMAIN as MEDIA_PLAYER_DOMAIN,
|
||||||
@ -43,13 +41,10 @@ from homeassistant.components.media_player import (
|
|||||||
SERVICE_PLAY_MEDIA,
|
SERVICE_PLAY_MEDIA,
|
||||||
SERVICE_SELECT_SOURCE,
|
SERVICE_SELECT_SOURCE,
|
||||||
SERVICE_UNJOIN,
|
SERVICE_UNJOIN,
|
||||||
MediaPlayerEntityFeature,
|
|
||||||
MediaType,
|
MediaType,
|
||||||
)
|
)
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_ENTITY_ID,
|
ATTR_ENTITY_ID,
|
||||||
ATTR_FRIENDLY_NAME,
|
|
||||||
ATTR_SUPPORTED_FEATURES,
|
|
||||||
SERVICE_MEDIA_NEXT_TRACK,
|
SERVICE_MEDIA_NEXT_TRACK,
|
||||||
SERVICE_MEDIA_PAUSE,
|
SERVICE_MEDIA_PAUSE,
|
||||||
SERVICE_MEDIA_PLAY,
|
SERVICE_MEDIA_PLAY,
|
||||||
@ -72,42 +67,20 @@ from tests.common import MockConfigEntry
|
|||||||
|
|
||||||
@pytest.mark.usefixtures("controller")
|
@pytest.mark.usefixtures("controller")
|
||||||
async def test_state_attributes(
|
async def test_state_attributes(
|
||||||
hass: HomeAssistant, config_entry: MockConfigEntry
|
hass: HomeAssistant, config_entry: MockConfigEntry, snapshot: SnapshotAssertion
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Tests the state attributes."""
|
"""Tests the state attributes."""
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||||
state = hass.states.get("media_player.test_player")
|
state = hass.states.get("media_player.test_player")
|
||||||
assert state.state == STATE_IDLE
|
assert state == snapshot(
|
||||||
assert state.attributes[ATTR_MEDIA_VOLUME_LEVEL] == 0.25
|
exclude=props(
|
||||||
assert not state.attributes[ATTR_MEDIA_VOLUME_MUTED]
|
"entity_picture_local",
|
||||||
assert state.attributes[ATTR_MEDIA_CONTENT_ID] == "1"
|
"context",
|
||||||
assert state.attributes[ATTR_MEDIA_CONTENT_TYPE] == MediaType.MUSIC
|
"last_changed",
|
||||||
assert ATTR_MEDIA_DURATION not in state.attributes
|
"last_reported",
|
||||||
assert ATTR_MEDIA_POSITION not in state.attributes
|
"last_updated",
|
||||||
assert state.attributes[ATTR_MEDIA_TITLE] == "Song"
|
|
||||||
assert state.attributes[ATTR_MEDIA_ARTIST] == "Artist"
|
|
||||||
assert state.attributes[ATTR_MEDIA_ALBUM_NAME] == "Album"
|
|
||||||
assert not state.attributes[ATTR_MEDIA_SHUFFLE]
|
|
||||||
assert state.attributes["media_album_id"] == 1
|
|
||||||
assert state.attributes["media_queue_id"] == 1
|
|
||||||
assert state.attributes["media_source_id"] == 1
|
|
||||||
assert state.attributes["media_station"] == "Station Name"
|
|
||||||
assert state.attributes["media_type"] == "Station"
|
|
||||||
assert state.attributes[ATTR_FRIENDLY_NAME] == "Test Player"
|
|
||||||
assert (
|
|
||||||
state.attributes[ATTR_SUPPORTED_FEATURES]
|
|
||||||
== MediaPlayerEntityFeature.PLAY
|
|
||||||
| MediaPlayerEntityFeature.PAUSE
|
|
||||||
| MediaPlayerEntityFeature.STOP
|
|
||||||
| MediaPlayerEntityFeature.NEXT_TRACK
|
|
||||||
| MediaPlayerEntityFeature.PREVIOUS_TRACK
|
|
||||||
| media_player.BASE_SUPPORTED_FEATURES
|
|
||||||
)
|
)
|
||||||
assert ATTR_INPUT_SOURCE not in state.attributes
|
|
||||||
assert (
|
|
||||||
state.attributes[ATTR_INPUT_SOURCE_LIST]
|
|
||||||
== config_entry.runtime_data.source_manager.source_list
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user