Add binary sensor tests to Skybell (#79568)

* Add tests to Skybell

* better way to manage cache

* uno mas

* try ci fix

* temporary

* undo temporary

* ruff

* black

* uno mas

* uno mas

* remove problematic test for now

* reduce to binary sensor tests

* coverage

* move cache to json

* Update tests/components/skybell/conftest.py

---------

Co-authored-by: Erik Montnemery <erik@montnemery.com>
This commit is contained in:
Robert Hillis 2023-11-22 12:56:50 -05:00 committed by GitHub
parent 9278db7344
commit 200804237f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 322 additions and 27 deletions

View File

@ -1124,10 +1124,7 @@ omit =
homeassistant/components/sky_hub/*
homeassistant/components/skybeacon/sensor.py
homeassistant/components/skybell/__init__.py
homeassistant/components/skybell/binary_sensor.py
homeassistant/components/skybell/camera.py
homeassistant/components/skybell/coordinator.py
homeassistant/components/skybell/entity.py
homeassistant/components/skybell/light.py
homeassistant/components/skybell/sensor.py
homeassistant/components/skybell/switch.py

View File

@ -1,12 +1 @@
"""Tests for the SkyBell integration."""
from homeassistant.const import CONF_EMAIL, CONF_PASSWORD
USERNAME = "user"
PASSWORD = "password"
USER_ID = "123456789012345678901234"
CONF_CONFIG_FLOW = {
CONF_EMAIL: USERNAME,
CONF_PASSWORD: PASSWORD,
}

View File

@ -1,11 +1,28 @@
"""Test setup for the SkyBell integration."""
"""Configure pytest for Skybell tests."""
from unittest.mock import AsyncMock, patch
from aioskybell import Skybell, SkybellDevice
from aioskybell.helpers.const import BASE_URL, USERS_ME_URL
import orjson
import pytest
from . import USER_ID
from homeassistant.components.skybell.const import DOMAIN
from homeassistant.const import CONF_EMAIL, CONF_PASSWORD
from homeassistant.core import HomeAssistant
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from tests.common import MockConfigEntry, load_fixture
from tests.test_util.aiohttp import AiohttpClientMocker
USERNAME = "user"
PASSWORD = "password"
USER_ID = "1234567890abcdef12345678"
DEVICE_ID = "012345670123456789abcdef"
CONF_DATA = {
CONF_EMAIL: USERNAME,
CONF_PASSWORD: PASSWORD,
}
@pytest.fixture(autouse=True)
@ -23,3 +40,88 @@ def skybell_mock():
return_value=mocked_skybell,
), patch("homeassistant.components.skybell.Skybell", return_value=mocked_skybell):
yield mocked_skybell
def create_entry(hass: HomeAssistant) -> MockConfigEntry:
"""Create fixture for adding config entry in Home Assistant."""
entry = MockConfigEntry(domain=DOMAIN, unique_id=USER_ID, data=CONF_DATA)
entry.add_to_hass(hass)
return entry
async def set_aioclient_responses(aioclient_mock: AiohttpClientMocker) -> None:
"""Set AioClient responses."""
aioclient_mock.get(
f"{BASE_URL}devices/{DEVICE_ID}/info/",
text=load_fixture("skybell/device_info.json"),
)
aioclient_mock.get(
f"{BASE_URL}devices/{DEVICE_ID}/settings/",
text=load_fixture("skybell/device_settings.json"),
)
aioclient_mock.get(
f"{BASE_URL}devices/{DEVICE_ID}/activities/",
text=load_fixture("skybell/activities.json"),
)
aioclient_mock.get(
f"{BASE_URL}devices/",
text=load_fixture("skybell/device.json"),
)
aioclient_mock.get(
USERS_ME_URL,
text=load_fixture("skybell/me.json"),
)
aioclient_mock.post(
f"{BASE_URL}login/",
text=load_fixture("skybell/login.json"),
)
aioclient_mock.get(
f"{BASE_URL}devices/{DEVICE_ID}/activities/1234567890ab1234567890ac/video/",
text=load_fixture("skybell/video.json"),
)
aioclient_mock.get(
f"{BASE_URL}devices/{DEVICE_ID}/avatar/",
text=load_fixture("skybell/avatar.json"),
)
aioclient_mock.get(
f"https://v3-production-devices-avatar.s3.us-west-2.amazonaws.com/{DEVICE_ID}.jpg",
)
aioclient_mock.get(
f"https://skybell-thumbnails-stage.s3.amazonaws.com/{DEVICE_ID}/1646859244793-951{DEVICE_ID}_{DEVICE_ID}.jpeg",
)
@pytest.fixture
async def connection(aioclient_mock: AiohttpClientMocker) -> None:
"""Fixture for good connection responses."""
await set_aioclient_responses(aioclient_mock)
def create_skybell(hass: HomeAssistant) -> Skybell:
"""Create Skybell object."""
skybell = Skybell(
username=USERNAME,
password=PASSWORD,
get_devices=True,
session=async_get_clientsession(hass),
)
skybell._cache = orjson.loads(load_fixture("skybell/cache.json"))
return skybell
def mock_skybell(hass: HomeAssistant):
"""Mock Skybell object."""
return patch(
"homeassistant.components.skybell.Skybell", return_value=create_skybell(hass)
)
async def async_init_integration(hass: HomeAssistant) -> MockConfigEntry:
"""Set up the Skybell integration in Home Assistant."""
config_entry = create_entry(hass)
with mock_skybell(hass), patch("aioskybell.utils.async_save_cache"):
await hass.config_entries.async_setup(config_entry.entry_id)
await hass.async_block_till_done()
return config_entry

View File

@ -0,0 +1,30 @@
[
{
"videoState": "download:ready",
"_id": "1234567890ab1234567890ab",
"device": "0123456789abcdef01234567",
"callId": "1234567890123-1234567890abcd1234567890abcd",
"event": "device:sensor:motion",
"state": "ready",
"ttlStartDate": "2020-03-30T12:35:02.204Z",
"createdAt": "2020-03-30T12:35:02.204Z",
"updatedAt": "2020-03-30T12:35:02.566Z",
"id": "1234567890ab1234567890ab",
"media": "https://skybell-thumbnails-stage.s3.amazonaws.com/012345670123456789abcdef/1646859244793-951012345670123456789abcdef_012345670123456789abcdef.jpeg",
"mediaSmall": "https://skybell-thumbnails-stage.s3.amazonaws.com/012345670123456789abcdef/1646859244793-951012345670123456789abcdef_012345670123456789abcdef_small.jpeg"
},
{
"videoState": "download:ready",
"_id": "1234567890ab1234567890a9",
"device": "0123456789abcdef01234567",
"callId": "1234567890123-1234567890abcd1234567890abc9",
"event": "application:on-demand",
"state": "ready",
"ttlStartDate": "2020-03-30T11:35:02.204Z",
"createdAt": "2020-03-30T11:35:02.204Z",
"updatedAt": "2020-03-30T11:35:02.566Z",
"id": "1234567890ab1234567890a9",
"media": "https://skybell-thumbnails-stage.s3.amazonaws.com/012345670123456789abcdef/1646859244793-951012345670123456789abcdef_012345670123456789abcde9.jpeg",
"mediaSmall": "https://skybell-thumbnails-stage.s3.amazonaws.com/012345670123456789abcdef/1646859244793-951012345670123456789abcdef_012345670123456789abcde9_small.jpeg"
}
]

View File

@ -0,0 +1,4 @@
{
"createdAt": "2020-03-31T04:13:48.640Z",
"url": "https://v3-production-devices-avatar.s3.us-west-2.amazonaws.com/012345670123456789abcdef.jpg"
}

View File

@ -0,0 +1,40 @@
{
"app_id": "secret",
"client_id": "secret",
"token": "secret",
"access_token": "secret",
"devices": {
"5f8ef594362f31000833d959": {
"event": {
"device:sensor:motion": {
"videoState": "download:ready",
"_id": "1234567890ab1234567890ab",
"device": "0123456789abcdef01234567",
"callId": "1234567890123-1234567890abcd1234567890abcd",
"event": "device:sensor:motion",
"state": "ready",
"ttlStartDate": "2020-03-30T12:35:02.204Z",
"createdAt": "2020-03-30T12:35:02.204Z",
"updatedAt": "2020-03-30T12:35:02.566Z",
"id": "1234567890ab1234567890ab",
"media": "https://skybell-thumbnails-stage.s3.amazonaws.com/012345670123456789abcdef/1646859244793-951012345670123456789abcdef_012345670123456789abcdef.jpeg",
"mediaSmall": "https://skybell-thumbnails-stage.s3.amazonaws.com/012345670123456789abcdef/1646859244793-951012345670123456789abcdef_012345670123456789abcdef_small.jpeg"
},
"device:sensor:button": {
"videoState": "download:ready",
"_id": "1234567890ab1234567890a9",
"device": "0123456789abcdef01234567",
"callId": "1234567890123-1234567890abcd1234567890abc9",
"event": "application:on-demand",
"state": "ready",
"ttlStartDate": "2020-03-30T11:35:02.204Z",
"createdAt": "2020-03-30T11:35:02.204Z",
"updatedAt": "2020-03-30T11:35:02.566Z",
"id": "1234567890ab1234567890a9",
"media": "https://skybell-thumbnails-stage.s3.amazonaws.com/012345670123456789abcdef/1646859244793-951012345670123456789abcdef_012345670123456789abcde9.jpeg",
"mediaSmall": "https://skybell-thumbnails-stage.s3.amazonaws.com/012345670123456789abcdef/1646859244793-951012345670123456789abcdef_012345670123456789abcde9_small.jpeg"
}
}
}
}
}

View File

@ -0,0 +1,19 @@
[
{
"user": "0123456789abcdef01234567",
"uuid": "0123456789",
"resourceId": "012345670123456789abcdef",
"deviceInviteToken": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
"location": {
"lat": "-1.0",
"lng": "1.0"
},
"name": "Front Door",
"type": "skybell hd",
"status": "up",
"createdAt": "2020-10-20T14:35:00.745Z",
"updatedAt": "2020-10-20T14:35:00.745Z",
"id": "012345670123456789abcdef",
"acl": "owner"
}
]

View File

@ -0,0 +1,25 @@
{
"essid": "wifi",
"wifiBitrate": "39",
"proxy_port": "5683",
"wifiLinkQuality": "43",
"port": "5683",
"mac": "ff:ff:ff:ff:ff:ff",
"serialNo": "0123456789",
"wifiTxPwrEeprom": "12",
"region": "us-west-2",
"hardwareRevision": "SKYBELL_TRIMPLUS_1000030-F",
"proxy_address": "34.209.204.201",
"wifiSignalLevel": "-67",
"localHostname": "ip-10-0-0-67.us-west-2.compute.internal",
"wifiNoise": "0",
"address": "1.2.3.4",
"clientId": "1234567890abcdef1234567890abcdef1234567890abcdef",
"timestamp": "60000000000",
"deviceId": "01234567890abcdef1234567",
"firmwareVersion": "7082",
"checkedInAt": "2020-03-31T04:13:37.000Z",
"status": {
"wifiLink": "poor"
}
}

View File

@ -0,0 +1,22 @@
{
"ring_tone": "0",
"do_not_ring": "false",
"do_not_disturb": "false",
"digital_doorbell": "false",
"video_profile": "1",
"mic_volume": "63",
"speaker_volume": "96",
"chime_level": "1",
"motion_threshold": "32",
"low_lux_threshold": "50",
"med_lux_threshold": "150",
"high_lux_threshold": "400",
"low_front_led_dac": "10",
"med_front_led_dac": "10",
"high_front_led_dac": "10",
"green_r": "0",
"green_g": "0",
"green_b": "255",
"led_intensity": "0",
"motion_policy": "call"
}

View File

@ -0,0 +1,22 @@
{
"ring_tone": "0",
"do_not_ring": "false",
"do_not_disturb": "false",
"digital_doorbell": "false",
"video_profile": "1",
"mic_volume": "63",
"speaker_volume": "96",
"chime_level": "1",
"motion_threshold": "32",
"low_lux_threshold": "50",
"med_lux_threshold": "150",
"high_lux_threshold": "400",
"low_front_led_dac": "10",
"med_front_led_dac": "10",
"high_front_led_dac": "10",
"green_r": "10",
"green_g": "125",
"green_b": "255",
"led_intensity": "50",
"motion_policy": "disabled"
}

View File

@ -0,0 +1,10 @@
{
"firstName": "John",
"lastName": "Doe",
"resourceId": "0123456789abcdef01234567",
"createdAt": "2018-07-06T02:02:14.050Z",
"updatedAt": "2018-07-06T02:02:14.050Z",
"id": "0123456789abcdef01234567",
"userLinks": [],
"access_token": "superlongkey"
}

View File

@ -0,0 +1,5 @@
{
"errors": {
"message": "Invalid Login - SmartAuth"
}
}

View File

@ -0,0 +1,9 @@
{
"firstName": "First",
"lastName": "Last",
"resourceId": "123456789012345678901234",
"createdAt": "2018-10-06T02:02:14.050Z",
"updatedAt": "2018-10-06T02:02:14.050Z",
"id": "1234567890abcdef12345678",
"userLinks": []
}

View File

@ -0,0 +1,3 @@
{
"url": "https://production-video-download.s3.us-west-2.amazonaws.com/012345670123456789abcdef/1654307756676-0123456789120123456789abcdef_012345670123456789abcdef.mp4?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=01234567890123456789%2F20203030%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20200330T201225Z&X-Amz-Expires=300&X-Amz-Signature=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef&X-Amz-SignedHeaders=host"
}

View File

@ -0,0 +1,18 @@
"""Binary sensor tests for the Skybell integration."""
from homeassistant.components.binary_sensor import BinarySensorDeviceClass
from homeassistant.const import ATTR_DEVICE_CLASS, STATE_OFF, STATE_ON
from homeassistant.core import HomeAssistant
from .conftest import async_init_integration
async def test_binary_sensors(hass: HomeAssistant, connection) -> None:
"""Test we get sensor data."""
await async_init_integration(hass)
state = hass.states.get("binary_sensor.front_door_button")
assert state.state == STATE_OFF
assert state.attributes.get(ATTR_DEVICE_CLASS) == BinarySensorDeviceClass.OCCUPANCY
state = hass.states.get("binary_sensor.front_door_motion")
assert state.state == STATE_ON
assert state.attributes.get(ATTR_DEVICE_CLASS) == BinarySensorDeviceClass.MOTION

View File

@ -11,7 +11,7 @@ from homeassistant.const import CONF_PASSWORD, CONF_SOURCE
from homeassistant.core import HomeAssistant
from homeassistant.data_entry_flow import FlowResultType
from . import CONF_CONFIG_FLOW, PASSWORD, USER_ID
from .conftest import CONF_DATA, PASSWORD, USER_ID
from tests.common import MockConfigEntry
@ -37,12 +37,12 @@ async def test_flow_user(hass: HomeAssistant) -> None:
result = await hass.config_entries.flow.async_configure(
result["flow_id"],
user_input=CONF_CONFIG_FLOW,
user_input=CONF_DATA,
)
assert result["type"] == FlowResultType.CREATE_ENTRY
assert result["title"] == "user"
assert result["data"] == CONF_CONFIG_FLOW
assert result["data"] == CONF_DATA
assert result["result"].unique_id == USER_ID
@ -50,12 +50,12 @@ async def test_flow_user_already_configured(hass: HomeAssistant) -> None:
"""Test user initialized flow with duplicate server."""
entry = MockConfigEntry(
domain=DOMAIN,
data=CONF_CONFIG_FLOW,
data=CONF_DATA,
)
entry.add_to_hass(hass)
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}, data=CONF_CONFIG_FLOW
DOMAIN, context={"source": SOURCE_USER}, data=CONF_DATA
)
assert result["type"] == FlowResultType.ABORT
@ -66,7 +66,7 @@ async def test_flow_user_cannot_connect(hass: HomeAssistant, skybell_mock) -> No
"""Test user initialized flow with unreachable server."""
skybell_mock.async_initialize.side_effect = exceptions.SkybellException(hass)
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}, data=CONF_CONFIG_FLOW
DOMAIN, context={"source": SOURCE_USER}, data=CONF_DATA
)
assert result["type"] == FlowResultType.FORM
assert result["step_id"] == "user"
@ -79,7 +79,7 @@ async def test_invalid_credentials(hass: HomeAssistant, skybell_mock) -> None:
exceptions.SkybellAuthenticationException(hass)
)
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}, data=CONF_CONFIG_FLOW
DOMAIN, context={"source": SOURCE_USER}, data=CONF_DATA
)
assert result["type"] == FlowResultType.FORM
@ -91,7 +91,7 @@ async def test_flow_user_unknown_error(hass: HomeAssistant, skybell_mock) -> Non
"""Test user initialized flow with unreachable server."""
skybell_mock.async_initialize.side_effect = Exception
result = await hass.config_entries.flow.async_init(
DOMAIN, context={"source": SOURCE_USER}, data=CONF_CONFIG_FLOW
DOMAIN, context={"source": SOURCE_USER}, data=CONF_DATA
)
assert result["type"] == FlowResultType.FORM
assert result["step_id"] == "user"
@ -100,7 +100,7 @@ async def test_flow_user_unknown_error(hass: HomeAssistant, skybell_mock) -> Non
async def test_step_reauth(hass: HomeAssistant) -> None:
"""Test the reauth flow."""
entry = MockConfigEntry(domain=DOMAIN, unique_id=USER_ID, data=CONF_CONFIG_FLOW)
entry = MockConfigEntry(domain=DOMAIN, unique_id=USER_ID, data=CONF_DATA)
entry.add_to_hass(hass)
result = await hass.config_entries.flow.async_init(
@ -126,7 +126,7 @@ async def test_step_reauth(hass: HomeAssistant) -> None:
async def test_step_reauth_failed(hass: HomeAssistant, skybell_mock) -> None:
"""Test the reauth flow fails and recovers."""
entry = MockConfigEntry(domain=DOMAIN, unique_id=USER_ID, data=CONF_CONFIG_FLOW)
entry = MockConfigEntry(domain=DOMAIN, unique_id=USER_ID, data=CONF_DATA)
entry.add_to_hass(hass)
result = await hass.config_entries.flow.async_init(