Separate Plex/Sonos cross-integration tests (#45370)

This commit is contained in:
jjlawren 2021-01-21 05:22:19 -05:00 committed by GitHub
parent 16843d9ec7
commit 1f66457a34
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 117 additions and 103 deletions

View File

@ -70,8 +70,9 @@ def refresh_library(hass, service_call):
def get_plex_server(hass, plex_server_name=None): def get_plex_server(hass, plex_server_name=None):
"""Retrieve a configured Plex server by name.""" """Retrieve a configured Plex server by name."""
if DOMAIN not in hass.data:
raise HomeAssistantError("Plex integration not configured")
plex_servers = hass.data[DOMAIN][SERVERS].values() plex_servers = hass.data[DOMAIN][SERVERS].values()
if not plex_servers: if not plex_servers:
raise HomeAssistantError("No Plex servers available") raise HomeAssistantError("No Plex servers available")
@ -106,7 +107,7 @@ def lookup_plex_media(hass, content_type, content_id):
plex_server_name = content.pop("plex_server", None) plex_server_name = content.pop("plex_server", None)
shuffle = content.pop("shuffle", 0) shuffle = content.pop("shuffle", 0)
plex_server = get_plex_server(hass, plex_server_name=plex_server_name) plex_server = get_plex_server(hass, plex_server_name)
media = plex_server.lookup_media(content_type, **content) media = plex_server.lookup_media(content_type, **content)
if media is None: if media is None:

View File

@ -254,6 +254,12 @@ def show_seasons_fixture():
return load_fixture("plex/show_seasons.xml") return load_fixture("plex/show_seasons.xml")
@pytest.fixture(name="sonos_resources", scope="session")
def sonos_resources_fixture():
"""Load Sonos resources payload and return it."""
return load_fixture("plex/sonos_resources.xml")
@pytest.fixture(name="entry") @pytest.fixture(name="entry")
def mock_config_entry(): def mock_config_entry():
"""Return the default mocked config entry.""" """Return the default mocked config entry."""

View File

@ -1,6 +1,10 @@
"""Tests for various Plex services.""" """Tests for various Plex services."""
from unittest.mock import patch
from plexapi.exceptions import NotFound
import pytest import pytest
from homeassistant.components.media_player.const import MEDIA_TYPE_MUSIC
from homeassistant.components.plex.const import ( from homeassistant.components.plex.const import (
CONF_SERVER, CONF_SERVER,
CONF_SERVER_IDENTIFIER, CONF_SERVER_IDENTIFIER,
@ -9,6 +13,7 @@ from homeassistant.components.plex.const import (
SERVICE_REFRESH_LIBRARY, SERVICE_REFRESH_LIBRARY,
SERVICE_SCAN_CLIENTS, SERVICE_SCAN_CLIENTS,
) )
from homeassistant.components.plex.services import play_on_sonos
from homeassistant.const import CONF_URL from homeassistant.const import CONF_URL
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
@ -100,3 +105,76 @@ async def test_scan_clients(hass, mock_plex_server):
SERVICE_SCAN_CLIENTS, SERVICE_SCAN_CLIENTS,
blocking=True, blocking=True,
) )
async def test_sonos_play_media(
hass,
entry,
setup_plex_server,
requests_mock,
empty_payload,
playqueue_created,
plextv_account,
sonos_resources,
):
"""Test playback from a Sonos media_player.play_media call."""
media_content_id = (
'{"library_name": "Music", "artist_name": "Artist", "album_name": "Album"}'
)
sonos_speaker_name = "Zone A"
requests_mock.get("https://plex.tv/users/account", text=plextv_account)
requests_mock.post("/playqueues", text=playqueue_created)
playback_mock = requests_mock.get("/player/playback/playMedia", status_code=200)
# Test with no Plex integration available
with pytest.raises(HomeAssistantError) as excinfo:
play_on_sonos(hass, MEDIA_TYPE_MUSIC, media_content_id, sonos_speaker_name)
assert "Plex integration not configured" in str(excinfo.value)
with patch(
"homeassistant.components.plex.PlexServer.connect", side_effect=NotFound
):
# Initialize Plex integration without setting up a server
with pytest.raises(AssertionError):
await setup_plex_server()
# Test with no Plex servers available
with pytest.raises(HomeAssistantError) as excinfo:
play_on_sonos(hass, MEDIA_TYPE_MUSIC, media_content_id, sonos_speaker_name)
assert "No Plex servers available" in str(excinfo.value)
# Complete setup of a Plex server
await hass.config_entries.async_unload(entry.entry_id)
mock_plex_server = await setup_plex_server()
# Test with no speakers available
requests_mock.get("https://sonos.plex.tv/resources", text=empty_payload)
with pytest.raises(HomeAssistantError) as excinfo:
play_on_sonos(hass, MEDIA_TYPE_MUSIC, media_content_id, sonos_speaker_name)
assert f"Sonos speaker '{sonos_speaker_name}' is not associated with" in str(
excinfo.value
)
assert playback_mock.call_count == 0
# Test with speakers available
requests_mock.get("https://sonos.plex.tv/resources", text=sonos_resources)
with patch.object(mock_plex_server.account, "_sonos_cache_timestamp", 0):
play_on_sonos(hass, MEDIA_TYPE_MUSIC, media_content_id, sonos_speaker_name)
assert playback_mock.call_count == 1
# Test with speakers available and media key payload
play_on_sonos(hass, MEDIA_TYPE_MUSIC, "100", sonos_speaker_name)
assert playback_mock.call_count == 2
# Test with speakers available and Plex server specified
content_id_with_server = '{"plex_server": "Plex Server 1", "library_name": "Music", "artist_name": "Artist", "album_name": "Album"}'
play_on_sonos(hass, MEDIA_TYPE_MUSIC, content_id_with_server, sonos_speaker_name)
assert playback_mock.call_count == 3
# Test with speakers available but media not found
content_id_bad_media = '{"library_name": "Music", "artist_name": "Not an Artist"}'
with pytest.raises(HomeAssistantError) as excinfo:
play_on_sonos(hass, MEDIA_TYPE_MUSIC, content_id_bad_media, sonos_speaker_name)
assert "Plex media not found" in str(excinfo.value)
assert playback_mock.call_count == 3

