diff --git a/.coveragerc b/.coveragerc index 8ce4645bf0c..0d7c895f3f3 100644 --- a/.coveragerc +++ b/.coveragerc @@ -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 diff --git a/tests/components/skybell/__init__.py b/tests/components/skybell/__init__.py index fc049adcc3d..ae9b6d132e4 100644 --- a/tests/components/skybell/__init__.py +++ b/tests/components/skybell/__init__.py @@ -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, -} diff --git a/tests/components/skybell/conftest.py b/tests/components/skybell/conftest.py index 4318fa8c24f..beb3fec9b98 100644 --- a/tests/components/skybell/conftest.py +++ b/tests/components/skybell/conftest.py @@ -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 diff --git a/tests/components/skybell/fixtures/activities.json b/tests/components/skybell/fixtures/activities.json new file mode 100644 index 00000000000..4ed5c027821 --- /dev/null +++ b/tests/components/skybell/fixtures/activities.json @@ -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" + } +] diff --git a/tests/components/skybell/fixtures/avatar.json b/tests/components/skybell/fixtures/avatar.json new file mode 100644 index 00000000000..3f8157c15c8 --- /dev/null +++ b/tests/components/skybell/fixtures/avatar.json @@ -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" +} diff --git a/tests/components/skybell/fixtures/cache.json b/tests/components/skybell/fixtures/cache.json new file mode 100644 index 00000000000..1276c2cfc0f --- /dev/null +++ b/tests/components/skybell/fixtures/cache.json @@ -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" + } + } + } + } +} diff --git a/tests/components/skybell/fixtures/device.json b/tests/components/skybell/fixtures/device.json new file mode 100644 index 00000000000..7b522aa687d --- /dev/null +++ b/tests/components/skybell/fixtures/device.json @@ -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" + } +] diff --git a/tests/components/skybell/fixtures/device_info.json b/tests/components/skybell/fixtures/device_info.json new file mode 100644 index 00000000000..d858bb20e36 --- /dev/null +++ b/tests/components/skybell/fixtures/device_info.json @@ -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" + } +} diff --git a/tests/components/skybell/fixtures/device_settings.json b/tests/components/skybell/fixtures/device_settings.json new file mode 100644 index 00000000000..46af5f0bd4b --- /dev/null +++ b/tests/components/skybell/fixtures/device_settings.json @@ -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" +} diff --git a/tests/components/skybell/fixtures/device_settings_change.json b/tests/components/skybell/fixtures/device_settings_change.json new file mode 100644 index 00000000000..6e2c8dd199b --- /dev/null +++ b/tests/components/skybell/fixtures/device_settings_change.json @@ -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" +} diff --git a/tests/components/skybell/fixtures/login.json b/tests/components/skybell/fixtures/login.json new file mode 100644 index 00000000000..c7eaa44b5ab --- /dev/null +++ b/tests/components/skybell/fixtures/login.json @@ -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" +} diff --git a/tests/components/skybell/fixtures/login_401.json b/tests/components/skybell/fixtures/login_401.json new file mode 100644 index 00000000000..ab6bfd7053c --- /dev/null +++ b/tests/components/skybell/fixtures/login_401.json @@ -0,0 +1,5 @@ +{ + "errors": { + "message": "Invalid Login - SmartAuth" + } +} diff --git a/tests/components/skybell/fixtures/me.json b/tests/components/skybell/fixtures/me.json new file mode 100644 index 00000000000..7b27c95ec01 --- /dev/null +++ b/tests/components/skybell/fixtures/me.json @@ -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": [] +} diff --git a/tests/components/skybell/fixtures/video.json b/tests/components/skybell/fixtures/video.json new file mode 100644 index 00000000000..e674df1c9c8 --- /dev/null +++ b/tests/components/skybell/fixtures/video.json @@ -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" +} diff --git a/tests/components/skybell/test_binary_sensor.py b/tests/components/skybell/test_binary_sensor.py new file mode 100644 index 00000000000..8e0bc884730 --- /dev/null +++ b/tests/components/skybell/test_binary_sensor.py @@ -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 diff --git a/tests/components/skybell/test_config_flow.py b/tests/components/skybell/test_config_flow.py index f93c1d6ae4f..d83f4243d7f 100644 --- a/tests/components/skybell/test_config_flow.py +++ b/tests/components/skybell/test_config_flow.py @@ -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(