Use non-autospec mock for Reolink's init tests (#146991)

This commit is contained in:
Abílio Costa 2025-06-18 17:58:50 +01:00 committed by GitHub
parent a29d5fb56c
commit 9adf493acd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 125 additions and 128 deletions

View File

@ -65,11 +65,14 @@ def mock_setup_entry() -> Generator[AsyncMock]:
def _init_host_mock(host_mock: MagicMock) -> None: def _init_host_mock(host_mock: MagicMock) -> None:
host_mock.get_host_data = AsyncMock(return_value=None) host_mock.get_host_data = AsyncMock(return_value=None)
host_mock.get_states = AsyncMock(return_value=None) host_mock.get_states = AsyncMock(return_value=None)
host_mock.get_state = AsyncMock()
host_mock.check_new_firmware = AsyncMock(return_value=False) host_mock.check_new_firmware = AsyncMock(return_value=False)
host_mock.subscribe = AsyncMock()
host_mock.unsubscribe = AsyncMock(return_value=True) host_mock.unsubscribe = AsyncMock(return_value=True)
host_mock.logout = AsyncMock(return_value=True) host_mock.logout = AsyncMock(return_value=True)
host_mock.reboot = AsyncMock() host_mock.reboot = AsyncMock()
host_mock.set_ptz_command = AsyncMock() host_mock.set_ptz_command = AsyncMock()
host_mock.get_motion_state_all_ch = AsyncMock(return_value=False)
host_mock.is_nvr = True host_mock.is_nvr = True
host_mock.is_hub = False host_mock.is_hub = False
host_mock.mac_address = TEST_MAC host_mock.mac_address = TEST_MAC
@ -138,8 +141,10 @@ def _init_host_mock(host_mock: MagicMock) -> None:
# Disable tcp push by default for tests # Disable tcp push by default for tests
host_mock.baichuan.port = TEST_BC_PORT host_mock.baichuan.port = TEST_BC_PORT
host_mock.baichuan.events_active = False host_mock.baichuan.events_active = False
host_mock.baichuan.subscribe_events = AsyncMock()
host_mock.baichuan.unsubscribe_events = AsyncMock() host_mock.baichuan.unsubscribe_events = AsyncMock()
host_mock.baichuan.check_subscribe_events = AsyncMock() host_mock.baichuan.check_subscribe_events = AsyncMock()
host_mock.baichuan.get_privacy_mode = AsyncMock()
host_mock.baichuan.mac_address.return_value = TEST_MAC_CAM host_mock.baichuan.mac_address.return_value = TEST_MAC_CAM
host_mock.baichuan.privacy_mode.return_value = False host_mock.baichuan.privacy_mode.return_value = False
host_mock.baichuan.day_night_state.return_value = "day" host_mock.baichuan.day_night_state.return_value = "day"
@ -242,3 +247,26 @@ def test_chime(reolink_connect: MagicMock) -> None:
reolink_connect.chime_list = [TEST_CHIME] reolink_connect.chime_list = [TEST_CHIME]
reolink_connect.chime.return_value = TEST_CHIME reolink_connect.chime.return_value = TEST_CHIME
return TEST_CHIME return TEST_CHIME
@pytest.fixture
def reolink_chime(reolink_host: MagicMock) -> None:
"""Mock a reolink chime."""
TEST_CHIME = Chime(
host=reolink_host,
dev_id=12345678,
channel=0,
)
TEST_CHIME.name = "Test chime"
TEST_CHIME.volume = 3
TEST_CHIME.connect_state = 2
TEST_CHIME.led_state = True
TEST_CHIME.event_info = {
"md": {"switch": 0, "musicId": 0},
"people": {"switch": 0, "musicId": 1},
"visitor": {"switch": 1, "musicId": 2},
}
reolink_host.chime_list = [TEST_CHIME]
reolink_host.chime.return_value = TEST_CHIME
return TEST_CHIME

View File

