244 lines
7.2 KiB
Python
244 lines
7.2 KiB
Python
"""Test the cloud.iot module."""
|
|
import asyncio
|
|
from unittest.mock import patch, MagicMock, PropertyMock
|
|
|
|
from aiohttp import WSMsgType, client_exceptions
|
|
import pytest
|
|
|
|
from homeassistant.components.cloud import iot, auth_api
|
|
from tests.common import mock_coro
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_client():
|
|
"""Mock the IoT client."""
|
|
client = MagicMock()
|
|
type(client).closed = PropertyMock(side_effect=[False, True])
|
|
|
|
with patch('asyncio.sleep'), \
|
|
patch('homeassistant.components.cloud.iot'
|
|
'.async_get_clientsession') as session:
|
|
session().ws_connect.return_value = mock_coro(client)
|
|
yield client
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_handle_message():
|
|
"""Mock handle message."""
|
|
with patch('homeassistant.components.cloud.iot'
|
|
'.async_handle_message') as mock:
|
|
yield mock
|
|
|
|
|
|
@asyncio.coroutine
|
|
def test_cloud_calling_handler(mock_client, mock_handle_message):
|
|
"""Test we call handle message with correct info."""
|
|
cloud = MagicMock()
|
|
conn = iot.CloudIoT(cloud)
|
|
mock_client.receive.return_value = mock_coro(MagicMock(
|
|
type=WSMsgType.text,
|
|
json=MagicMock(return_value={
|
|
'msgid': 'test-msg-id',
|
|
'handler': 'test-handler',
|
|
'payload': 'test-payload'
|
|
})
|
|
))
|
|
mock_handle_message.return_value = mock_coro('response')
|
|
mock_client.send_json.return_value = mock_coro(None)
|
|
|
|
yield from conn.connect()
|
|
|
|
# Check that we sent message to handler correctly
|
|
assert len(mock_handle_message.mock_calls) == 1
|
|
p_hass, p_cloud, handler_name, payload = \
|
|
mock_handle_message.mock_calls[0][1]
|
|
|
|
assert p_hass is cloud.hass
|
|
assert p_cloud is cloud
|
|
assert handler_name == 'test-handler'
|
|
assert payload == 'test-payload'
|
|
|
|
# Check that we forwarded response from handler to cloud
|
|
assert len(mock_client.send_json.mock_calls) == 1
|
|
assert mock_client.send_json.mock_calls[0][1][0] == {
|
|
'msgid': 'test-msg-id',
|
|
'payload': 'response'
|
|
}
|
|
|
|
|
|
@asyncio.coroutine
|
|
def test_connection_msg_for_unknown_handler(mock_client):
|
|
"""Test a msg for an unknown handler."""
|
|
cloud = MagicMock()
|
|
conn = iot.CloudIoT(cloud)
|
|
mock_client.receive.return_value = mock_coro(MagicMock(
|
|
type=WSMsgType.text,
|
|
json=MagicMock(return_value={
|
|
'msgid': 'test-msg-id',
|
|
'handler': 'non-existing-handler',
|
|
'payload': 'test-payload'
|
|
})
|
|
))
|
|
mock_client.send_json.return_value = mock_coro(None)
|
|
|
|
yield from conn.connect()
|
|
|
|
# Check that we sent the correct error
|
|
assert len(mock_client.send_json.mock_calls) == 1
|
|
assert mock_client.send_json.mock_calls[0][1][0] == {
|
|
'msgid': 'test-msg-id',
|
|
'error': 'unknown-handler',
|
|
}
|
|
|
|
|
|
@asyncio.coroutine
|
|
def test_connection_msg_for_handler_raising(mock_client, mock_handle_message):
|
|
"""Test we sent error when handler raises exception."""
|
|
cloud = MagicMock()
|
|
conn = iot.CloudIoT(cloud)
|
|
mock_client.receive.return_value = mock_coro(MagicMock(
|
|
type=WSMsgType.text,
|
|
json=MagicMock(return_value={
|
|
'msgid': 'test-msg-id',
|
|
'handler': 'test-handler',
|
|
'payload': 'test-payload'
|
|
})
|
|
))
|
|
mock_handle_message.side_effect = Exception('Broken')
|
|
mock_client.send_json.return_value = mock_coro(None)
|
|
|
|
yield from conn.connect()
|
|
|
|
# Check that we sent the correct error
|
|
assert len(mock_client.send_json.mock_calls) == 1
|
|
assert mock_client.send_json.mock_calls[0][1][0] == {
|
|
'msgid': 'test-msg-id',
|
|
'error': 'exception',
|
|
}
|
|
|
|
|
|
@asyncio.coroutine
|
|
def test_handler_forwarding():
|
|
"""Test we forward messages to correct handler."""
|
|
handler = MagicMock()
|
|
handler.return_value = mock_coro()
|
|
hass = object()
|
|
cloud = object()
|
|
with patch.dict(iot.HANDLERS, {'test': handler}):
|
|
yield from iot.async_handle_message(
|
|
hass, cloud, 'test', 'payload')
|
|
|
|
assert len(handler.mock_calls) == 1
|
|
r_hass, r_cloud, payload = handler.mock_calls[0][1]
|
|
assert r_hass is hass
|
|
assert r_cloud is cloud
|
|
assert payload == 'payload'
|
|
|
|
|
|
@asyncio.coroutine
|
|
def test_handling_core_messages(hass):
|
|
"""Test handling core messages."""
|
|
cloud = MagicMock()
|
|
cloud.logout.return_value = mock_coro()
|
|
yield from iot.async_handle_cloud(hass, cloud, {
|
|
'action': 'logout',
|
|
'reason': 'Logged in at two places.'
|
|
})
|
|
assert len(cloud.logout.mock_calls) == 1
|
|
|
|
|
|
@asyncio.coroutine
|
|
def test_cloud_getting_disconnected_by_server(mock_client, caplog):
|
|
"""Test server disconnecting instance."""
|
|
cloud = MagicMock()
|
|
conn = iot.CloudIoT(cloud)
|
|
mock_client.receive.return_value = mock_coro(MagicMock(
|
|
type=WSMsgType.CLOSING,
|
|
))
|
|
|
|
yield from conn.connect()
|
|
|
|
assert 'Connection closed: Closed by server' in caplog.text
|
|
assert 'connect' in str(cloud.hass.async_add_job.mock_calls[-1][1][0])
|
|
|
|
|
|
@asyncio.coroutine
|
|
def test_cloud_receiving_bytes(mock_client, caplog):
|
|
"""Test server disconnecting instance."""
|
|
cloud = MagicMock()
|
|
conn = iot.CloudIoT(cloud)
|
|
mock_client.receive.return_value = mock_coro(MagicMock(
|
|
type=WSMsgType.BINARY,
|
|
))
|
|
|
|
yield from conn.connect()
|
|
|
|
assert 'Connection closed: Received non-Text message' in caplog.text
|
|
assert 'connect' in str(cloud.hass.async_add_job.mock_calls[-1][1][0])
|
|
|
|
|
|
@asyncio.coroutine
|
|
def test_cloud_sending_invalid_json(mock_client, caplog):
|
|
"""Test cloud sending invalid JSON."""
|
|
cloud = MagicMock()
|
|
conn = iot.CloudIoT(cloud)
|
|
mock_client.receive.return_value = mock_coro(MagicMock(
|
|
type=WSMsgType.TEXT,
|
|
json=MagicMock(side_effect=ValueError)
|
|
))
|
|
|
|
yield from conn.connect()
|
|
|
|
assert 'Connection closed: Received invalid JSON.' in caplog.text
|
|
assert 'connect' in str(cloud.hass.async_add_job.mock_calls[-1][1][0])
|
|
|
|
|
|
@asyncio.coroutine
|
|
def test_cloud_check_token_raising(mock_client, caplog):
|
|
"""Test cloud sending invalid JSON."""
|
|
cloud = MagicMock()
|
|
conn = iot.CloudIoT(cloud)
|
|
mock_client.receive.side_effect = auth_api.CloudError
|
|
|
|
yield from conn.connect()
|
|
|
|
assert 'Unable to connect: Unable to refresh token.' in caplog.text
|
|
assert 'connect' in str(cloud.hass.async_add_job.mock_calls[-1][1][0])
|
|
|
|
|
|
@asyncio.coroutine
|
|
def test_cloud_connect_invalid_auth(mock_client, caplog):
|
|
"""Test invalid auth detected by server."""
|
|
cloud = MagicMock()
|
|
conn = iot.CloudIoT(cloud)
|
|
mock_client.receive.side_effect = \
|
|
client_exceptions.WSServerHandshakeError(None, None, code=401)
|
|
|
|
yield from conn.connect()
|
|
|
|
assert 'Connection closed: Invalid auth.' in caplog.text
|
|
|
|
|
|
@asyncio.coroutine
|
|
def test_cloud_unable_to_connect(mock_client, caplog):
|
|
"""Test unable to connect error."""
|
|
cloud = MagicMock()
|
|
conn = iot.CloudIoT(cloud)
|
|
mock_client.receive.side_effect = client_exceptions.ClientError(None, None)
|
|
|
|
yield from conn.connect()
|
|
|
|
assert 'Unable to connect:' in caplog.text
|
|
|
|
|
|
@asyncio.coroutine
|
|
def test_cloud_random_exception(mock_client, caplog):
|
|
"""Test random exception."""
|
|
cloud = MagicMock()
|
|
conn = iot.CloudIoT(cloud)
|
|
mock_client.receive.side_effect = Exception
|
|
|
|
yield from conn.connect()
|
|
|
|
assert 'Unexpected error' in caplog.text
|