Improve SmartThings test mocking (#25028)
* Migrate to asynctest * Simplify mock access * Use mocks
This commit is contained in:
parent
2fbbcafaed
commit
a31e49c857
5 changed files with 186 additions and 336 deletions
|
@ -1,12 +1,11 @@
|
||||||
"""Test configuration and mocks for the SmartThings component."""
|
"""Test configuration and mocks for the SmartThings component."""
|
||||||
from collections import defaultdict
|
|
||||||
from unittest.mock import Mock, patch
|
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
|
from asynctest import Mock, patch
|
||||||
from pysmartthings import (
|
from pysmartthings import (
|
||||||
CLASSIFICATION_AUTOMATION, AppEntity, AppOAuthClient, AppSettings,
|
CLASSIFICATION_AUTOMATION, AppEntity, AppOAuthClient, AppSettings,
|
||||||
DeviceEntity, InstalledApp, Location, SceneEntity, SmartThings,
|
DeviceEntity, DeviceStatus, InstalledApp, InstalledAppStatus,
|
||||||
Subscription)
|
InstalledAppType, Location, SceneEntity, SmartThings, Subscription)
|
||||||
from pysmartthings.api import Api
|
from pysmartthings.api import Api
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
@ -22,8 +21,6 @@ from homeassistant.config_entries import (
|
||||||
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_WEBHOOK_ID
|
from homeassistant.const import CONF_ACCESS_TOKEN, CONF_WEBHOOK_ID
|
||||||
from homeassistant.setup import async_setup_component
|
from homeassistant.setup import async_setup_component
|
||||||
|
|
||||||
from tests.common import mock_coro
|
|
||||||
|
|
||||||
COMPONENT_PREFIX = "homeassistant.components.smartthings."
|
COMPONENT_PREFIX = "homeassistant.components.smartthings."
|
||||||
|
|
||||||
|
|
||||||
|
@ -58,11 +55,9 @@ async def setup_component(hass, config_file, hass_storage):
|
||||||
|
|
||||||
|
|
||||||
def _create_location():
|
def _create_location():
|
||||||
loc = Location()
|
loc = Mock(Location)
|
||||||
loc.apply_data({
|
loc.name = 'Test Location'
|
||||||
'name': 'Test Location',
|
loc.location_id = str(uuid4())
|
||||||
'locationId': str(uuid4())
|
|
||||||
})
|
|
||||||
return loc
|
return loc
|
||||||
|
|
||||||
|
|
||||||
|
@ -81,58 +76,50 @@ def locations_fixture(location):
|
||||||
@pytest.fixture(name="app")
|
@pytest.fixture(name="app")
|
||||||
def app_fixture(hass, config_file):
|
def app_fixture(hass, config_file):
|
||||||
"""Fixture for a single app."""
|
"""Fixture for a single app."""
|
||||||
app = AppEntity(Mock())
|
app = Mock(AppEntity)
|
||||||
app.apply_data({
|
app.app_name = APP_NAME_PREFIX + str(uuid4())
|
||||||
'appName': APP_NAME_PREFIX + str(uuid4()),
|
app.app_id = str(uuid4())
|
||||||
'appId': str(uuid4()),
|
app.app_type = 'WEBHOOK_SMART_APP'
|
||||||
'appType': 'WEBHOOK_SMART_APP',
|
app.classifications = [CLASSIFICATION_AUTOMATION]
|
||||||
'classifications': [CLASSIFICATION_AUTOMATION],
|
app.display_name = 'Home Assistant'
|
||||||
'displayName': 'Home Assistant',
|
app.description = hass.config.location_name + " at " + \
|
||||||
'description':
|
hass.config.api.base_url
|
||||||
hass.config.location_name + " at " + hass.config.api.base_url,
|
app.single_instance = True
|
||||||
'singleInstance': True,
|
app.webhook_target_url = webhook.async_generate_url(
|
||||||
'webhookSmartApp': {
|
hass, hass.data[DOMAIN][CONF_WEBHOOK_ID])
|
||||||
'targetUrl': webhook.async_generate_url(
|
|
||||||
hass, hass.data[DOMAIN][CONF_WEBHOOK_ID]),
|
settings = Mock(AppSettings)
|
||||||
'publicKey': ''}
|
settings.app_id = app.app_id
|
||||||
})
|
settings.settings = {SETTINGS_INSTANCE_ID: config_file[CONF_INSTANCE_ID]}
|
||||||
app.refresh = Mock()
|
app.settings.return_value = settings
|
||||||
app.refresh.return_value = mock_coro()
|
|
||||||
app.save = Mock()
|
|
||||||
app.save.return_value = mock_coro()
|
|
||||||
settings = AppSettings(app.app_id)
|
|
||||||
settings.settings[SETTINGS_INSTANCE_ID] = config_file[CONF_INSTANCE_ID]
|
|
||||||
app.settings = Mock()
|
|
||||||
app.settings.return_value = mock_coro(return_value=settings)
|
|
||||||
return app
|
return app
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name="app_oauth_client")
|
@pytest.fixture(name="app_oauth_client")
|
||||||
def app_oauth_client_fixture():
|
def app_oauth_client_fixture():
|
||||||
"""Fixture for a single app's oauth."""
|
"""Fixture for a single app's oauth."""
|
||||||
return AppOAuthClient({
|
client = Mock(AppOAuthClient)
|
||||||
'oauthClientId': str(uuid4()),
|
client.client_id = str(uuid4())
|
||||||
'oauthClientSecret': str(uuid4())
|
client.client_secret = str(uuid4())
|
||||||
})
|
return client
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name='app_settings')
|
@pytest.fixture(name='app_settings')
|
||||||
def app_settings_fixture(app, config_file):
|
def app_settings_fixture(app, config_file):
|
||||||
"""Fixture for an app settings."""
|
"""Fixture for an app settings."""
|
||||||
settings = AppSettings(app.app_id)
|
settings = Mock(AppSettings)
|
||||||
settings.settings[SETTINGS_INSTANCE_ID] = config_file[CONF_INSTANCE_ID]
|
settings.app_id = app.app_id
|
||||||
|
settings.settings = {SETTINGS_INSTANCE_ID: config_file[CONF_INSTANCE_ID]}
|
||||||
return settings
|
return settings
|
||||||
|
|
||||||
|
|
||||||
def _create_installed_app(location_id, app_id):
|
def _create_installed_app(location_id, app_id):
|
||||||
item = InstalledApp()
|
item = Mock(InstalledApp)
|
||||||
item.apply_data(defaultdict(str, {
|
item.installed_app_id = str(uuid4())
|
||||||
'installedAppId': str(uuid4()),
|
item.installed_app_status = InstalledAppStatus.AUTHORIZED
|
||||||
'installedAppStatus': 'AUTHORIZED',
|
item.installed_app_type = InstalledAppType.WEBHOOK_SMART_APP
|
||||||
'installedAppType': 'UNKNOWN',
|
item.app_id = app_id
|
||||||
'appId': app_id,
|
item.location_id = location_id
|
||||||
'locationId': location_id
|
|
||||||
}))
|
|
||||||
return item
|
return item
|
||||||
|
|
||||||
|
|
||||||
|
@ -161,10 +148,9 @@ def config_file_fixture():
|
||||||
@pytest.fixture(name='smartthings_mock')
|
@pytest.fixture(name='smartthings_mock')
|
||||||
def smartthings_mock_fixture(locations):
|
def smartthings_mock_fixture(locations):
|
||||||
"""Fixture to mock smartthings API calls."""
|
"""Fixture to mock smartthings API calls."""
|
||||||
def _location(location_id):
|
async def _location(location_id):
|
||||||
return mock_coro(
|
return next(location for location in locations
|
||||||
return_value=next(location for location in locations
|
if location.location_id == location_id)
|
||||||
if location.location_id == location_id))
|
|
||||||
|
|
||||||
smartthings_mock = Mock(SmartThings)
|
smartthings_mock = Mock(SmartThings)
|
||||||
smartthings_mock.location.side_effect = _location
|
smartthings_mock.location.side_effect = _location
|
||||||
|
@ -172,71 +158,23 @@ def smartthings_mock_fixture(locations):
|
||||||
with patch(COMPONENT_PREFIX + "SmartThings", new=mock), \
|
with patch(COMPONENT_PREFIX + "SmartThings", new=mock), \
|
||||||
patch(COMPONENT_PREFIX + "config_flow.SmartThings", new=mock), \
|
patch(COMPONENT_PREFIX + "config_flow.SmartThings", new=mock), \
|
||||||
patch(COMPONENT_PREFIX + "smartapp.SmartThings", new=mock):
|
patch(COMPONENT_PREFIX + "smartapp.SmartThings", new=mock):
|
||||||
yield mock
|
yield smartthings_mock
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(name='device')
|
@pytest.fixture(name='device')
|
||||||
def device_fixture(location):
|
def device_fixture(location):
|
||||||
"""Fixture representing devices loaded."""
|
"""Fixture representing devices loaded."""
|
||||||
item = DeviceEntity(None)
|
item = Mock(DeviceEntity)
|
||||||
item.status.refresh = Mock()
|
item.device_id = "743de49f-036f-4e9c-839a-2f89d57607db"
|
||||||
item.status.refresh.return_value = mock_coro()
|
item.name = "GE In-Wall Smart Dimmer"
|
||||||
item.apply_data({
|
item.label = "Front Porch Lights"
|
||||||
"deviceId": "743de49f-036f-4e9c-839a-2f89d57607db",
|
item.location_id = location.location_id
|
||||||
"name": "GE In-Wall Smart Dimmer",
|
item.capabilities = [
|
||||||
"label": "Front Porch Lights",
|
"switch", "switchLevel", "refresh", "indicator", "sensor", "actuator",
|
||||||
"deviceManufacturerCode": "0063-4944-3038",
|
"healthCheck", "light"
|
||||||
"locationId": location.location_id,
|
]
|
||||||
"deviceTypeId": "8a9d4b1e3b9b1fe3013b9b206a7f000d",
|
item.components = {"main": item.capabilities}
|
||||||
"deviceTypeName": "Dimmer Switch",
|
item.status = Mock(DeviceStatus)
|
||||||
"deviceNetworkType": "ZWAVE",
|
|
||||||
"components": [
|
|
||||||
{
|
|
||||||
"id": "main",
|
|
||||||
"capabilities": [
|
|
||||||
{
|
|
||||||
"id": "switch",
|
|
||||||
"version": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "switchLevel",
|
|
||||||
"version": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "refresh",
|
|
||||||
"version": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "indicator",
|
|
||||||
"version": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "sensor",
|
|
||||||
"version": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "actuator",
|
|
||||||
"version": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "healthCheck",
|
|
||||||
"version": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "light",
|
|
||||||
"version": 1
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"dth": {
|
|
||||||
"deviceTypeId": "8a9d4b1e3b9b1fe3013b9b206a7f000d",
|
|
||||||
"deviceTypeName": "Dimmer Switch",
|
|
||||||
"deviceNetworkType": "ZWAVE",
|
|
||||||
"completedSetup": False
|
|
||||||
},
|
|
||||||
"type": "DTH"
|
|
||||||
})
|
|
||||||
return item
|
return item
|
||||||
|
|
||||||
|
|
||||||
|
@ -269,9 +207,8 @@ def subscription_factory_fixture():
|
||||||
@pytest.fixture(name="device_factory")
|
@pytest.fixture(name="device_factory")
|
||||||
def device_factory_fixture():
|
def device_factory_fixture():
|
||||||
"""Fixture for creating mock devices."""
|
"""Fixture for creating mock devices."""
|
||||||
api = Mock(spec=Api)
|
api = Mock(Api)
|
||||||
api.post_device_command.side_effect = \
|
api.post_device_command.return_value = {}
|
||||||
lambda *args, **kwargs: mock_coro(return_value={})
|
|
||||||
|
|
||||||
def _factory(label, capabilities, status: dict = None):
|
def _factory(label, capabilities, status: dict = None):
|
||||||
device_data = {
|
device_data = {
|
||||||
|
@ -308,19 +245,12 @@ def device_factory_fixture():
|
||||||
@pytest.fixture(name="scene_factory")
|
@pytest.fixture(name="scene_factory")
|
||||||
def scene_factory_fixture(location):
|
def scene_factory_fixture(location):
|
||||||
"""Fixture for creating mock devices."""
|
"""Fixture for creating mock devices."""
|
||||||
api = Mock(spec=Api)
|
|
||||||
api.execute_scene.side_effect = \
|
|
||||||
lambda *args, **kwargs: mock_coro(return_value={})
|
|
||||||
|
|
||||||
def _factory(name):
|
def _factory(name):
|
||||||
scene_data = {
|
scene = Mock(SceneEntity)
|
||||||
'sceneId': str(uuid4()),
|
scene.scene_id = str(uuid4())
|
||||||
'sceneName': name,
|
scene.name = name
|
||||||
'sceneIcon': '',
|
scene.location_id = location.location_id
|
||||||
'sceneColor': '',
|
return scene
|
||||||
'locationId': location.location_id
|
|
||||||
}
|
|
||||||
return SceneEntity(api, scene_data)
|
|
||||||
return _factory
|
return _factory
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
"""Tests for the SmartThings config flow module."""
|
"""Tests for the SmartThings config flow module."""
|
||||||
from unittest.mock import Mock, patch
|
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
from aiohttp import ClientResponseError
|
from aiohttp import ClientResponseError
|
||||||
|
from asynctest import Mock, patch
|
||||||
from pysmartthings import APIResponseError
|
from pysmartthings import APIResponseError
|
||||||
|
|
||||||
from homeassistant import data_entry_flow
|
from homeassistant import data_entry_flow
|
||||||
|
@ -15,8 +15,6 @@ from homeassistant.components.smartthings.const import (
|
||||||
CONF_REFRESH_TOKEN, DOMAIN)
|
CONF_REFRESH_TOKEN, DOMAIN)
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
|
||||||
from tests.common import mock_coro
|
|
||||||
|
|
||||||
|
|
||||||
async def test_step_user(hass):
|
async def test_step_user(hass):
|
||||||
"""Test the access token form is shown for a user initiated flow."""
|
"""Test the access token form is shown for a user initiated flow."""
|
||||||
|
@ -84,8 +82,8 @@ async def test_token_unauthorized(hass, smartthings_mock):
|
||||||
flow = SmartThingsFlowHandler()
|
flow = SmartThingsFlowHandler()
|
||||||
flow.hass = hass
|
flow.hass = hass
|
||||||
|
|
||||||
smartthings_mock.return_value.apps.return_value = mock_coro(
|
smartthings_mock.apps.side_effect = \
|
||||||
exception=ClientResponseError(None, None, status=401))
|
ClientResponseError(None, None, status=401)
|
||||||
|
|
||||||
result = await flow.async_step_user({'access_token': str(uuid4())})
|
result = await flow.async_step_user({'access_token': str(uuid4())})
|
||||||
|
|
||||||
|
@ -99,8 +97,8 @@ async def test_token_forbidden(hass, smartthings_mock):
|
||||||
flow = SmartThingsFlowHandler()
|
flow = SmartThingsFlowHandler()
|
||||||
flow.hass = hass
|
flow.hass = hass
|
||||||
|
|
||||||
smartthings_mock.return_value.apps.return_value = mock_coro(
|
smartthings_mock.apps.side_effect = \
|
||||||
exception=ClientResponseError(None, None, status=403))
|
ClientResponseError(None, None, status=403)
|
||||||
|
|
||||||
result = await flow.async_step_user({'access_token': str(uuid4())})
|
result = await flow.async_step_user({'access_token': str(uuid4())})
|
||||||
|
|
||||||
|
@ -118,8 +116,7 @@ async def test_webhook_error(hass, smartthings_mock):
|
||||||
error = APIResponseError(None, None, data=data, status=422)
|
error = APIResponseError(None, None, data=data, status=422)
|
||||||
error.is_target_error = Mock(return_value=True)
|
error.is_target_error = Mock(return_value=True)
|
||||||
|
|
||||||
smartthings_mock.return_value.apps.return_value = mock_coro(
|
smartthings_mock.apps.side_effect = error
|
||||||
exception=error)
|
|
||||||
|
|
||||||
result = await flow.async_step_user({'access_token': str(uuid4())})
|
result = await flow.async_step_user({'access_token': str(uuid4())})
|
||||||
|
|
||||||
|
@ -136,8 +133,7 @@ async def test_api_error(hass, smartthings_mock):
|
||||||
data = {'error': {}}
|
data = {'error': {}}
|
||||||
error = APIResponseError(None, None, data=data, status=400)
|
error = APIResponseError(None, None, data=data, status=400)
|
||||||
|
|
||||||
smartthings_mock.return_value.apps.return_value = mock_coro(
|
smartthings_mock.apps.side_effect = error
|
||||||
exception=error)
|
|
||||||
|
|
||||||
result = await flow.async_step_user({'access_token': str(uuid4())})
|
result = await flow.async_step_user({'access_token': str(uuid4())})
|
||||||
|
|
||||||
|
@ -151,8 +147,8 @@ async def test_unknown_api_error(hass, smartthings_mock):
|
||||||
flow = SmartThingsFlowHandler()
|
flow = SmartThingsFlowHandler()
|
||||||
flow.hass = hass
|
flow.hass = hass
|
||||||
|
|
||||||
smartthings_mock.return_value.apps.return_value = mock_coro(
|
smartthings_mock.apps.side_effect = \
|
||||||
exception=ClientResponseError(None, None, status=404))
|
ClientResponseError(None, None, status=404)
|
||||||
|
|
||||||
result = await flow.async_step_user({'access_token': str(uuid4())})
|
result = await flow.async_step_user({'access_token': str(uuid4())})
|
||||||
|
|
||||||
|
@ -166,8 +162,7 @@ async def test_unknown_error(hass, smartthings_mock):
|
||||||
flow = SmartThingsFlowHandler()
|
flow = SmartThingsFlowHandler()
|
||||||
flow.hass = hass
|
flow.hass = hass
|
||||||
|
|
||||||
smartthings_mock.return_value.apps.return_value = mock_coro(
|
smartthings_mock.apps.side_effect = Exception('Unknown error')
|
||||||
exception=Exception('Unknown error'))
|
|
||||||
|
|
||||||
result = await flow.async_step_user({'access_token': str(uuid4())})
|
result = await flow.async_step_user({'access_token': str(uuid4())})
|
||||||
|
|
||||||
|
@ -182,12 +177,8 @@ async def test_app_created_then_show_wait_form(
|
||||||
flow = SmartThingsFlowHandler()
|
flow = SmartThingsFlowHandler()
|
||||||
flow.hass = hass
|
flow.hass = hass
|
||||||
|
|
||||||
smartthings = smartthings_mock.return_value
|
smartthings_mock.apps.return_value = []
|
||||||
smartthings.apps.return_value = mock_coro(return_value=[])
|
smartthings_mock.create_app.return_value = (app, app_oauth_client)
|
||||||
smartthings.create_app.return_value = \
|
|
||||||
mock_coro(return_value=(app, app_oauth_client))
|
|
||||||
smartthings.update_app_settings.return_value = mock_coro()
|
|
||||||
smartthings.update_app_oauth.return_value = mock_coro()
|
|
||||||
|
|
||||||
result = await flow.async_step_user({'access_token': str(uuid4())})
|
result = await flow.async_step_user({'access_token': str(uuid4())})
|
||||||
|
|
||||||
|
@ -201,24 +192,17 @@ async def test_cloudhook_app_created_then_show_wait_form(
|
||||||
# Unload the endpoint so we can reload it under the cloud.
|
# Unload the endpoint so we can reload it under the cloud.
|
||||||
await smartapp.unload_smartapp_endpoint(hass)
|
await smartapp.unload_smartapp_endpoint(hass)
|
||||||
|
|
||||||
mock_async_active_subscription = Mock(return_value=True)
|
with patch.object(cloud, 'async_active_subscription', return_value=True), \
|
||||||
mock_create_cloudhook = Mock(return_value=mock_coro(
|
patch.object(
|
||||||
return_value="http://cloud.test"))
|
cloud, 'async_create_cloudhook',
|
||||||
with patch.object(cloud, 'async_active_subscription',
|
return_value='http://cloud.test') as mock_create_cloudhook:
|
||||||
new=mock_async_active_subscription), \
|
|
||||||
patch.object(cloud, 'async_create_cloudhook',
|
|
||||||
new=mock_create_cloudhook):
|
|
||||||
|
|
||||||
await smartapp.setup_smartapp_endpoint(hass)
|
await smartapp.setup_smartapp_endpoint(hass)
|
||||||
|
|
||||||
flow = SmartThingsFlowHandler()
|
flow = SmartThingsFlowHandler()
|
||||||
flow.hass = hass
|
flow.hass = hass
|
||||||
smartthings = smartthings_mock.return_value
|
smartthings_mock.apps.return_value = []
|
||||||
smartthings.apps.return_value = mock_coro(return_value=[])
|
smartthings_mock.create_app.return_value = (app, app_oauth_client)
|
||||||
smartthings.create_app.return_value = \
|
|
||||||
mock_coro(return_value=(app, app_oauth_client))
|
|
||||||
smartthings.update_app_settings.return_value = mock_coro()
|
|
||||||
smartthings.update_app_oauth.return_value = mock_coro()
|
|
||||||
|
|
||||||
result = await flow.async_step_user({'access_token': str(uuid4())})
|
result = await flow.async_step_user({'access_token': str(uuid4())})
|
||||||
|
|
||||||
|
@ -233,10 +217,8 @@ async def test_app_updated_then_show_wait_form(
|
||||||
flow = SmartThingsFlowHandler()
|
flow = SmartThingsFlowHandler()
|
||||||
flow.hass = hass
|
flow.hass = hass
|
||||||
|
|
||||||
api = smartthings_mock.return_value
|
smartthings_mock.apps.return_value = [app]
|
||||||
api.apps.return_value = mock_coro(return_value=[app])
|
smartthings_mock.generate_app_oauth.return_value = app_oauth_client
|
||||||
api.generate_app_oauth.return_value = \
|
|
||||||
mock_coro(return_value=app_oauth_client)
|
|
||||||
|
|
||||||
result = await flow.async_step_user({'access_token': str(uuid4())})
|
result = await flow.async_step_user({'access_token': str(uuid4())})
|
||||||
|
|
||||||
|
@ -275,7 +257,7 @@ async def test_config_entry_created_when_installed(
|
||||||
flow.hass = hass
|
flow.hass = hass
|
||||||
flow.access_token = str(uuid4())
|
flow.access_token = str(uuid4())
|
||||||
flow.app_id = installed_app.app_id
|
flow.app_id = installed_app.app_id
|
||||||
flow.api = smartthings_mock.return_value
|
flow.api = smartthings_mock
|
||||||
flow.oauth_client_id = str(uuid4())
|
flow.oauth_client_id = str(uuid4())
|
||||||
flow.oauth_client_secret = str(uuid4())
|
flow.oauth_client_secret = str(uuid4())
|
||||||
data = {
|
data = {
|
||||||
|
@ -307,7 +289,7 @@ async def test_multiple_config_entry_created_when_installed(
|
||||||
flow.hass = hass
|
flow.hass = hass
|
||||||
flow.access_token = str(uuid4())
|
flow.access_token = str(uuid4())
|
||||||
flow.app_id = app.app_id
|
flow.app_id = app.app_id
|
||||||
flow.api = smartthings_mock.return_value
|
flow.api = smartthings_mock
|
||||||
flow.oauth_client_id = str(uuid4())
|
flow.oauth_client_id = str(uuid4())
|
||||||
flow.oauth_client_secret = str(uuid4())
|
flow.oauth_client_secret = str(uuid4())
|
||||||
for installed_app in installed_apps:
|
for installed_app in installed_apps:
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
"""Tests for the SmartThings component init module."""
|
"""Tests for the SmartThings component init module."""
|
||||||
from unittest.mock import Mock, patch
|
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
from aiohttp import ClientConnectionError, ClientResponseError
|
from aiohttp import ClientConnectionError, ClientResponseError
|
||||||
from pysmartthings import InstalledAppStatus
|
from asynctest import Mock, patch
|
||||||
|
from pysmartthings import InstalledAppStatus, OAuthToken
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.components import cloud, smartthings
|
from homeassistant.components import cloud, smartthings
|
||||||
|
@ -14,7 +14,7 @@ from homeassistant.components.smartthings.const import (
|
||||||
from homeassistant.exceptions import ConfigEntryNotReady
|
from homeassistant.exceptions import ConfigEntryNotReady
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
|
|
||||||
from tests.common import MockConfigEntry, mock_coro
|
from tests.common import MockConfigEntry
|
||||||
|
|
||||||
|
|
||||||
async def test_migration_creates_new_flow(
|
async def test_migration_creates_new_flow(
|
||||||
|
@ -22,15 +22,12 @@ async def test_migration_creates_new_flow(
|
||||||
"""Test migration deletes app and creates new flow."""
|
"""Test migration deletes app and creates new flow."""
|
||||||
config_entry.version = 1
|
config_entry.version = 1
|
||||||
setattr(hass.config_entries, '_entries', [config_entry])
|
setattr(hass.config_entries, '_entries', [config_entry])
|
||||||
api = smartthings_mock.return_value
|
|
||||||
api.delete_installed_app.side_effect = lambda _: mock_coro()
|
|
||||||
api.delete_app.side_effect = lambda _: mock_coro()
|
|
||||||
|
|
||||||
await smartthings.async_migrate_entry(hass, config_entry)
|
await smartthings.async_migrate_entry(hass, config_entry)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
assert api.delete_installed_app.call_count == 1
|
assert smartthings_mock.delete_installed_app.call_count == 1
|
||||||
assert api.delete_app.call_count == 1
|
assert smartthings_mock.delete_app.call_count == 1
|
||||||
assert not hass.config_entries.async_entries(DOMAIN)
|
assert not hass.config_entries.async_entries(DOMAIN)
|
||||||
flows = hass.config_entries.flow.async_progress()
|
flows = hass.config_entries.flow.async_progress()
|
||||||
assert len(flows) == 1
|
assert len(flows) == 1
|
||||||
|
@ -47,34 +44,30 @@ async def test_unrecoverable_api_errors_create_new_flow(
|
||||||
403 (forbidden/not found): Occurs when the app or installed app could
|
403 (forbidden/not found): Occurs when the app or installed app could
|
||||||
not be retrieved/found (likely deleted?)
|
not be retrieved/found (likely deleted?)
|
||||||
"""
|
"""
|
||||||
api = smartthings_mock.return_value
|
setattr(hass.config_entries, '_entries', [config_entry])
|
||||||
for error_status in (401, 403):
|
smartthings_mock.app.side_effect = \
|
||||||
setattr(hass.config_entries, '_entries', [config_entry])
|
ClientResponseError(None, None, status=401)
|
||||||
api.app.return_value = mock_coro(
|
|
||||||
exception=ClientResponseError(None, None,
|
|
||||||
status=error_status))
|
|
||||||
|
|
||||||
# Assert setup returns false
|
# Assert setup returns false
|
||||||
result = await smartthings.async_setup_entry(hass, config_entry)
|
result = await smartthings.async_setup_entry(hass, config_entry)
|
||||||
assert not result
|
assert not result
|
||||||
|
|
||||||
# Assert entry was removed and new flow created
|
# Assert entry was removed and new flow created
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert not hass.config_entries.async_entries(DOMAIN)
|
assert not hass.config_entries.async_entries(DOMAIN)
|
||||||
flows = hass.config_entries.flow.async_progress()
|
flows = hass.config_entries.flow.async_progress()
|
||||||
assert len(flows) == 1
|
assert len(flows) == 1
|
||||||
assert flows[0]['handler'] == 'smartthings'
|
assert flows[0]['handler'] == 'smartthings'
|
||||||
assert flows[0]['context'] == {'source': 'import'}
|
assert flows[0]['context'] == {'source': 'import'}
|
||||||
hass.config_entries.flow.async_abort(flows[0]['flow_id'])
|
hass.config_entries.flow.async_abort(flows[0]['flow_id'])
|
||||||
|
|
||||||
|
|
||||||
async def test_recoverable_api_errors_raise_not_ready(
|
async def test_recoverable_api_errors_raise_not_ready(
|
||||||
hass, config_entry, smartthings_mock):
|
hass, config_entry, smartthings_mock):
|
||||||
"""Test config entry not ready raised for recoverable API errors."""
|
"""Test config entry not ready raised for recoverable API errors."""
|
||||||
setattr(hass.config_entries, '_entries', [config_entry])
|
setattr(hass.config_entries, '_entries', [config_entry])
|
||||||
api = smartthings_mock.return_value
|
smartthings_mock.app.side_effect = \
|
||||||
api.app.return_value = mock_coro(
|
ClientResponseError(None, None, status=500)
|
||||||
exception=ClientResponseError(None, None, status=500))
|
|
||||||
|
|
||||||
with pytest.raises(ConfigEntryNotReady):
|
with pytest.raises(ConfigEntryNotReady):
|
||||||
await smartthings.async_setup_entry(hass, config_entry)
|
await smartthings.async_setup_entry(hass, config_entry)
|
||||||
|
@ -84,11 +77,10 @@ async def test_scenes_api_errors_raise_not_ready(
|
||||||
hass, config_entry, app, installed_app, smartthings_mock):
|
hass, config_entry, app, installed_app, smartthings_mock):
|
||||||
"""Test if scenes are unauthorized we continue to load platforms."""
|
"""Test if scenes are unauthorized we continue to load platforms."""
|
||||||
setattr(hass.config_entries, '_entries', [config_entry])
|
setattr(hass.config_entries, '_entries', [config_entry])
|
||||||
api = smartthings_mock.return_value
|
smartthings_mock.app.return_value = app
|
||||||
api.app.return_value = mock_coro(return_value=app)
|
smartthings_mock.installed_app.return_value = installed_app
|
||||||
api.installed_app.return_value = mock_coro(return_value=installed_app)
|
smartthings_mock.scenes.side_effect = \
|
||||||
api.scenes.return_value = mock_coro(
|
ClientResponseError(None, None, status=500)
|
||||||
exception=ClientResponseError(None, None, status=500))
|
|
||||||
with pytest.raises(ConfigEntryNotReady):
|
with pytest.raises(ConfigEntryNotReady):
|
||||||
await smartthings.async_setup_entry(hass, config_entry)
|
await smartthings.async_setup_entry(hass, config_entry)
|
||||||
|
|
||||||
|
@ -97,9 +89,7 @@ async def test_connection_errors_raise_not_ready(
|
||||||
hass, config_entry, smartthings_mock):
|
hass, config_entry, smartthings_mock):
|
||||||
"""Test config entry not ready raised for connection errors."""
|
"""Test config entry not ready raised for connection errors."""
|
||||||
setattr(hass.config_entries, '_entries', [config_entry])
|
setattr(hass.config_entries, '_entries', [config_entry])
|
||||||
api = smartthings_mock.return_value
|
smartthings_mock.app.side_effect = ClientConnectionError()
|
||||||
api.app.return_value = mock_coro(
|
|
||||||
exception=ClientConnectionError())
|
|
||||||
|
|
||||||
with pytest.raises(ConfigEntryNotReady):
|
with pytest.raises(ConfigEntryNotReady):
|
||||||
await smartthings.async_setup_entry(hass, config_entry)
|
await smartthings.async_setup_entry(hass, config_entry)
|
||||||
|
@ -110,8 +100,7 @@ async def test_base_url_no_longer_https_does_not_load(
|
||||||
"""Test base_url no longer valid creates a new flow."""
|
"""Test base_url no longer valid creates a new flow."""
|
||||||
hass.config.api.base_url = 'http://0.0.0.0'
|
hass.config.api.base_url = 'http://0.0.0.0'
|
||||||
setattr(hass.config_entries, '_entries', [config_entry])
|
setattr(hass.config_entries, '_entries', [config_entry])
|
||||||
api = smartthings_mock.return_value
|
smartthings_mock.app.return_value = app
|
||||||
api.app.return_value = mock_coro(return_value=app)
|
|
||||||
|
|
||||||
# Assert setup returns false
|
# Assert setup returns false
|
||||||
result = await smartthings.async_setup_entry(hass, config_entry)
|
result = await smartthings.async_setup_entry(hass, config_entry)
|
||||||
|
@ -123,12 +112,10 @@ async def test_unauthorized_installed_app_raises_not_ready(
|
||||||
smartthings_mock):
|
smartthings_mock):
|
||||||
"""Test config entry not ready raised when the app isn't authorized."""
|
"""Test config entry not ready raised when the app isn't authorized."""
|
||||||
setattr(hass.config_entries, '_entries', [config_entry])
|
setattr(hass.config_entries, '_entries', [config_entry])
|
||||||
setattr(installed_app, '_installed_app_status',
|
installed_app.installed_app_status = InstalledAppStatus.PENDING
|
||||||
InstalledAppStatus.PENDING)
|
|
||||||
|
|
||||||
api = smartthings_mock.return_value
|
smartthings_mock.app.return_value = app
|
||||||
api.app.return_value = mock_coro(return_value=app)
|
smartthings_mock.installed_app.return_value = installed_app
|
||||||
api.installed_app.return_value = mock_coro(return_value=installed_app)
|
|
||||||
|
|
||||||
with pytest.raises(ConfigEntryNotReady):
|
with pytest.raises(ConfigEntryNotReady):
|
||||||
await smartthings.async_setup_entry(hass, config_entry)
|
await smartthings.async_setup_entry(hass, config_entry)
|
||||||
|
@ -139,23 +126,21 @@ async def test_scenes_unauthorized_loads_platforms(
|
||||||
device, smartthings_mock, subscription_factory):
|
device, smartthings_mock, subscription_factory):
|
||||||
"""Test if scenes are unauthorized we continue to load platforms."""
|
"""Test if scenes are unauthorized we continue to load platforms."""
|
||||||
setattr(hass.config_entries, '_entries', [config_entry])
|
setattr(hass.config_entries, '_entries', [config_entry])
|
||||||
api = smartthings_mock.return_value
|
smartthings_mock.app.return_value = app
|
||||||
api.app.return_value = mock_coro(return_value=app)
|
smartthings_mock.installed_app.return_value = installed_app
|
||||||
api.installed_app.return_value = mock_coro(return_value=installed_app)
|
smartthings_mock.devices.return_value = [device]
|
||||||
api.devices.side_effect = \
|
smartthings_mock.scenes.side_effect = \
|
||||||
lambda *args, **kwargs: mock_coro(return_value=[device])
|
ClientResponseError(None, None, status=403)
|
||||||
api.scenes.return_value = mock_coro(
|
|
||||||
exception=ClientResponseError(None, None, status=403))
|
|
||||||
mock_token = Mock()
|
mock_token = Mock()
|
||||||
mock_token.access_token.return_value = str(uuid4())
|
mock_token.access_token.return_value = str(uuid4())
|
||||||
mock_token.refresh_token.return_value = str(uuid4())
|
mock_token.refresh_token.return_value = str(uuid4())
|
||||||
api.generate_tokens.return_value = mock_coro(return_value=mock_token)
|
smartthings_mock.generate_tokens.return_value = mock_token
|
||||||
subscriptions = [subscription_factory(capability)
|
subscriptions = [subscription_factory(capability)
|
||||||
for capability in device.capabilities]
|
for capability in device.capabilities]
|
||||||
api.subscriptions.return_value = mock_coro(return_value=subscriptions)
|
smartthings_mock.subscriptions.return_value = subscriptions
|
||||||
|
|
||||||
with patch.object(hass.config_entries, 'async_forward_entry_setup',
|
with patch.object(hass.config_entries,
|
||||||
return_value=mock_coro()) as forward_mock:
|
'async_forward_entry_setup') as forward_mock:
|
||||||
assert await smartthings.async_setup_entry(hass, config_entry)
|
assert await smartthings.async_setup_entry(hass, config_entry)
|
||||||
# Assert platforms loaded
|
# Assert platforms loaded
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
@ -167,22 +152,20 @@ async def test_config_entry_loads_platforms(
|
||||||
device, smartthings_mock, subscription_factory, scene):
|
device, smartthings_mock, subscription_factory, scene):
|
||||||
"""Test config entry loads properly and proxies to platforms."""
|
"""Test config entry loads properly and proxies to platforms."""
|
||||||
setattr(hass.config_entries, '_entries', [config_entry])
|
setattr(hass.config_entries, '_entries', [config_entry])
|
||||||
api = smartthings_mock.return_value
|
smartthings_mock.app.return_value = app
|
||||||
api.app.return_value = mock_coro(return_value=app)
|
smartthings_mock.installed_app.return_value = installed_app
|
||||||
api.installed_app.return_value = mock_coro(return_value=installed_app)
|
smartthings_mock.devices.return_value = [device]
|
||||||
api.devices.side_effect = \
|
smartthings_mock.scenes.return_value = [scene]
|
||||||
lambda *args, **kwargs: mock_coro(return_value=[device])
|
|
||||||
api.scenes.return_value = mock_coro(return_value=[scene])
|
|
||||||
mock_token = Mock()
|
mock_token = Mock()
|
||||||
mock_token.access_token.return_value = str(uuid4())
|
mock_token.access_token.return_value = str(uuid4())
|
||||||
mock_token.refresh_token.return_value = str(uuid4())
|
mock_token.refresh_token.return_value = str(uuid4())
|
||||||
api.generate_tokens.return_value = mock_coro(return_value=mock_token)
|
smartthings_mock.generate_tokens.return_value = mock_token
|
||||||
subscriptions = [subscription_factory(capability)
|
subscriptions = [subscription_factory(capability)
|
||||||
for capability in device.capabilities]
|
for capability in device.capabilities]
|
||||||
api.subscriptions.return_value = mock_coro(return_value=subscriptions)
|
smartthings_mock.subscriptions.return_value = subscriptions
|
||||||
|
|
||||||
with patch.object(hass.config_entries, 'async_forward_entry_setup',
|
with patch.object(hass.config_entries,
|
||||||
return_value=mock_coro()) as forward_mock:
|
'async_forward_entry_setup') as forward_mock:
|
||||||
assert await smartthings.async_setup_entry(hass, config_entry)
|
assert await smartthings.async_setup_entry(hass, config_entry)
|
||||||
# Assert platforms loaded
|
# Assert platforms loaded
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
|
@ -196,21 +179,19 @@ async def test_config_entry_loads_unconnected_cloud(
|
||||||
setattr(hass.config_entries, '_entries', [config_entry])
|
setattr(hass.config_entries, '_entries', [config_entry])
|
||||||
hass.data[DOMAIN][CONF_CLOUDHOOK_URL] = "https://test.cloud"
|
hass.data[DOMAIN][CONF_CLOUDHOOK_URL] = "https://test.cloud"
|
||||||
hass.config.api.base_url = 'http://0.0.0.0'
|
hass.config.api.base_url = 'http://0.0.0.0'
|
||||||
api = smartthings_mock.return_value
|
smartthings_mock.app.return_value = app
|
||||||
api.app.return_value = mock_coro(return_value=app)
|
smartthings_mock.installed_app.return_value = installed_app
|
||||||
api.installed_app.return_value = mock_coro(return_value=installed_app)
|
smartthings_mock.devices.return_value = [device]
|
||||||
api.devices.side_effect = \
|
smartthings_mock.scenes.return_value = [scene]
|
||||||
lambda *args, **kwargs: mock_coro(return_value=[device])
|
|
||||||
api.scenes.return_value = mock_coro(return_value=[scene])
|
|
||||||
mock_token = Mock()
|
mock_token = Mock()
|
||||||
mock_token.access_token.return_value = str(uuid4())
|
mock_token.access_token.return_value = str(uuid4())
|
||||||
mock_token.refresh_token.return_value = str(uuid4())
|
mock_token.refresh_token.return_value = str(uuid4())
|
||||||
api.generate_tokens.return_value = mock_coro(return_value=mock_token)
|
smartthings_mock.generate_tokens.return_value = mock_token
|
||||||
subscriptions = [subscription_factory(capability)
|
subscriptions = [subscription_factory(capability)
|
||||||
for capability in device.capabilities]
|
for capability in device.capabilities]
|
||||||
api.subscriptions.return_value = mock_coro(return_value=subscriptions)
|
smartthings_mock.subscriptions.return_value = subscriptions
|
||||||
with patch.object(hass.config_entries, 'async_forward_entry_setup',
|
with patch.object(
|
||||||
return_value=mock_coro()) as forward_mock:
|
hass.config_entries, 'async_forward_entry_setup') as forward_mock:
|
||||||
assert await smartthings.async_setup_entry(hass, config_entry)
|
assert await smartthings.async_setup_entry(hass, config_entry)
|
||||||
await hass.async_block_till_done()
|
await hass.async_block_till_done()
|
||||||
assert forward_mock.call_count == len(SUPPORTED_PLATFORMS)
|
assert forward_mock.call_count == len(SUPPORTED_PLATFORMS)
|
||||||
|
@ -227,9 +208,7 @@ async def test_unload_entry(hass, config_entry):
|
||||||
hass.data[DOMAIN][DATA_BROKERS][config_entry.entry_id] = broker
|
hass.data[DOMAIN][DATA_BROKERS][config_entry.entry_id] = broker
|
||||||
|
|
||||||
with patch.object(hass.config_entries, 'async_forward_entry_unload',
|
with patch.object(hass.config_entries, 'async_forward_entry_unload',
|
||||||
return_value=mock_coro(
|
return_value=True) as forward_mock:
|
||||||
return_value=True
|
|
||||||
)) as forward_mock:
|
|
||||||
assert await smartthings.async_unload_entry(hass, config_entry)
|
assert await smartthings.async_unload_entry(hass, config_entry)
|
||||||
|
|
||||||
assert connect_disconnect.call_count == 1
|
assert connect_disconnect.call_count == 1
|
||||||
|
@ -241,15 +220,11 @@ async def test_unload_entry(hass, config_entry):
|
||||||
|
|
||||||
async def test_remove_entry(hass, config_entry, smartthings_mock):
|
async def test_remove_entry(hass, config_entry, smartthings_mock):
|
||||||
"""Test that the installed app and app are removed up."""
|
"""Test that the installed app and app are removed up."""
|
||||||
# Arrange
|
|
||||||
api = smartthings_mock.return_value
|
|
||||||
api.delete_installed_app.side_effect = lambda _: mock_coro()
|
|
||||||
api.delete_app.side_effect = lambda _: mock_coro()
|
|
||||||
# Act
|
# Act
|
||||||
await smartthings.async_remove_entry(hass, config_entry)
|
await smartthings.async_remove_entry(hass, config_entry)
|
||||||
# Assert
|
# Assert
|
||||||
assert api.delete_installed_app.call_count == 1
|
assert smartthings_mock.delete_installed_app.call_count == 1
|
||||||
assert api.delete_app.call_count == 1
|
assert smartthings_mock.delete_app.call_count == 1
|
||||||
|
|
||||||
|
|
||||||
async def test_remove_entry_cloudhook(hass, config_entry, smartthings_mock):
|
async def test_remove_entry_cloudhook(hass, config_entry, smartthings_mock):
|
||||||
|
@ -257,20 +232,15 @@ async def test_remove_entry_cloudhook(hass, config_entry, smartthings_mock):
|
||||||
# Arrange
|
# Arrange
|
||||||
setattr(hass.config_entries, '_entries', [config_entry])
|
setattr(hass.config_entries, '_entries', [config_entry])
|
||||||
hass.data[DOMAIN][CONF_CLOUDHOOK_URL] = "https://test.cloud"
|
hass.data[DOMAIN][CONF_CLOUDHOOK_URL] = "https://test.cloud"
|
||||||
api = smartthings_mock.return_value
|
|
||||||
api.delete_installed_app.side_effect = lambda _: mock_coro()
|
|
||||||
api.delete_app.side_effect = lambda _: mock_coro()
|
|
||||||
mock_async_is_logged_in = Mock(return_value=True)
|
|
||||||
mock_async_delete_cloudhook = Mock(return_value=mock_coro())
|
|
||||||
# Act
|
# Act
|
||||||
with patch.object(cloud, 'async_is_logged_in',
|
with patch.object(cloud, 'async_is_logged_in',
|
||||||
new=mock_async_is_logged_in), \
|
return_value=True) as mock_async_is_logged_in, \
|
||||||
patch.object(cloud, 'async_delete_cloudhook',
|
patch.object(cloud, 'async_delete_cloudhook') \
|
||||||
new=mock_async_delete_cloudhook):
|
as mock_async_delete_cloudhook:
|
||||||
await smartthings.async_remove_entry(hass, config_entry)
|
await smartthings.async_remove_entry(hass, config_entry)
|
||||||
# Assert
|
# Assert
|
||||||
assert api.delete_installed_app.call_count == 1
|
assert smartthings_mock.delete_installed_app.call_count == 1
|
||||||
assert api.delete_app.call_count == 1
|
assert smartthings_mock.delete_app.call_count == 1
|
||||||
assert mock_async_is_logged_in.call_count == 1
|
assert mock_async_is_logged_in.call_count == 1
|
||||||
assert mock_async_delete_cloudhook.call_count == 1
|
assert mock_async_delete_cloudhook.call_count == 1
|
||||||
|
|
||||||
|
@ -282,99 +252,87 @@ async def test_remove_entry_app_in_use(hass, config_entry, smartthings_mock):
|
||||||
data[CONF_INSTALLED_APP_ID] = str(uuid4())
|
data[CONF_INSTALLED_APP_ID] = str(uuid4())
|
||||||
entry2 = MockConfigEntry(version=2, domain=DOMAIN, data=data)
|
entry2 = MockConfigEntry(version=2, domain=DOMAIN, data=data)
|
||||||
setattr(hass.config_entries, '_entries', [config_entry, entry2])
|
setattr(hass.config_entries, '_entries', [config_entry, entry2])
|
||||||
api = smartthings_mock.return_value
|
|
||||||
api.delete_installed_app.side_effect = lambda _: mock_coro()
|
|
||||||
# Act
|
# Act
|
||||||
await smartthings.async_remove_entry(hass, config_entry)
|
await smartthings.async_remove_entry(hass, config_entry)
|
||||||
# Assert
|
# Assert
|
||||||
assert api.delete_installed_app.call_count == 1
|
assert smartthings_mock.delete_installed_app.call_count == 1
|
||||||
assert api.delete_app.call_count == 0
|
assert smartthings_mock.delete_app.call_count == 0
|
||||||
|
|
||||||
|
|
||||||
async def test_remove_entry_already_deleted(
|
async def test_remove_entry_already_deleted(
|
||||||
hass, config_entry, smartthings_mock):
|
hass, config_entry, smartthings_mock):
|
||||||
"""Test handles when the apps have already been removed."""
|
"""Test handles when the apps have already been removed."""
|
||||||
# Arrange
|
# Arrange
|
||||||
api = smartthings_mock.return_value
|
smartthings_mock.delete_installed_app.side_effect = ClientResponseError(
|
||||||
api.delete_installed_app.side_effect = lambda _: mock_coro(
|
None, None, status=403)
|
||||||
exception=ClientResponseError(None, None, status=403))
|
smartthings_mock.delete_app.side_effect = ClientResponseError(
|
||||||
api.delete_app.side_effect = lambda _: mock_coro(
|
None, None, status=403)
|
||||||
exception=ClientResponseError(None, None, status=403))
|
|
||||||
# Act
|
# Act
|
||||||
await smartthings.async_remove_entry(hass, config_entry)
|
await smartthings.async_remove_entry(hass, config_entry)
|
||||||
# Assert
|
# Assert
|
||||||
assert api.delete_installed_app.call_count == 1
|
assert smartthings_mock.delete_installed_app.call_count == 1
|
||||||
assert api.delete_app.call_count == 1
|
assert smartthings_mock.delete_app.call_count == 1
|
||||||
|
|
||||||
|
|
||||||
async def test_remove_entry_installedapp_api_error(
|
async def test_remove_entry_installedapp_api_error(
|
||||||
hass, config_entry, smartthings_mock):
|
hass, config_entry, smartthings_mock):
|
||||||
"""Test raises exceptions removing the installed app."""
|
"""Test raises exceptions removing the installed app."""
|
||||||
# Arrange
|
# Arrange
|
||||||
api = smartthings_mock.return_value
|
smartthings_mock.delete_installed_app.side_effect = \
|
||||||
api.delete_installed_app.side_effect = lambda _: mock_coro(
|
ClientResponseError(None, None, status=500)
|
||||||
exception=ClientResponseError(None, None, status=500))
|
|
||||||
# Act
|
# Act
|
||||||
with pytest.raises(ClientResponseError):
|
with pytest.raises(ClientResponseError):
|
||||||
await smartthings.async_remove_entry(hass, config_entry)
|
await smartthings.async_remove_entry(hass, config_entry)
|
||||||
# Assert
|
# Assert
|
||||||
assert api.delete_installed_app.call_count == 1
|
assert smartthings_mock.delete_installed_app.call_count == 1
|
||||||
assert api.delete_app.call_count == 0
|
assert smartthings_mock.delete_app.call_count == 0
|
||||||
|
|
||||||
|
|
||||||
async def test_remove_entry_installedapp_unknown_error(
|
async def test_remove_entry_installedapp_unknown_error(
|
||||||
hass, config_entry, smartthings_mock):
|
hass, config_entry, smartthings_mock):
|
||||||
"""Test raises exceptions removing the installed app."""
|
"""Test raises exceptions removing the installed app."""
|
||||||
# Arrange
|
# Arrange
|
||||||
api = smartthings_mock.return_value
|
smartthings_mock.delete_installed_app.side_effect = Exception
|
||||||
api.delete_installed_app.side_effect = lambda _: mock_coro(
|
|
||||||
exception=Exception)
|
|
||||||
# Act
|
# Act
|
||||||
with pytest.raises(Exception):
|
with pytest.raises(Exception):
|
||||||
await smartthings.async_remove_entry(hass, config_entry)
|
await smartthings.async_remove_entry(hass, config_entry)
|
||||||
# Assert
|
# Assert
|
||||||
assert api.delete_installed_app.call_count == 1
|
assert smartthings_mock.delete_installed_app.call_count == 1
|
||||||
assert api.delete_app.call_count == 0
|
assert smartthings_mock.delete_app.call_count == 0
|
||||||
|
|
||||||
|
|
||||||
async def test_remove_entry_app_api_error(
|
async def test_remove_entry_app_api_error(
|
||||||
hass, config_entry, smartthings_mock):
|
hass, config_entry, smartthings_mock):
|
||||||
"""Test raises exceptions removing the app."""
|
"""Test raises exceptions removing the app."""
|
||||||
# Arrange
|
# Arrange
|
||||||
api = smartthings_mock.return_value
|
smartthings_mock.delete_app.side_effect = \
|
||||||
api.delete_installed_app.side_effect = lambda _: mock_coro()
|
ClientResponseError(None, None, status=500)
|
||||||
api.delete_app.side_effect = lambda _: mock_coro(
|
|
||||||
exception=ClientResponseError(None, None, status=500))
|
|
||||||
# Act
|
# Act
|
||||||
with pytest.raises(ClientResponseError):
|
with pytest.raises(ClientResponseError):
|
||||||
await smartthings.async_remove_entry(hass, config_entry)
|
await smartthings.async_remove_entry(hass, config_entry)
|
||||||
# Assert
|
# Assert
|
||||||
assert api.delete_installed_app.call_count == 1
|
assert smartthings_mock.delete_installed_app.call_count == 1
|
||||||
assert api.delete_app.call_count == 1
|
assert smartthings_mock.delete_app.call_count == 1
|
||||||
|
|
||||||
|
|
||||||
async def test_remove_entry_app_unknown_error(
|
async def test_remove_entry_app_unknown_error(
|
||||||
hass, config_entry, smartthings_mock):
|
hass, config_entry, smartthings_mock):
|
||||||
"""Test raises exceptions removing the app."""
|
"""Test raises exceptions removing the app."""
|
||||||
# Arrange
|
# Arrange
|
||||||
api = smartthings_mock.return_value
|
smartthings_mock.delete_app.side_effect = Exception
|
||||||
api.delete_installed_app.side_effect = lambda _: mock_coro()
|
|
||||||
api.delete_app.side_effect = lambda _: mock_coro(
|
|
||||||
exception=Exception)
|
|
||||||
# Act
|
# Act
|
||||||
with pytest.raises(Exception):
|
with pytest.raises(Exception):
|
||||||
await smartthings.async_remove_entry(hass, config_entry)
|
await smartthings.async_remove_entry(hass, config_entry)
|
||||||
# Assert
|
# Assert
|
||||||
assert api.delete_installed_app.call_count == 1
|
assert smartthings_mock.delete_installed_app.call_count == 1
|
||||||
assert api.delete_app.call_count == 1
|
assert smartthings_mock.delete_app.call_count == 1
|
||||||
|
|
||||||
|
|
||||||
async def test_broker_regenerates_token(
|
async def test_broker_regenerates_token(
|
||||||
hass, config_entry):
|
hass, config_entry):
|
||||||
"""Test the device broker regenerates the refresh token."""
|
"""Test the device broker regenerates the refresh token."""
|
||||||
token = Mock()
|
token = Mock(OAuthToken)
|
||||||
token.refresh_token = str(uuid4())
|
token.refresh_token = str(uuid4())
|
||||||
token.refresh.return_value = mock_coro()
|
|
||||||
stored_action = None
|
stored_action = None
|
||||||
|
|
||||||
def async_track_time_interval(hass, action, interval):
|
def async_track_time_interval(hass, action, interval):
|
||||||
|
|
|
@ -40,7 +40,7 @@ async def test_scene_activate(hass, scene):
|
||||||
assert state.attributes['color'] == scene.color
|
assert state.attributes['color'] == scene.color
|
||||||
assert state.attributes['location_id'] == scene.location_id
|
assert state.attributes['location_id'] == scene.location_id
|
||||||
# pylint: disable=protected-access
|
# pylint: disable=protected-access
|
||||||
assert scene._api.execute_scene.call_count == 1 # type: ignore
|
assert scene.execute.call_count == 1 # type: ignore
|
||||||
|
|
||||||
|
|
||||||
async def test_unload_config_entry(hass, scene):
|
async def test_unload_config_entry(hass, scene):
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
"""Tests for the smartapp module."""
|
"""Tests for the smartapp module."""
|
||||||
from unittest.mock import Mock, patch
|
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
|
from asynctest import CoroutineMock, Mock, patch
|
||||||
from pysmartthings import AppEntity, Capability
|
from pysmartthings import AppEntity, Capability
|
||||||
|
|
||||||
from homeassistant.components.smartthings import smartapp
|
from homeassistant.components.smartthings import smartapp
|
||||||
|
@ -9,8 +9,6 @@ from homeassistant.components.smartthings.const import (
|
||||||
CONF_INSTALLED_APP_ID, CONF_INSTALLED_APPS, CONF_LOCATION_ID,
|
CONF_INSTALLED_APP_ID, CONF_INSTALLED_APPS, CONF_LOCATION_ID,
|
||||||
CONF_REFRESH_TOKEN, DATA_MANAGER, DOMAIN)
|
CONF_REFRESH_TOKEN, DATA_MANAGER, DOMAIN)
|
||||||
|
|
||||||
from tests.common import mock_coro
|
|
||||||
|
|
||||||
|
|
||||||
async def test_update_app(hass, app):
|
async def test_update_app(hass, app):
|
||||||
"""Test update_app does not save if app is current."""
|
"""Test update_app does not save if app is current."""
|
||||||
|
@ -20,10 +18,8 @@ async def test_update_app(hass, app):
|
||||||
|
|
||||||
async def test_update_app_updated_needed(hass, app):
|
async def test_update_app_updated_needed(hass, app):
|
||||||
"""Test update_app updates when an app is needed."""
|
"""Test update_app updates when an app is needed."""
|
||||||
mock_app = Mock(spec=AppEntity)
|
mock_app = Mock(AppEntity)
|
||||||
mock_app.app_name = 'Test'
|
mock_app.app_name = 'Test'
|
||||||
mock_app.refresh.return_value = mock_coro()
|
|
||||||
mock_app.save.return_value = mock_coro()
|
|
||||||
|
|
||||||
await smartapp.update_app(hass, mock_app)
|
await smartapp.update_app(hass, mock_app)
|
||||||
|
|
||||||
|
@ -64,7 +60,6 @@ async def test_smartapp_install_creates_flow(
|
||||||
"""Test installation creates flow."""
|
"""Test installation creates flow."""
|
||||||
# Arrange
|
# Arrange
|
||||||
setattr(hass.config_entries, '_entries', [config_entry])
|
setattr(hass.config_entries, '_entries', [config_entry])
|
||||||
api = smartthings_mock.return_value
|
|
||||||
app = Mock()
|
app = Mock()
|
||||||
app.app_id = config_entry.data['app_id']
|
app.app_id = config_entry.data['app_id']
|
||||||
request = Mock()
|
request = Mock()
|
||||||
|
@ -77,8 +72,7 @@ async def test_smartapp_install_creates_flow(
|
||||||
device_factory('', [Capability.switch, Capability.switch_level]),
|
device_factory('', [Capability.switch, Capability.switch_level]),
|
||||||
device_factory('', [Capability.switch])
|
device_factory('', [Capability.switch])
|
||||||
]
|
]
|
||||||
api.devices = Mock()
|
smartthings_mock.devices.return_value = devices
|
||||||
api.devices.return_value = mock_coro(return_value=devices)
|
|
||||||
# Act
|
# Act
|
||||||
await smartapp.smartapp_install(hass, request, None, app)
|
await smartapp.smartapp_install(hass, request, None, app)
|
||||||
# Assert
|
# Assert
|
||||||
|
@ -131,8 +125,7 @@ async def test_smartapp_uninstall(hass, config_entry):
|
||||||
request = Mock()
|
request = Mock()
|
||||||
request.installed_app_id = config_entry.data['installed_app_id']
|
request.installed_app_id = config_entry.data['installed_app_id']
|
||||||
|
|
||||||
with patch.object(hass.config_entries, 'async_remove',
|
with patch.object(hass.config_entries, 'async_remove') as remove:
|
||||||
return_value=mock_coro()) as remove:
|
|
||||||
await smartapp.smartapp_uninstall(hass, request, None, app)
|
await smartapp.smartapp_uninstall(hass, request, None, app)
|
||||||
assert remove.call_count == 1
|
assert remove.call_count == 1
|
||||||
|
|
||||||
|
@ -140,12 +133,11 @@ async def test_smartapp_uninstall(hass, config_entry):
|
||||||
async def test_smartapp_webhook(hass):
|
async def test_smartapp_webhook(hass):
|
||||||
"""Test the smartapp webhook calls the manager."""
|
"""Test the smartapp webhook calls the manager."""
|
||||||
manager = Mock()
|
manager = Mock()
|
||||||
manager.handle_request = Mock()
|
manager.handle_request = CoroutineMock(return_value={})
|
||||||
manager.handle_request.return_value = mock_coro(return_value={})
|
|
||||||
hass.data[DOMAIN][DATA_MANAGER] = manager
|
hass.data[DOMAIN][DATA_MANAGER] = manager
|
||||||
request = Mock()
|
request = Mock()
|
||||||
request.headers = []
|
request.headers = []
|
||||||
request.json.return_value = mock_coro(return_value={})
|
request.json = CoroutineMock(return_value={})
|
||||||
result = await smartapp.smartapp_webhook(hass, '', request)
|
result = await smartapp.smartapp_webhook(hass, '', request)
|
||||||
|
|
||||||
assert result.body == b'{}'
|
assert result.body == b'{}'
|
||||||
|
@ -154,15 +146,11 @@ async def test_smartapp_webhook(hass):
|
||||||
async def test_smartapp_sync_subscriptions(
|
async def test_smartapp_sync_subscriptions(
|
||||||
hass, smartthings_mock, device_factory, subscription_factory):
|
hass, smartthings_mock, device_factory, subscription_factory):
|
||||||
"""Test synchronization adds and removes."""
|
"""Test synchronization adds and removes."""
|
||||||
api = smartthings_mock.return_value
|
smartthings_mock.subscriptions.return_value = [
|
||||||
api.delete_subscription.side_effect = lambda loc_id, sub_id: mock_coro()
|
|
||||||
api.create_subscription.side_effect = lambda sub: mock_coro()
|
|
||||||
subscriptions = [
|
|
||||||
subscription_factory(Capability.thermostat),
|
subscription_factory(Capability.thermostat),
|
||||||
subscription_factory(Capability.switch),
|
subscription_factory(Capability.switch),
|
||||||
subscription_factory(Capability.switch_level)
|
subscription_factory(Capability.switch_level)
|
||||||
]
|
]
|
||||||
api.subscriptions.return_value = mock_coro(return_value=subscriptions)
|
|
||||||
devices = [
|
devices = [
|
||||||
device_factory('', [Capability.battery, 'ping']),
|
device_factory('', [Capability.battery, 'ping']),
|
||||||
device_factory('', [Capability.switch, Capability.switch_level]),
|
device_factory('', [Capability.switch, Capability.switch_level]),
|
||||||
|
@ -172,23 +160,19 @@ async def test_smartapp_sync_subscriptions(
|
||||||
await smartapp.smartapp_sync_subscriptions(
|
await smartapp.smartapp_sync_subscriptions(
|
||||||
hass, str(uuid4()), str(uuid4()), str(uuid4()), devices)
|
hass, str(uuid4()), str(uuid4()), str(uuid4()), devices)
|
||||||
|
|
||||||
assert api.subscriptions.call_count == 1
|
assert smartthings_mock.subscriptions.call_count == 1
|
||||||
assert api.delete_subscription.call_count == 1
|
assert smartthings_mock.delete_subscription.call_count == 1
|
||||||
assert api.create_subscription.call_count == 1
|
assert smartthings_mock.create_subscription.call_count == 1
|
||||||
|
|
||||||
|
|
||||||
async def test_smartapp_sync_subscriptions_up_to_date(
|
async def test_smartapp_sync_subscriptions_up_to_date(
|
||||||
hass, smartthings_mock, device_factory, subscription_factory):
|
hass, smartthings_mock, device_factory, subscription_factory):
|
||||||
"""Test synchronization does nothing when current."""
|
"""Test synchronization does nothing when current."""
|
||||||
api = smartthings_mock.return_value
|
smartthings_mock.subscriptions.return_value = [
|
||||||
api.delete_subscription.side_effect = lambda loc_id, sub_id: mock_coro()
|
|
||||||
api.create_subscription.side_effect = lambda sub: mock_coro()
|
|
||||||
subscriptions = [
|
|
||||||
subscription_factory(Capability.battery),
|
subscription_factory(Capability.battery),
|
||||||
subscription_factory(Capability.switch),
|
subscription_factory(Capability.switch),
|
||||||
subscription_factory(Capability.switch_level)
|
subscription_factory(Capability.switch_level)
|
||||||
]
|
]
|
||||||
api.subscriptions.return_value = mock_coro(return_value=subscriptions)
|
|
||||||
devices = [
|
devices = [
|
||||||
device_factory('', [Capability.battery, 'ping']),
|
device_factory('', [Capability.battery, 'ping']),
|
||||||
device_factory('', [Capability.switch, Capability.switch_level]),
|
device_factory('', [Capability.switch, Capability.switch_level]),
|
||||||
|
@ -198,25 +182,21 @@ async def test_smartapp_sync_subscriptions_up_to_date(
|
||||||
await smartapp.smartapp_sync_subscriptions(
|
await smartapp.smartapp_sync_subscriptions(
|
||||||
hass, str(uuid4()), str(uuid4()), str(uuid4()), devices)
|
hass, str(uuid4()), str(uuid4()), str(uuid4()), devices)
|
||||||
|
|
||||||
assert api.subscriptions.call_count == 1
|
assert smartthings_mock.subscriptions.call_count == 1
|
||||||
assert api.delete_subscription.call_count == 0
|
assert smartthings_mock.delete_subscription.call_count == 0
|
||||||
assert api.create_subscription.call_count == 0
|
assert smartthings_mock.create_subscription.call_count == 0
|
||||||
|
|
||||||
|
|
||||||
async def test_smartapp_sync_subscriptions_handles_exceptions(
|
async def test_smartapp_sync_subscriptions_handles_exceptions(
|
||||||
hass, smartthings_mock, device_factory, subscription_factory):
|
hass, smartthings_mock, device_factory, subscription_factory):
|
||||||
"""Test synchronization does nothing when current."""
|
"""Test synchronization does nothing when current."""
|
||||||
api = smartthings_mock.return_value
|
smartthings_mock.delete_subscription.side_effect = Exception
|
||||||
api.delete_subscription.side_effect = \
|
smartthings_mock.create_subscription.side_effect = Exception
|
||||||
lambda loc_id, sub_id: mock_coro(exception=Exception)
|
smartthings_mock.subscriptions.return_value = [
|
||||||
api.create_subscription.side_effect = \
|
|
||||||
lambda sub: mock_coro(exception=Exception)
|
|
||||||
subscriptions = [
|
|
||||||
subscription_factory(Capability.battery),
|
subscription_factory(Capability.battery),
|
||||||
subscription_factory(Capability.switch),
|
subscription_factory(Capability.switch),
|
||||||
subscription_factory(Capability.switch_level)
|
subscription_factory(Capability.switch_level)
|
||||||
]
|
]
|
||||||
api.subscriptions.return_value = mock_coro(return_value=subscriptions)
|
|
||||||
devices = [
|
devices = [
|
||||||
device_factory('', [Capability.thermostat, 'ping']),
|
device_factory('', [Capability.thermostat, 'ping']),
|
||||||
device_factory('', [Capability.switch, Capability.switch_level]),
|
device_factory('', [Capability.switch, Capability.switch_level]),
|
||||||
|
@ -226,6 +206,6 @@ async def test_smartapp_sync_subscriptions_handles_exceptions(
|
||||||
await smartapp.smartapp_sync_subscriptions(
|
await smartapp.smartapp_sync_subscriptions(
|
||||||
hass, str(uuid4()), str(uuid4()), str(uuid4()), devices)
|
hass, str(uuid4()), str(uuid4()), str(uuid4()), devices)
|
||||||
|
|
||||||
assert api.subscriptions.call_count == 1
|
assert smartthings_mock.subscriptions.call_count == 1
|
||||||
assert api.delete_subscription.call_count == 1
|
assert smartthings_mock.delete_subscription.call_count == 1
|
||||||
assert api.create_subscription.call_count == 1
|
assert smartthings_mock.create_subscription.call_count == 1
|
||||||
|
|
Loading…
Add table
Reference in a new issue