@ -115,6 +115,7 @@ async def test_webhook_callback(
assert hass.states.get(entity_id).state == STATE_OFF assert hass.states.get(entity_id).state == STATE_OFF
# test webhook callback success all channels # test webhook callback success all channels
reolink_connect.get_motion_state_all_ch.return_value = True
reolink_connect.motion_detected.return_value = True reolink_connect.motion_detected.return_value = True
reolink_connect.ONVIF_event_callback.return_value = None reolink_connect.ONVIF_event_callback.return_value = None
await client.post(f"/api/webhook/{webhook_id}") await client.post(f"/api/webhook/{webhook_id}")

View File

@ -69,7 +69,7 @@ from .conftest import (
from tests.common import MockConfigEntry, async_fire_time_changed from tests.common import MockConfigEntry, async_fire_time_changed
from tests.typing import WebSocketGenerator from tests.typing import WebSocketGenerator
pytestmark = pytest.mark.usefixtures("reolink_connect", "reolink_platforms") pytestmark = pytest.mark.usefixtures("reolink_host", "reolink_platforms")
CHIME_MODEL = "Reolink Chime" CHIME_MODEL = "Reolink Chime"
@ -116,15 +116,14 @@ async def test_wait(*args, **key_args) -> None:
) )
async def test_failures_parametrized( async def test_failures_parametrized(
hass: HomeAssistant, hass: HomeAssistant,
reolink_connect: MagicMock, reolink_host: MagicMock,
config_entry: MockConfigEntry, config_entry: MockConfigEntry,
attr: str, attr: str,
value: Any, value: Any,
expected: ConfigEntryState, expected: ConfigEntryState,
) -> None: ) -> None:
"""Test outcomes when changing errors.""" """Test outcomes when changing errors."""
original = getattr(reolink_connect, attr) setattr(reolink_host, attr, value)
setattr(reolink_connect, attr, value)
assert await hass.config_entries.async_setup(config_entry.entry_id) is ( assert await hass.config_entries.async_setup(config_entry.entry_id) is (
expected is ConfigEntryState.LOADED expected is ConfigEntryState.LOADED
) )
@ -132,17 +131,15 @@ async def test_failures_parametrized(
assert config_entry.state == expected assert config_entry.state == expected
setattr(reolink_connect, attr, original)
async def test_firmware_error_twice( async def test_firmware_error_twice(
hass: HomeAssistant, hass: HomeAssistant,
freezer: FrozenDateTimeFactory, freezer: FrozenDateTimeFactory,
reolink_connect: MagicMock, reolink_host: MagicMock,
config_entry: MockConfigEntry, config_entry: MockConfigEntry,
) -> None: ) -> None:
"""Test when the firmware update fails 2 times.""" """Test when the firmware update fails 2 times."""
reolink_connect.check_new_firmware.side_effect = ReolinkError("Test error") reolink_host.check_new_firmware.side_effect = ReolinkError("Test error")
with patch("homeassistant.components.reolink.PLATFORMS", [Platform.UPDATE]): with patch("homeassistant.components.reolink.PLATFORMS", [Platform.UPDATE]):
assert await hass.config_entries.async_setup(config_entry.entry_id) assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
@ -158,13 +155,11 @@ async def test_firmware_error_twice(
assert hass.states.get(entity_id).state == STATE_UNAVAILABLE assert hass.states.get(entity_id).state == STATE_UNAVAILABLE
reolink_connect.check_new_firmware.reset_mock(side_effect=True)
async def test_credential_error_three( async def test_credential_error_three(
hass: HomeAssistant, hass: HomeAssistant,
freezer: FrozenDateTimeFactory, freezer: FrozenDateTimeFactory,
reolink_connect: MagicMock, reolink_host: MagicMock,
config_entry: MockConfigEntry, config_entry: MockConfigEntry,
issue_registry: ir.IssueRegistry, issue_registry: ir.IssueRegistry,
) -> None: ) -> None:
@ -174,7 +169,7 @@ async def test_credential_error_three(
await hass.async_block_till_done() await hass.async_block_till_done()
assert config_entry.state is ConfigEntryState.LOADED assert config_entry.state is ConfigEntryState.LOADED
reolink_connect.get_states.side_effect = CredentialsInvalidError("Test error") reolink_host.get_states.side_effect = CredentialsInvalidError("Test error")
issue_id = f"config_entry_reauth_{DOMAIN}_{config_entry.entry_id}" issue_id = f"config_entry_reauth_{DOMAIN}_{config_entry.entry_id}"
for _ in range(NUM_CRED_ERRORS): for _ in range(NUM_CRED_ERRORS):
@ -185,31 +180,26 @@ async def test_credential_error_three(
assert (HOMEASSISTANT_DOMAIN, issue_id) in issue_registry.issues assert (HOMEASSISTANT_DOMAIN, issue_id) in issue_registry.issues
reolink_connect.get_states.reset_mock(side_effect=True)
async def test_entry_reloading( async def test_entry_reloading(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: MockConfigEntry, config_entry: MockConfigEntry,
reolink_connect: MagicMock, reolink_host: MagicMock,
) -> None: ) -> None:
"""Test the entry is reloaded correctly when settings change.""" """Test the entry is reloaded correctly when settings change."""
reolink_connect.is_nvr = False reolink_host.is_nvr = False
reolink_connect.logout.reset_mock()
assert await hass.config_entries.async_setup(config_entry.entry_id) assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
assert reolink_connect.logout.call_count == 0 assert reolink_host.logout.call_count == 0
assert config_entry.title == "test_reolink_name" assert config_entry.title == "test_reolink_name"
hass.config_entries.async_update_entry(config_entry, title="New Name") hass.config_entries.async_update_entry(config_entry, title="New Name")
await hass.async_block_till_done() await hass.async_block_till_done()
assert reolink_connect.logout.call_count == 1 assert reolink_host.logout.call_count == 1
assert config_entry.title == "New Name" assert config_entry.title == "New Name"
reolink_connect.is_nvr = True
@pytest.mark.parametrize( @pytest.mark.parametrize(
("attr", "value", "expected_models"), ("attr", "value", "expected_models"),
@ -241,7 +231,7 @@ async def test_removing_disconnected_cams(
hass: HomeAssistant, hass: HomeAssistant,
hass_ws_client: WebSocketGenerator, hass_ws_client: WebSocketGenerator,
config_entry: MockConfigEntry, config_entry: MockConfigEntry,
reolink_connect: MagicMock, reolink_host: MagicMock,
device_registry: dr.DeviceRegistry, device_registry: dr.DeviceRegistry,
entity_registry: er.EntityRegistry, entity_registry: er.EntityRegistry,
attr: str | None, attr: str | None,
@ -249,7 +239,7 @@ async def test_removing_disconnected_cams(
expected_models: list[str], expected_models: list[str],
) -> None: ) -> None:
"""Test device and entity registry are cleaned up when camera is removed.""" """Test device and entity registry are cleaned up when camera is removed."""
reolink_connect.channels = [0] reolink_host.channels = [0]
assert await async_setup_component(hass, "config", {}) assert await async_setup_component(hass, "config", {})
client = await hass_ws_client(hass) client = await hass_ws_client(hass)
# setup CH 0 and NVR switch entities/device # setup CH 0 and NVR switch entities/device
@ -265,8 +255,7 @@ async def test_removing_disconnected_cams(
# Try to remove the device after 'disconnecting' a camera. # Try to remove the device after 'disconnecting' a camera.
if attr is not None: if attr is not None:
original = getattr(reolink_connect, attr) setattr(reolink_host, attr, value)
setattr(reolink_connect, attr, value)
expected_success = TEST_CAM_MODEL not in expected_models expected_success = TEST_CAM_MODEL not in expected_models
for device in device_entries: for device in device_entries:
if device.model == TEST_CAM_MODEL: if device.model == TEST_CAM_MODEL:
@ -279,9 +268,6 @@ async def test_removing_disconnected_cams(
device_models = [device.model for device in device_entries] device_models = [device.model for device in device_entries]
assert sorted(device_models) == sorted(expected_models) assert sorted(device_models) == sorted(expected_models)
if attr is not None:
setattr(reolink_connect, attr, original)
@pytest.mark.parametrize( @pytest.mark.parametrize(
("attr", "value", "expected_models"), ("attr", "value", "expected_models"),
@ -307,8 +293,8 @@ async def test_removing_chime(
hass: HomeAssistant, hass: HomeAssistant,
hass_ws_client: WebSocketGenerator, hass_ws_client: WebSocketGenerator,
config_entry: MockConfigEntry, config_entry: MockConfigEntry,
reolink_connect: MagicMock, reolink_host: MagicMock,
test_chime: Chime, reolink_chime: Chime,
device_registry: dr.DeviceRegistry, device_registry: dr.DeviceRegistry,
entity_registry: er.EntityRegistry, entity_registry: er.EntityRegistry,
attr: str | None, attr: str | None,
@ -316,7 +302,7 @@ async def test_removing_chime(
expected_models: list[str], expected_models: list[str],
) -> None: ) -> None:
"""Test removing a chime.""" """Test removing a chime."""
reolink_connect.channels = [0] reolink_host.channels = [0]
assert await async_setup_component(hass, "config", {}) assert await async_setup_component(hass, "config", {})
client = await hass_ws_client(hass) client = await hass_ws_client(hass)
# setup CH 0 and NVR switch entities/device # setup CH 0 and NVR switch entities/device
@ -336,11 +322,11 @@ async def test_removing_chime(
async def test_remove_chime(*args, **key_args): async def test_remove_chime(*args, **key_args):
"""Remove chime.""" """Remove chime."""
test_chime.connect_state = -1 reolink_chime.connect_state = -1
test_chime.remove = test_remove_chime reolink_chime.remove = test_remove_chime
elif attr is not None: elif attr is not None:
setattr(test_chime, attr, value) setattr(reolink_chime, attr, value)
# Try to remove the device after 'disconnecting' a chime. # Try to remove the device after 'disconnecting' a chime.
expected_success = CHIME_MODEL not in expected_models expected_success = CHIME_MODEL not in expected_models
@ -444,7 +430,7 @@ async def test_removing_chime(
async def test_migrate_entity_ids( async def test_migrate_entity_ids(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: MockConfigEntry, config_entry: MockConfigEntry,
reolink_connect: MagicMock, reolink_host: MagicMock,
entity_registry: er.EntityRegistry, entity_registry: er.EntityRegistry,
device_registry: dr.DeviceRegistry, device_registry: dr.DeviceRegistry,
original_id: str, original_id: str,
@ -464,8 +450,8 @@ async def test_migrate_entity_ids(
return support_ch_uid return support_ch_uid
return True return True
reolink_connect.channels = [0] reolink_host.channels = [0]
reolink_connect.supported = mock_supported reolink_host.supported = mock_supported
dev_entry = device_registry.async_get_or_create( dev_entry = device_registry.async_get_or_create(
identifiers={(DOMAIN, original_dev_id)}, identifiers={(DOMAIN, original_dev_id)},
@ -513,7 +499,7 @@ async def test_migrate_entity_ids(
async def test_migrate_with_already_existing_device( async def test_migrate_with_already_existing_device(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: MockConfigEntry, config_entry: MockConfigEntry,
reolink_connect: MagicMock, reolink_host: MagicMock,
entity_registry: er.EntityRegistry, entity_registry: er.EntityRegistry,
device_registry: dr.DeviceRegistry, device_registry: dr.DeviceRegistry,
) -> None: ) -> None:
@ -529,8 +515,8 @@ async def test_migrate_with_already_existing_device(
return True return True
return True return True
reolink_connect.channels = [0] reolink_host.channels = [0]
reolink_connect.supported = mock_supported reolink_host.supported = mock_supported
device_registry.async_get_or_create( device_registry.async_get_or_create(
identifiers={(DOMAIN, new_dev_id)}, identifiers={(DOMAIN, new_dev_id)},
@ -562,7 +548,7 @@ async def test_migrate_with_already_existing_device(
async def test_migrate_with_already_existing_entity( async def test_migrate_with_already_existing_entity(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: MockConfigEntry, config_entry: MockConfigEntry,
reolink_connect: MagicMock, reolink_host: MagicMock,
entity_registry: er.EntityRegistry, entity_registry: er.EntityRegistry,
device_registry: dr.DeviceRegistry, device_registry: dr.DeviceRegistry,
) -> None: ) -> None:
@ -579,8 +565,8 @@ async def test_migrate_with_already_existing_entity(
return True return True
return True return True
reolink_connect.channels = [0] reolink_host.channels = [0]
reolink_connect.supported = mock_supported reolink_host.supported = mock_supported
dev_entry = device_registry.async_get_or_create( dev_entry = device_registry.async_get_or_create(
identifiers={(DOMAIN, dev_id)}, identifiers={(DOMAIN, dev_id)},
@ -623,13 +609,13 @@ async def test_migrate_with_already_existing_entity(
async def test_cleanup_mac_connection( async def test_cleanup_mac_connection(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: MockConfigEntry, config_entry: MockConfigEntry,
reolink_connect: MagicMock, reolink_host: MagicMock,
entity_registry: er.EntityRegistry, entity_registry: er.EntityRegistry,
device_registry: dr.DeviceRegistry, device_registry: dr.DeviceRegistry,
) -> None: ) -> None:
"""Test cleanup of the MAC of a IPC which was set to the MAC of the host.""" """Test cleanup of the MAC of a IPC which was set to the MAC of the host."""
reolink_connect.channels = [0] reolink_host.channels = [0]
reolink_connect.baichuan.mac_address.return_value = None reolink_host.baichuan.mac_address.return_value = None
entity_id = f"{TEST_UID}_{TEST_UID_CAM}_record_audio" entity_id = f"{TEST_UID}_{TEST_UID_CAM}_record_audio"
dev_id = f"{TEST_UID}_{TEST_UID_CAM}" dev_id = f"{TEST_UID}_{TEST_UID_CAM}"
domain = Platform.SWITCH domain = Platform.SWITCH
@ -666,19 +652,17 @@ async def test_cleanup_mac_connection(
assert device assert device
assert device.connections == set() assert device.connections == set()
reolink_connect.baichuan.mac_address.return_value = TEST_MAC_CAM
async def test_cleanup_combined_with_NVR( async def test_cleanup_combined_with_NVR(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: MockConfigEntry, config_entry: MockConfigEntry,
reolink_connect: MagicMock, reolink_host: MagicMock,
entity_registry: er.EntityRegistry, entity_registry: er.EntityRegistry,
device_registry: dr.DeviceRegistry, device_registry: dr.DeviceRegistry,
) -> None: ) -> None:
"""Test cleanup of the device registry if IPC camera device was combined with the NVR device.""" """Test cleanup of the device registry if IPC camera device was combined with the NVR device."""
reolink_connect.channels = [0] reolink_host.channels = [0]
reolink_connect.baichuan.mac_address.return_value = None reolink_host.baichuan.mac_address.return_value = None
entity_id = f"{TEST_UID}_{TEST_UID_CAM}_record_audio" entity_id = f"{TEST_UID}_{TEST_UID_CAM}_record_audio"
dev_id = f"{TEST_UID}_{TEST_UID_CAM}" dev_id = f"{TEST_UID}_{TEST_UID_CAM}"
domain = Platform.SWITCH domain = Platform.SWITCH
@ -726,18 +710,16 @@ async def test_cleanup_combined_with_NVR(
("OTHER_INTEGRATION", "SOME_ID"), ("OTHER_INTEGRATION", "SOME_ID"),
} }
reolink_connect.baichuan.mac_address.return_value = TEST_MAC_CAM
async def test_cleanup_hub_and_direct_connection( async def test_cleanup_hub_and_direct_connection(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: MockConfigEntry, config_entry: MockConfigEntry,
reolink_connect: MagicMock, reolink_host: MagicMock,
entity_registry: er.EntityRegistry, entity_registry: er.EntityRegistry,
device_registry: dr.DeviceRegistry, device_registry: dr.DeviceRegistry,
) -> None: ) -> None:
"""Test cleanup of the device registry if IPC camera device was connected directly and through the hub/NVR.""" """Test cleanup of the device registry if IPC camera device was connected directly and through the hub/NVR."""
reolink_connect.channels = [0] reolink_host.channels = [0]
entity_id = f"{TEST_UID}_{TEST_UID_CAM}_record_audio" entity_id = f"{TEST_UID}_{TEST_UID_CAM}_record_audio"
dev_id = f"{TEST_UID}_{TEST_UID_CAM}" dev_id = f"{TEST_UID}_{TEST_UID_CAM}"
domain = Platform.SWITCH domain = Platform.SWITCH
@ -801,11 +783,11 @@ async def test_no_repair_issue(
async def test_https_repair_issue( async def test_https_repair_issue(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: MockConfigEntry, config_entry: MockConfigEntry,
reolink_connect: MagicMock, reolink_host: MagicMock,
issue_registry: ir.IssueRegistry, issue_registry: ir.IssueRegistry,
) -> None: ) -> None:
"""Test repairs issue is raised when https local url is used.""" """Test repairs issue is raised when https local url is used."""
reolink_connect.get_states = test_wait reolink_host.get_states = test_wait
await async_process_ha_core_config( await async_process_ha_core_config(
hass, {"country": "GB", "internal_url": "https://test_homeassistant_address"} hass, {"country": "GB", "internal_url": "https://test_homeassistant_address"}
) )
@ -828,11 +810,11 @@ async def test_https_repair_issue(
async def test_ssl_repair_issue( async def test_ssl_repair_issue(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: MockConfigEntry, config_entry: MockConfigEntry,
reolink_connect: MagicMock, reolink_host: MagicMock,
issue_registry: ir.IssueRegistry, issue_registry: ir.IssueRegistry,
) -> None: ) -> None:
"""Test repairs issue is raised when global ssl certificate is used.""" """Test repairs issue is raised when global ssl certificate is used."""
reolink_connect.get_states = test_wait reolink_host.get_states = test_wait
assert await async_setup_component(hass, "webhook", {}) assert await async_setup_component(hass, "webhook", {})
hass.config.api.use_ssl = True hass.config.api.use_ssl = True
@ -859,32 +841,30 @@ async def test_ssl_repair_issue(
async def test_port_repair_issue( async def test_port_repair_issue(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: MockConfigEntry, config_entry: MockConfigEntry,
reolink_connect: MagicMock, reolink_host: MagicMock,
protocol: str, protocol: str,
issue_registry: ir.IssueRegistry, issue_registry: ir.IssueRegistry,
) -> None: ) -> None:
"""Test repairs issue is raised when auto enable of ports fails.""" """Test repairs issue is raised when auto enable of ports fails."""
reolink_connect.set_net_port.side_effect = ReolinkError("Test error") reolink_host.set_net_port.side_effect = ReolinkError("Test error")
reolink_connect.onvif_enabled = False reolink_host.onvif_enabled = False
reolink_connect.rtsp_enabled = False reolink_host.rtsp_enabled = False
reolink_connect.rtmp_enabled = False reolink_host.rtmp_enabled = False
reolink_connect.protocol = protocol reolink_host.protocol = protocol
assert await hass.config_entries.async_setup(config_entry.entry_id) assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
assert (DOMAIN, "enable_port") in issue_registry.issues assert (DOMAIN, "enable_port") in issue_registry.issues
reolink_connect.set_net_port.reset_mock(side_effect=True)
async def test_webhook_repair_issue( async def test_webhook_repair_issue(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: MockConfigEntry, config_entry: MockConfigEntry,
reolink_connect: MagicMock, reolink_host: MagicMock,
issue_registry: ir.IssueRegistry, issue_registry: ir.IssueRegistry,
) -> None: ) -> None:
"""Test repairs issue is raised when the webhook url is unreachable.""" """Test repairs issue is raised when the webhook url is unreachable."""
reolink_connect.get_states = test_wait reolink_host.get_states = test_wait
with ( with (
patch("homeassistant.components.reolink.host.FIRST_ONVIF_TIMEOUT", new=0), patch("homeassistant.components.reolink.host.FIRST_ONVIF_TIMEOUT", new=0),
patch( patch(
@ -903,25 +883,24 @@ async def test_webhook_repair_issue(
async def test_firmware_repair_issue( async def test_firmware_repair_issue(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: MockConfigEntry, config_entry: MockConfigEntry,
reolink_connect: MagicMock, reolink_host: MagicMock,
issue_registry: ir.IssueRegistry, issue_registry: ir.IssueRegistry,
) -> None: ) -> None:
"""Test firmware issue is raised when too old firmware is used.""" """Test firmware issue is raised when too old firmware is used."""
reolink_connect.camera_sw_version_update_required.return_value = True reolink_host.camera_sw_version_update_required.return_value = True
assert await hass.config_entries.async_setup(config_entry.entry_id) assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
assert (DOMAIN, "firmware_update_host") in issue_registry.issues assert (DOMAIN, "firmware_update_host") in issue_registry.issues
reolink_connect.camera_sw_version_update_required.return_value = False
async def test_password_too_long_repair_issue( async def test_password_too_long_repair_issue(
hass: HomeAssistant, hass: HomeAssistant,
reolink_connect: MagicMock, reolink_host: MagicMock,
issue_registry: ir.IssueRegistry, issue_registry: ir.IssueRegistry,
) -> None: ) -> None:
"""Test password too long issue is raised.""" """Test password too long issue is raised."""
reolink_connect.valid_password.return_value = False reolink_host.valid_password.return_value = False
config_entry = MockConfigEntry( config_entry = MockConfigEntry(
domain=DOMAIN, domain=DOMAIN,
unique_id=format_mac(TEST_MAC), unique_id=format_mac(TEST_MAC),
@ -946,13 +925,12 @@ async def test_password_too_long_repair_issue(
DOMAIN, DOMAIN,
f"password_too_long_{config_entry.entry_id}", f"password_too_long_{config_entry.entry_id}",
) in issue_registry.issues ) in issue_registry.issues
reolink_connect.valid_password.return_value = True
async def test_new_device_discovered( async def test_new_device_discovered(
hass: HomeAssistant, hass: HomeAssistant,
freezer: FrozenDateTimeFactory, freezer: FrozenDateTimeFactory,
reolink_connect: MagicMock, reolink_host: MagicMock,
config_entry: MockConfigEntry, config_entry: MockConfigEntry,
) -> None: ) -> None:
"""Test the entry is reloaded when a new camera or chime is detected.""" """Test the entry is reloaded when a new camera or chime is detected."""
@ -960,26 +938,24 @@ async def test_new_device_discovered(
assert await hass.config_entries.async_setup(config_entry.entry_id) assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
reolink_connect.logout.reset_mock() assert reolink_host.logout.call_count == 0
reolink_host.new_devices = True
assert reolink_connect.logout.call_count == 0
reolink_connect.new_devices = True
freezer.tick(DEVICE_UPDATE_INTERVAL) freezer.tick(DEVICE_UPDATE_INTERVAL)
async_fire_time_changed(hass) async_fire_time_changed(hass)
await hass.async_block_till_done() await hass.async_block_till_done()
assert reolink_connect.logout.call_count == 1 assert reolink_host.logout.call_count == 1
async def test_port_changed( async def test_port_changed(
hass: HomeAssistant, hass: HomeAssistant,
reolink_connect: MagicMock, reolink_host: MagicMock,
config_entry: MockConfigEntry, config_entry: MockConfigEntry,
) -> None: ) -> None:
"""Test config_entry port update when it has changed during initial login.""" """Test config_entry port update when it has changed during initial login."""
assert config_entry.data[CONF_PORT] == TEST_PORT assert config_entry.data[CONF_PORT] == TEST_PORT
reolink_connect.port = 4567 reolink_host.port = 4567
assert await hass.config_entries.async_setup(config_entry.entry_id) assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
@ -989,12 +965,12 @@ async def test_port_changed(
async def test_baichuan_port_changed( async def test_baichuan_port_changed(
hass: HomeAssistant, hass: HomeAssistant,
reolink_connect: MagicMock, reolink_host: MagicMock,
config_entry: MockConfigEntry, config_entry: MockConfigEntry,
) -> None: ) -> None:
"""Test config_entry baichuan port update when it has changed during initial login.""" """Test config_entry baichuan port update when it has changed during initial login."""
assert config_entry.data[CONF_BC_PORT] == TEST_BC_PORT assert config_entry.data[CONF_BC_PORT] == TEST_BC_PORT
reolink_connect.baichuan.port = 8901 reolink_host.baichuan.port = 8901
assert await hass.config_entries.async_setup(config_entry.entry_id) assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
@ -1005,14 +981,12 @@ async def test_baichuan_port_changed(
async def test_privacy_mode_on( async def test_privacy_mode_on(
hass: HomeAssistant, hass: HomeAssistant,
freezer: FrozenDateTimeFactory, freezer: FrozenDateTimeFactory,
reolink_connect: MagicMock, reolink_host: MagicMock,
config_entry: MockConfigEntry, config_entry: MockConfigEntry,
) -> None: ) -> None:
"""Test successful setup even when privacy mode is turned on.""" """Test successful setup even when privacy mode is turned on."""
reolink_connect.baichuan.privacy_mode.return_value = True reolink_host.baichuan.privacy_mode.return_value = True
reolink_connect.get_states = AsyncMock( reolink_host.get_states = AsyncMock(side_effect=LoginPrivacyModeError("Test error"))
side_effect=LoginPrivacyModeError("Test error")
)
with patch("homeassistant.components.reolink.PLATFORMS", [Platform.SWITCH]): with patch("homeassistant.components.reolink.PLATFORMS", [Platform.SWITCH]):
assert await hass.config_entries.async_setup(config_entry.entry_id) assert await hass.config_entries.async_setup(config_entry.entry_id)
@ -1020,40 +994,36 @@ async def test_privacy_mode_on(
assert config_entry.state == ConfigEntryState.LOADED assert config_entry.state == ConfigEntryState.LOADED
reolink_connect.baichuan.privacy_mode.return_value = False
async def test_LoginPrivacyModeError( async def test_LoginPrivacyModeError(
hass: HomeAssistant, hass: HomeAssistant,
freezer: FrozenDateTimeFactory, freezer: FrozenDateTimeFactory,
reolink_connect: MagicMock, reolink_host: MagicMock,
config_entry: MockConfigEntry, config_entry: MockConfigEntry,
) -> None: ) -> None:
"""Test normal update when get_states returns a LoginPrivacyModeError.""" """Test normal update when get_states returns a LoginPrivacyModeError."""
reolink_connect.baichuan.privacy_mode.return_value = False reolink_host.baichuan.privacy_mode.return_value = False
reolink_connect.get_states = AsyncMock( reolink_host.get_states = AsyncMock(side_effect=LoginPrivacyModeError("Test error"))
side_effect=LoginPrivacyModeError("Test error")
)
with patch("homeassistant.components.reolink.PLATFORMS", [Platform.SWITCH]): with patch("homeassistant.components.reolink.PLATFORMS", [Platform.SWITCH]):
assert await hass.config_entries.async_setup(config_entry.entry_id) assert await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done() await hass.async_block_till_done()
reolink_connect.baichuan.check_subscribe_events.reset_mock() reolink_host.baichuan.check_subscribe_events.reset_mock()
assert reolink_connect.baichuan.check_subscribe_events.call_count == 0 assert reolink_host.baichuan.check_subscribe_events.call_count == 0
freezer.tick(DEVICE_UPDATE_INTERVAL) freezer.tick(DEVICE_UPDATE_INTERVAL)
async_fire_time_changed(hass) async_fire_time_changed(hass)
await hass.async_block_till_done() await hass.async_block_till_done()
assert reolink_connect.baichuan.check_subscribe_events.call_count >= 1 assert reolink_host.baichuan.check_subscribe_events.call_count >= 1
async def test_privacy_mode_change_callback( async def test_privacy_mode_change_callback(
hass: HomeAssistant, hass: HomeAssistant,
freezer: FrozenDateTimeFactory, freezer: FrozenDateTimeFactory,
config_entry: MockConfigEntry, config_entry: MockConfigEntry,
reolink_connect: MagicMock, reolink_host: MagicMock,
) -> None: ) -> None:
"""Test privacy mode changed callback.""" """Test privacy mode changed callback."""
@ -1068,13 +1038,12 @@ async def test_privacy_mode_change_callback(
callback_mock = callback_mock_class() callback_mock = callback_mock_class()
reolink_connect.model = TEST_HOST_MODEL reolink_host.model = TEST_HOST_MODEL
reolink_connect.baichuan.events_active = True reolink_host.baichuan.events_active = True
reolink_connect.baichuan.subscribe_events.reset_mock(side_effect=True) reolink_host.baichuan.subscribe_events.reset_mock(side_effect=True)
reolink_connect.baichuan.register_callback = callback_mock.register_callback reolink_host.baichuan.register_callback = callback_mock.register_callback
reolink_connect.baichuan.privacy_mode.return_value = True reolink_host.baichuan.privacy_mode.return_value = True
reolink_connect.audio_record.return_value = True reolink_host.audio_record.return_value = True
reolink_connect.get_states = AsyncMock()
with patch("homeassistant.components.reolink.PLATFORMS", [Platform.SWITCH]): with patch("homeassistant.components.reolink.PLATFORMS", [Platform.SWITCH]):
assert await hass.config_entries.async_setup(config_entry.entry_id) assert await hass.config_entries.async_setup(config_entry.entry_id)
@ -1085,29 +1054,29 @@ async def test_privacy_mode_change_callback(
assert hass.states.get(entity_id).state == STATE_UNAVAILABLE assert hass.states.get(entity_id).state == STATE_UNAVAILABLE
# simulate a TCP push callback signaling a privacy mode change # simulate a TCP push callback signaling a privacy mode change
reolink_connect.baichuan.privacy_mode.return_value = False reolink_host.baichuan.privacy_mode.return_value = False
assert callback_mock.callback_func is not None assert callback_mock.callback_func is not None
callback_mock.callback_func() callback_mock.callback_func()
# check that a coordinator update was scheduled. # check that a coordinator update was scheduled.
reolink_connect.get_states.reset_mock() reolink_host.get_states.reset_mock()
assert reolink_connect.get_states.call_count == 0 assert reolink_host.get_states.call_count == 0
freezer.tick(5) freezer.tick(5)
async_fire_time_changed(hass) async_fire_time_changed(hass)
await hass.async_block_till_done() await hass.async_block_till_done()
assert reolink_connect.get_states.call_count >= 1 assert reolink_host.get_states.call_count >= 1
assert hass.states.get(entity_id).state == STATE_ON assert hass.states.get(entity_id).state == STATE_ON
# test cleanup during unloading, first reset to privacy mode ON # test cleanup during unloading, first reset to privacy mode ON
reolink_connect.baichuan.privacy_mode.return_value = True reolink_host.baichuan.privacy_mode.return_value = True
callback_mock.callback_func() callback_mock.callback_func()
freezer.tick(5) freezer.tick(5)
async_fire_time_changed(hass) async_fire_time_changed(hass)
await hass.async_block_till_done() await hass.async_block_till_done()
# now fire the callback again, but unload before refresh took place # now fire the callback again, but unload before refresh took place
reolink_connect.baichuan.privacy_mode.return_value = False reolink_host.baichuan.privacy_mode.return_value = False
callback_mock.callback_func() callback_mock.callback_func()
await hass.async_block_till_done() await hass.async_block_till_done()
@ -1120,7 +1089,7 @@ async def test_camera_wake_callback(
hass: HomeAssistant, hass: HomeAssistant,
freezer: FrozenDateTimeFactory, freezer: FrozenDateTimeFactory,
config_entry: MockConfigEntry, config_entry: MockConfigEntry,
reolink_connect: MagicMock, reolink_host: MagicMock,
) -> None: ) -> None:
"""Test camera wake callback.""" """Test camera wake callback."""
@ -1135,13 +1104,12 @@ async def test_camera_wake_callback(
callback_mock = callback_mock_class() callback_mock = callback_mock_class()
reolink_connect.model = TEST_HOST_MODEL reolink_host.model = TEST_HOST_MODEL
reolink_connect.baichuan.events_active = True reolink_host.baichuan.events_active = True
reolink_connect.baichuan.subscribe_events.reset_mock(side_effect=True) reolink_host.baichuan.subscribe_events.reset_mock(side_effect=True)
reolink_connect.baichuan.register_callback = callback_mock.register_callback reolink_host.baichuan.register_callback = callback_mock.register_callback
reolink_connect.sleeping.return_value = True reolink_host.sleeping.return_value = True
reolink_connect.audio_record.return_value = True reolink_host.audio_record.return_value = True
reolink_connect.get_states = AsyncMock()
with ( with (
patch("homeassistant.components.reolink.PLATFORMS", [Platform.SWITCH]), patch("homeassistant.components.reolink.PLATFORMS", [Platform.SWITCH]),
@ -1157,12 +1125,12 @@ async def test_camera_wake_callback(
entity_id = f"{Platform.SWITCH}.{TEST_NVR_NAME}_record_audio" entity_id = f"{Platform.SWITCH}.{TEST_NVR_NAME}_record_audio"
assert hass.states.get(entity_id).state == STATE_ON assert hass.states.get(entity_id).state == STATE_ON
reolink_connect.sleeping.return_value = False reolink_host.sleeping.return_value = False
reolink_connect.get_states.reset_mock() reolink_host.get_states.reset_mock()
assert reolink_connect.get_states.call_count == 0 assert reolink_host.get_states.call_count == 0
# simulate a TCP push callback signaling the battery camera woke up # simulate a TCP push callback signaling the battery camera woke up
reolink_connect.audio_record.return_value = False reolink_host.audio_record.return_value = False
assert callback_mock.callback_func is not None assert callback_mock.callback_func is not None
with ( with (
patch( patch(
@ -1182,7 +1150,7 @@ async def test_camera_wake_callback(
await hass.async_block_till_done() await hass.async_block_till_done()
# check that a coordinator update was scheduled. # check that a coordinator update was scheduled.
assert reolink_connect.get_states.call_count >= 1 assert reolink_host.get_states.call_count >= 1
assert hass.states.get(entity_id).state == STATE_OFF assert hass.states.get(entity_id).state == STATE_OFF
@ -1201,7 +1169,7 @@ async def test_baichaun_only(
async def test_remove( async def test_remove(
hass: HomeAssistant, hass: HomeAssistant,
reolink_connect: MagicMock, reolink_host: MagicMock,
config_entry: MockConfigEntry, config_entry: MockConfigEntry,
) -> None: ) -> None:
"""Test removing of the reolink integration.""" """Test removing of the reolink integration."""