core/tests/components/jellyfin/test_media_source.py

353 lines
11 KiB
Python

"""Tests for the Jellyfin media_player platform."""
from unittest.mock import MagicMock
import pytest
from syrupy.assertion import SnapshotAssertion
from homeassistant.components.jellyfin.const import DOMAIN
from homeassistant.components.media_player import BrowseError
from homeassistant.components.media_source import (
DOMAIN as MEDIA_SOURCE_DOMAIN,
URI_SCHEME,
async_browse_media,
async_resolve_media,
)
from homeassistant.core import HomeAssistant
from homeassistant.setup import async_setup_component
from . import load_json_fixture
from tests.common import MockConfigEntry
@pytest.fixture(autouse=True)
async def setup_component(hass: HomeAssistant) -> None:
"""Set up component."""
assert await async_setup_component(hass, MEDIA_SOURCE_DOMAIN, {})
async def test_resolve(
hass: HomeAssistant,
mock_client: MagicMock,
init_integration: MockConfigEntry,
mock_jellyfin: MagicMock,
mock_api: MagicMock,
snapshot: SnapshotAssertion,
) -> None:
"""Test resolving Jellyfin media items."""
# Test resolving a track
mock_api.get_item.side_effect = None
mock_api.get_item.return_value = load_json_fixture("track.json")
play_media = await async_resolve_media(
hass, f"{URI_SCHEME}{DOMAIN}/TRACK-UUID", "media_player.jellyfin_device"
)
assert play_media.mime_type == "audio/flac"
assert play_media.url == snapshot
mock_api.audio_url.assert_called_with("TRACK-UUID")
assert mock_api.audio_url.call_count == 1
mock_api.audio_url.reset_mock()
# Test resolving a movie
mock_api.get_item.side_effect = None
mock_api.get_item.return_value = load_json_fixture("movie.json")
play_media = await async_resolve_media(
hass, f"{URI_SCHEME}{DOMAIN}/MOVIE-UUID", "media_player.jellyfin_device"
)
assert play_media.mime_type == "video/mp4"
assert play_media.url == snapshot
# Test resolving an unsupported item
mock_api.get_item.side_effect = None
mock_api.get_item.return_value = load_json_fixture("unsupported-item.json")
with pytest.raises(BrowseError):
await async_resolve_media(
hass,
f"{URI_SCHEME}{DOMAIN}/UNSUPPORTED-ITEM-UUID",
"media_player.jellyfin_device",
)
@pytest.mark.parametrize(
"audio_codec",
[("aac"), ("wma"), ("vorbis"), ("mp3")],
)
async def test_audio_codec_resolve(
hass: HomeAssistant,
mock_client: MagicMock,
init_integration: MockConfigEntry,
mock_jellyfin: MagicMock,
mock_api: MagicMock,
snapshot: SnapshotAssertion,
audio_codec: str,
) -> None:
"""Test resolving Jellyfin media items with audio codec."""
# Test resolving a track
mock_api.get_item.side_effect = None
mock_api.get_item.return_value = load_json_fixture("track.json")
result = await hass.config_entries.options.async_init(init_integration.entry_id)
await hass.config_entries.options.async_configure(
result["flow_id"], user_input={"audio_codec": audio_codec}
)
assert init_integration.options["audio_codec"] == audio_codec
play_media = await async_resolve_media(
hass, f"{URI_SCHEME}{DOMAIN}/TRACK-UUID", "media_player.jellyfin_device"
)
assert play_media.mime_type == "audio/flac"
assert play_media.url == snapshot
mock_api.audio_url.assert_called_with("TRACK-UUID", audio_codec=audio_codec)
assert mock_api.audio_url.call_count == 1
async def test_root(
hass: HomeAssistant,
mock_client: MagicMock,
init_integration: MockConfigEntry,
mock_jellyfin: MagicMock,
mock_api: MagicMock,
snapshot: SnapshotAssertion,
) -> None:
"""Test browsing the Jellyfin root."""
browse = await async_browse_media(hass, f"{URI_SCHEME}{DOMAIN}")
assert browse.domain == DOMAIN
assert browse.identifier is None
assert browse.title == "Jellyfin"
assert vars(browse.children[0]) == snapshot
async def test_tv_library(
hass: HomeAssistant,
mock_client: MagicMock,
init_integration: MockConfigEntry,
mock_jellyfin: MagicMock,
mock_api: MagicMock,
snapshot: SnapshotAssertion,
) -> None:
"""Test browsing a Jellyfin TV Library."""
# Test browsing an empty tv library
mock_api.get_item.side_effect = None
mock_api.get_item.return_value = load_json_fixture("tv-collection.json")
mock_api.user_items.side_effect = None
mock_api.user_items.return_value = {"Items": []}
browse = await async_browse_media(
hass, f"{URI_SCHEME}{DOMAIN}/TV-COLLECTION-FOLDER-UUID"
)
assert browse.domain == DOMAIN
assert browse.identifier == "TV-COLLECTION-FOLDER-UUID"
assert browse.title == "TVShows"
assert browse.children == []
# Test browsing a tv library containing series
mock_api.user_items.side_effect = None
mock_api.user_items.return_value = load_json_fixture("series-list.json")
browse = await async_browse_media(
hass, f"{URI_SCHEME}{DOMAIN}/TV-COLLECTION-FOLDER-UUID"
)
assert browse.domain == DOMAIN
assert browse.identifier == "TV-COLLECTION-FOLDER-UUID"
assert browse.title == "TVShows"
assert vars(browse.children[0]) == snapshot
# Test browsing a series
mock_api.get_item.side_effect = None
mock_api.get_item.return_value = load_json_fixture("series.json")
mock_api.user_items.side_effect = None
mock_api.user_items.return_value = load_json_fixture("seasons.json")
browse = await async_browse_media(hass, f"{URI_SCHEME}{DOMAIN}/SERIES-UUID")
assert browse.domain == DOMAIN
assert browse.identifier == "SERIES-UUID"
assert browse.title == "SERIES"
assert vars(browse.children[0]) == snapshot
# Test browsing a season
mock_api.get_item.side_effect = None
mock_api.get_item.return_value = load_json_fixture("season.json")
mock_api.user_items.side_effect = None
mock_api.user_items.return_value = load_json_fixture("episodes.json")
browse = await async_browse_media(hass, f"{URI_SCHEME}{DOMAIN}/SEASON-UUID")
assert browse.domain == DOMAIN
assert browse.identifier == "SEASON-UUID"
assert browse.title == "SEASON"
assert vars(browse.children[0]) == snapshot
async def test_movie_library(
hass: HomeAssistant,
mock_client: MagicMock,
init_integration: MockConfigEntry,
mock_jellyfin: MagicMock,
mock_api: MagicMock,
snapshot: SnapshotAssertion,
) -> None:
"""Test browsing a Jellyfin Movie Library."""
# Test empty movie library
mock_api.get_item.side_effect = None
mock_api.get_item.return_value = load_json_fixture("movie-collection.json")
mock_api.user_items.side_effect = None
mock_api.user_items.return_value = {"Items": []}
browse = await async_browse_media(
hass, f"{URI_SCHEME}{DOMAIN}/MOVIE-COLLECTION-FOLDER-UUID"
)
assert browse.domain == DOMAIN
assert browse.identifier == "MOVIE-COLLECTION-FOLDER-UUID"
assert browse.title == "Movies"
assert browse.children == []
# Test browsing a movie library containing movies
mock_api.user_items.side_effect = None
mock_api.user_items.return_value = load_json_fixture("movies.json")
browse = await async_browse_media(
hass, f"{URI_SCHEME}{DOMAIN}/MOVIE-COLLECTION-FOLDER-UUID"
)
assert browse.domain == DOMAIN
assert browse.identifier == "MOVIE-COLLECTION-FOLDER-UUID"
assert browse.title == "Movies"
assert vars(browse.children[0]) == snapshot
async def test_music_library(
hass: HomeAssistant,
mock_client: MagicMock,
init_integration: MockConfigEntry,
mock_jellyfin: MagicMock,
mock_api: MagicMock,
snapshot: SnapshotAssertion,
) -> None:
"""Test browsing a Jellyfin Music Library."""
# Test browsinng an empty music library
mock_api.get_item.side_effect = None
mock_api.get_item.return_value = load_json_fixture("music-collection.json")
mock_api.user_items.side_effect = None
mock_api.user_items.return_value = {"Items": []}
browse = await async_browse_media(
hass, f"{URI_SCHEME}{DOMAIN}/MUSIC-COLLECTION-FOLDER-UUID"
)
assert browse.domain == DOMAIN
assert browse.identifier == "MUSIC-COLLECTION-FOLDER-UUID"
assert browse.title == "Music"
assert browse.children == []
# Test browsing a music library containing albums
mock_api.user_items.side_effect = None
mock_api.user_items.return_value = load_json_fixture("albums.json")
browse = await async_browse_media(
hass, f"{URI_SCHEME}{DOMAIN}/MUSIC-COLLECTION-FOLDER-UUID"
)
assert browse.domain == DOMAIN
assert browse.identifier == "MUSIC-COLLECTION-FOLDER-UUID"
assert browse.title == "Music"
assert vars(browse.children[0]) == snapshot
# Test browsing an artist
mock_api.get_item.side_effect = None
mock_api.get_item.return_value = load_json_fixture("artist.json")
mock_api.user_items.side_effect = None
mock_api.user_items.return_value = load_json_fixture("albums.json")
browse = await async_browse_media(hass, f"{URI_SCHEME}{DOMAIN}/ARTIST-UUID")
assert browse.domain == DOMAIN
assert browse.identifier == "ARTIST-UUID"
assert browse.title == "ARTIST"
assert vars(browse.children[0]) == snapshot
# Test browsing an album
mock_api.get_item.side_effect = None
mock_api.get_item.return_value = load_json_fixture("album.json")
mock_api.user_items.side_effect = None
mock_api.user_items.return_value = load_json_fixture("tracks.json")
browse = await async_browse_media(hass, f"{URI_SCHEME}{DOMAIN}/ALBUM-UUID")
assert browse.domain == DOMAIN
assert browse.identifier == "ALBUM-UUID"
assert browse.title == "ALBUM"
assert vars(browse.children[0]) == snapshot
# Test browsing an album with a track with no source
mock_api.user_items.side_effect = None
mock_api.user_items.return_value = load_json_fixture("tracks-nosource.json")
browse = await async_browse_media(hass, f"{URI_SCHEME}{DOMAIN}/ALBUM-UUID")
assert browse.domain == DOMAIN
assert browse.identifier == "ALBUM-UUID"
assert browse.title == "ALBUM"
assert browse.children == []
# Test browsing an album with a track with no path
mock_api.user_items.side_effect = None
mock_api.user_items.return_value = load_json_fixture("tracks-nopath.json")
browse = await async_browse_media(hass, f"{URI_SCHEME}{DOMAIN}/ALBUM-UUID")
assert browse.domain == DOMAIN
assert browse.identifier == "ALBUM-UUID"
assert browse.title == "ALBUM"
assert browse.children == []
# Test browsing an album with a track with an unknown file extension
mock_api.user_items.side_effect = None
mock_api.user_items.return_value = load_json_fixture(
"tracks-unknown-extension.json"
)
browse = await async_browse_media(hass, f"{URI_SCHEME}{DOMAIN}/ALBUM-UUID")
assert browse.domain == DOMAIN
assert browse.identifier == "ALBUM-UUID"
assert browse.title == "ALBUM"
assert browse.children == []
async def test_browse_unsupported(
hass: HomeAssistant,
mock_client: MagicMock,
init_integration: MockConfigEntry,
mock_jellyfin: MagicMock,
mock_api: MagicMock,
) -> None:
"""Test browsing an unsupported item."""
mock_api.get_item.side_effect = None
mock_api.get_item.return_value = load_json_fixture("unsupported-item.json")
with pytest.raises(BrowseError):
await async_browse_media(hass, f"{URI_SCHEME}{DOMAIN}/UNSUPPORTED-ITEM-UUID")