Improve SmartThings test mocking (#25028)

* Migrate to asynctest

* Simplify mock access

* Use mocks
This commit is contained in:
Andrew Sayre 2019-07-08 22:39:55 -04:00 committed by GitHub
parent 2fbbcafaed
commit a31e49c857
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 186 additions and 336 deletions

View file

@ -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

View file

@ -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:

View file

@ -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):

View file

@ -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):

View file

@ -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