Files
RelayTV-HA/tests/test_media_player.py
markandClaude Fable 5 f2bf565623 Harden RelayTV integration and prepare HACS 0.4.0 (#2)
* docs: add Home Assistant 0.4.0 hardening roadmap

* fix: protect media credentials and resume state

* feat: authenticate RelayTV API operations

* fix: make targeting and media state reliable

* test: add Home Assistant integration CI

* fix: validate API credentials during setup

* release: prepare HACS 0.4.0

* fix: align pytest dependency pin

* fix: close URL sanitizer gaps and correct player state reporting

Sync the sensitive-query-key list with the RelayTV server (adds auth,
exp, jwt, X-Emby-Token, X-Jellyfin-Token), filter query credentials
from relative URLs instead of returning them verbatim, and preserve
brackets around IPv6 literal hosts.

Also report volume on RelayTV's 0-100 scale unconditionally (a raw 1
is 1%, not full volume) and give the coordinator its own
position_updated_at stamp — the base DataUpdateCoordinator has no
last_update_success_time, so media_position_updated_at silently fell
back to now() on every read and the seek bar never extrapolated.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 21:08:54 -05:00

60 lines
2.0 KiB
Python

"""Tests for RelayTV media-player behavior."""
from unittest.mock import AsyncMock, MagicMock
from homeassistant.components.media_player.const import MediaPlayerEntityFeature
from pytest_homeassistant_custom_component.common import MockConfigEntry
from custom_components.relaytv.media_player import RelayTVMediaPlayer, _parse_status
def test_status_parser_preserves_zero_values() -> None:
status = _parse_status(
{
"playing": True,
"volume": 0,
"position": 0,
"pos": 25,
"duration": 0,
"len": 120,
"mute": True,
}
)
assert status.volume == 0
assert status.position == 0
assert status.duration == 0
assert status.muted is True
def test_status_parser_reads_volume_on_percent_scale() -> None:
# RelayTV reports 0-100; a raw 1 is 1%, not full volume.
assert _parse_status({"volume": 1}).volume == 0.01
assert _parse_status({"volume": 100}).volume == 1.0
async def test_media_player_exposes_device_and_native_mute(hass) -> None:
entry = MockConfigEntry(
domain="relaytv",
title="Living Room",
data={"base_url": "http://relaytv.local:8787", "name": "Living Room"},
)
coordinator = MagicMock()
coordinator.data = {"playing": True, "volume": 25, "mute": False}
coordinator.last_update_success = True
coordinator.async_request_refresh = AsyncMock()
api = MagicMock(base_url="http://relaytv.local:8787")
api.mute = AsyncMock(return_value=True)
player = RelayTVMediaPlayer(entry, coordinator, api)
assert player.device_info["name"] == "Living Room"
assert player.device_info["manufacturer"] == "RelayTV"
assert player.volume_level == 0.25
assert player.is_volume_muted is False
assert player.supported_features & MediaPlayerEntityFeature.VOLUME_MUTE
await player.async_mute_volume(True)
api.mute.assert_awaited_once_with(True)
coordinator.async_request_refresh.assert_awaited_once_with()