core/tests/components/websocket_api/test_commands.py

470 lines
14 KiB
Python
Raw Normal View History

"""Tests for WebSocket API commands."""
from async_timeout import timeout
from homeassistant.core import callback
from homeassistant.components.websocket_api.const import URL
from homeassistant.components.websocket_api.auth import (
2019-07-31 19:25:30 +00:00
TYPE_AUTH,
TYPE_AUTH_OK,
TYPE_AUTH_REQUIRED,
)
from homeassistant.components.websocket_api import const
from homeassistant.exceptions import HomeAssistantError
from homeassistant.setup import async_setup_component
from tests.common import async_mock_service
async def test_call_service(hass, websocket_client):
"""Test call service command."""
calls = []
@callback
def service_call(call):
calls.append(call)
2019-07-31 19:25:30 +00:00
hass.services.async_register("domain_test", "test_service", service_call)
2019-07-31 19:25:30 +00:00
await websocket_client.send_json(
{
"id": 5,
"type": "call_service",
"domain": "domain_test",
"service": "test_service",
"service_data": {"hello": "world"},
}
2019-07-31 19:25:30 +00:00
)
msg = await websocket_client.receive_json()
2019-07-31 19:25:30 +00:00
assert msg["id"] == 5
assert msg["type"] == const.TYPE_RESULT
assert msg["success"]
assert len(calls) == 1
call = calls[0]
2019-07-31 19:25:30 +00:00
assert call.domain == "domain_test"
assert call.service == "test_service"
assert call.data == {"hello": "world"}
async def test_call_service_not_found(hass, websocket_client):
"""Test call service command."""
2019-07-31 19:25:30 +00:00
await websocket_client.send_json(
{
"id": 5,
"type": "call_service",
"domain": "domain_test",
"service": "test_service",
"service_data": {"hello": "world"},
}
2019-07-31 19:25:30 +00:00
)
msg = await websocket_client.receive_json()
2019-07-31 19:25:30 +00:00
assert msg["id"] == 5
assert msg["type"] == const.TYPE_RESULT
assert not msg["success"]
assert msg["error"]["code"] == const.ERR_NOT_FOUND
async def test_call_service_child_not_found(hass, websocket_client):
"""Test not reporting not found errors if it's not the called service."""
2019-07-31 19:25:30 +00:00
async def serv_handler(call):
2019-07-31 19:25:30 +00:00
await hass.services.async_call("non", "existing")
2019-07-31 19:25:30 +00:00
hass.services.async_register("domain_test", "test_service", serv_handler)
2019-07-31 19:25:30 +00:00
await websocket_client.send_json(
{
"id": 5,
"type": "call_service",
"domain": "domain_test",
"service": "test_service",
"service_data": {"hello": "world"},
}
2019-07-31 19:25:30 +00:00
)
msg = await websocket_client.receive_json()
2019-07-31 19:25:30 +00:00
assert msg["id"] == 5
assert msg["type"] == const.TYPE_RESULT
assert not msg["success"]
assert msg["error"]["code"] == const.ERR_HOME_ASSISTANT_ERROR
async def test_call_service_error(hass, websocket_client):
"""Test call service command with error."""
2019-07-31 19:25:30 +00:00
@callback
def ha_error_call(_):
2019-07-31 19:25:30 +00:00
raise HomeAssistantError("error_message")
2019-07-31 19:25:30 +00:00
hass.services.async_register("domain_test", "ha_error", ha_error_call)
async def unknown_error_call(_):
2019-07-31 19:25:30 +00:00
raise ValueError("value_error")
2019-07-31 19:25:30 +00:00
hass.services.async_register("domain_test", "unknown_error", unknown_error_call)
2019-07-31 19:25:30 +00:00
await websocket_client.send_json(
{
"id": 5,
"type": "call_service",
"domain": "domain_test",
"service": "ha_error",
}
)
msg = await websocket_client.receive_json()
print(msg)
2019-07-31 19:25:30 +00:00
assert msg["id"] == 5
assert msg["type"] == const.TYPE_RESULT
assert msg["success"] is False
assert msg["error"]["code"] == "home_assistant_error"
assert msg["error"]["message"] == "error_message"
await websocket_client.send_json(
{
"id": 6,
"type": "call_service",
"domain": "domain_test",
"service": "unknown_error",
}
)
msg = await websocket_client.receive_json()
print(msg)
2019-07-31 19:25:30 +00:00
assert msg["id"] == 6
assert msg["type"] == const.TYPE_RESULT
assert msg["success"] is False
assert msg["error"]["code"] == "unknown_error"
assert msg["error"]["message"] == "value_error"
async def test_subscribe_unsubscribe_events(hass, websocket_client):
"""Test subscribe/unsubscribe events command."""
init_count = sum(hass.bus.async_listeners().values())
2019-07-31 19:25:30 +00:00
await websocket_client.send_json(
{"id": 5, "type": "subscribe_events", "event_type": "test_event"}
)
msg = await websocket_client.receive_json()
2019-07-31 19:25:30 +00:00
assert msg["id"] == 5
assert msg["type"] == const.TYPE_RESULT
assert msg["success"]
# Verify we have a new listener
assert sum(hass.bus.async_listeners().values()) == init_count + 1
2019-07-31 19:25:30 +00:00
hass.bus.async_fire("ignore_event")
hass.bus.async_fire("test_event", {"hello": "world"})
hass.bus.async_fire("ignore_event")
with timeout(3):
msg = await websocket_client.receive_json()
2019-07-31 19:25:30 +00:00
assert msg["id"] == 5
assert msg["type"] == "event"
event = msg["event"]
2019-07-31 19:25:30 +00:00
assert event["event_type"] == "test_event"
assert event["data"] == {"hello": "world"}
assert event["origin"] == "LOCAL"
2019-07-31 19:25:30 +00:00
await websocket_client.send_json(
{"id": 6, "type": "unsubscribe_events", "subscription": 5}
)
msg = await websocket_client.receive_json()
2019-07-31 19:25:30 +00:00
assert msg["id"] == 6
assert msg["type"] == const.TYPE_RESULT
assert msg["success"]
# Check our listener got unsubscribed
assert sum(hass.bus.async_listeners().values()) == init_count
async def test_get_states(hass, websocket_client):
"""Test get_states command."""
2019-07-31 19:25:30 +00:00
hass.states.async_set("greeting.hello", "world")
hass.states.async_set("greeting.bye", "universe")
2019-07-31 19:25:30 +00:00
await websocket_client.send_json({"id": 5, "type": "get_states"})
msg = await websocket_client.receive_json()
2019-07-31 19:25:30 +00:00
assert msg["id"] == 5
assert msg["type"] == const.TYPE_RESULT
assert msg["success"]
states = []
for state in hass.states.async_all():
state = state.as_dict()
2019-07-31 19:25:30 +00:00
state["last_changed"] = state["last_changed"].isoformat()
state["last_updated"] = state["last_updated"].isoformat()
states.append(state)
2019-07-31 19:25:30 +00:00
assert msg["result"] == states
async def test_get_services(hass, websocket_client):
"""Test get_services command."""
2019-07-31 19:25:30 +00:00
await websocket_client.send_json({"id": 5, "type": "get_services"})
msg = await websocket_client.receive_json()
2019-07-31 19:25:30 +00:00
assert msg["id"] == 5
assert msg["type"] == const.TYPE_RESULT
assert msg["success"]
assert msg["result"] == hass.services.async_services()
async def test_get_config(hass, websocket_client):
"""Test get_config command."""
2019-07-31 19:25:30 +00:00
await websocket_client.send_json({"id": 5, "type": "get_config"})
msg = await websocket_client.receive_json()
2019-07-31 19:25:30 +00:00
assert msg["id"] == 5
assert msg["type"] == const.TYPE_RESULT
assert msg["success"]
2019-07-31 19:25:30 +00:00
if "components" in msg["result"]:
msg["result"]["components"] = set(msg["result"]["components"])
if "whitelist_external_dirs" in msg["result"]:
msg["result"]["whitelist_external_dirs"] = set(
msg["result"]["whitelist_external_dirs"]
)
2019-07-31 19:25:30 +00:00
assert msg["result"] == hass.config.as_dict()
async def test_ping(websocket_client):
"""Test get_panels command."""
2019-07-31 19:25:30 +00:00
await websocket_client.send_json({"id": 5, "type": "ping"})
msg = await websocket_client.receive_json()
2019-07-31 19:25:30 +00:00
assert msg["id"] == 5
assert msg["type"] == "pong"
2019-07-31 19:25:30 +00:00
async def test_call_service_context_with_user(hass, aiohttp_client, hass_access_token):
"""Test that the user is set in the service call context."""
assert await async_setup_component(hass, "websocket_api", {})
2019-07-31 19:25:30 +00:00
calls = async_mock_service(hass, "domain_test", "test_service")
client = await aiohttp_client(hass.http.app)
async with client.ws_connect(URL) as ws:
auth_msg = await ws.receive_json()
2019-07-31 19:25:30 +00:00
assert auth_msg["type"] == TYPE_AUTH_REQUIRED
2019-07-31 19:25:30 +00:00
await ws.send_json({"type": TYPE_AUTH, "access_token": hass_access_token})
auth_msg = await ws.receive_json()
2019-07-31 19:25:30 +00:00
assert auth_msg["type"] == TYPE_AUTH_OK
await ws.send_json(
{
"id": 5,
"type": "call_service",
"domain": "domain_test",
"service": "test_service",
"service_data": {"hello": "world"},
}
2019-07-31 19:25:30 +00:00
)
msg = await ws.receive_json()
2019-07-31 19:25:30 +00:00
assert msg["success"]
2019-07-31 19:25:30 +00:00
refresh_token = await hass.auth.async_validate_access_token(hass_access_token)
assert len(calls) == 1
call = calls[0]
2019-07-31 19:25:30 +00:00
assert call.domain == "domain_test"
assert call.service == "test_service"
assert call.data == {"hello": "world"}
assert call.context.user_id == refresh_token.user.id
async def test_subscribe_requires_admin(websocket_client, hass_admin_user):
"""Test subscribing events without being admin."""
hass_admin_user.groups = []
2019-07-31 19:25:30 +00:00
await websocket_client.send_json(
{"id": 5, "type": "subscribe_events", "event_type": "test_event"}
)
msg = await websocket_client.receive_json()
2019-07-31 19:25:30 +00:00
assert not msg["success"]
assert msg["error"]["code"] == const.ERR_UNAUTHORIZED
async def test_states_filters_visible(hass, hass_admin_user, websocket_client):
"""Test we only get entities that we're allowed to see."""
2019-07-31 19:25:30 +00:00
hass_admin_user.mock_policy({"entities": {"entity_ids": {"test.entity": True}}})
hass.states.async_set("test.entity", "hello")
hass.states.async_set("test.not_visible_entity", "invisible")
await websocket_client.send_json({"id": 5, "type": "get_states"})
msg = await websocket_client.receive_json()
2019-07-31 19:25:30 +00:00
assert msg["id"] == 5
assert msg["type"] == const.TYPE_RESULT
assert msg["success"]
2019-07-31 19:25:30 +00:00
assert len(msg["result"]) == 1
assert msg["result"][0]["entity_id"] == "test.entity"
2018-11-28 12:25:23 +00:00
async def test_get_states_not_allows_nan(hass, websocket_client):
"""Test get_states command not allows NaN floats."""
2019-07-31 19:25:30 +00:00
hass.states.async_set("greeting.hello", "world", {"hello": float("NaN")})
2018-11-28 12:25:23 +00:00
2019-07-31 19:25:30 +00:00
await websocket_client.send_json({"id": 5, "type": "get_states"})
2018-11-28 12:25:23 +00:00
msg = await websocket_client.receive_json()
2019-07-31 19:25:30 +00:00
assert not msg["success"]
assert msg["error"]["code"] == const.ERR_UNKNOWN_ERROR
async def test_subscribe_unsubscribe_events_whitelist(
2019-07-31 19:25:30 +00:00
hass, websocket_client, hass_admin_user
):
"""Test subscribe/unsubscribe events on whitelist."""
hass_admin_user.groups = []
2019-07-31 19:25:30 +00:00
await websocket_client.send_json(
{"id": 5, "type": "subscribe_events", "event_type": "not-in-whitelist"}
)
msg = await websocket_client.receive_json()
2019-07-31 19:25:30 +00:00
assert msg["id"] == 5
assert msg["type"] == const.TYPE_RESULT
assert not msg["success"]
assert msg["error"]["code"] == "unauthorized"
2019-07-31 19:25:30 +00:00
await websocket_client.send_json(
{"id": 6, "type": "subscribe_events", "event_type": "themes_updated"}
)
msg = await websocket_client.receive_json()
2019-07-31 19:25:30 +00:00
assert msg["id"] == 6
assert msg["type"] == const.TYPE_RESULT
assert msg["success"]
2019-07-31 19:25:30 +00:00
hass.bus.async_fire("themes_updated")
with timeout(3):
msg = await websocket_client.receive_json()
2019-07-31 19:25:30 +00:00
assert msg["id"] == 6
assert msg["type"] == "event"
event = msg["event"]
assert event["event_type"] == "themes_updated"
assert event["origin"] == "LOCAL"
async def test_subscribe_unsubscribe_events_state_changed(
2019-07-31 19:25:30 +00:00
hass, websocket_client, hass_admin_user
):
"""Test subscribe/unsubscribe state_changed events."""
hass_admin_user.groups = []
2019-07-31 19:25:30 +00:00
hass_admin_user.mock_policy({"entities": {"entity_ids": {"light.permitted": True}}})
2019-07-31 19:25:30 +00:00
await websocket_client.send_json(
{"id": 7, "type": "subscribe_events", "event_type": "state_changed"}
)
msg = await websocket_client.receive_json()
2019-07-31 19:25:30 +00:00
assert msg["id"] == 7
assert msg["type"] == const.TYPE_RESULT
assert msg["success"]
2019-07-31 19:25:30 +00:00
hass.states.async_set("light.not_permitted", "on")
hass.states.async_set("light.permitted", "on")
msg = await websocket_client.receive_json()
2019-07-31 19:25:30 +00:00
assert msg["id"] == 7
assert msg["type"] == "event"
assert msg["event"]["event_type"] == "state_changed"
assert msg["event"]["data"]["entity_id"] == "light.permitted"
async def test_render_template_renders_template(
hass, websocket_client, hass_admin_user
):
"""Test simple template is rendered and updated."""
hass.states.async_set("light.test", "on")
await websocket_client.send_json(
{
"id": 5,
"type": "render_template",
"template": "State is: {{ states('light.test') }}",
}
)
msg = await websocket_client.receive_json()
assert msg["id"] == 5
assert msg["type"] == const.TYPE_RESULT
assert msg["success"]
msg = await websocket_client.receive_json()
assert msg["id"] == 5
assert msg["type"] == "event"
event = msg["event"]
assert event == {"result": "State is: on"}
hass.states.async_set("light.test", "off")
msg = await websocket_client.receive_json()
assert msg["id"] == 5
assert msg["type"] == "event"
event = msg["event"]
assert event == {"result": "State is: off"}
async def test_render_template_with_manual_entity_ids(
hass, websocket_client, hass_admin_user
):
"""Test that updates to specified entity ids cause a template rerender."""
hass.states.async_set("light.test", "on")
hass.states.async_set("light.test2", "on")
await websocket_client.send_json(
{
"id": 5,
"type": "render_template",
"template": "State is: {{ states('light.test') }}",
"entity_ids": ["light.test2"],
}
)
msg = await websocket_client.receive_json()
assert msg["id"] == 5
assert msg["type"] == const.TYPE_RESULT
assert msg["success"]
msg = await websocket_client.receive_json()
assert msg["id"] == 5
assert msg["type"] == "event"
event = msg["event"]
assert event == {"result": "State is: on"}
hass.states.async_set("light.test2", "off")
msg = await websocket_client.receive_json()
assert msg["id"] == 5
assert msg["type"] == "event"
event = msg["event"]
assert event == {"result": "State is: on"}
async def test_render_template_returns_with_match_all(
hass, websocket_client, hass_admin_user
):
"""Test that a template that would match with all entities still return success."""
await websocket_client.send_json(
{"id": 5, "type": "render_template", "template": "State is: {{ 42 }}"}
)
msg = await websocket_client.receive_json()
assert msg["id"] == 5
assert msg["type"] == const.TYPE_RESULT
assert msg["success"]