core/tests/components/unifiprotect/conftest.py

294 lines
7.7 KiB
Python
Raw Normal View History

"""Fixtures and test data for UniFi Protect methods."""
# pylint: disable=protected-access
from __future__ import annotations
from collections.abc import Callable
from dataclasses import dataclass
from datetime import timedelta
from ipaddress import IPv4Address
import json
from typing import Any
from unittest.mock import AsyncMock, Mock, patch
import pytest
from pyunifiprotect.data import (
NVR,
Bootstrap,
Camera,
Chime,
Doorlock,
Light,
Liveview,
2022-05-20 01:34:58 +00:00
ProtectAdoptableDeviceModel,
Sensor,
Viewer,
WSSubscriptionMessage,
)
from pyunifiprotect.test_util.anonymize import random_hex
from homeassistant.components.unifiprotect.const import DOMAIN
from homeassistant.const import Platform
from homeassistant.core import HomeAssistant, split_entity_id
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.entity import EntityDescription
import homeassistant.util.dt as dt_util
from . import _patch_discovery
from tests.common import MockConfigEntry, async_fire_time_changed, load_fixture
MAC_ADDR = "aa:bb:cc:dd:ee:ff"
@dataclass
class MockEntityFixture:
"""Mock for NVR."""
entry: MockConfigEntry
api: Mock
@pytest.fixture(name="mock_nvr")
def mock_nvr_fixture():
"""Mock UniFi Protect Camera device."""
data = json.loads(load_fixture("sample_nvr.json", integration=DOMAIN))
nvr = NVR.from_unifi_dict(**data)
# disable pydantic validation so mocking can happen
NVR.__config__.validate_assignment = False
yield nvr
NVR.__config__.validate_assignment = True
@pytest.fixture(name="mock_ufp_config_entry")
def mock_ufp_config_entry():
"""Mock the unifiprotect config entry."""
return MockConfigEntry(
domain=DOMAIN,
data={
"host": "1.1.1.1",
"username": "test-username",
"password": "test-password",
"id": "UnifiProtect",
"port": 443,
"verify_ssl": False,
},
version=2,
)
@pytest.fixture(name="mock_old_nvr")
def mock_old_nvr_fixture():
"""Mock UniFi Protect Camera device."""
data = json.loads(load_fixture("sample_nvr.json", integration=DOMAIN))
data["version"] = "1.19.0"
return NVR.from_unifi_dict(**data)
@pytest.fixture(name="mock_bootstrap")
def mock_bootstrap_fixture(mock_nvr: NVR):
"""Mock Bootstrap fixture."""
data = json.loads(load_fixture("sample_bootstrap.json", integration=DOMAIN))
data["nvr"] = mock_nvr
data["cameras"] = []
data["lights"] = []
data["sensors"] = []
data["viewers"] = []
data["liveviews"] = []
data["events"] = []
data["doorlocks"] = []
data["chimes"] = []
return Bootstrap.from_unifi_dict(**data)
def reset_objects(bootstrap: Bootstrap):
"""Reset bootstrap objects."""
bootstrap.cameras = {}
bootstrap.lights = {}
bootstrap.sensors = {}
bootstrap.viewers = {}
bootstrap.liveviews = {}
bootstrap.events = {}
bootstrap.doorlocks = {}
bootstrap.chimes = {}
@pytest.fixture
def mock_client(mock_bootstrap: Bootstrap):
"""Mock ProtectApiClient for testing."""
client = Mock()
client.bootstrap = mock_bootstrap
nvr = client.bootstrap.nvr
nvr._api = client
client.bootstrap._api = client
client.base_url = "https://127.0.0.1"
client.connection_host = IPv4Address("127.0.0.1")
client.get_nvr = AsyncMock(return_value=nvr)
client.update = AsyncMock(return_value=mock_bootstrap)
client.async_disconnect_ws = AsyncMock()
def subscribe(ws_callback: Callable[[WSSubscriptionMessage], None]) -> Any:
client.ws_subscription = ws_callback
return Mock()
client.subscribe_websocket = subscribe
return client
@pytest.fixture
def mock_entry(
hass: HomeAssistant,
mock_ufp_config_entry: MockConfigEntry,
mock_client, # pylint: disable=redefined-outer-name
):
"""Mock ProtectApiClient for testing."""
with _patch_discovery(no_device=True), patch(
"homeassistant.components.unifiprotect.ProtectApiClient"
) as mock_api:
mock_ufp_config_entry.add_to_hass(hass)
mock_api.return_value = mock_client
yield MockEntityFixture(mock_ufp_config_entry, mock_client)
@pytest.fixture
def mock_liveview():
"""Mock UniFi Protect Liveview."""
data = json.loads(load_fixture("sample_liveview.json", integration=DOMAIN))
return Liveview.from_unifi_dict(**data)
@pytest.fixture
def mock_camera():
"""Mock UniFi Protect Camera device."""
data = json.loads(load_fixture("sample_camera.json", integration=DOMAIN))
return Camera.from_unifi_dict(**data)
@pytest.fixture
def mock_light():
"""Mock UniFi Protect Light device."""
data = json.loads(load_fixture("sample_light.json", integration=DOMAIN))
return Light.from_unifi_dict(**data)
@pytest.fixture
def mock_viewer():
"""Mock UniFi Protect Viewport device."""
data = json.loads(load_fixture("sample_viewport.json", integration=DOMAIN))
return Viewer.from_unifi_dict(**data)
@pytest.fixture
def mock_sensor():
"""Mock UniFi Protect Sensor device."""
data = json.loads(load_fixture("sample_sensor.json", integration=DOMAIN))
return Sensor.from_unifi_dict(**data)
@pytest.fixture
def mock_doorlock():
"""Mock UniFi Protect Doorlock device."""
data = json.loads(load_fixture("sample_doorlock.json", integration=DOMAIN))
return Doorlock.from_unifi_dict(**data)
@pytest.fixture
def mock_chime():
"""Mock UniFi Protect Chime device."""
data = json.loads(load_fixture("sample_chime.json", integration=DOMAIN))
return Chime.from_unifi_dict(**data)
@pytest.fixture
def now():
"""Return datetime object that will be consistent throughout test."""
return dt_util.utcnow()
async def time_changed(hass: HomeAssistant, seconds: int) -> None:
"""Trigger time changed."""
next_update = dt_util.utcnow() + timedelta(seconds)
async_fire_time_changed(hass, next_update)
await hass.async_block_till_done()
async def enable_entity(
hass: HomeAssistant, entry_id: str, entity_id: str
) -> er.RegistryEntry:
"""Enable a disabled entity."""
entity_registry = er.async_get(hass)
updated_entity = entity_registry.async_update_entity(entity_id, disabled_by=None)
assert not updated_entity.disabled
await hass.config_entries.async_reload(entry_id)
await hass.async_block_till_done()
return updated_entity
def assert_entity_counts(
hass: HomeAssistant, platform: Platform, total: int, enabled: int
) -> None:
"""Assert entity counts for a given platform."""
entity_registry = er.async_get(hass)
entities = [
e for e in entity_registry.entities if split_entity_id(e)[0] == platform.value
]
assert len(entities) == total
assert len(hass.states.async_all(platform.value)) == enabled
def ids_from_device_description(
platform: Platform,
device: ProtectAdoptableDeviceModel,
description: EntityDescription,
) -> tuple[str, str]:
"""Return expected unique_id and entity_id for a give platform/device/description combination."""
entity_name = (
device.name.lower().replace(":", "").replace(" ", "_").replace("-", "_")
)
description_entity_name = (
description.name.lower().replace(":", "").replace(" ", "_").replace("-", "_")
)
unique_id = f"{device.mac}_{description.key}"
entity_id = f"{platform.value}.{entity_name}_{description_entity_name}"
return unique_id, entity_id
def generate_random_ids() -> tuple[str, str]:
"""Generate random IDs for device."""
return random_hex(24).lower(), random_hex(12).upper()
def regenerate_device_ids(device: ProtectAdoptableDeviceModel) -> None:
"""Regenerate the IDs on UFP device."""
device.id, device.mac = generate_random_ids()