Files
core/tests/components/youtube/test_sensor.py
T

281 lines
9.4 KiB
Python

"""Sensor tests for the YouTube integration."""
import asyncio
from datetime import timedelta
from unittest.mock import patch
import pytest
from syrupy.assertion import SnapshotAssertion
from youtubeaio.types import UnauthorizedError, YouTubeBackendError
from homeassistant import config_entries
from homeassistant.components.youtube.const import DOMAIN
from homeassistant.core import HomeAssistant
from homeassistant.util import dt as dt_util
from . import MockYouTube
from .conftest import ComponentSetup
from tests.common import async_fire_time_changed
async def test_sensor(
hass: HomeAssistant, snapshot: SnapshotAssertion, setup_integration: ComponentSetup
) -> None:
"""Test sensor."""
await setup_integration()
state = hass.states.get("sensor.google_for_developers_latest_upload")
assert state == snapshot
state = hass.states.get("sensor.google_for_developers_latest_short")
assert state == snapshot
state = hass.states.get("sensor.google_for_developers_latest_video")
assert state == snapshot
state = hass.states.get("sensor.google_for_developers_subscribers")
assert state == snapshot
state = hass.states.get("sensor.google_for_developers_videos")
assert state == snapshot
state = hass.states.get("sensor.google_for_developers_views")
assert state == snapshot
async def test_sensor_without_uploaded_video(
hass: HomeAssistant, snapshot: SnapshotAssertion, setup_integration: ComponentSetup
) -> None:
"""Test sensor when there is no video on the channel."""
await setup_integration()
with patch(
"homeassistant.components.youtube.api.AsyncConfigEntryAuth.get_resource",
return_value=MockYouTube(
hass, playlist_items_fixture="get_no_playlist_items.json"
),
):
future = dt_util.utcnow() + timedelta(minutes=15)
async_fire_time_changed(hass, future)
await hass.async_block_till_done()
await asyncio.sleep(0.1)
state = hass.states.get("sensor.google_for_developers_latest_upload")
assert state == snapshot
state = hass.states.get("sensor.google_for_developers_subscribers")
assert state == snapshot
state = hass.states.get("sensor.google_for_developers_videos")
assert state == snapshot
state = hass.states.get("sensor.google_for_developers_views")
assert state == snapshot
async def test_sensor_with_short(
hass: HomeAssistant, snapshot: SnapshotAssertion, setup_integration: ComponentSetup
) -> None:
"""Test sensors when the channel has a Short upload."""
with patch(
"homeassistant.components.youtube.api.AsyncConfigEntryAuth.get_resource",
return_value=MockYouTube(hass, short_video_ids={"wysukDrMdqU"}),
):
await setup_integration()
state = hass.states.get("sensor.google_for_developers_latest_short")
assert state == snapshot
state = hass.states.get("sensor.google_for_developers_latest_video")
assert state == snapshot
async def test_sensor_updating(
hass: HomeAssistant, setup_integration: ComponentSetup
) -> None:
"""Test updating sensor."""
await setup_integration()
state = hass.states.get("sensor.google_for_developers_latest_upload")
assert state
assert state.attributes["video_id"] == "wysukDrMdqU"
state = hass.states.get("sensor.google_for_developers_latest_video")
assert state
assert state.attributes["video_id"] == "wysukDrMdqU"
with patch(
"homeassistant.components.youtube.api.AsyncConfigEntryAuth.get_resource",
return_value=MockYouTube(
hass, playlist_items_fixture="get_playlist_items_2.json"
),
):
future = dt_util.utcnow() + timedelta(minutes=15)
async_fire_time_changed(hass, future)
await hass.async_block_till_done()
await asyncio.sleep(0.1)
state = hass.states.get("sensor.google_for_developers_latest_upload")
assert state
assert state.name == "Google for Developers Latest upload"
assert state.state == "Google I/O 2023 Developer Keynote in 5 minutes"
assert (
state.attributes["entity_picture"]
== "https://i.ytimg.com/vi/hleLlcHwQLM/maxresdefault.jpg"
)
assert state.attributes["video_id"] == "hleLlcHwQLM"
async def test_sensor_reauth_trigger(
hass: HomeAssistant, setup_integration: ComponentSetup
) -> None:
"""Test reauth is triggered after a refresh error."""
mock = await setup_integration()
state = hass.states.get("sensor.google_for_developers_latest_upload")
assert state.state == "What's new in Google Home in less than 1 minute"
state = hass.states.get("sensor.google_for_developers_latest_video")
assert state.state == "What's new in Google Home in less than 1 minute"
state = hass.states.get("sensor.google_for_developers_subscribers")
assert state.state == "2290000"
state = hass.states.get("sensor.google_for_developers_views")
assert state.state == "214141263"
mock.set_thrown_exception(UnauthorizedError())
future = dt_util.utcnow() + timedelta(minutes=15)
async_fire_time_changed(hass, future)
await hass.async_block_till_done()
flows = hass.config_entries.flow.async_progress()
assert len(flows) == 1
flow = flows[0]
assert flow["step_id"] == "reauth_confirm"
assert flow["handler"] == DOMAIN
assert flow["context"]["source"] == config_entries.SOURCE_REAUTH
async def test_sensor_unavailable(
hass: HomeAssistant, setup_integration: ComponentSetup
) -> None:
"""Test update failed."""
mock = await setup_integration()
state = hass.states.get("sensor.google_for_developers_latest_upload")
assert state.state == "What's new in Google Home in less than 1 minute"
state = hass.states.get("sensor.google_for_developers_subscribers")
assert state.state == "2290000"
state = hass.states.get("sensor.google_for_developers_views")
assert state.state == "214141263"
mock.set_thrown_exception(YouTubeBackendError())
future = dt_util.utcnow() + timedelta(minutes=15)
async_fire_time_changed(hass, future)
await hass.async_block_till_done()
state = hass.states.get("sensor.google_for_developers_latest_upload")
assert state.state == "unavailable"
state = hass.states.get("sensor.google_for_developers_latest_short")
assert state.state == "unavailable"
state = hass.states.get("sensor.google_for_developers_latest_video")
assert state.state == "unavailable"
state = hass.states.get("sensor.google_for_developers_subscribers")
assert state.state == "unavailable"
state = hass.states.get("sensor.google_for_developers_videos")
assert state.state == "unavailable"
state = hass.states.get("sensor.google_for_developers_views")
assert state.state == "unavailable"
@pytest.mark.parametrize(
("short_video_ids", "expected_yielded", "expected_latest_video_state"),
[
pytest.param(
{"wysukDrMdqU"},
2,
"Google I/O 2023 Developer Keynote in 5 minutes",
id="stops_after_short_and_non_short_found",
),
pytest.param(
{"wysukDrMdqU", "hleLlcHwQLM", "lMKjtSFujcw", "c0mqBuXPrpA", "_n9xwuTORas"},
10,
"unavailable",
id="caps_at_page_size_when_only_shorts",
),
],
)
async def test_sensor_playlist_iteration(
hass: HomeAssistant,
setup_integration: ComponentSetup,
short_video_ids: set[str],
expected_yielded: int,
expected_latest_video_state: str,
) -> None:
"""Test playlist iteration stops as early as possible and never paginates."""
mock = MockYouTube(hass, short_video_ids=short_video_ids)
with patch(
"homeassistant.components.youtube.api.AsyncConfigEntryAuth.get_resource",
return_value=mock,
):
await setup_integration()
assert mock.playlist_item_requests == 1
assert mock.playlist_items_yielded == expected_yielded
state = hass.states.get("sensor.google_for_developers_latest_short")
assert state.state == "What's new in Google Home in less than 1 minute"
state = hass.states.get("sensor.google_for_developers_latest_video")
assert state.state == expected_latest_video_state
async def test_sensor_shorts_detection_timeout(
hass: HomeAssistant,
caplog: pytest.LogCaptureFixture,
setup_integration: ComponentSetup,
) -> None:
"""Test the per-channel pass is bounded when is_short hangs.
Channel-level sensors stay available while the video sensors report
unavailable from the partial results.
"""
mock = MockYouTube(hass, short_check_delay=1)
with (
patch(
"homeassistant.components.youtube.api.AsyncConfigEntryAuth.get_resource",
return_value=mock,
),
patch(
"homeassistant.components.youtube.coordinator._SHORTS_DETECTION_TIMEOUT",
0.1,
),
):
await setup_integration()
assert "Timed out processing recent uploads" in caplog.text
state = hass.states.get("sensor.google_for_developers_latest_upload")
assert state.state == "unavailable"
state = hass.states.get("sensor.google_for_developers_latest_short")
assert state.state == "unavailable"
state = hass.states.get("sensor.google_for_developers_latest_video")
assert state.state == "unavailable"
state = hass.states.get("sensor.google_for_developers_subscribers")
assert state.state == "2290000"
state = hass.states.get("sensor.google_for_developers_views")
assert state.state == "214141263"