mirror of
https://github.com/home-assistant/core.git
synced 2025-04-24 09:17:53 +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.
|
||||
reauthentication-flow: done
|
||||
test-coverage:
|
||||
status: todo
|
||||
comment: |
|
||||
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.
|
||||
status: done
|
||||
comment: 99% test coverage
|
||||
# Gold
|
||||
devices: done
|
||||
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
|
||||
import pytest
|
||||
|
||||
from homeassistant.components import heos
|
||||
from homeassistant.components.heos.const import DOMAIN
|
||||
from homeassistant.config_entries import SOURCE_SSDP, SOURCE_USER
|
||||
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."""
|
||||
controller.connect.side_effect = HeosError()
|
||||
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["step_id"] == "user"
|
||||
@ -60,7 +59,7 @@ async def test_create_entry_when_host_valid(
|
||||
data = {CONF_HOST: "127.0.0.1"}
|
||||
|
||||
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["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)"}
|
||||
|
||||
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
|
||||
@ -99,7 +98,7 @@ async def test_discovery_shows_create_form(
|
||||
|
||||
# Single discovered host shows form for user to finish setup.
|
||||
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 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.
|
||||
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] == {
|
||||
"Office (127.0.0.1)": "127.0.0.1",
|
||||
|
@ -2,30 +2,22 @@
|
||||
|
||||
import asyncio
|
||||
from typing import cast
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
from pyheos import (
|
||||
CommandFailedError,
|
||||
Heos,
|
||||
HeosError,
|
||||
HeosOptions,
|
||||
SignalHeosEvent,
|
||||
SignalType,
|
||||
const,
|
||||
)
|
||||
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.config_entries import SOURCE_REAUTH, ConfigEntryState
|
||||
from homeassistant.const import CONF_HOST, CONF_PASSWORD, CONF_USERNAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import ConfigEntryNotReady
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
@ -38,18 +30,14 @@ async def test_async_setup_entry_loads_platforms(
|
||||
) -> None:
|
||||
"""Test load connects to heos, retrieves players, and loads platforms."""
|
||||
config_entry.add_to_hass(hass)
|
||||
with patch.object(
|
||||
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.get_players.call_count == 1
|
||||
assert controller.get_favorites.call_count == 1
|
||||
assert controller.get_input_sources.call_count == 1
|
||||
controller.disconnect.assert_not_called()
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
assert config_entry.state == ConfigEntryState.LOADED
|
||||
assert hass.states.get("media_player.test_player") is not None
|
||||
assert controller.connect.call_count == 1
|
||||
assert controller.get_players.call_count == 1
|
||||
assert controller.get_favorites.call_count == 1
|
||||
assert controller.get_input_sources.call_count == 1
|
||||
controller.disconnect.assert_not_called()
|
||||
|
||||
|
||||
async def test_async_setup_entry_with_options_loads_platforms(
|
||||
@ -75,7 +63,7 @@ async def test_async_setup_entry_with_options_loads_platforms(
|
||||
async def test_async_setup_entry_auth_failure_starts_reauth(
|
||||
hass: HomeAssistant,
|
||||
config_entry_options: MockConfigEntry,
|
||||
controller: Mock,
|
||||
controller: Heos,
|
||||
) -> None:
|
||||
"""Test load with auth failure starts reauth, loads platforms."""
|
||||
config_entry_options.add_to_hass(hass)
|
||||
@ -110,18 +98,12 @@ async def test_async_setup_entry_not_signed_in_loads_platforms(
|
||||
"""Test setup does not retrieve favorites when not logged in."""
|
||||
config_entry.add_to_hass(hass)
|
||||
controller._signed_in_username = None
|
||||
with patch.object(
|
||||
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.get_players.call_count == 1
|
||||
assert controller.get_favorites.call_count == 0
|
||||
assert controller.get_input_sources.call_count == 1
|
||||
controller.disconnect.assert_not_called()
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
assert controller.connect.call_count == 1
|
||||
assert controller.get_players.call_count == 1
|
||||
assert controller.get_favorites.call_count == 0
|
||||
assert controller.get_input_sources.call_count == 1
|
||||
controller.disconnect.assert_not_called()
|
||||
assert (
|
||||
"The HEOS System is not logged in: Enter credentials in the integration options to access favorites and streaming services"
|
||||
in caplog.text
|
||||
@ -134,8 +116,8 @@ async def test_async_setup_entry_connect_failure(
|
||||
"""Connection failure raises ConfigEntryNotReady."""
|
||||
config_entry.add_to_hass(hass)
|
||||
controller.connect.side_effect = HeosError()
|
||||
with pytest.raises(ConfigEntryNotReady):
|
||||
await async_setup_entry(hass, config_entry)
|
||||
assert not await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
assert config_entry.state == ConfigEntryState.SETUP_RETRY
|
||||
assert controller.connect.call_count == 1
|
||||
assert controller.disconnect.call_count == 1
|
||||
controller.connect.reset_mock()
|
||||
@ -148,27 +130,21 @@ async def test_async_setup_entry_player_failure(
|
||||
"""Failure to retrieve players/sources raises ConfigEntryNotReady."""
|
||||
config_entry.add_to_hass(hass)
|
||||
controller.get_players.side_effect = HeosError()
|
||||
with pytest.raises(ConfigEntryNotReady):
|
||||
await async_setup_entry(hass, config_entry)
|
||||
assert not await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
assert controller.connect.call_count == 1
|
||||
assert controller.disconnect.call_count == 1
|
||||
controller.connect.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."""
|
||||
controller_manager = Mock(ControllerManager)
|
||||
config_entry.runtime_data = HeosRuntimeData(controller_manager, None, None, {})
|
||||
|
||||
with patch.object(
|
||||
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
|
||||
config_entry.add_to_hass(hass)
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
assert await hass.config_entries.async_unload(config_entry.entry_id)
|
||||
assert controller.disconnect.call_count == 1
|
||||
|
||||
|
||||
async def test_update_sources_retry(
|
||||
|
@ -18,15 +18,14 @@ from pyheos import (
|
||||
const,
|
||||
)
|
||||
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.media_player import (
|
||||
ATTR_GROUP_MEMBERS,
|
||||
ATTR_INPUT_SOURCE,
|
||||
ATTR_INPUT_SOURCE_LIST,
|
||||
ATTR_MEDIA_ALBUM_NAME,
|
||||
ATTR_MEDIA_ARTIST,
|
||||
ATTR_MEDIA_CONTENT_ID,
|
||||
ATTR_MEDIA_CONTENT_TYPE,
|
||||
ATTR_MEDIA_DURATION,
|
||||
@ -34,7 +33,6 @@ from homeassistant.components.media_player import (
|
||||
ATTR_MEDIA_POSITION,
|
||||
ATTR_MEDIA_POSITION_UPDATED_AT,
|
||||
ATTR_MEDIA_SHUFFLE,
|
||||
ATTR_MEDIA_TITLE,
|
||||
ATTR_MEDIA_VOLUME_LEVEL,
|
||||
ATTR_MEDIA_VOLUME_MUTED,
|
||||
DOMAIN as MEDIA_PLAYER_DOMAIN,
|
||||
@ -43,13 +41,10 @@ from homeassistant.components.media_player import (
|
||||
SERVICE_PLAY_MEDIA,
|
||||
SERVICE_SELECT_SOURCE,
|
||||
SERVICE_UNJOIN,
|
||||
MediaPlayerEntityFeature,
|
||||
MediaType,
|
||||
)
|
||||
from homeassistant.const import (
|
||||
ATTR_ENTITY_ID,
|
||||
ATTR_FRIENDLY_NAME,
|
||||
ATTR_SUPPORTED_FEATURES,
|
||||
SERVICE_MEDIA_NEXT_TRACK,
|
||||
SERVICE_MEDIA_PAUSE,
|
||||
SERVICE_MEDIA_PLAY,
|
||||
@ -72,42 +67,20 @@ from tests.common import MockConfigEntry
|
||||
|
||||
@pytest.mark.usefixtures("controller")
|
||||
async def test_state_attributes(
|
||||
hass: HomeAssistant, config_entry: MockConfigEntry
|
||||
hass: HomeAssistant, config_entry: MockConfigEntry, snapshot: SnapshotAssertion
|
||||
) -> None:
|
||||
"""Tests the state attributes."""
|
||||
config_entry.add_to_hass(hass)
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
state = hass.states.get("media_player.test_player")
|
||||
assert state.state == STATE_IDLE
|
||||
assert state.attributes[ATTR_MEDIA_VOLUME_LEVEL] == 0.25
|
||||
assert not state.attributes[ATTR_MEDIA_VOLUME_MUTED]
|
||||
assert state.attributes[ATTR_MEDIA_CONTENT_ID] == "1"
|
||||
assert state.attributes[ATTR_MEDIA_CONTENT_TYPE] == MediaType.MUSIC
|
||||
assert ATTR_MEDIA_DURATION not in state.attributes
|
||||
assert ATTR_MEDIA_POSITION not in state.attributes
|
||||
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
|
||||
assert state == snapshot(
|
||||
exclude=props(
|
||||
"entity_picture_local",
|
||||
"context",
|
||||
"last_changed",
|
||||
"last_reported",
|
||||
"last_updated",
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user