mirror of
https://github.com/home-assistant/core.git
synced 2025-07-29 16:17:20 +00:00
Fix AttributeError in radio_browser media source when runtime_data is missing
The media_player.play_media action was failing with AttributeError when the radio_browser integration wasn't properly loaded. This happened when accessing the media source before the config entry was fully initialized. - Add proper error handling in RadioMediaSource.radios property - Raise Unresolvable exception when runtime_data is missing or None - Add comprehensive tests for the error handling scenario Fixes #141755 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
470baa782e
commit
35c8fefbd6
@ -50,6 +50,8 @@ class RadioMediaSource(MediaSource):
|
||||
@property
|
||||
def radios(self) -> RadioBrowser:
|
||||
"""Return the radio browser."""
|
||||
if not hasattr(self.entry, "runtime_data") or self.entry.runtime_data is None:
|
||||
raise Unresolvable("Radio Browser integration not properly loaded")
|
||||
return self.entry.runtime_data
|
||||
|
||||
async def async_resolve_media(self, item: MediaSourceItem) -> PlayMedia:
|
||||
|
139
tests/components/radio_browser/test_media_source.py
Normal file
139
tests/components/radio_browser/test_media_source.py
Normal file
@ -0,0 +1,139 @@
|
||||
"""Test the Radio Browser media source."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
import pytest
|
||||
from radios import RadioBrowser
|
||||
|
||||
from homeassistant.components.media_source import MediaSourceItem, Unresolvable
|
||||
from homeassistant.components.radio_browser.const import DOMAIN
|
||||
from homeassistant.components.radio_browser.media_source import (
|
||||
RadioMediaSource,
|
||||
async_get_media_source,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_radio_browser() -> AsyncMock:
|
||||
"""Mock RadioBrowser."""
|
||||
radio_browser = AsyncMock(spec=RadioBrowser)
|
||||
# Mock station without full Station object to avoid constructor complexity
|
||||
mock_station = AsyncMock()
|
||||
mock_station.uuid = "test-uuid"
|
||||
mock_station.name = "Test Station"
|
||||
mock_station.url = "https://example.com/stream"
|
||||
mock_station.codec = "MP3"
|
||||
mock_station.favicon = "https://example.com/favicon.ico"
|
||||
radio_browser.station.return_value = mock_station
|
||||
return radio_browser
|
||||
|
||||
|
||||
async def test_media_source_without_runtime_data(
|
||||
hass: HomeAssistant, mock_config_entry: MockConfigEntry
|
||||
) -> None:
|
||||
"""Test media source raises error when runtime_data is missing."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
|
||||
# Don't set runtime_data to simulate the error condition
|
||||
media_source = RadioMediaSource(hass, mock_config_entry)
|
||||
|
||||
with pytest.raises(
|
||||
Unresolvable, match="Radio Browser integration not properly loaded"
|
||||
):
|
||||
_ = media_source.radios
|
||||
|
||||
|
||||
async def test_media_source_with_none_runtime_data(
|
||||
hass: HomeAssistant, mock_config_entry: MockConfigEntry
|
||||
) -> None:
|
||||
"""Test media source raises error when runtime_data is None."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
mock_config_entry.runtime_data = None
|
||||
|
||||
media_source = RadioMediaSource(hass, mock_config_entry)
|
||||
|
||||
with pytest.raises(
|
||||
Unresolvable, match="Radio Browser integration not properly loaded"
|
||||
):
|
||||
_ = media_source.radios
|
||||
|
||||
|
||||
async def test_media_source_with_runtime_data(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_radio_browser: AsyncMock,
|
||||
) -> None:
|
||||
"""Test media source works correctly with runtime_data."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
mock_config_entry.runtime_data = mock_radio_browser
|
||||
|
||||
media_source = RadioMediaSource(hass, mock_config_entry)
|
||||
|
||||
# Should not raise an error
|
||||
radios = media_source.radios
|
||||
assert radios is mock_radio_browser
|
||||
|
||||
|
||||
async def test_async_get_media_source(
|
||||
hass: HomeAssistant,
|
||||
mock_config_entry: MockConfigEntry,
|
||||
mock_radio_browser: AsyncMock,
|
||||
) -> None:
|
||||
"""Test async_get_media_source function."""
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
mock_config_entry.runtime_data = mock_radio_browser
|
||||
|
||||
media_source = await async_get_media_source(hass)
|
||||
|
||||
assert isinstance(media_source, RadioMediaSource)
|
||||
assert media_source.entry is mock_config_entry
|
||||
assert media_source.radios is mock_radio_browser
|
||||
|
||||
|
||||
async def test_async_resolve_media_with_missing_runtime_data(
|
||||
hass: HomeAssistant, mock_config_entry: MockConfigEntry
|
||||
) -> None:
|
||||
"""Test async_resolve_media raises error when runtime_data is missing."""
|
||||
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
# Don't set runtime_data to simulate the error condition
|
||||
|
||||
media_source = RadioMediaSource(hass, mock_config_entry)
|
||||
item = MediaSourceItem(
|
||||
hass=hass,
|
||||
domain=DOMAIN,
|
||||
identifier="test-uuid",
|
||||
target_media_player=None,
|
||||
)
|
||||
|
||||
with pytest.raises(
|
||||
Unresolvable, match="Radio Browser integration not properly loaded"
|
||||
):
|
||||
await media_source.async_resolve_media(item)
|
||||
|
||||
|
||||
async def test_async_browse_media_with_missing_runtime_data(
|
||||
hass: HomeAssistant, mock_config_entry: MockConfigEntry
|
||||
) -> None:
|
||||
"""Test async_browse_media raises error when runtime_data is missing."""
|
||||
|
||||
mock_config_entry.add_to_hass(hass)
|
||||
# Don't set runtime_data to simulate the error condition
|
||||
|
||||
media_source = RadioMediaSource(hass, mock_config_entry)
|
||||
item = MediaSourceItem(
|
||||
hass=hass,
|
||||
domain=DOMAIN,
|
||||
identifier="",
|
||||
target_media_player=None,
|
||||
)
|
||||
|
||||
with pytest.raises(
|
||||
Unresolvable, match="Radio Browser integration not properly loaded"
|
||||
):
|
||||
await media_source.async_browse_media(item)
|
Loading…
x
Reference in New Issue
Block a user