View File

@ -7,7 +7,7 @@ from homeassistant.components.media_player import DOMAIN as MP_DOMAIN
from homeassistant.components.sonos import DOMAIN from homeassistant.components.sonos import DOMAIN
from homeassistant.const import CONF_HOSTS from homeassistant.const import CONF_HOSTS
from tests.common import MockConfigEntry, load_fixture from tests.common import MockConfigEntry
@pytest.fixture(name="config_entry") @pytest.fixture(name="config_entry")
@ -77,21 +77,3 @@ def speaker_info_fixture():
"software_version": "49.2-64250", "software_version": "49.2-64250",
"mac_address": "00-11-22-33-44-55", "mac_address": "00-11-22-33-44-55",
} }
@pytest.fixture(name="plex_empty_payload", scope="session")
def plex_empty_payload_fixture():
"""Load an empty payload and return it."""
return load_fixture("plex/empty_payload.xml")
@pytest.fixture(name="plextv_account", scope="session")
def plextv_account_fixture():
"""Load account info from plex.tv and return it."""
return load_fixture("plex/plextv_account.xml")
@pytest.fixture(name="plex_sonos_resources", scope="session")
def plex_sonos_resources_fixture():
"""Load Sonos resources payload and return it."""
return load_fixture("plex/sonos_resources.xml")

View File

