Allow managing cloud webhook (#18672)
* Add cloud webhook support * Simplify payload * Add cloud http api tests * Fix tests * Lint * Handle cloud webhooks * Fix things * Fix name * Rename it to cloudhook * Final rename * Final final rename? * Fix docstring * More tests * Lint * Add types * Fix things
This commit is contained in:
parent
4a661e351f
commit
7848381f43
15 changed files with 611 additions and 39 deletions
33
tests/components/cloud/test_cloud_api.py
Normal file
33
tests/components/cloud/test_cloud_api.py
Normal file
|
@ -0,0 +1,33 @@
|
|||
"""Test cloud API."""
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.cloud import cloud_api
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def mock_check_token():
|
||||
"""Mock check token."""
|
||||
with patch('homeassistant.components.cloud.auth_api.'
|
||||
'check_token') as mock_check_token:
|
||||
yield mock_check_token
|
||||
|
||||
|
||||
async def test_create_cloudhook(hass, aioclient_mock):
|
||||
"""Test creating a cloudhook."""
|
||||
aioclient_mock.post('https://example.com/bla', json={
|
||||
'cloudhook_id': 'mock-webhook',
|
||||
'url': 'https://blabla'
|
||||
})
|
||||
cloud = Mock(
|
||||
hass=hass,
|
||||
id_token='mock-id-token',
|
||||
cloudhook_create_url='https://example.com/bla',
|
||||
)
|
||||
resp = await cloud_api.async_create_cloudhook(cloud)
|
||||
assert len(aioclient_mock.mock_calls) == 1
|
||||
assert await resp.json() == {
|
||||
'cloudhook_id': 'mock-webhook',
|
||||
'url': 'https://blabla'
|
||||
}
|
70
tests/components/cloud/test_cloudhooks.py
Normal file
70
tests/components/cloud/test_cloudhooks.py
Normal file
|
@ -0,0 +1,70 @@
|
|||
"""Test cloud cloudhooks."""
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant.components.cloud import prefs, cloudhooks
|
||||
|
||||
from tests.common import mock_coro
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_cloudhooks(hass):
|
||||
"""Mock cloudhooks class."""
|
||||
cloud = Mock()
|
||||
cloud.hass = hass
|
||||
cloud.hass.async_add_executor_job = Mock(return_value=mock_coro())
|
||||
cloud.iot = Mock(async_send_message=Mock(return_value=mock_coro()))
|
||||
cloud.cloudhook_create_url = 'https://webhook-create.url'
|
||||
cloud.prefs = prefs.CloudPreferences(hass)
|
||||
hass.loop.run_until_complete(cloud.prefs.async_initialize(True))
|
||||
return cloudhooks.Cloudhooks(cloud)
|
||||
|
||||
|
||||
async def test_enable(mock_cloudhooks, aioclient_mock):
|
||||
"""Test enabling cloudhooks."""
|
||||
aioclient_mock.post('https://webhook-create.url', json={
|
||||
'cloudhook_id': 'mock-cloud-id',
|
||||
'url': 'https://hooks.nabu.casa/ZXCZCXZ',
|
||||
})
|
||||
|
||||
hook = {
|
||||
'webhook_id': 'mock-webhook-id',
|
||||
'cloudhook_id': 'mock-cloud-id',
|
||||
'cloudhook_url': 'https://hooks.nabu.casa/ZXCZCXZ',
|
||||
}
|
||||
|
||||
assert hook == await mock_cloudhooks.async_create('mock-webhook-id')
|
||||
|
||||
assert mock_cloudhooks.cloud.prefs.cloudhooks == {
|
||||
'mock-webhook-id': hook
|
||||
}
|
||||
|
||||
publish_calls = mock_cloudhooks.cloud.iot.async_send_message.mock_calls
|
||||
assert len(publish_calls) == 1
|
||||
assert publish_calls[0][1][0] == 'webhook-register'
|
||||
assert publish_calls[0][1][1] == {
|
||||
'cloudhook_ids': ['mock-cloud-id']
|
||||
}
|
||||
|
||||
|
||||
async def test_disable(mock_cloudhooks):
|
||||
"""Test disabling cloudhooks."""
|
||||
mock_cloudhooks.cloud.prefs._prefs['cloudhooks'] = {
|
||||
'mock-webhook-id': {
|
||||
'webhook_id': 'mock-webhook-id',
|
||||
'cloudhook_id': 'mock-cloud-id',
|
||||
'cloudhook_url': 'https://hooks.nabu.casa/ZXCZCXZ',
|
||||
}
|
||||
}
|
||||
|
||||
await mock_cloudhooks.async_delete('mock-webhook-id')
|
||||
|
||||
assert mock_cloudhooks.cloud.prefs.cloudhooks == {}
|
||||
|
||||
publish_calls = mock_cloudhooks.cloud.iot.async_send_message.mock_calls
|
||||
assert len(publish_calls) == 1
|
||||
assert publish_calls[0][1][0] == 'webhook-register'
|
||||
assert publish_calls[0][1][1] == {
|
||||
'cloudhook_ids': []
|
||||
}
|
|
@ -527,3 +527,45 @@ async def test_websocket_update_preferences(hass, hass_ws_client,
|
|||
assert not setup_api[PREF_ENABLE_GOOGLE]
|
||||
assert not setup_api[PREF_ENABLE_ALEXA]
|
||||
assert not setup_api[PREF_GOOGLE_ALLOW_UNLOCK]
|
||||
|
||||
|
||||
async def test_enabling_webhook(hass, hass_ws_client, setup_api):
|
||||
"""Test we call right code to enable webhooks."""
|
||||
hass.data[DOMAIN].id_token = jwt.encode({
|
||||
'email': 'hello@home-assistant.io',
|
||||
'custom:sub-exp': '2018-01-03'
|
||||
}, 'test')
|
||||
client = await hass_ws_client(hass)
|
||||
with patch('homeassistant.components.cloud.cloudhooks.Cloudhooks'
|
||||
'.async_create', return_value=mock_coro()) as mock_enable:
|
||||
await client.send_json({
|
||||
'id': 5,
|
||||
'type': 'cloud/cloudhook/create',
|
||||
'webhook_id': 'mock-webhook-id',
|
||||
})
|
||||
response = await client.receive_json()
|
||||
assert response['success']
|
||||
|
||||
assert len(mock_enable.mock_calls) == 1
|
||||
assert mock_enable.mock_calls[0][1][0] == 'mock-webhook-id'
|
||||
|
||||
|
||||
async def test_disabling_webhook(hass, hass_ws_client, setup_api):
|
||||
"""Test we call right code to disable webhooks."""
|
||||
hass.data[DOMAIN].id_token = jwt.encode({
|
||||
'email': 'hello@home-assistant.io',
|
||||
'custom:sub-exp': '2018-01-03'
|
||||
}, 'test')
|
||||
client = await hass_ws_client(hass)
|
||||
with patch('homeassistant.components.cloud.cloudhooks.Cloudhooks'
|
||||
'.async_delete', return_value=mock_coro()) as mock_disable:
|
||||
await client.send_json({
|
||||
'id': 5,
|
||||
'type': 'cloud/cloudhook/delete',
|
||||
'webhook_id': 'mock-webhook-id',
|
||||
})
|
||||
response = await client.receive_json()
|
||||
assert response['success']
|
||||
|
||||
assert len(mock_disable.mock_calls) == 1
|
||||
assert mock_disable.mock_calls[0][1][0] == 'mock-webhook-id'
|
||||
|
|
|
@ -30,7 +30,8 @@ def test_constructor_loads_info_from_constant():
|
|||
'region': 'test-region',
|
||||
'relayer': 'test-relayer',
|
||||
'google_actions_sync_url': 'test-google_actions_sync_url',
|
||||
'subscription_info_url': 'test-subscription-info-url'
|
||||
'subscription_info_url': 'test-subscription-info-url',
|
||||
'cloudhook_create_url': 'test-cloudhook_create_url',
|
||||
}
|
||||
}):
|
||||
result = yield from cloud.async_setup(hass, {
|
||||
|
@ -46,6 +47,7 @@ def test_constructor_loads_info_from_constant():
|
|||
assert cl.relayer == 'test-relayer'
|
||||
assert cl.google_actions_sync_url == 'test-google_actions_sync_url'
|
||||
assert cl.subscription_info_url == 'test-subscription-info-url'
|
||||
assert cl.cloudhook_create_url == 'test-cloudhook_create_url'
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
import asyncio
|
||||
from unittest.mock import patch, MagicMock, PropertyMock
|
||||
|
||||
from aiohttp import WSMsgType, client_exceptions
|
||||
from aiohttp import WSMsgType, client_exceptions, web
|
||||
import pytest
|
||||
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
@ -406,3 +406,48 @@ async def test_refresh_token_expired(hass):
|
|||
|
||||
assert len(mock_check_token.mock_calls) == 1
|
||||
assert len(mock_create.mock_calls) == 1
|
||||
|
||||
|
||||
async def test_webhook_msg(hass):
|
||||
"""Test webhook msg."""
|
||||
cloud = Cloud(hass, MODE_DEV, None, None)
|
||||
await cloud.prefs.async_initialize(True)
|
||||
await cloud.prefs.async_update(cloudhooks={
|
||||
'hello': {
|
||||
'webhook_id': 'mock-webhook-id',
|
||||
'cloudhook_id': 'mock-cloud-id'
|
||||
}
|
||||
})
|
||||
|
||||
received = []
|
||||
|
||||
async def handler(hass, webhook_id, request):
|
||||
"""Handle a webhook."""
|
||||
received.append(request)
|
||||
return web.json_response({'from': 'handler'})
|
||||
|
||||
hass.components.webhook.async_register(
|
||||
'test', 'Test', 'mock-webhook-id', handler)
|
||||
|
||||
response = await iot.async_handle_webhook(hass, cloud, {
|
||||
'cloudhook_id': 'mock-cloud-id',
|
||||
'body': '{"hello": "world"}',
|
||||
'headers': {
|
||||
'content-type': 'application/json'
|
||||
},
|
||||
'method': 'POST',
|
||||
'query': None,
|
||||
})
|
||||
|
||||
assert response == {
|
||||
'status': 200,
|
||||
'body': '{"from": "handler"}',
|
||||
'headers': {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
}
|
||||
|
||||
assert len(received) == 1
|
||||
assert await received[0].json() == {
|
||||
'hello': 'world'
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue