mirror of
https://github.com/home-assistant/core.git
synced 2025-04-23 08:47:57 +00:00
Reolink add 100% coverage of binary_sensor platfrom (#123862)
* Implement 100% coverage of binary_sensor * fix styling * Apply suggestions from code review Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com> * use get().state instead of is_state * Remove unneeded "is True" * Remove unneeded "is True" * reset the mock and use assert_not_called * use freezer * fix styling * fix styling --------- Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
This commit is contained in:
parent
461ef33553
commit
0093276e93
@ -53,10 +53,6 @@ def mock_setup_entry() -> Generator[AsyncMock]:
|
||||
def reolink_connect_class() -> Generator[MagicMock]:
|
||||
"""Mock reolink connection and return both the host_mock and host_mock_class."""
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.reolink.host.webhook.async_register",
|
||||
return_value=True,
|
||||
),
|
||||
patch(
|
||||
"homeassistant.components.reolink.host.Host", autospec=True
|
||||
) as host_mock_class,
|
||||
|
52
tests/components/reolink/test_binary_sensor.py
Normal file
52
tests/components/reolink/test_binary_sensor.py
Normal file
@ -0,0 +1,52 @@
|
||||
"""Test the Reolink binary sensor platform."""
|
||||
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
|
||||
from homeassistant.components.reolink import DEVICE_UPDATE_INTERVAL, const
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.const import STATE_OFF, STATE_ON, Platform
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
|
||||
from .conftest import TEST_NVR_NAME, TEST_UID
|
||||
|
||||
from tests.common import MockConfigEntry, async_fire_time_changed
|
||||
from tests.typing import ClientSessionGenerator
|
||||
|
||||
|
||||
async def test_motion_sensor(
|
||||
hass: HomeAssistant,
|
||||
hass_client_no_auth: ClientSessionGenerator,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
config_entry: MockConfigEntry,
|
||||
reolink_connect: MagicMock,
|
||||
entity_registry: er.EntityRegistry,
|
||||
) -> None:
|
||||
"""Test binary sensor entity with motion sensor."""
|
||||
reolink_connect.model = "Reolink Duo PoE"
|
||||
reolink_connect.motion_detected.return_value = True
|
||||
with patch("homeassistant.components.reolink.PLATFORMS", [Platform.BINARY_SENSOR]):
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert config_entry.state is ConfigEntryState.LOADED
|
||||
|
||||
entity_id = f"{Platform.BINARY_SENSOR}.{TEST_NVR_NAME}_motion_lens_0"
|
||||
assert hass.states.get(entity_id).state == STATE_ON
|
||||
|
||||
reolink_connect.motion_detected.return_value = False
|
||||
freezer.tick(DEVICE_UPDATE_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get(entity_id).state == STATE_OFF
|
||||
|
||||
# test webhook callback
|
||||
reolink_connect.motion_detected.return_value = True
|
||||
reolink_connect.ONVIF_event_callback.return_value = [0]
|
||||
webhook_id = f"{const.DOMAIN}_{TEST_UID.replace(':', '')}_ONVIF"
|
||||
client = await hass_client_no_auth()
|
||||
await client.post(f"/api/webhook/{webhook_id}", data="test_data")
|
||||
|
||||
assert hass.states.get(entity_id).state == STATE_ON
|
@ -1,10 +1,10 @@
|
||||
"""Test the Reolink config flow."""
|
||||
|
||||
from datetime import timedelta
|
||||
import json
|
||||
from typing import Any
|
||||
from unittest.mock import AsyncMock, MagicMock, call
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
import pytest
|
||||
from reolink_aio.exceptions import ApiError, CredentialsInvalidError, ReolinkError
|
||||
|
||||
@ -25,7 +25,6 @@ from homeassistant.const import (
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResultType
|
||||
from homeassistant.helpers.device_registry import format_mac
|
||||
from homeassistant.util.dt import utcnow
|
||||
|
||||
from .conftest import (
|
||||
DHCP_FORMATTED_MAC,
|
||||
@ -439,6 +438,7 @@ async def test_dhcp_flow(hass: HomeAssistant, mock_setup_entry: MagicMock) -> No
|
||||
)
|
||||
async def test_dhcp_ip_update(
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
reolink_connect_class: MagicMock,
|
||||
reolink_connect: MagicMock,
|
||||
last_update_success: bool,
|
||||
@ -472,9 +472,8 @@ async def test_dhcp_ip_update(
|
||||
if not last_update_success:
|
||||
# ensure the last_update_succes is False for the device_coordinator.
|
||||
reolink_connect.get_states = AsyncMock(side_effect=ReolinkError("Test error"))
|
||||
async_fire_time_changed(
|
||||
hass, utcnow() + DEVICE_UPDATE_INTERVAL + timedelta(minutes=1)
|
||||
)
|
||||
freezer.tick(DEVICE_UPDATE_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
dhcp_data = dhcp.DhcpServiceInfo(
|
||||
|
85
tests/components/reolink/test_host.py
Normal file
85
tests/components/reolink/test_host.py
Normal file
@ -0,0 +1,85 @@
|
||||
"""Test the Reolink host."""
|
||||
|
||||
from asyncio import CancelledError
|
||||
from unittest.mock import AsyncMock, MagicMock
|
||||
|
||||
from aiohttp import ClientResponseError
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.reolink import const
|
||||
from homeassistant.components.webhook import async_handle_webhook
|
||||
from homeassistant.config_entries import ConfigEntryState
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.util.aiohttp import MockRequest
|
||||
|
||||
from .conftest import TEST_UID
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
from tests.typing import ClientSessionGenerator
|
||||
|
||||
|
||||
async def test_webhook_callback(
|
||||
hass: HomeAssistant,
|
||||
hass_client_no_auth: ClientSessionGenerator,
|
||||
config_entry: MockConfigEntry,
|
||||
reolink_connect: MagicMock,
|
||||
entity_registry: er.EntityRegistry,
|
||||
) -> None:
|
||||
"""Test webhook callback with motion sensor."""
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert config_entry.state is ConfigEntryState.LOADED
|
||||
|
||||
webhook_id = f"{const.DOMAIN}_{TEST_UID.replace(':', '')}_ONVIF"
|
||||
|
||||
signal_all = MagicMock()
|
||||
signal_ch = MagicMock()
|
||||
async_dispatcher_connect(hass, f"{webhook_id}_all", signal_all)
|
||||
async_dispatcher_connect(hass, f"{webhook_id}_0", signal_ch)
|
||||
|
||||
client = await hass_client_no_auth()
|
||||
|
||||
# test webhook callback success all channels
|
||||
reolink_connect.ONVIF_event_callback.return_value = None
|
||||
await client.post(f"/api/webhook/{webhook_id}")
|
||||
signal_all.assert_called_once()
|
||||
|
||||
# test webhook callback all channels with failure to read motion_state
|
||||
signal_all.reset_mock()
|
||||
reolink_connect.get_motion_state_all_ch.return_value = False
|
||||
await client.post(f"/api/webhook/{webhook_id}")
|
||||
signal_all.assert_not_called()
|
||||
|
||||
# test webhook callback success single channel
|
||||
reolink_connect.ONVIF_event_callback.return_value = [0]
|
||||
await client.post(f"/api/webhook/{webhook_id}", data="test_data")
|
||||
signal_ch.assert_called_once()
|
||||
|
||||
# test webhook callback single channel with error in event callback
|
||||
signal_ch.reset_mock()
|
||||
reolink_connect.ONVIF_event_callback = AsyncMock(
|
||||
side_effect=Exception("Test error")
|
||||
)
|
||||
await client.post(f"/api/webhook/{webhook_id}", data="test_data")
|
||||
signal_ch.assert_not_called()
|
||||
|
||||
# test failure to read date from webhook post
|
||||
request = MockRequest(
|
||||
method="POST",
|
||||
content=bytes("test", "utf-8"),
|
||||
mock_source="test",
|
||||
)
|
||||
request.read = AsyncMock(side_effect=ConnectionResetError("Test error"))
|
||||
await async_handle_webhook(hass, webhook_id, request)
|
||||
signal_all.assert_not_called()
|
||||
|
||||
request.read = AsyncMock(side_effect=ClientResponseError("Test error", "Test"))
|
||||
await async_handle_webhook(hass, webhook_id, request)
|
||||
signal_all.assert_not_called()
|
||||
|
||||
request.read = AsyncMock(side_effect=CancelledError("Test error"))
|
||||
with pytest.raises(CancelledError):
|
||||
await async_handle_webhook(hass, webhook_id, request)
|
||||
signal_all.assert_not_called()
|
@ -1,10 +1,10 @@
|
||||
"""Test the Reolink init."""
|
||||
|
||||
import asyncio
|
||||
from datetime import timedelta
|
||||
from typing import Any
|
||||
from unittest.mock import AsyncMock, MagicMock, Mock, patch
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
import pytest
|
||||
from reolink_aio.api import Chime
|
||||
from reolink_aio.exceptions import CredentialsInvalidError, ReolinkError
|
||||
@ -25,7 +25,6 @@ from homeassistant.helpers import (
|
||||
issue_registry as ir,
|
||||
)
|
||||
from homeassistant.setup import async_setup_component
|
||||
from homeassistant.util.dt import utcnow
|
||||
|
||||
from .conftest import (
|
||||
TEST_CAM_MODEL,
|
||||
@ -104,6 +103,7 @@ async def test_failures_parametrized(
|
||||
|
||||
async def test_firmware_error_twice(
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
reolink_connect: MagicMock,
|
||||
config_entry: MockConfigEntry,
|
||||
) -> None:
|
||||
@ -112,31 +112,31 @@ async def test_firmware_error_twice(
|
||||
side_effect=ReolinkError("Test error")
|
||||
)
|
||||
with patch("homeassistant.components.reolink.PLATFORMS", [Platform.UPDATE]):
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id) is True
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert config_entry.state is ConfigEntryState.LOADED
|
||||
|
||||
entity_id = f"{Platform.UPDATE}.{TEST_NVR_NAME}_firmware"
|
||||
assert hass.states.is_state(entity_id, STATE_OFF)
|
||||
assert hass.states.get(entity_id).state == STATE_OFF
|
||||
|
||||
async_fire_time_changed(
|
||||
hass, utcnow() + FIRMWARE_UPDATE_INTERVAL + timedelta(minutes=1)
|
||||
)
|
||||
freezer.tick(FIRMWARE_UPDATE_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.is_state(entity_id, STATE_UNAVAILABLE)
|
||||
assert hass.states.get(entity_id).state == STATE_UNAVAILABLE
|
||||
|
||||
|
||||
async def test_credential_error_three(
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
reolink_connect: MagicMock,
|
||||
config_entry: MockConfigEntry,
|
||||
issue_registry: ir.IssueRegistry,
|
||||
) -> None:
|
||||
"""Test when the update gives credential error 3 times."""
|
||||
with patch("homeassistant.components.reolink.PLATFORMS", [Platform.SWITCH]):
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id) is True
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert config_entry.state is ConfigEntryState.LOADED
|
||||
|
||||
@ -147,9 +147,8 @@ async def test_credential_error_three(
|
||||
issue_id = f"config_entry_reauth_{const.DOMAIN}_{config_entry.entry_id}"
|
||||
for _ in range(NUM_CRED_ERRORS):
|
||||
assert (HOMEASSISTANT_DOMAIN, issue_id) not in issue_registry.issues
|
||||
async_fire_time_changed(
|
||||
hass, utcnow() + DEVICE_UPDATE_INTERVAL + timedelta(seconds=30)
|
||||
)
|
||||
freezer.tick(DEVICE_UPDATE_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert (HOMEASSISTANT_DOMAIN, issue_id) in issue_registry.issues
|
||||
|
@ -275,7 +275,7 @@ async def test_browsing_rec_playback_unsupported(
|
||||
reolink_connect.api_version.return_value = 0
|
||||
|
||||
with patch("homeassistant.components.reolink.PLATFORMS", [Platform.CAMERA]):
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id) is True
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# browse root
|
||||
@ -296,7 +296,7 @@ async def test_browsing_errors(
|
||||
reolink_connect.api_version.return_value = 1
|
||||
|
||||
with patch("homeassistant.components.reolink.PLATFORMS", [Platform.CAMERA]):
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id) is True
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
# browse root
|
||||
@ -315,7 +315,7 @@ async def test_browsing_not_loaded(
|
||||
reolink_connect.api_version.return_value = 1
|
||||
|
||||
with patch("homeassistant.components.reolink.PLATFORMS", [Platform.CAMERA]):
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id) is True
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
reolink_connect.get_host_data = AsyncMock(side_effect=ReolinkError("Test error"))
|
||||
|
@ -1,8 +1,8 @@
|
||||
"""Test the Reolink select platform."""
|
||||
|
||||
from datetime import timedelta
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
import pytest
|
||||
from reolink_aio.api import Chime
|
||||
from reolink_aio.exceptions import InvalidParameterError, ReolinkError
|
||||
@ -19,7 +19,6 @@ from homeassistant.const import (
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.exceptions import HomeAssistantError, ServiceValidationError
|
||||
from homeassistant.helpers import entity_registry as er
|
||||
from homeassistant.util.dt import utcnow
|
||||
|
||||
from .conftest import TEST_NVR_NAME
|
||||
|
||||
@ -28,18 +27,19 @@ from tests.common import MockConfigEntry, async_fire_time_changed
|
||||
|
||||
async def test_floodlight_mode_select(
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
config_entry: MockConfigEntry,
|
||||
reolink_connect: MagicMock,
|
||||
entity_registry: er.EntityRegistry,
|
||||
) -> None:
|
||||
"""Test select entity with floodlight_mode."""
|
||||
with patch("homeassistant.components.reolink.PLATFORMS", [Platform.SELECT]):
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id) is True
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert config_entry.state is ConfigEntryState.LOADED
|
||||
|
||||
entity_id = f"{Platform.SELECT}.{TEST_NVR_NAME}_floodlight_mode"
|
||||
assert hass.states.is_state(entity_id, "auto")
|
||||
assert hass.states.get(entity_id).state == "auto"
|
||||
|
||||
reolink_connect.set_whiteled = AsyncMock()
|
||||
await hass.services.async_call(
|
||||
@ -71,12 +71,11 @@ async def test_floodlight_mode_select(
|
||||
)
|
||||
|
||||
reolink_connect.whiteled_mode.return_value = -99 # invalid value
|
||||
async_fire_time_changed(
|
||||
hass, utcnow() + DEVICE_UPDATE_INTERVAL + timedelta(seconds=30)
|
||||
)
|
||||
freezer.tick(DEVICE_UPDATE_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.is_state(entity_id, STATE_UNKNOWN)
|
||||
assert hass.states.get(entity_id).state == STATE_UNKNOWN
|
||||
|
||||
|
||||
async def test_play_quick_reply_message(
|
||||
@ -88,12 +87,12 @@ async def test_play_quick_reply_message(
|
||||
"""Test select play_quick_reply_message entity."""
|
||||
reolink_connect.quick_reply_dict.return_value = {0: "off", 1: "test message"}
|
||||
with patch("homeassistant.components.reolink.PLATFORMS", [Platform.SELECT]):
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id) is True
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
assert config_entry.state is ConfigEntryState.LOADED
|
||||
|
||||
entity_id = f"{Platform.SELECT}.{TEST_NVR_NAME}_play_quick_reply_message"
|
||||
assert hass.states.is_state(entity_id, STATE_UNKNOWN)
|
||||
assert hass.states.get(entity_id).state == STATE_UNKNOWN
|
||||
|
||||
reolink_connect.play_quick_reply = AsyncMock()
|
||||
await hass.services.async_call(
|
||||
@ -107,6 +106,7 @@ async def test_play_quick_reply_message(
|
||||
|
||||
async def test_chime_select(
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
config_entry: MockConfigEntry,
|
||||
reolink_connect: MagicMock,
|
||||
test_chime: Chime,
|
||||
@ -114,13 +114,13 @@ async def test_chime_select(
|
||||
) -> None:
|
||||
"""Test chime select entity."""
|
||||
with patch("homeassistant.components.reolink.PLATFORMS", [Platform.SELECT]):
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id) is True
|
||||
assert await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert config_entry.state is ConfigEntryState.LOADED
|
||||
|
||||
entity_id = f"{Platform.SELECT}.test_chime_visitor_ringtone"
|
||||
assert hass.states.is_state(entity_id, "pianokey")
|
||||
assert hass.states.get(entity_id).state == "pianokey"
|
||||
|
||||
test_chime.set_tone = AsyncMock()
|
||||
await hass.services.async_call(
|
||||
@ -150,9 +150,8 @@ async def test_chime_select(
|
||||
)
|
||||
|
||||
test_chime.event_info = {}
|
||||
async_fire_time_changed(
|
||||
hass, utcnow() + DEVICE_UPDATE_INTERVAL + timedelta(seconds=30)
|
||||
)
|
||||
freezer.tick(DEVICE_UPDATE_INTERVAL)
|
||||
async_fire_time_changed(hass)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.is_state(entity_id, STATE_UNKNOWN)
|
||||
assert hass.states.get(entity_id).state == STATE_UNKNOWN
|
||||
|
Loading…
x
Reference in New Issue
Block a user