core/tests/components/smartthings/test_smartapp.py

187 lines
6.4 KiB
Python

"""Tests for the smartapp module."""
from unittest.mock import AsyncMock, Mock, patch
from uuid import uuid4
from pysmartthings import CAPABILITIES, AppEntity, Capability
import pytest
from homeassistant.components.smartthings import smartapp
from homeassistant.components.smartthings.const import (
CONF_REFRESH_TOKEN,
DATA_MANAGER,
DOMAIN,
)
from homeassistant.core import HomeAssistant
from tests.common import MockConfigEntry
async def test_update_app(hass: HomeAssistant, app) -> None:
"""Test update_app does not save if app is current."""
await smartapp.update_app(hass, app)
assert app.save.call_count == 0
async def test_update_app_updated_needed(hass: HomeAssistant, app) -> None:
"""Test update_app updates when an app is needed."""
mock_app = Mock(AppEntity)
mock_app.app_name = "Test"
await smartapp.update_app(hass, mock_app)
assert mock_app.save.call_count == 1
assert mock_app.app_name == "Test"
assert mock_app.display_name == app.display_name
assert mock_app.description == app.description
assert mock_app.webhook_target_url == app.webhook_target_url
assert mock_app.app_type == app.app_type
assert mock_app.single_instance == app.single_instance
assert mock_app.classifications == app.classifications
async def test_smartapp_update_saves_token(
hass: HomeAssistant, smartthings_mock, location, device_factory
) -> None:
"""Test update saves token."""
# Arrange
entry = MockConfigEntry(
domain=DOMAIN, data={"installed_app_id": str(uuid4()), "app_id": str(uuid4())}
)
entry.add_to_hass(hass)
app = Mock()
app.app_id = entry.data["app_id"]
request = Mock()
request.installed_app_id = entry.data["installed_app_id"]
request.auth_token = str(uuid4())
request.refresh_token = str(uuid4())
request.location_id = location.location_id
# Act
await smartapp.smartapp_update(hass, request, None, app)
# Assert
assert entry.data[CONF_REFRESH_TOKEN] == request.refresh_token
async def test_smartapp_uninstall(hass: HomeAssistant, config_entry) -> None:
"""Test the config entry is unloaded when the app is uninstalled."""
config_entry.add_to_hass(hass)
app = Mock()
app.app_id = config_entry.data["app_id"]
request = Mock()
request.installed_app_id = config_entry.data["installed_app_id"]
with patch.object(hass.config_entries, "async_remove") as remove:
await smartapp.smartapp_uninstall(hass, request, None, app)
assert remove.call_count == 1
async def test_smartapp_webhook(hass: HomeAssistant) -> None:
"""Test the smartapp webhook calls the manager."""
manager = Mock()
manager.handle_request = AsyncMock(return_value={})
hass.data[DOMAIN][DATA_MANAGER] = manager
request = Mock()
request.headers = []
request.json = AsyncMock(return_value={})
result = await smartapp.smartapp_webhook(hass, "", request)
assert result.body == b"{}"
async def test_smartapp_sync_subscriptions(
hass: HomeAssistant, smartthings_mock, device_factory, subscription_factory
) -> None:
"""Test synchronization adds and removes and ignores unused."""
smartthings_mock.subscriptions.return_value = [
subscription_factory(Capability.thermostat),
subscription_factory(Capability.switch),
subscription_factory(Capability.switch_level),
]
devices = [
device_factory("", [Capability.battery, "ping"]),
device_factory("", [Capability.switch, Capability.switch_level]),
device_factory("", [Capability.switch, Capability.execute]),
]
await smartapp.smartapp_sync_subscriptions(
hass, str(uuid4()), str(uuid4()), str(uuid4()), devices
)
assert smartthings_mock.subscriptions.call_count == 1
assert smartthings_mock.delete_subscription.call_count == 1
assert smartthings_mock.create_subscription.call_count == 1
async def test_smartapp_sync_subscriptions_up_to_date(
hass: HomeAssistant, smartthings_mock, device_factory, subscription_factory
) -> None:
"""Test synchronization does nothing when current."""
smartthings_mock.subscriptions.return_value = [
subscription_factory(Capability.battery),
subscription_factory(Capability.switch),
subscription_factory(Capability.switch_level),
]
devices = [
device_factory("", [Capability.battery, "ping"]),
device_factory("", [Capability.switch, Capability.switch_level]),
device_factory("", [Capability.switch]),
]
await smartapp.smartapp_sync_subscriptions(
hass, str(uuid4()), str(uuid4()), str(uuid4()), devices
)
assert smartthings_mock.subscriptions.call_count == 1
assert smartthings_mock.delete_subscription.call_count == 0
assert smartthings_mock.create_subscription.call_count == 0
async def test_smartapp_sync_subscriptions_limit_warning(
hass: HomeAssistant,
smartthings_mock,
device_factory,
subscription_factory,
caplog: pytest.LogCaptureFixture,
) -> None:
"""Test synchronization over the limit logs a warning."""
smartthings_mock.subscriptions.return_value = []
devices = [
device_factory("", CAPABILITIES),
]
await smartapp.smartapp_sync_subscriptions(
hass, str(uuid4()), str(uuid4()), str(uuid4()), devices
)
assert (
"Some device attributes may not receive push updates and there may be "
"subscription creation failures" in caplog.text
)
async def test_smartapp_sync_subscriptions_handles_exceptions(
hass: HomeAssistant, smartthings_mock, device_factory, subscription_factory
) -> None:
"""Test synchronization does nothing when current."""
smartthings_mock.delete_subscription.side_effect = Exception
smartthings_mock.create_subscription.side_effect = Exception
smartthings_mock.subscriptions.return_value = [
subscription_factory(Capability.battery),
subscription_factory(Capability.switch),
subscription_factory(Capability.switch_level),
]
devices = [
device_factory("", [Capability.thermostat, "ping"]),
device_factory("", [Capability.switch, Capability.switch_level]),
device_factory("", [Capability.switch]),
]
await smartapp.smartapp_sync_subscriptions(
hass, str(uuid4()), str(uuid4()), str(uuid4()), devices
)
assert smartthings_mock.subscriptions.call_count == 1
assert smartthings_mock.delete_subscription.call_count == 1
assert smartthings_mock.create_subscription.call_count == 1