1
0
mirror of https://github.com/home-assistant/core.git synced 2026-07-15 10:34:19 +01:00
Files
core/tests/components/ezviz/test_init.py
T

340 lines
11 KiB
Python

"""Tests for EZVIZ entities."""
from datetime import timedelta
from unittest.mock import AsyncMock, Mock, patch
from freezegun.api import FrozenDateTimeFactory
import pytest
from homeassistant.components import image
from homeassistant.components.ezviz.const import ATTR_TYPE_CLOUD
from homeassistant.const import STATE_UNKNOWN
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from . import setup_integration
from tests.common import MockConfigEntry, async_fire_time_changed
SCAN_INTERVAL = timedelta(seconds=30)
def _mock_camera_data(**kwargs: object) -> dict[str, object]:
"""Return a valid mocked EZVIZ camera payload for integration setup."""
data: dict[str, object] = {
"name": "Camera 1",
"device_sub_category": "CAMERA",
"mac_address": "AA:BB:CC:DD:EE:FF",
"version": "1.0.0",
"status": 1,
"encrypted": False,
"supportExt": {},
"switches": {},
"local_ip": "192.168.1.100",
"local_rtsp_port": 554,
"alarm_notify": False,
"upgrade_available": False,
"upgrade_in_progress": False,
"upgrade_percent": 0,
"latest_firmware_info": None,
}
data.update(kwargs)
return data
@pytest.fixture
def mock_config_entry() -> MockConfigEntry:
"""Return the default mocked config entry."""
return MockConfigEntry(
domain="ezviz",
unique_id="test-username",
title="test-username",
data={
"session_id": "test-username",
"rf_session_id": "test-password",
"url": "apiieu.ezvizlife.com",
"type": ATTR_TYPE_CLOUD,
},
)
async def test_image_entity_does_not_expose_last_alarm_pic_attribute(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_ezviz_client: AsyncMock,
) -> None:
"""Test that image entity does not include last_alarm_pic as an attribute."""
# Mock coordinator data with last_alarm_pic
mock_coordinator_data = {
"C123456789": _mock_camera_data(
last_alarm_time="2023-01-01T12:00:00Z",
last_alarm_pic="https://example.com/image.jpg",
)
}
# Mock the load_cameras method to return our test data
mock_ezviz_client.load_cameras.return_value = mock_coordinator_data
await setup_integration(hass, mock_config_entry)
# Check that image entity was created without exposing the image URL.
state = hass.states.get("image.camera_1_last_motion_image")
assert state is not None
assert "last_alarm_pic" not in state.attributes
async def test_last_alarm_pic_sensor_not_created(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_ezviz_client: AsyncMock,
) -> None:
"""Test that last_alarm_pic sensor is not created."""
# Mock coordinator data with all sensor fields
mock_coordinator_data = {
"C123456789": _mock_camera_data(
battery_level=85,
alarm_sound_mod="soft",
last_alarm_time="2023-01-01T12:00:00Z",
last_alarm_pic="https://example.com/image.jpg",
supported_channels=1,
local_ip="192.168.1.100",
wan_ip="203.0.113.1",
PIR_Status="active",
last_alarm_type_code="motion",
last_alarm_type_name="Motion Detected",
)
}
# Mock the load_cameras method to return our test data
mock_ezviz_client.load_cameras.return_value = mock_coordinator_data
await setup_integration(hass, mock_config_entry)
# Check that last_alarm_pic sensor was NOT created
last_alarm_pic_state = hass.states.get("sensor.camera_1_last_alarm_pic")
assert last_alarm_pic_state is None
# But other sensors should be created
registry = er.async_get(hass)
battery_entity = registry.async_get("sensor.camera_1_battery")
assert battery_entity is not None
async def test_migrated_last_alarm_pic_sensor_is_removed(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_ezviz_client: AsyncMock,
entity_registry: er.EntityRegistry,
) -> None:
"""Test migrated last_alarm_pic sensor entities are removed on setup."""
mock_config_entry.add_to_hass(hass)
migrated_entry = entity_registry.async_get_or_create(
"sensor",
"ezviz",
"C123456789_Camera 1.last_alarm_pic",
config_entry=mock_config_entry,
)
mock_ezviz_client.load_cameras.return_value = {
"C123456789": _mock_camera_data(
last_alarm_time="2023-01-01T12:00:00Z",
last_alarm_pic="https://example.com/image.jpg",
)
}
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
assert entity_registry.async_get(migrated_entry.entity_id) is None
async def test_sensor_cleanup_ignores_entries_without_unique_id(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_ezviz_client: AsyncMock,
entity_registry: er.EntityRegistry,
) -> None:
"""Test migrated sensor cleanup ignores entries with no unique_id."""
mock_config_entry.add_to_hass(hass)
entity_without_unique_id = entity_registry.async_get_or_create(
"sensor",
"ezviz",
"C123456789_Camera 1.battery_level",
config_entry=mock_config_entry,
)
entity_registry.async_update_entity(
entity_without_unique_id.entity_id,
new_unique_id=None,
)
mock_ezviz_client.load_cameras.return_value = {
"C123456789": _mock_camera_data(
last_alarm_time="2023-01-01T12:00:00Z",
last_alarm_pic="https://example.com/image.jpg",
)
}
await hass.config_entries.async_setup(mock_config_entry.entry_id)
await hass.async_block_till_done()
assert entity_registry.async_get(entity_without_unique_id.entity_id) is not None
async def test_image_entity_created_without_alarm_pic(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_ezviz_client: AsyncMock,
) -> None:
"""Test that image entity is created even when last_alarm_pic is missing."""
# Mock coordinator data without last_alarm_pic
mock_coordinator_data = {
"C123456789": _mock_camera_data(
battery_level=85,
)
}
# Mock the load_cameras method to return our test data
mock_ezviz_client.load_cameras.return_value = mock_coordinator_data
await setup_integration(hass, mock_config_entry)
# Check that image entity was created without exposing the image URL.
state = hass.states.get("image.camera_1_last_motion_image")
assert state is not None
assert "last_alarm_pic" not in state.attributes
@pytest.mark.parametrize(
("last_alarm_time", "expected_state"),
[
pytest.param(
"2023-01-01T12:00:00Z",
"2023-01-01T12:00:00+00:00",
id="valid timestamp",
),
pytest.param(None, STATE_UNKNOWN, id="missing timestamp"),
pytest.param(123, STATE_UNKNOWN, id="non-string timestamp"),
],
)
async def test_image_entity_last_alarm_time_state(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_ezviz_client: AsyncMock,
last_alarm_time: object,
expected_state: str,
) -> None:
"""Test image entity state from last_alarm_time data."""
mock_ezviz_client.load_cameras.return_value = {
"C123456789": _mock_camera_data(
last_alarm_time=last_alarm_time,
last_alarm_pic="https://example.com/image.jpg",
)
}
await setup_integration(hass, mock_config_entry)
state = hass.states.get("image.camera_1_last_motion_image")
assert state is not None
assert state.state == expected_state
async def test_image_entity_updates_last_alarm_pic_on_refresh(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_ezviz_client: AsyncMock,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test the image entity updates its alarm picture on refresh."""
mock_ezviz_client.load_cameras.return_value = {
"C123456789": _mock_camera_data(
last_alarm_time="2023-01-01T12:00:00Z",
last_alarm_pic="https://example.com/image-1.jpg",
)
}
await setup_integration(hass, mock_config_entry)
with patch(
"homeassistant.components.ezviz.image.EzvizLastMotion._fetch_url",
new_callable=AsyncMock,
return_value=Mock(content=b"image-1"),
):
image_data = await image.async_get_image(
hass, "image.camera_1_last_motion_image"
)
assert image_data.content == b"image-1"
mock_ezviz_client.load_cameras.return_value = {
"C123456789": _mock_camera_data(
last_alarm_time="2023-01-01T12:05:00Z",
last_alarm_pic="https://example.com/image-2.jpg",
)
}
freezer.tick(SCAN_INTERVAL)
async_fire_time_changed(hass)
await hass.async_block_till_done()
with patch(
"homeassistant.components.ezviz.image.EzvizLastMotion._fetch_url",
new_callable=AsyncMock,
return_value=Mock(content=b"image-2"),
):
image_data = await image.async_get_image(
hass, "image.camera_1_last_motion_image"
)
assert image_data.content == b"image-2"
state = hass.states.get("image.camera_1_last_motion_image")
assert state is not None
assert "last_alarm_pic" not in state.attributes
async def test_image_entity_keeps_last_alarm_pic_when_refresh_omits_it(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
mock_ezviz_client: AsyncMock,
freezer: FrozenDateTimeFactory,
) -> None:
"""Test the image entity keeps the previous alarm picture when omitted."""
mock_ezviz_client.load_cameras.return_value = {
"C123456789": _mock_camera_data(
last_alarm_time="2023-01-01T12:00:00Z",
last_alarm_pic="https://example.com/image-1.jpg",
)
}
await setup_integration(hass, mock_config_entry)
with patch(
"homeassistant.components.ezviz.image.EzvizLastMotion._fetch_url",
new_callable=AsyncMock,
return_value=Mock(content=b"image-1"),
):
image_data = await image.async_get_image(
hass, "image.camera_1_last_motion_image"
)
assert image_data.content == b"image-1"
mock_ezviz_client.load_cameras.return_value = {
"C123456789": _mock_camera_data(
last_alarm_time="2023-01-01T12:05:00Z",
)
}
freezer.tick(SCAN_INTERVAL)
async_fire_time_changed(hass)
await hass.async_block_till_done()
with patch(
"homeassistant.components.ezviz.image.EzvizLastMotion._fetch_url",
new_callable=AsyncMock,
) as mock_fetch_url:
image_data = await image.async_get_image(
hass, "image.camera_1_last_motion_image"
)
assert image_data.content == b"image-1"
mock_fetch_url.assert_not_called()
state = hass.states.get("image.camera_1_last_motion_image")
assert state is not None
assert "last_alarm_pic" not in state.attributes