Files
homeassistant-core/tests/components/cloud/test_http_api.py
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1689 lines
51 KiB
Python
Raw Normal View History

2017-08-29 13:40:08 -07:00
"""Tests for the HTTP API for the cloud component."""
2023-12-13 13:53:22 +01:00
from copy import deepcopy
from http import HTTPStatus
2024-03-20 09:42:40 +01:00
import json
2023-08-08 11:02:42 +02:00
from typing import Any
2021-01-01 22:31:56 +01:00
from unittest.mock import AsyncMock, MagicMock, Mock, patch
2017-08-29 13:40:08 -07:00
2020-06-11 22:56:00 -07:00
import aiohttp
2024-03-20 09:42:40 +01:00
from hass_nabucasa import thingtalk
2019-03-11 20:21:20 +01:00
from hass_nabucasa.auth import Unauthenticated, UnknownError
from hass_nabucasa.const import STATE_CONNECTED
2024-03-20 09:42:40 +01:00
from hass_nabucasa.voice import TTS_VOICES
import pytest
2017-08-29 13:40:08 -07:00
from homeassistant.components.alexa import errors as alexa_errors
from homeassistant.components.alexa.entities import LightCapabilities
2023-12-13 13:53:22 +01:00
from homeassistant.components.assist_pipeline.pipeline import STORAGE_KEY
from homeassistant.components.cloud.const import DEFAULT_EXPOSED_DOMAINS, DOMAIN
from homeassistant.components.google_assistant.helpers import GoogleEntity
from homeassistant.components.homeassistant import exposed_entities
2024-03-20 09:42:40 +01:00
from homeassistant.components.websocket_api import ERR_INVALID_FORMAT
from homeassistant.core import HomeAssistant, State
from homeassistant.helpers import entity_registry as er
from homeassistant.setup import async_setup_component
from homeassistant.util.location import LocationInfo
2019-06-21 02:17:21 -07:00
from tests.components.google_assistant import MockConfig
from tests.test_util.aiohttp import AiohttpClientMocker
2023-08-08 11:02:42 +02:00
from tests.typing import ClientSessionGenerator, WebSocketGenerator
2017-10-14 19:43:14 -07:00
2023-12-13 13:53:22 +01:00
PIPELINE_DATA_LEGACY = {
"items": [
{
"conversation_engine": "homeassistant",
"conversation_language": "language_1",
"id": "12345",
"language": "language_1",
"name": "Home Assistant Cloud",
"stt_engine": "cloud",
"stt_language": "language_1",
"tts_engine": "cloud",
"tts_language": "language_1",
"tts_voice": "Arnold Schwarzenegger",
"wake_word_entity": None,
"wake_word_id": None,
},
],
"preferred_item": "12345",
}
PIPELINE_DATA = {
"items": [
{
"conversation_engine": "homeassistant",
"conversation_language": "language_1",
"id": "12345",
"language": "language_1",
"name": "Home Assistant Cloud",
"stt_engine": "stt.home_assistant_cloud",
"stt_language": "language_1",
"tts_engine": "cloud",
"tts_language": "language_1",
"tts_voice": "Arnold Schwarzenegger",
"wake_word_entity": None,
"wake_word_id": None,
},
],
"preferred_item": "12345",
}
2023-12-13 13:53:22 +01:00
PIPELINE_DATA_OTHER = {
"items": [
{
"conversation_engine": "other",
"conversation_language": "language_1",
"id": "12345",
"language": "language_1",
"name": "Home Assistant",
"stt_engine": "stt.other",
"stt_language": "language_1",
"tts_engine": "other",
"tts_language": "language_1",
"tts_voice": "Arnold Schwarzenegger",
"wake_word_entity": None,
"wake_word_id": None,
},
],
"preferred_item": "12345",
}
SUBSCRIPTION_INFO_URL = "https://api-test.hass.io/payments/subscription_info"
2018-09-20 14:53:13 +02:00
2023-12-13 13:53:22 +01:00
@pytest.fixture(name="setup_cloud")
async def setup_cloud_fixture(hass: HomeAssistant, cloud: MagicMock) -> None:
"""Fixture that sets up cloud."""
assert await async_setup_component(hass, "homeassistant", {})
assert await async_setup_component(
hass,
DOMAIN,
2019-03-12 07:54:04 -07:00
{
2023-12-13 13:53:22 +01:00
DOMAIN: {
"mode": "development",
"cognito_client_id": "cognito_client_id",
"user_pool_id": "user_pool_id",
"region": "region",
2023-12-13 13:53:22 +01:00
"alexa_server": "alexa-api.nabucasa.com",
"relayer_server": "relayer",
"accounts_server": "api-test.hass.io",
2018-11-05 13:21:03 +01:00
"google_actions": {"filter": {"include_domains": "light"}},
"alexa": {
"filter": {"include_entities": ["light.kitchen", "switch.ac"]}
2019-07-31 12:25:30 -07:00
},
2018-11-05 13:21:03 +01:00
},
2023-12-13 13:53:22 +01:00
},
2019-06-21 02:17:21 -07:00
)
2023-12-13 13:53:22 +01:00
await hass.async_block_till_done()
2024-01-12 09:47:08 +01:00
await cloud.login("test-user", "test-pass")
cloud.login.reset_mock()
2018-03-23 12:13:52 -07:00
async def test_google_actions_sync(
2023-12-13 13:53:22 +01:00
setup_cloud: None,
hass_client: ClientSessionGenerator,
) -> None:
2018-03-23 12:13:52 -07:00
"""Test syncing Google Actions."""
cloud_client = await hass_client()
with patch(
"hass_nabucasa.cloud_api.async_google_actions_request_sync",
2020-04-30 13:29:50 -07:00
return_value=Mock(status=200),
) as mock_request_sync:
req = await cloud_client.post("/api/cloud/google_actions/sync")
assert req.status == HTTPStatus.OK
2023-12-13 13:53:22 +01:00
assert mock_request_sync.call_count == 1
2018-03-23 12:13:52 -07:00
async def test_google_actions_sync_fails(
2023-12-13 13:53:22 +01:00
setup_cloud: None,
hass_client: ClientSessionGenerator,
) -> None:
2018-03-23 12:13:52 -07:00
"""Test syncing Google Actions gone bad."""
cloud_client = await hass_client()
with patch(
"hass_nabucasa.cloud_api.async_google_actions_request_sync",
return_value=Mock(status=HTTPStatus.INTERNAL_SERVER_ERROR),
) as mock_request_sync:
req = await cloud_client.post("/api/cloud/google_actions/sync")
assert req.status == HTTPStatus.INTERNAL_SERVER_ERROR
2023-12-13 13:53:22 +01:00
assert mock_request_sync.call_count == 1
2018-03-23 12:13:52 -07:00
2024-01-22 17:24:15 +01:00
@pytest.mark.parametrize(
"entity_id", ["stt.home_assistant_cloud", "tts.home_assistant_cloud"]
)
async def test_login_view_missing_entity(
hass: HomeAssistant,
setup_cloud: None,
entity_registry: er.EntityRegistry,
hass_client: ClientSessionGenerator,
2024-01-22 17:24:15 +01:00
entity_id: str,
) -> None:
2024-01-22 17:24:15 +01:00
"""Test logging in when a cloud assist pipeline needed entity is missing."""
# Make sure that the cloud entity does not exist.
entity_registry.async_remove(entity_id)
await hass.async_block_till_done()
cloud_client = await hass_client()
# We assume the user needs to login again for some reason.
with patch(
"homeassistant.components.cloud.assist_pipeline.async_create_default_pipeline",
) as create_pipeline_mock:
req = await cloud_client.post(
"/api/cloud/login", json={"email": "my_username", "password": "my_password"}
)
assert req.status == HTTPStatus.OK
result = await req.json()
assert result == {"success": True, "cloud_pipeline": None}
create_pipeline_mock.assert_not_awaited()
@pytest.mark.parametrize("pipeline_data", [PIPELINE_DATA, PIPELINE_DATA_LEGACY])
2023-12-13 13:53:22 +01:00
async def test_login_view_existing_pipeline(
hass: HomeAssistant,
cloud: MagicMock,
hass_client: ClientSessionGenerator,
hass_storage: dict[str, Any],
pipeline_data: dict[str, Any],
) -> None:
"""Test logging in when an assist pipeline is available."""
2023-12-13 13:53:22 +01:00
hass_storage[STORAGE_KEY] = {
"version": 1,
"minor_version": 1,
"key": STORAGE_KEY,
"data": deepcopy(pipeline_data),
}
assert await async_setup_component(hass, "homeassistant", {})
assert await async_setup_component(hass, DOMAIN, {"cloud": {}})
await hass.async_block_till_done()
cloud_client = await hass_client()
2017-10-14 19:43:14 -07:00
with patch(
2023-12-14 23:56:08 +01:00
"homeassistant.components.cloud.assist_pipeline.async_create_default_pipeline",
) as create_pipeline_mock:
req = await cloud_client.post(
"/api/cloud/login", json={"email": "my_username", "password": "my_password"}
)
2017-08-29 13:40:08 -07:00
assert req.status == HTTPStatus.OK
2019-03-11 20:21:20 +01:00
result = await req.json()
2023-04-26 15:28:08 +02:00
assert result == {"success": True, "cloud_pipeline": None}
create_pipeline_mock.assert_not_awaited()
2023-12-13 13:53:22 +01:00
async def test_login_view_create_pipeline(
hass: HomeAssistant,
cloud: MagicMock,
hass_client: ClientSessionGenerator,
hass_storage: dict[str, Any],
) -> None:
"""Test logging in when no existing cloud assist pipeline is available."""
hass_storage[STORAGE_KEY] = {
"version": 1,
"minor_version": 1,
"key": STORAGE_KEY,
"data": deepcopy(PIPELINE_DATA_OTHER),
}
assert await async_setup_component(hass, "homeassistant", {})
2024-04-01 21:34:25 -04:00
assert await async_setup_component(hass, "assist_pipeline", {})
2023-12-13 13:53:22 +01:00
assert await async_setup_component(hass, DOMAIN, {"cloud": {}})
await hass.async_block_till_done()
cloud_client = await hass_client()
with patch(
2023-12-14 23:56:08 +01:00
"homeassistant.components.cloud.assist_pipeline.async_create_default_pipeline",
return_value=AsyncMock(id="12345"),
) as create_pipeline_mock:
req = await cloud_client.post(
"/api/cloud/login", json={"email": "my_username", "password": "my_password"}
)
assert req.status == HTTPStatus.OK
result = await req.json()
assert result == {"success": True, "cloud_pipeline": "12345"}
create_pipeline_mock.assert_awaited_once_with(
hass,
stt_engine_id="stt.home_assistant_cloud",
2024-01-22 17:24:15 +01:00
tts_engine_id="tts.home_assistant_cloud",
pipeline_name="Home Assistant Cloud",
)
async def test_login_view_create_pipeline_fail(
2023-12-13 13:53:22 +01:00
hass: HomeAssistant,
cloud: MagicMock,
hass_client: ClientSessionGenerator,
hass_storage: dict[str, Any],
) -> None:
"""Test logging in when no assist pipeline is available."""
2023-12-13 13:53:22 +01:00
hass_storage[STORAGE_KEY] = {
"version": 1,
"minor_version": 1,
"key": STORAGE_KEY,
"data": deepcopy(PIPELINE_DATA_OTHER),
}
assert await async_setup_component(hass, "homeassistant", {})
2024-04-01 21:34:25 -04:00
assert await async_setup_component(hass, "assist_pipeline", {})
2023-12-13 13:53:22 +01:00
assert await async_setup_component(hass, DOMAIN, {"cloud": {}})
await hass.async_block_till_done()
cloud_client = await hass_client()
with patch(
2023-12-14 23:56:08 +01:00
"homeassistant.components.cloud.assist_pipeline.async_create_default_pipeline",
return_value=None,
) as create_pipeline_mock:
req = await cloud_client.post(
"/api/cloud/login", json={"email": "my_username", "password": "my_password"}
)
assert req.status == HTTPStatus.OK
result = await req.json()
assert result == {"success": True, "cloud_pipeline": None}
create_pipeline_mock.assert_awaited_once_with(
hass,
stt_engine_id="stt.home_assistant_cloud",
2024-01-22 17:24:15 +01:00
tts_engine_id="tts.home_assistant_cloud",
pipeline_name="Home Assistant Cloud",
)
2017-11-14 23:16:19 -08:00
2017-08-29 13:40:08 -07:00
2023-12-13 13:53:22 +01:00
async def test_login_view_random_exception(
cloud: MagicMock,
setup_cloud: None,
hass_client: ClientSessionGenerator,
2023-12-13 13:53:22 +01:00
) -> None:
"""Try logging in with random exception."""
cloud_client = await hass_client()
2023-12-13 13:53:22 +01:00
cloud.login.side_effect = ValueError("Boom")
req = await cloud_client.post(
"/api/cloud/login", json={"email": "my_username", "password": "my_password"}
)
assert req.status == HTTPStatus.BAD_GATEWAY
2019-02-04 01:14:30 -08:00
resp = await req.json()
assert resp == {"code": "valueerror", "message": "Unexpected error: Boom"}
2023-12-13 13:53:22 +01:00
async def test_login_view_invalid_json(
cloud: MagicMock,
setup_cloud: None,
hass_client: ClientSessionGenerator,
2023-12-13 13:53:22 +01:00
) -> None:
2017-08-29 13:40:08 -07:00
"""Try logging in with invalid JSON."""
cloud_client = await hass_client()
2023-12-13 13:53:22 +01:00
mock_login = cloud.login
req = await cloud_client.post("/api/cloud/login", data="Not JSON")
assert req.status == HTTPStatus.BAD_REQUEST
2023-12-13 13:53:22 +01:00
assert mock_login.call_count == 0
2017-08-29 13:40:08 -07:00
2023-12-13 13:53:22 +01:00
async def test_login_view_invalid_schema(
cloud: MagicMock,
setup_cloud: None,
hass_client: ClientSessionGenerator,
2023-12-13 13:53:22 +01:00
) -> None:
2017-08-29 13:40:08 -07:00
"""Try logging in with invalid schema."""
cloud_client = await hass_client()
2023-12-13 13:53:22 +01:00
mock_login = cloud.login
req = await cloud_client.post("/api/cloud/login", json={"invalid": "schema"})
assert req.status == HTTPStatus.BAD_REQUEST
2023-12-13 13:53:22 +01:00
assert mock_login.call_count == 0
2017-08-29 13:40:08 -07:00
2023-12-13 13:53:22 +01:00
async def test_login_view_request_timeout(
cloud: MagicMock,
setup_cloud: None,
hass_client: ClientSessionGenerator,
2023-12-13 13:53:22 +01:00
) -> None:
2017-08-29 13:40:08 -07:00
"""Test request timeout while trying to log in."""
cloud_client = await hass_client()
2024-02-05 11:31:33 +01:00
cloud.login.side_effect = TimeoutError
2023-12-13 13:53:22 +01:00
req = await cloud_client.post(
"/api/cloud/login", json={"email": "my_username", "password": "my_password"}
)
2017-08-29 13:40:08 -07:00
assert req.status == HTTPStatus.BAD_GATEWAY
2017-08-29 13:40:08 -07:00
2023-12-13 13:53:22 +01:00
async def test_login_view_invalid_credentials(
cloud: MagicMock,
setup_cloud: None,
hass_client: ClientSessionGenerator,
2023-12-13 13:53:22 +01:00
) -> None:
2017-08-29 13:40:08 -07:00
"""Test logging in with invalid credentials."""
cloud_client = await hass_client()
2023-12-13 13:53:22 +01:00
cloud.login.side_effect = Unauthenticated
req = await cloud_client.post(
"/api/cloud/login", json={"email": "my_username", "password": "my_password"}
)
2017-08-29 13:40:08 -07:00
assert req.status == HTTPStatus.UNAUTHORIZED
2017-08-29 13:40:08 -07:00
2023-12-13 13:53:22 +01:00
async def test_login_view_unknown_error(
cloud: MagicMock,
setup_cloud: None,
hass_client: ClientSessionGenerator,
2023-12-13 13:53:22 +01:00
) -> None:
2017-08-29 13:40:08 -07:00
"""Test unknown error while logging in."""
cloud_client = await hass_client()
2023-12-13 13:53:22 +01:00
cloud.login.side_effect = UnknownError
req = await cloud_client.post(
"/api/cloud/login", json={"email": "my_username", "password": "my_password"}
)
2017-08-29 13:40:08 -07:00
assert req.status == HTTPStatus.BAD_GATEWAY
2017-08-29 13:40:08 -07:00
2023-12-13 13:53:22 +01:00
async def test_logout_view(
cloud: MagicMock,
setup_cloud: None,
hass_client: ClientSessionGenerator,
2023-12-13 13:53:22 +01:00
) -> None:
2017-08-29 13:40:08 -07:00
"""Test logging out."""
cloud_client = await hass_client()
2019-03-11 20:21:20 +01:00
req = await cloud_client.post("/api/cloud/logout")
2023-12-13 13:53:22 +01:00
assert req.status == HTTPStatus.OK
2019-03-11 20:21:20 +01:00
data = await req.json()
2017-09-12 09:47:04 -07:00
assert data == {"message": "ok"}
2023-12-13 13:53:22 +01:00
assert cloud.logout.call_count == 1
2017-08-29 13:40:08 -07:00
2023-12-13 13:53:22 +01:00
async def test_logout_view_request_timeout(
cloud: MagicMock,
setup_cloud: None,
hass_client: ClientSessionGenerator,
2023-12-13 13:53:22 +01:00
) -> None:
2017-08-29 13:40:08 -07:00
"""Test timeout while logging out."""
cloud_client = await hass_client()
2024-02-05 11:31:33 +01:00
cloud.logout.side_effect = TimeoutError
2023-12-13 13:53:22 +01:00
2019-03-11 20:21:20 +01:00
req = await cloud_client.post("/api/cloud/logout")
2023-12-13 13:53:22 +01:00
assert req.status == HTTPStatus.BAD_GATEWAY
2017-08-29 13:40:08 -07:00
2023-12-13 13:53:22 +01:00
async def test_logout_view_unknown_error(
cloud: MagicMock,
setup_cloud: None,
hass_client: ClientSessionGenerator,
2023-12-13 13:53:22 +01:00
) -> None:
2017-09-23 17:15:46 +02:00
"""Test unknown error while logging out."""
cloud_client = await hass_client()
2019-03-11 20:21:20 +01:00
cloud.logout.side_effect = UnknownError
2023-12-13 13:53:22 +01:00
2019-03-11 20:21:20 +01:00
req = await cloud_client.post("/api/cloud/logout")
2023-12-13 13:53:22 +01:00
assert req.status == HTTPStatus.BAD_GATEWAY
2017-09-12 09:47:04 -07:00
2023-12-13 13:53:22 +01:00
async def test_register_view_no_location(
cloud: MagicMock,
setup_cloud: None,
hass_client: ClientSessionGenerator,
2023-12-13 13:53:22 +01:00
) -> None:
"""Test register without location."""
cloud_client = await hass_client()
2023-12-13 13:53:22 +01:00
mock_cognito = cloud.auth
with patch(
"homeassistant.components.cloud.http_api.async_detect_location_info",
return_value=None,
):
req = await cloud_client.post(
"/api/cloud/register",
json={"email": "hello@bla.com", "password": "falcon42"},
)
2023-12-13 13:53:22 +01:00
assert req.status == HTTPStatus.OK
2023-12-13 13:53:22 +01:00
assert mock_cognito.async_register.call_count == 1
call = mock_cognito.async_register.mock_calls[0]
result_email, result_pass = call.args
2017-11-27 01:09:17 -08:00
assert result_email == "hello@bla.com"
2017-09-12 09:47:04 -07:00
assert result_pass == "falcon42"
assert call.kwargs["client_metadata"] is None
2023-12-13 13:53:22 +01:00
async def test_register_view_with_location(
cloud: MagicMock,
setup_cloud: None,
hass_client: ClientSessionGenerator,
2023-12-13 13:53:22 +01:00
) -> None:
"""Test register with location."""
cloud_client = await hass_client()
2023-12-13 13:53:22 +01:00
mock_cognito = cloud.auth
with patch(
"homeassistant.components.cloud.http_api.async_detect_location_info",
return_value=LocationInfo(
2024-03-16 23:45:18 +01:00
country_code="XX",
zip_code="12345",
region_code="GH",
ip="1.2.3.4",
city="Gotham",
region_name="Gotham",
time_zone="Earth/Gotham",
currency="XXX",
latitude="12.34567",
longitude="12.34567",
use_metric=True,
),
):
req = await cloud_client.post(
"/api/cloud/register",
json={"email": "hello@bla.com", "password": "falcon42"},
)
2023-12-13 13:53:22 +01:00
assert req.status == HTTPStatus.OK
2023-12-13 13:53:22 +01:00
assert mock_cognito.async_register.call_count == 1
call = mock_cognito.async_register.mock_calls[0]
result_email, result_pass = call.args
assert result_email == "hello@bla.com"
assert result_pass == "falcon42"
assert call.kwargs["client_metadata"] == {
"NC_COUNTRY_CODE": "XX",
"NC_REGION_CODE": "GH",
"NC_ZIP_CODE": "12345",
}
2017-09-12 09:47:04 -07:00
2023-12-13 13:53:22 +01:00
async def test_register_view_bad_data(
cloud: MagicMock,
setup_cloud: None,
hass_client: ClientSessionGenerator,
2023-12-13 13:53:22 +01:00
) -> None:
"""Test register bad data."""
cloud_client = await hass_client()
2023-12-13 13:53:22 +01:00
mock_cognito = cloud.auth
2019-03-11 20:21:20 +01:00
req = await cloud_client.post(
"/api/cloud/register", json={"email": "hello@bla.com", "not_password": "falcon"}
2017-09-12 09:47:04 -07:00
)
2023-12-13 13:53:22 +01:00
assert req.status == HTTPStatus.BAD_REQUEST
2023-12-13 13:53:22 +01:00
assert mock_cognito.async_register.call_count == 0
2017-09-12 09:47:04 -07:00
2023-12-13 13:53:22 +01:00
async def test_register_view_request_timeout(
cloud: MagicMock,
setup_cloud: None,
hass_client: ClientSessionGenerator,
2023-12-13 13:53:22 +01:00
) -> None:
"""Test timeout while registering."""
cloud_client = await hass_client()
2024-02-05 11:31:33 +01:00
cloud.auth.async_register.side_effect = TimeoutError
2023-12-13 13:53:22 +01:00
2019-03-11 20:21:20 +01:00
req = await cloud_client.post(
"/api/cloud/register", json={"email": "hello@bla.com", "password": "falcon42"}
2017-09-12 09:47:04 -07:00
)
2023-12-13 13:53:22 +01:00
assert req.status == HTTPStatus.BAD_GATEWAY
2017-09-12 09:47:04 -07:00
2023-12-13 13:53:22 +01:00
async def test_register_view_unknown_error(
cloud: MagicMock,
setup_cloud: None,
hass_client: ClientSessionGenerator,
2023-12-13 13:53:22 +01:00
) -> None:
"""Test unknown error while registering."""
cloud_client = await hass_client()
2023-12-13 13:53:22 +01:00
cloud.auth.async_register.side_effect = UnknownError
2019-03-11 20:21:20 +01:00
req = await cloud_client.post(
"/api/cloud/register", json={"email": "hello@bla.com", "password": "falcon42"}
2017-09-12 09:47:04 -07:00
)
2023-12-13 13:53:22 +01:00
assert req.status == HTTPStatus.BAD_GATEWAY
2017-09-12 09:47:04 -07:00
2023-12-13 13:53:22 +01:00
async def test_forgot_password_view(
cloud: MagicMock,
setup_cloud: None,
hass_client: ClientSessionGenerator,
2023-12-13 13:53:22 +01:00
) -> None:
"""Test forgot password."""
cloud_client = await hass_client()
2023-12-13 13:53:22 +01:00
mock_cognito = cloud.auth
2019-03-11 20:21:20 +01:00
req = await cloud_client.post(
"/api/cloud/forgot_password", json={"email": "hello@bla.com"}
2017-09-12 09:47:04 -07:00
)
2023-12-13 13:53:22 +01:00
assert req.status == HTTPStatus.OK
2023-12-13 13:53:22 +01:00
assert mock_cognito.async_forgot_password.call_count == 1
2017-09-12 09:47:04 -07:00
2023-12-13 13:53:22 +01:00
async def test_forgot_password_view_bad_data(
cloud: MagicMock,
setup_cloud: None,
hass_client: ClientSessionGenerator,
2023-12-13 13:53:22 +01:00
) -> None:
"""Test forgot password bad data."""
cloud_client = await hass_client()
2023-12-13 13:53:22 +01:00
mock_cognito = cloud.auth
2019-03-11 20:21:20 +01:00
req = await cloud_client.post(
"/api/cloud/forgot_password", json={"not_email": "hello@bla.com"}
2017-09-12 09:47:04 -07:00
)
2023-12-13 13:53:22 +01:00
assert req.status == HTTPStatus.BAD_REQUEST
2023-12-13 13:53:22 +01:00
assert mock_cognito.async_forgot_password.call_count == 0
2017-09-12 09:47:04 -07:00
2023-12-13 13:53:22 +01:00
async def test_forgot_password_view_request_timeout(
cloud: MagicMock,
setup_cloud: None,
hass_client: ClientSessionGenerator,
2023-12-13 13:53:22 +01:00
) -> None:
"""Test timeout while forgot password."""
cloud_client = await hass_client()
2024-02-05 11:31:33 +01:00
cloud.auth.async_forgot_password.side_effect = TimeoutError
2023-12-13 13:53:22 +01:00
2019-03-11 20:21:20 +01:00
req = await cloud_client.post(
"/api/cloud/forgot_password", json={"email": "hello@bla.com"}
2017-09-12 09:47:04 -07:00
)
2023-12-13 13:53:22 +01:00
assert req.status == HTTPStatus.BAD_GATEWAY
2017-09-12 09:47:04 -07:00
2023-12-13 13:53:22 +01:00
async def test_forgot_password_view_unknown_error(
cloud: MagicMock,
setup_cloud: None,
hass_client: ClientSessionGenerator,
2023-12-13 13:53:22 +01:00
) -> None:
"""Test unknown error while forgot password."""
cloud_client = await hass_client()
2023-12-13 13:53:22 +01:00
cloud.auth.async_forgot_password.side_effect = UnknownError
2019-03-11 20:21:20 +01:00
req = await cloud_client.post(
"/api/cloud/forgot_password", json={"email": "hello@bla.com"}
2017-09-12 09:47:04 -07:00
)
2023-12-13 13:53:22 +01:00
assert req.status == HTTPStatus.BAD_GATEWAY
2017-09-12 09:47:04 -07:00
2023-12-13 13:53:22 +01:00
async def test_forgot_password_view_aiohttp_error(
cloud: MagicMock,
setup_cloud: None,
hass_client: ClientSessionGenerator,
2023-12-13 13:53:22 +01:00
) -> None:
"""Test unknown error while forgot password."""
cloud_client = await hass_client()
2023-12-13 13:53:22 +01:00
cloud.auth.async_forgot_password.side_effect = aiohttp.ClientResponseError(
2020-06-11 22:56:00 -07:00
Mock(), Mock()
)
2023-12-13 13:53:22 +01:00
2020-06-11 22:56:00 -07:00
req = await cloud_client.post(
"/api/cloud/forgot_password", json={"email": "hello@bla.com"}
)
2023-12-13 13:53:22 +01:00
assert req.status == HTTPStatus.INTERNAL_SERVER_ERROR
2020-06-11 22:56:00 -07:00
2023-12-13 13:53:22 +01:00
async def test_resend_confirm_view(
cloud: MagicMock,
setup_cloud: None,
hass_client: ClientSessionGenerator,
2023-12-13 13:53:22 +01:00
) -> None:
"""Test resend confirm."""
cloud_client = await hass_client()
2023-12-13 13:53:22 +01:00
mock_cognito = cloud.auth
2019-03-11 20:21:20 +01:00
req = await cloud_client.post(
"/api/cloud/resend_confirm", json={"email": "hello@bla.com"}
)
2023-12-13 13:53:22 +01:00
assert req.status == HTTPStatus.OK
2023-12-13 13:53:22 +01:00
assert mock_cognito.async_resend_email_confirm.call_count == 1
2023-12-13 13:53:22 +01:00
async def test_resend_confirm_view_bad_data(
cloud: MagicMock,
setup_cloud: None,
hass_client: ClientSessionGenerator,
2023-12-13 13:53:22 +01:00
) -> None:
"""Test resend confirm bad data."""
cloud_client = await hass_client()
2023-12-13 13:53:22 +01:00
mock_cognito = cloud.auth
2019-03-11 20:21:20 +01:00
req = await cloud_client.post(
"/api/cloud/resend_confirm", json={"not_email": "hello@bla.com"}
)
2023-12-13 13:53:22 +01:00
assert req.status == HTTPStatus.BAD_REQUEST
2023-12-13 13:53:22 +01:00
assert mock_cognito.async_resend_email_confirm.call_count == 0
2023-12-13 13:53:22 +01:00
async def test_resend_confirm_view_request_timeout(
cloud: MagicMock,
setup_cloud: None,
hass_client: ClientSessionGenerator,
2023-12-13 13:53:22 +01:00
) -> None:
"""Test timeout while resend confirm."""
cloud_client = await hass_client()
2024-02-05 11:31:33 +01:00
cloud.auth.async_resend_email_confirm.side_effect = TimeoutError
2023-12-13 13:53:22 +01:00
2019-03-11 20:21:20 +01:00
req = await cloud_client.post(
"/api/cloud/resend_confirm", json={"email": "hello@bla.com"}
)
2023-12-13 13:53:22 +01:00
assert req.status == HTTPStatus.BAD_GATEWAY
2023-12-13 13:53:22 +01:00
async def test_resend_confirm_view_unknown_error(
cloud: MagicMock,
setup_cloud: None,
hass_client: ClientSessionGenerator,
2023-12-13 13:53:22 +01:00
) -> None:
"""Test unknown error while resend confirm."""
cloud_client = await hass_client()
2023-12-13 13:53:22 +01:00
cloud.auth.async_resend_email_confirm.side_effect = UnknownError
2019-03-11 20:21:20 +01:00
req = await cloud_client.post(
"/api/cloud/resend_confirm", json={"email": "hello@bla.com"}
)
2023-12-13 13:53:22 +01:00
assert req.status == HTTPStatus.BAD_GATEWAY
2018-09-20 14:53:13 +02:00
2024-03-19 10:22:58 +01:00
async def test_websocket_remove_data(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
cloud: MagicMock,
setup_cloud: None,
) -> None:
"""Test removing cloud data."""
cloud.id_token = None
client = await hass_ws_client(hass)
with patch.object(cloud.client.prefs, "async_erase_config") as mock_erase_config:
await client.send_json_auto_id({"type": "cloud/remove_data"})
response = await client.receive_json()
assert response["success"]
cloud.remove_data.assert_awaited_once_with()
mock_erase_config.assert_awaited_once_with()
async def test_websocket_remove_data_logged_in(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
cloud: MagicMock,
setup_cloud: None,
) -> None:
"""Test removing cloud data."""
cloud.iot.state = STATE_CONNECTED
client = await hass_ws_client(hass)
await client.send_json_auto_id({"type": "cloud/remove_data"})
response = await client.receive_json()
assert not response["success"]
assert response["error"] == {
"code": "logged_in",
"message": "Can't remove data when logged in.",
}
2019-03-12 07:54:04 -07:00
async def test_websocket_status(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
2023-12-13 13:53:22 +01:00
cloud: MagicMock,
setup_cloud: None,
) -> None:
2018-09-20 14:53:13 +02:00
"""Test querying the status."""
2023-12-13 13:53:22 +01:00
cloud.iot.state = STATE_CONNECTED
2018-09-20 14:53:13 +02:00
client = await hass_ws_client(hass)
2018-11-05 13:21:03 +01:00
with (
patch.dict(
"homeassistant.components.google_assistant.const.DOMAIN_TO_GOOGLE_TYPES",
2018-11-05 13:21:03 +01:00
{"light": None},
clear=True,
2019-06-13 08:43:57 -07:00
),
patch.dict(
"homeassistant.components.alexa.entities.ENTITY_ADAPTERS",
2018-11-05 13:21:03 +01:00
{"switch": None},
clear=True,
2024-03-26 00:02:16 +01:00
),
2018-11-05 13:21:03 +01:00
):
await client.send_json({"id": 5, "type": "cloud/status"})
response = await client.receive_json()
2023-12-13 13:53:22 +01:00
2018-09-20 14:53:13 +02:00
assert response["result"] == {
"logged_in": True,
"email": "hello@home-assistant.io",
"cloud": "connected",
2022-02-18 11:53:02 -08:00
"cloud_last_disconnect_reason": None,
"prefs": {
"alexa_enabled": True,
"cloudhooks": {},
"google_enabled": True,
"google_secure_devices_pin": None,
2023-12-13 13:53:22 +01:00
"google_default_expose": DEFAULT_EXPOSED_DOMAINS,
"alexa_default_expose": DEFAULT_EXPOSED_DOMAINS,
"alexa_report_state": True,
"google_report_state": True,
"remote_allow_remote_enable": True,
"remote_enabled": False,
2024-03-20 09:42:40 +01:00
"tts_default_voice": ["en-US", "JennyNeural"],
},
2018-11-05 13:21:03 +01:00
"alexa_entities": {
"include_domains": [],
"include_entity_globs": [],
2018-11-05 13:21:03 +01:00
"include_entities": ["light.kitchen", "switch.ac"],
"exclude_domains": [],
"exclude_entity_globs": [],
2018-11-05 13:21:03 +01:00
"exclude_entities": [],
},
"alexa_registered": False,
2018-11-05 13:21:03 +01:00
"google_entities": {
"include_domains": ["light"],
"include_entity_globs": [],
2018-11-05 13:21:03 +01:00
"include_entities": [],
"exclude_domains": [],
"exclude_entity_globs": [],
2018-11-05 13:21:03 +01:00
"exclude_entities": [],
},
"google_registered": False,
2022-02-16 07:54:59 -08:00
"google_local_connected": False,
2019-03-12 07:54:04 -07:00
"remote_domain": None,
"remote_connected": False,
"remote_certificate_status": None,
2019-03-12 07:54:04 -07:00
"remote_certificate": None,
"http_use_ssl": False,
2023-12-13 13:53:22 +01:00
"active_subscription": True,
2018-09-20 14:53:13 +02:00
}
async def test_websocket_status_not_logged_in(
2023-12-13 13:53:22 +01:00
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
cloud: MagicMock,
setup_cloud: None,
) -> None:
2023-12-13 13:53:22 +01:00
"""Test querying the status not logged in."""
cloud.id_token = None
2018-09-20 14:53:13 +02:00
client = await hass_ws_client(hass)
2023-12-13 13:53:22 +01:00
2018-09-20 14:53:13 +02:00
await client.send_json({"id": 5, "type": "cloud/status"})
response = await client.receive_json()
2023-12-13 13:53:22 +01:00
assert response["result"] == {
"logged_in": False,
"cloud": "disconnected",
"http_use_ssl": False,
}
2018-09-20 14:53:13 +02:00
2021-09-06 16:05:33 -07:00
async def test_websocket_subscription_info(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
aioclient_mock: AiohttpClientMocker,
2023-12-13 13:53:22 +01:00
cloud: MagicMock,
setup_cloud: None,
) -> None:
2023-12-13 13:53:22 +01:00
"""Test subscription info and connecting because valid account."""
aioclient_mock.get(SUBSCRIPTION_INFO_URL, json={"provider": "stripe"})
client = await hass_ws_client(hass)
2023-12-13 13:53:22 +01:00
mock_renew = cloud.auth.async_renew_access_token
await client.send_json({"id": 5, "type": "cloud/subscription"})
response = await client.receive_json()
assert response["result"] == {"provider": "stripe"}
2023-12-13 13:53:22 +01:00
assert mock_renew.call_count == 1
2018-09-20 14:53:13 +02:00
async def test_websocket_subscription_fail(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
aioclient_mock: AiohttpClientMocker,
2023-12-13 13:53:22 +01:00
cloud: MagicMock,
setup_cloud: None,
) -> None:
2023-12-13 13:53:22 +01:00
"""Test subscription info fail."""
aioclient_mock.get(SUBSCRIPTION_INFO_URL, status=HTTPStatus.INTERNAL_SERVER_ERROR)
2018-09-20 14:53:13 +02:00
client = await hass_ws_client(hass)
2023-12-13 13:53:22 +01:00
2018-09-20 14:53:13 +02:00
await client.send_json({"id": 5, "type": "cloud/subscription"})
response = await client.receive_json()
assert not response["success"]
assert response["error"]["code"] == "request_failed"
async def test_websocket_subscription_not_logged_in(
2023-12-13 13:53:22 +01:00
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
cloud: MagicMock,
setup_cloud: None,
) -> None:
2023-12-13 13:53:22 +01:00
"""Test subscription info not logged in."""
cloud.id_token = None
2018-09-20 14:53:13 +02:00
client = await hass_ws_client(hass)
2023-12-13 13:53:22 +01:00
2019-03-11 20:21:20 +01:00
with patch(
2021-09-06 16:05:33 -07:00
"hass_nabucasa.cloud_api.async_subscription_info",
2020-08-27 13:56:20 +02:00
return_value={"return": "value"},
2018-09-20 14:53:13 +02:00
):
await client.send_json({"id": 5, "type": "cloud/subscription"})
response = await client.receive_json()
assert not response["success"]
assert response["error"]["code"] == "not_logged_in"
async def test_websocket_update_preferences(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
2023-12-13 13:53:22 +01:00
cloud: MagicMock,
setup_cloud: None,
) -> None:
"""Test updating preference."""
2023-12-13 13:53:22 +01:00
assert cloud.client.prefs.google_enabled
assert cloud.client.prefs.alexa_enabled
assert cloud.client.prefs.google_secure_devices_pin is None
assert cloud.client.prefs.remote_allow_remote_enable is True
2023-12-13 13:53:22 +01:00
client = await hass_ws_client(hass)
2023-12-13 13:53:22 +01:00
2024-03-20 09:42:40 +01:00
await client.send_json_auto_id(
{
"type": "cloud/update_prefs",
"alexa_enabled": False,
"google_enabled": False,
"google_secure_devices_pin": "1234",
2024-03-20 09:42:40 +01:00
"tts_default_voice": ["en-GB", "RyanNeural"],
"remote_allow_remote_enable": False,
}
)
response = await client.receive_json()
assert response["success"]
2023-12-13 13:53:22 +01:00
assert not cloud.client.prefs.google_enabled
assert not cloud.client.prefs.alexa_enabled
assert cloud.client.prefs.google_secure_devices_pin == "1234"
assert cloud.client.prefs.remote_allow_remote_enable is False
2024-03-20 09:42:40 +01:00
assert cloud.client.prefs.tts_default_voice == ("en-GB", "RyanNeural")
@pytest.mark.parametrize(
("language", "voice"), [("en-GB", "bad_voice"), ("bad_language", "RyanNeural")]
)
async def test_websocket_update_preferences_bad_voice(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
cloud: MagicMock,
setup_cloud: None,
language: str,
voice: str,
) -> None:
"""Test updating preference."""
client = await hass_ws_client(hass)
await client.send_json_auto_id(
{
"type": "cloud/update_prefs",
"tts_default_voice": [language, voice],
}
)
response = await client.receive_json()
assert not response["success"]
assert response["error"]["code"] == ERR_INVALID_FORMAT
assert cloud.client.prefs.tts_default_voice == ("en-US", "JennyNeural")
2018-11-26 14:10:18 +01:00
async def test_websocket_update_preferences_alexa_report_state(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
2023-12-13 13:53:22 +01:00
setup_cloud: None,
) -> None:
"""Test updating alexa_report_state sets alexa authorized."""
client = await hass_ws_client(hass)
with (
patch(
"homeassistant.components.cloud.alexa_config.CloudAlexaConfig.async_sync_entities"
),
patch(
2024-03-26 00:02:16 +01:00
(
"homeassistant.components.cloud.alexa_config.CloudAlexaConfig"
".async_get_access_token"
),
2024-03-26 00:02:16 +01:00
),
patch(
"homeassistant.components.cloud.alexa_config.CloudAlexaConfig.set_authorized"
) as set_authorized_mock,
):
set_authorized_mock.assert_not_called()
2023-12-13 13:53:22 +01:00
await client.send_json(
{"id": 5, "type": "cloud/update_prefs", "alexa_report_state": True}
)
response = await client.receive_json()
2023-12-13 13:53:22 +01:00
set_authorized_mock.assert_called_once_with(True)
await hass.async_block_till_done()
assert response["success"]
2019-06-26 20:24:20 -07:00
async def test_websocket_update_preferences_require_relink(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
2023-12-13 13:53:22 +01:00
setup_cloud: None,
) -> None:
2019-06-26 20:24:20 -07:00
"""Test updating preference requires relink."""
client = await hass_ws_client(hass)
with (
patch(
(
"homeassistant.components.cloud.alexa_config.CloudAlexaConfig"
".async_get_access_token"
2024-03-26 00:02:16 +01:00
),
side_effect=alexa_errors.RequireRelink,
),
patch(
"homeassistant.components.cloud.alexa_config.CloudAlexaConfig.set_authorized"
) as set_authorized_mock,
):
set_authorized_mock.assert_not_called()
2023-12-13 13:53:22 +01:00
2019-06-26 20:24:20 -07:00
await client.send_json(
{"id": 5, "type": "cloud/update_prefs", "alexa_report_state": True}
)
response = await client.receive_json()
2023-12-13 13:53:22 +01:00
set_authorized_mock.assert_called_once_with(False)
2019-06-26 20:24:20 -07:00
assert not response["success"]
assert response["error"]["code"] == "alexa_relink"
async def test_websocket_update_preferences_no_token(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
2023-12-13 13:53:22 +01:00
setup_cloud: None,
) -> None:
2019-06-26 20:24:20 -07:00
"""Test updating preference no token available."""
client = await hass_ws_client(hass)
with (
patch(
(
"homeassistant.components.cloud.alexa_config.CloudAlexaConfig"
".async_get_access_token"
2024-03-26 00:02:16 +01:00
),
2019-06-26 20:24:20 -07:00
side_effect=alexa_errors.NoTokenAvailable,
),
patch(
"homeassistant.components.cloud.alexa_config.CloudAlexaConfig.set_authorized"
) as set_authorized_mock,
):
set_authorized_mock.assert_not_called()
2023-12-13 13:53:22 +01:00
2019-06-26 20:24:20 -07:00
await client.send_json(
{"id": 5, "type": "cloud/update_prefs", "alexa_report_state": True}
)
response = await client.receive_json()
2023-12-13 13:53:22 +01:00
set_authorized_mock.assert_called_once_with(False)
2019-06-26 20:24:20 -07:00
assert not response["success"]
assert response["error"]["code"] == "alexa_relink"
async def test_enabling_webhook(
2023-12-13 13:53:22 +01:00
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
cloud: MagicMock,
setup_cloud: None,
) -> None:
2018-11-26 14:10:18 +01:00
"""Test we call right code to enable webhooks."""
client = await hass_ws_client(hass)
2023-12-13 13:53:22 +01:00
mock_enable = cloud.cloudhooks.async_create
mock_enable.return_value = {}
2018-11-26 14:10:18 +01:00
2023-12-13 13:53:22 +01:00
await client.send_json(
{"id": 5, "type": "cloud/cloudhook/create", "webhook_id": "mock-webhook-id"}
)
response = await client.receive_json()
assert response["success"]
assert mock_enable.call_count == 1
2018-11-26 14:10:18 +01:00
assert mock_enable.mock_calls[0][1][0] == "mock-webhook-id"
async def test_disabling_webhook(
2023-12-13 13:53:22 +01:00
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
cloud: MagicMock,
setup_cloud: None,
) -> None:
2018-11-26 14:10:18 +01:00
"""Test we call right code to disable webhooks."""
client = await hass_ws_client(hass)
2023-12-13 13:53:22 +01:00
mock_disable = cloud.cloudhooks.async_delete
2018-11-26 14:10:18 +01:00
2023-12-13 13:53:22 +01:00
await client.send_json(
{"id": 5, "type": "cloud/cloudhook/delete", "webhook_id": "mock-webhook-id"}
)
response = await client.receive_json()
assert response["success"]
assert mock_disable.call_count == 1
2018-11-26 14:10:18 +01:00
assert mock_disable.mock_calls[0][1][0] == "mock-webhook-id"
2019-03-12 07:54:04 -07:00
async def test_enabling_remote(
2023-12-13 13:53:22 +01:00
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
cloud: MagicMock,
setup_cloud: None,
) -> None:
2019-03-12 07:54:04 -07:00
"""Test we call right code to enable remote UI."""
client = await hass_ws_client(hass)
2023-12-13 13:53:22 +01:00
mock_connect = cloud.remote.connect
2019-03-12 07:54:04 -07:00
assert not cloud.client.remote_autostart
2023-12-13 13:53:22 +01:00
await client.send_json({"id": 5, "type": "cloud/remote/connect"})
response = await client.receive_json()
assert response["success"]
assert cloud.client.remote_autostart
assert mock_connect.call_count == 1
mock_disconnect = cloud.remote.disconnect
await client.send_json({"id": 6, "type": "cloud/remote/disconnect"})
response = await client.receive_json()
assert response["success"]
assert not cloud.client.remote_autostart
assert mock_disconnect.call_count == 1
async def test_enabling_remote_remote_activation_not_allowed(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
cloud: MagicMock,
setup_cloud: None,
) -> None:
"""Test we can enable remote UI locally when blocked remotely."""
client = await hass_ws_client(hass)
mock_connect = cloud.remote.connect
assert not cloud.client.remote_autostart
2024-02-19 10:09:59 +01:00
await cloud.client.prefs.async_update(remote_allow_remote_enable=False)
await client.send_json({"id": 5, "type": "cloud/remote/connect"})
response = await client.receive_json()
assert response["success"]
assert cloud.client.remote_autostart
assert mock_connect.call_count == 1
mock_disconnect = cloud.remote.disconnect
await client.send_json({"id": 6, "type": "cloud/remote/disconnect"})
response = await client.receive_json()
assert response["success"]
assert not cloud.client.remote_autostart
assert mock_disconnect.call_count == 1
async def test_list_google_entities(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
hass_ws_client: WebSocketGenerator,
2023-12-13 13:53:22 +01:00
setup_cloud: None,
) -> None:
"""Test that we can list Google entities."""
client = await hass_ws_client(hass)
2019-06-21 02:17:21 -07:00
entity = GoogleEntity(
hass, MockConfig(should_expose=lambda *_: False), State("light.kitchen", "on")
)
entity2 = GoogleEntity(
hass,
MockConfig(should_expose=lambda *_: True, should_2fa=lambda *_: False),
State("cover.garage", "open", {"device_class": "garage"}),
)
with patch(
"homeassistant.components.google_assistant.helpers.async_get_entities",
return_value=[entity, entity2],
):
await client.send_json_auto_id({"type": "cloud/google_assistant/entities"})
response = await client.receive_json()
2023-12-13 13:53:22 +01:00
assert response["success"]
assert len(response["result"]) == 2
assert response["result"][0] == {
"entity_id": "light.kitchen",
"might_2fa": False,
"traits": ["action.devices.traits.OnOff"],
}
assert response["result"][1] == {
"entity_id": "cover.garage",
"might_2fa": True,
"traits": ["action.devices.traits.OpenClose"],
}
# Add the entities to the entity registry
entity_registry.async_get_or_create(
"light", "test", "unique", suggested_object_id="kitchen"
)
entity_registry.async_get_or_create(
"cover", "test", "unique", suggested_object_id="garage"
)
with patch(
"homeassistant.components.google_assistant.helpers.async_get_entities",
return_value=[entity, entity2],
):
await client.send_json_auto_id({"type": "cloud/google_assistant/entities"})
response = await client.receive_json()
2023-12-13 13:53:22 +01:00
assert response["success"]
assert len(response["result"]) == 2
assert response["result"][0] == {
"entity_id": "light.kitchen",
"might_2fa": False,
"traits": ["action.devices.traits.OnOff"],
}
assert response["result"][1] == {
"entity_id": "cover.garage",
"might_2fa": True,
"traits": ["action.devices.traits.OpenClose"],
}
async def test_get_google_entity(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
hass_ws_client: WebSocketGenerator,
2023-12-13 13:53:22 +01:00
setup_cloud: None,
) -> None:
"""Test that we can get a Google entity."""
client = await hass_ws_client(hass)
# Test getting an unknown entity
await client.send_json_auto_id(
{"type": "cloud/google_assistant/entities/get", "entity_id": "light.kitchen"}
)
response = await client.receive_json()
2023-12-13 13:53:22 +01:00
assert not response["success"]
assert response["error"] == {
"code": "not_found",
"message": "light.kitchen unknown",
}
# Test getting a blocked entity
entity_registry.async_get_or_create(
"group", "test", "unique", suggested_object_id="all_locks"
)
hass.states.async_set("group.all_locks", "bla")
2023-12-13 13:53:22 +01:00
await client.send_json_auto_id(
{"type": "cloud/google_assistant/entities/get", "entity_id": "group.all_locks"}
)
response = await client.receive_json()
2023-12-13 13:53:22 +01:00
assert not response["success"]
assert response["error"] == {
"code": "not_supported",
"message": "group.all_locks not supported by Google assistant",
}
entity_registry.async_get_or_create(
"light", "test", "unique", suggested_object_id="kitchen"
)
hass.states.async_set("light.kitchen", "on")
hass.states.async_set("cover.garage", "open", {"device_class": "garage"})
await client.send_json_auto_id(
{"type": "cloud/google_assistant/entities/get", "entity_id": "light.kitchen"}
)
response = await client.receive_json()
2023-12-13 13:53:22 +01:00
assert response["success"]
assert response["result"] == {
"disable_2fa": None,
"entity_id": "light.kitchen",
"might_2fa": False,
"traits": ["action.devices.traits.OnOff"],
}
await client.send_json_auto_id(
{"type": "cloud/google_assistant/entities/get", "entity_id": "cover.garage"}
)
response = await client.receive_json()
2023-12-13 13:53:22 +01:00
assert response["success"]
assert response["result"] == {
"disable_2fa": None,
"entity_id": "cover.garage",
"might_2fa": True,
"traits": ["action.devices.traits.OpenClose"],
}
# Set the disable 2fa flag
await client.send_json_auto_id(
{
"type": "cloud/google_assistant/entities/update",
"entity_id": "cover.garage",
"disable_2fa": True,
}
)
response = await client.receive_json()
2023-12-13 13:53:22 +01:00
assert response["success"]
await client.send_json_auto_id(
{"type": "cloud/google_assistant/entities/get", "entity_id": "cover.garage"}
)
response = await client.receive_json()
2023-12-13 13:53:22 +01:00
assert response["success"]
assert response["result"] == {
"disable_2fa": True,
"entity_id": "cover.garage",
"might_2fa": True,
"traits": ["action.devices.traits.OpenClose"],
}
async def test_update_google_entity(
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
2023-12-13 13:53:22 +01:00
setup_cloud: None,
) -> None:
"""Test that we can update config of a Google entity."""
client = await hass_ws_client(hass)
2023-12-13 13:53:22 +01:00
await client.send_json_auto_id(
{
"type": "cloud/google_assistant/entities/update",
"entity_id": "light.kitchen",
"disable_2fa": False,
}
)
response = await client.receive_json()
2023-12-13 13:53:22 +01:00
assert response["success"]
2019-06-07 23:08:55 -07:00
await client.send_json_auto_id(
2020-08-28 16:49:17 +02:00
{
"type": "homeassistant/expose_entity",
"assistants": ["cloud.google_assistant"],
"entity_ids": ["light.kitchen"],
"should_expose": False,
2020-08-28 16:49:17 +02:00
}
)
response = await client.receive_json()
2023-12-13 13:53:22 +01:00
assert response["success"]
assert exposed_entities.async_get_entity_settings(hass, "light.kitchen") == {
"cloud.google_assistant": {"disable_2fa": False, "should_expose": False}
}
2020-08-28 16:49:17 +02:00
2019-06-07 23:08:55 -07:00
async def test_list_alexa_entities(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
hass_ws_client: WebSocketGenerator,
2023-12-13 13:53:22 +01:00
setup_cloud: None,
) -> None:
"""Test that we can list Alexa entities."""
client = await hass_ws_client(hass)
entity = LightCapabilities(
hass, MagicMock(entity_config={}), State("light.kitchen", "on")
)
with patch(
"homeassistant.components.alexa.entities.async_get_entities",
return_value=[entity],
):
await client.send_json_auto_id({"id": 5, "type": "cloud/alexa/entities"})
response = await client.receive_json()
2023-12-13 13:53:22 +01:00
assert response["success"]
assert len(response["result"]) == 1
assert response["result"][0] == {
"entity_id": "light.kitchen",
"display_categories": ["LIGHT"],
"interfaces": ["Alexa.PowerController", "Alexa.EndpointHealth", "Alexa"],
}
2023-12-16 14:23:56 -10:00
with (
patch(
(
"homeassistant.components.cloud.alexa_config.CloudAlexaConfig"
".async_get_access_token"
2024-03-26 00:02:16 +01:00
),
),
patch(
2023-12-16 14:23:56 -10:00
"homeassistant.components.cloud.alexa_config.alexa_state_report.async_send_add_or_update_message"
),
):
# Add the entity to the entity registry
entity_registry.async_get_or_create(
"light", "test", "unique", suggested_object_id="kitchen"
)
await hass.async_block_till_done()
with patch(
"homeassistant.components.alexa.entities.async_get_entities",
return_value=[entity],
):
await client.send_json_auto_id({"type": "cloud/alexa/entities"})
response = await client.receive_json()
2023-12-13 13:53:22 +01:00
assert response["success"]
assert len(response["result"]) == 1
assert response["result"][0] == {
"entity_id": "light.kitchen",
"display_categories": ["LIGHT"],
"interfaces": ["Alexa.PowerController", "Alexa.EndpointHealth", "Alexa"],
}
async def test_get_alexa_entity(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
hass_ws_client: WebSocketGenerator,
2023-12-13 13:53:22 +01:00
setup_cloud: None,
) -> None:
"""Test that we can get an Alexa entity."""
client = await hass_ws_client(hass)
# Test getting an unknown entity
await client.send_json_auto_id(
{"type": "cloud/alexa/entities/get", "entity_id": "light.kitchen"}
)
response = await client.receive_json()
2023-12-13 13:53:22 +01:00
assert response["success"]
assert response["result"] is None
# Test getting an unknown sensor
await client.send_json_auto_id(
{"type": "cloud/alexa/entities/get", "entity_id": "sensor.temperature"}
)
response = await client.receive_json()
2023-12-13 13:53:22 +01:00
assert not response["success"]
assert response["error"] == {
"code": "not_supported",
"message": "sensor.temperature not supported by Alexa",
}
# Test getting a blocked entity
entity_registry.async_get_or_create(
"group", "test", "unique", suggested_object_id="all_locks"
)
hass.states.async_set("group.all_locks", "bla")
2023-12-13 13:53:22 +01:00
await client.send_json_auto_id(
{"type": "cloud/alexa/entities/get", "entity_id": "group.all_locks"}
)
response = await client.receive_json()
2023-12-13 13:53:22 +01:00
assert not response["success"]
assert response["error"] == {
"code": "not_supported",
"message": "group.all_locks not supported by Alexa",
}
entity_registry.async_get_or_create(
"light", "test", "unique", suggested_object_id="kitchen"
)
entity_registry.async_get_or_create(
"water_heater", "test", "unique", suggested_object_id="basement"
)
await client.send_json_auto_id(
{"type": "cloud/alexa/entities/get", "entity_id": "light.kitchen"}
)
response = await client.receive_json()
2023-12-13 13:53:22 +01:00
assert response["success"]
assert response["result"] is None
await client.send_json_auto_id(
{"type": "cloud/alexa/entities/get", "entity_id": "water_heater.basement"}
)
response = await client.receive_json()
2023-12-13 13:53:22 +01:00
assert not response["success"]
assert response["error"] == {
"code": "not_supported",
"message": "water_heater.basement not supported by Alexa",
}
async def test_update_alexa_entity(
hass: HomeAssistant,
entity_registry: er.EntityRegistry,
hass_ws_client: WebSocketGenerator,
2023-12-13 13:53:22 +01:00
setup_cloud: None,
) -> None:
"""Test that we can update config of an Alexa entity."""
entry = entity_registry.async_get_or_create(
"light", "test", "unique", suggested_object_id="kitchen"
)
client = await hass_ws_client(hass)
2023-12-13 13:53:22 +01:00
await client.send_json_auto_id(
{
"type": "homeassistant/expose_entity",
"assistants": ["cloud.alexa"],
"entity_ids": [entry.entity_id],
"should_expose": False,
}
)
response = await client.receive_json()
assert response["success"]
assert exposed_entities.async_get_entity_settings(hass, entry.entity_id) == {
"cloud.alexa": {"should_expose": False}
}
2020-08-28 16:49:17 +02:00
2019-06-24 22:04:31 -07:00
async def test_sync_alexa_entities_timeout(
2023-12-13 13:53:22 +01:00
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
setup_cloud: None,
) -> None:
2019-06-24 22:04:31 -07:00
"""Test that timeout syncing Alexa entities."""
client = await hass_ws_client(hass)
2023-12-13 13:53:22 +01:00
2019-06-24 22:04:31 -07:00
with patch(
(
"homeassistant.components.cloud.alexa_config.CloudAlexaConfig"
".async_sync_entities"
),
2024-02-05 11:31:33 +01:00
side_effect=TimeoutError,
2019-06-24 22:04:31 -07:00
):
await client.send_json({"id": 5, "type": "cloud/alexa/sync"})
response = await client.receive_json()
assert not response["success"]
assert response["error"]["code"] == "timeout"
async def test_sync_alexa_entities_no_token(
2023-12-13 13:53:22 +01:00
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
setup_cloud: None,
) -> None:
2019-06-24 22:04:31 -07:00
"""Test sync Alexa entities when we have no token."""
client = await hass_ws_client(hass)
2023-12-13 13:53:22 +01:00
2019-06-24 22:04:31 -07:00
with patch(
(
"homeassistant.components.cloud.alexa_config.CloudAlexaConfig"
".async_sync_entities"
),
2019-06-24 22:04:31 -07:00
side_effect=alexa_errors.NoTokenAvailable,
):
await client.send_json({"id": 5, "type": "cloud/alexa/sync"})
response = await client.receive_json()
assert not response["success"]
assert response["error"]["code"] == "alexa_relink"
async def test_enable_alexa_state_report_fail(
2023-12-13 13:53:22 +01:00
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
setup_cloud: None,
) -> None:
2019-06-24 22:04:31 -07:00
"""Test enable Alexa entities state reporting when no token available."""
client = await hass_ws_client(hass)
2023-12-13 13:53:22 +01:00
2019-06-24 22:04:31 -07:00
with patch(
(
"homeassistant.components.cloud.alexa_config.CloudAlexaConfig"
".async_sync_entities"
),
2019-06-24 22:04:31 -07:00
side_effect=alexa_errors.NoTokenAvailable,
):
await client.send_json({"id": 5, "type": "cloud/alexa/sync"})
response = await client.receive_json()
assert not response["success"]
assert response["error"]["code"] == "alexa_relink"
2019-11-08 01:06:16 -08:00
async def test_thingtalk_convert(
2023-12-13 13:53:22 +01:00
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
setup_cloud: None,
) -> None:
2019-11-08 01:06:16 -08:00
"""Test that we can convert a query."""
client = await hass_ws_client(hass)
with patch(
"homeassistant.components.cloud.http_api.thingtalk.async_convert",
2020-04-30 13:29:50 -07:00
return_value={"hello": "world"},
2019-11-08 01:06:16 -08:00
):
await client.send_json(
{"id": 5, "type": "cloud/thingtalk/convert", "query": "some-data"}
)
response = await client.receive_json()
assert response["success"]
assert response["result"] == {"hello": "world"}
async def test_thingtalk_convert_timeout(
2023-12-13 13:53:22 +01:00
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
setup_cloud: None,
) -> None:
2019-11-08 01:06:16 -08:00
"""Test that we can convert a query."""
client = await hass_ws_client(hass)
with patch(
"homeassistant.components.cloud.http_api.thingtalk.async_convert",
2024-02-05 11:31:33 +01:00
side_effect=TimeoutError,
2019-11-08 01:06:16 -08:00
):
await client.send_json(
{"id": 5, "type": "cloud/thingtalk/convert", "query": "some-data"}
)
response = await client.receive_json()
assert not response["success"]
assert response["error"]["code"] == "timeout"
async def test_thingtalk_convert_internal(
2023-12-13 13:53:22 +01:00
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
setup_cloud: None,
) -> None:
2019-11-08 01:06:16 -08:00
"""Test that we can convert a query."""
client = await hass_ws_client(hass)
with patch(
"homeassistant.components.cloud.http_api.thingtalk.async_convert",
side_effect=thingtalk.ThingTalkConversionError("Did not understand"),
):
await client.send_json(
{"id": 5, "type": "cloud/thingtalk/convert", "query": "some-data"}
)
response = await client.receive_json()
assert not response["success"]
assert response["error"]["code"] == "unknown_error"
assert response["error"]["message"] == "Did not understand"
async def test_tts_info(
2023-12-13 13:53:22 +01:00
hass: HomeAssistant,
hass_ws_client: WebSocketGenerator,
setup_cloud: None,
) -> None:
"""Test that we can get TTS info."""
client = await hass_ws_client(hass)
2024-03-20 09:42:40 +01:00
await client.send_json_auto_id({"type": "cloud/tts/info"})
response = await client.receive_json()
assert response["success"]
2024-03-20 09:42:40 +01:00
assert response["result"] == {
"languages": json.loads(
json.dumps(
[
(language, voice)
for language, voices in TTS_VOICES.items()
for voice in voices
]
)
)
}
2023-08-08 11:02:42 +02:00
@pytest.mark.parametrize(
("endpoint", "data"),
[
("/api/cloud/forgot_password", {"email": "fake@example.com"}),
("/api/cloud/google_actions/sync", None),
("/api/cloud/login", {"email": "fake@example.com", "password": "secret"}),
("/api/cloud/logout", None),
("/api/cloud/register", {"email": "fake@example.com", "password": "secret"}),
("/api/cloud/resend_confirm", {"email": "fake@example.com"}),
],
)
async def test_api_calls_require_admin(
hass: HomeAssistant,
2023-12-13 13:53:22 +01:00
setup_cloud: None,
2023-08-08 11:02:42 +02:00
hass_client: ClientSessionGenerator,
hass_read_only_access_token: str,
endpoint: str,
data: dict[str, Any] | None,
) -> None:
"""Test cloud APIs endpoints do not work as a normal user."""
client = await hass_client(hass_read_only_access_token)
resp = await client.post(endpoint, json=data)
assert resp.status == HTTPStatus.UNAUTHORIZED