@ -1,7 +1,6 @@
"""Tests for the Sonos Media Player platform.""" """Tests for the Sonos Media Player platform."""
from unittest.mock import patch from unittest.mock import patch
from plexapi.myplex import MyPlexAccount
import pytest import pytest
from homeassistant.components.media_player.const import ( from homeassistant.components.media_player.const import (
@ -11,7 +10,7 @@ from homeassistant.components.media_player.const import (
MEDIA_TYPE_MUSIC, MEDIA_TYPE_MUSIC,
SERVICE_PLAY_MEDIA, SERVICE_PLAY_MEDIA,
) )
from homeassistant.components.plex.const import DOMAIN as PLEX_DOMAIN, SERVERS from homeassistant.components.plex.const import PLEX_URI_SCHEME
from homeassistant.const import ATTR_ENTITY_ID from homeassistant.const import ATTR_ENTITY_ID
from homeassistant.exceptions import HomeAssistantError from homeassistant.exceptions import HomeAssistantError
@ -22,99 +21,47 @@ async def test_plex_play_media(
hass, hass,
config_entry, config_entry,
config, config,
requests_mock,
plextv_account,
plex_empty_payload,
plex_sonos_resources,
): ):
"""Test playing media via the Plex integration.""" """Test playing media via the Plex integration."""
requests_mock.get("https://plex.tv/users/account", text=plextv_account)
requests_mock.get("https://sonos.plex.tv/resources", text=plex_empty_payload)
class MockPlexServer:
"""Mock a PlexServer instance."""
def __init__(self, has_media=False):
self.account = MyPlexAccount(token="token")
self.friendly_name = "plex"
if has_media:
self.media = "media"
else:
self.media = None
def create_playqueue(self, media, **kwargs):
pass
def lookup_media(self, content_type, **kwargs):
return self.media
await setup_platform(hass, config_entry, config) await setup_platform(hass, config_entry, config)
hass.data[PLEX_DOMAIN] = {SERVERS: {}}
media_player = "media_player.zone_a" media_player = "media_player.zone_a"
media_content_id = (
'{"library_name": "Music", "artist_name": "Artist", "album_name": "Album"}'
)
# Test Plex service call with media key with patch(
with pytest.raises(HomeAssistantError) as excinfo: "homeassistant.components.sonos.media_player.play_on_sonos"
) as mock_play:
# Test successful Plex service call
assert await hass.services.async_call( assert await hass.services.async_call(
MP_DOMAIN, MP_DOMAIN,
SERVICE_PLAY_MEDIA, SERVICE_PLAY_MEDIA,
{ {
ATTR_ENTITY_ID: media_player, ATTR_ENTITY_ID: media_player,
ATTR_MEDIA_CONTENT_TYPE: MEDIA_TYPE_MUSIC, ATTR_MEDIA_CONTENT_TYPE: MEDIA_TYPE_MUSIC,
ATTR_MEDIA_CONTENT_ID: "plex://5", ATTR_MEDIA_CONTENT_ID: f"{PLEX_URI_SCHEME}{media_content_id}",
}, },
True, blocking=True,
) )
assert "No Plex servers available" in str(excinfo.value)
# Add a mocked Plex server with no media assert len(mock_play.mock_calls) == 1
hass.data[PLEX_DOMAIN][SERVERS] = {"plex": MockPlexServer()} assert mock_play.mock_calls[0][1][1] == MEDIA_TYPE_MUSIC
assert mock_play.mock_calls[0][1][2] == media_content_id
assert mock_play.mock_calls[0][1][3] == "Zone A"
# Test Plex service call with dict # Test failed Plex service call
with pytest.raises(HomeAssistantError) as excinfo: mock_play.reset_mock()
assert await hass.services.async_call( mock_play.side_effect = HomeAssistantError
MP_DOMAIN,
SERVICE_PLAY_MEDIA,
{
ATTR_ENTITY_ID: media_player,
ATTR_MEDIA_CONTENT_TYPE: MEDIA_TYPE_MUSIC,
ATTR_MEDIA_CONTENT_ID: 'plex://{"library_name": "Music", "artist_name": "Artist"}',
},
True,
)
assert "Plex media not found" in str(excinfo.value)
# Add a mocked Plex server with pytest.raises(HomeAssistantError):
hass.data[PLEX_DOMAIN][SERVERS] = {"plex": MockPlexServer(has_media=True)} await hass.services.async_call(
MP_DOMAIN,
# Test Plex service call with no Sonos speakers SERVICE_PLAY_MEDIA,
requests_mock.get("https://sonos.plex.tv/resources", text=plex_empty_payload) {
with pytest.raises(HomeAssistantError) as excinfo: ATTR_ENTITY_ID: media_player,
assert await hass.services.async_call( ATTR_MEDIA_CONTENT_TYPE: MEDIA_TYPE_MUSIC,
MP_DOMAIN, ATTR_MEDIA_CONTENT_ID: f"{PLEX_URI_SCHEME}{media_content_id}",
SERVICE_PLAY_MEDIA, },
{ blocking=True,
ATTR_ENTITY_ID: media_player, )
ATTR_MEDIA_CONTENT_TYPE: MEDIA_TYPE_MUSIC, assert mock_play.called
ATTR_MEDIA_CONTENT_ID: 'plex://{"library_name": "Music", "artist_name": "Artist"}',
},
True,
)
assert "Sonos speaker 'Zone A' is not associated with" in str(excinfo.value)
# Test successful Plex service call
account = hass.data[PLEX_DOMAIN][SERVERS]["plex"].account
requests_mock.get("https://sonos.plex.tv/resources", text=plex_sonos_resources)
with patch.object(account, "_sonos_cache_timestamp", 0), patch(
"plexapi.sonos.PlexSonosClient.playMedia"
):
assert await hass.services.async_call(
MP_DOMAIN,
SERVICE_PLAY_MEDIA,
{
ATTR_ENTITY_ID: media_player,
ATTR_MEDIA_CONTENT_TYPE: MEDIA_TYPE_MUSIC,
ATTR_MEDIA_CONTENT_ID: 'plex://{"plex_server": "plex", "library_name": "Music", "artist_name": "Artist", "album_name": "Album"}',
},
True,
)