mirror of
https://github.com/home-assistant/core.git
synced 2026-05-18 06:20:17 +01:00
ef6fd92079
Co-authored-by: Joost Lekkerkerker <joostlek@outlook.com>
838 lines
26 KiB
Python
838 lines
26 KiB
Python
"""Notify platform tests for mobile_app."""
|
|
|
|
import asyncio
|
|
from collections.abc import AsyncGenerator
|
|
from datetime import datetime, timedelta
|
|
from http import HTTPStatus
|
|
from unittest.mock import patch
|
|
|
|
from aiohttp import ClientError
|
|
import pytest
|
|
from syrupy.assertion import SnapshotAssertion
|
|
|
|
from homeassistant.components.mobile_app.const import DOMAIN
|
|
from homeassistant.components.notify import (
|
|
ATTR_MESSAGE,
|
|
ATTR_TITLE,
|
|
DOMAIN as NOTIFY_DOMAIN,
|
|
SERVICE_SEND_MESSAGE,
|
|
)
|
|
from homeassistant.config_entries import ConfigEntryState
|
|
from homeassistant.const import ATTR_ENTITY_ID, STATE_UNKNOWN, Platform
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.exceptions import HomeAssistantError
|
|
from homeassistant.helpers import entity_registry as er
|
|
from homeassistant.setup import async_setup_component
|
|
|
|
from tests.common import MockConfigEntry, MockUser, snapshot_platform
|
|
from tests.test_util.aiohttp import AiohttpClientMocker
|
|
from tests.typing import WebSocketGenerator
|
|
|
|
|
|
@pytest.fixture
|
|
async def setup_push_receiver(
|
|
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, hass_admin_user: MockUser
|
|
) -> None:
|
|
"""Fixture that sets up a mocked push receiver."""
|
|
push_url = "https://mobile-push.home-assistant.dev/push"
|
|
|
|
now = datetime.now() + timedelta(hours=24)
|
|
iso_time = now.strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
|
|
aioclient_mock.post(
|
|
push_url,
|
|
json={
|
|
"rateLimits": {
|
|
"attempts": 1,
|
|
"successful": 1,
|
|
"errors": 0,
|
|
"total": 1,
|
|
"maximum": 150,
|
|
"remaining": 149,
|
|
"resetsAt": iso_time,
|
|
}
|
|
},
|
|
)
|
|
|
|
entry = MockConfigEntry(
|
|
data={
|
|
"app_data": {"push_token": "PUSH_TOKEN", "push_url": push_url},
|
|
"app_id": "io.homeassistant.mobile_app",
|
|
"app_name": "mobile_app tests",
|
|
"app_version": "1.0",
|
|
"device_id": "4d5e6f",
|
|
"device_name": "Test",
|
|
"manufacturer": "Home Assistant",
|
|
"model": "mobile_app",
|
|
"os_name": "Linux",
|
|
"os_version": "5.0.6",
|
|
"secret": "123abc",
|
|
"supports_encryption": False,
|
|
"user_id": hass_admin_user.id,
|
|
"webhook_id": "mock-webhook_id",
|
|
},
|
|
domain=DOMAIN,
|
|
source="registration",
|
|
title="mobile_app test entry",
|
|
version=1,
|
|
)
|
|
entry.add_to_hass(hass)
|
|
|
|
await async_setup_component(hass, DOMAIN, {DOMAIN: {}})
|
|
await hass.async_block_till_done()
|
|
|
|
loaded_late_entry = MockConfigEntry(
|
|
data={
|
|
"app_data": {"push_token": "PUSH_TOKEN2", "push_url": f"{push_url}2"},
|
|
"app_id": "io.homeassistant.mobile_app",
|
|
"app_name": "mobile_app tests",
|
|
"app_version": "1.0",
|
|
"device_id": "4d5e6f2",
|
|
"device_name": "Loaded Late",
|
|
"manufacturer": "Home Assistant",
|
|
"model": "mobile_app",
|
|
"os_name": "Linux",
|
|
"os_version": "5.0.6",
|
|
"secret": "123abc2",
|
|
"supports_encryption": False,
|
|
"user_id": "1a2b3c2",
|
|
"webhook_id": "webhook_id_2",
|
|
},
|
|
domain=DOMAIN,
|
|
source="registration",
|
|
title="mobile_app 2 test entry",
|
|
version=1,
|
|
)
|
|
loaded_late_entry.add_to_hass(hass)
|
|
assert await hass.config_entries.async_setup(loaded_late_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
|
|
assert hass.services.has_service("notify", "mobile_app_loaded_late")
|
|
|
|
assert await hass.config_entries.async_remove(loaded_late_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
|
|
assert hass.services.has_service("notify", "mobile_app_test")
|
|
assert not hass.services.has_service("notify", "mobile_app_loaded_late")
|
|
|
|
loaded_late_entry.add_to_hass(hass)
|
|
assert await hass.config_entries.async_setup(loaded_late_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
|
|
assert hass.services.has_service("notify", "mobile_app_test")
|
|
assert hass.services.has_service("notify", "mobile_app_loaded_late")
|
|
|
|
|
|
@pytest.fixture
|
|
async def setup_websocket_channel_only_push(
|
|
hass: HomeAssistant, hass_admin_user: MockUser
|
|
) -> None:
|
|
"""Set up local push."""
|
|
entry = MockConfigEntry(
|
|
data={
|
|
"app_data": {"push_websocket_channel": True},
|
|
"app_id": "io.homeassistant.mobile_app",
|
|
"app_name": "mobile_app tests",
|
|
"app_version": "1.0",
|
|
"device_id": "websocket-push-device-id",
|
|
"device_name": "Websocket Push Name",
|
|
"manufacturer": "Home Assistant",
|
|
"model": "mobile_app",
|
|
"os_name": "Linux",
|
|
"os_version": "5.0.6",
|
|
"secret": "123abc2",
|
|
"supports_encryption": False,
|
|
"user_id": hass_admin_user.id,
|
|
"webhook_id": "websocket-push-webhook-id",
|
|
},
|
|
domain=DOMAIN,
|
|
source="registration",
|
|
title="websocket push test entry",
|
|
version=1,
|
|
)
|
|
entry.add_to_hass(hass)
|
|
assert await hass.config_entries.async_setup(entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
|
|
assert hass.services.has_service("notify", "mobile_app_websocket_push_name")
|
|
|
|
|
|
@pytest.fixture
|
|
async def notify_only() -> AsyncGenerator[None]:
|
|
"""Enable only the notify platform."""
|
|
with patch(
|
|
"homeassistant.components.mobile_app.PLATFORMS",
|
|
[Platform.NOTIFY],
|
|
):
|
|
yield
|
|
|
|
|
|
async def test_notify_works(
|
|
hass: HomeAssistant, aioclient_mock: AiohttpClientMocker, setup_push_receiver
|
|
) -> None:
|
|
"""Test notify works."""
|
|
assert hass.services.has_service("notify", "mobile_app_test") is True
|
|
await hass.services.async_call(
|
|
"notify",
|
|
"mobile_app_test",
|
|
{
|
|
"message": "Hello world",
|
|
"title": "Demo",
|
|
"target": ["mock-webhook_id"],
|
|
"data": {"field1": "value1"},
|
|
},
|
|
blocking=True,
|
|
)
|
|
|
|
assert len(aioclient_mock.mock_calls) == 1
|
|
call = aioclient_mock.mock_calls
|
|
|
|
call_json = call[0][2]
|
|
|
|
assert call_json["push_token"] == "PUSH_TOKEN"
|
|
assert call_json["message"] == "Hello world"
|
|
assert call_json["title"] == "Demo"
|
|
assert call_json["data"] == {"field1": "value1"}
|
|
assert call_json["registration_info"]["app_id"] == "io.homeassistant.mobile_app"
|
|
assert call_json["registration_info"]["app_version"] == "1.0"
|
|
assert call_json["registration_info"]["webhook_id"] == "mock-webhook_id"
|
|
|
|
|
|
async def test_notify_ws_works(
|
|
hass: HomeAssistant,
|
|
aioclient_mock: AiohttpClientMocker,
|
|
setup_push_receiver,
|
|
hass_ws_client: WebSocketGenerator,
|
|
) -> None:
|
|
"""Test notify works."""
|
|
client = await hass_ws_client(hass)
|
|
|
|
await client.send_json_auto_id(
|
|
{
|
|
"type": "mobile_app/push_notification_channel",
|
|
"webhook_id": "mock-webhook_id",
|
|
}
|
|
)
|
|
|
|
sub_result = await client.receive_json()
|
|
assert sub_result["success"]
|
|
|
|
# Subscribe twice, it should forward all messages to 2nd subscription
|
|
await client.send_json_auto_id(
|
|
{
|
|
"type": "mobile_app/push_notification_channel",
|
|
"webhook_id": "mock-webhook_id",
|
|
}
|
|
)
|
|
|
|
sub_result = await client.receive_json()
|
|
assert sub_result["success"]
|
|
new_sub_id = sub_result["id"]
|
|
|
|
await hass.services.async_call(
|
|
"notify", "mobile_app_test", {"message": "Hello world"}, blocking=True
|
|
)
|
|
|
|
assert len(aioclient_mock.mock_calls) == 0
|
|
|
|
msg_result = await client.receive_json()
|
|
assert msg_result["event"] == {"message": "Hello world"}
|
|
assert msg_result["id"] == new_sub_id # This is the new subscription
|
|
|
|
# Unsubscribe, now it should go over http
|
|
await client.send_json_auto_id(
|
|
{
|
|
"type": "unsubscribe_events",
|
|
"subscription": new_sub_id,
|
|
}
|
|
)
|
|
sub_result = await client.receive_json()
|
|
assert sub_result["success"]
|
|
|
|
await hass.services.async_call(
|
|
"notify", "mobile_app_test", {"message": "Hello world 2"}, blocking=True
|
|
)
|
|
|
|
assert len(aioclient_mock.mock_calls) == 1
|
|
|
|
# Test non-existing webhook ID
|
|
await client.send_json_auto_id(
|
|
{
|
|
"type": "mobile_app/push_notification_channel",
|
|
"webhook_id": "non-existing",
|
|
}
|
|
)
|
|
sub_result = await client.receive_json()
|
|
assert not sub_result["success"]
|
|
assert sub_result["error"] == {
|
|
"code": "not_found",
|
|
"message": "Webhook ID not found",
|
|
}
|
|
|
|
# Test webhook ID linked to other user
|
|
await client.send_json_auto_id(
|
|
{
|
|
"type": "mobile_app/push_notification_channel",
|
|
"webhook_id": "webhook_id_2",
|
|
}
|
|
)
|
|
sub_result = await client.receive_json()
|
|
assert not sub_result["success"]
|
|
assert sub_result["error"] == {
|
|
"code": "unauthorized",
|
|
"message": "User not linked to this webhook ID",
|
|
}
|
|
|
|
|
|
async def test_notify_ws_confirming_works(
|
|
hass: HomeAssistant,
|
|
aioclient_mock: AiohttpClientMocker,
|
|
setup_push_receiver,
|
|
hass_ws_client: WebSocketGenerator,
|
|
) -> None:
|
|
"""Test notify confirming works."""
|
|
client = await hass_ws_client(hass)
|
|
|
|
await client.send_json_auto_id(
|
|
{
|
|
"type": "mobile_app/push_notification_channel",
|
|
"webhook_id": "mock-webhook_id",
|
|
"support_confirm": True,
|
|
}
|
|
)
|
|
|
|
sub_result = await client.receive_json()
|
|
assert sub_result["success"]
|
|
sub_id = sub_result["id"]
|
|
|
|
# Sent a message that will be delivered locally
|
|
await hass.services.async_call(
|
|
"notify", "mobile_app_test", {"message": "Hello world"}, blocking=True
|
|
)
|
|
|
|
msg_result = await client.receive_json()
|
|
confirm_id = msg_result["event"].pop("hass_confirm_id")
|
|
assert confirm_id is not None
|
|
assert msg_result["event"] == {"message": "Hello world"}
|
|
|
|
# Try to confirm with incorrect confirm ID
|
|
await client.send_json_auto_id(
|
|
{
|
|
"type": "mobile_app/push_notification_confirm",
|
|
"webhook_id": "mock-webhook_id",
|
|
"confirm_id": "incorrect-confirm-id",
|
|
}
|
|
)
|
|
|
|
result = await client.receive_json()
|
|
assert not result["success"]
|
|
assert result["error"] == {
|
|
"code": "not_found",
|
|
"message": "Push notification channel not found",
|
|
}
|
|
|
|
# Confirm with correct confirm ID
|
|
await client.send_json_auto_id(
|
|
{
|
|
"type": "mobile_app/push_notification_confirm",
|
|
"webhook_id": "mock-webhook_id",
|
|
"confirm_id": confirm_id,
|
|
}
|
|
)
|
|
|
|
result = await client.receive_json()
|
|
assert result["success"]
|
|
|
|
# Drop local push channel and try to confirm another message
|
|
await client.send_json_auto_id(
|
|
{
|
|
"type": "unsubscribe_events",
|
|
"subscription": sub_id,
|
|
}
|
|
)
|
|
sub_result = await client.receive_json()
|
|
assert sub_result["success"]
|
|
|
|
await client.send_json_auto_id(
|
|
{
|
|
"type": "mobile_app/push_notification_confirm",
|
|
"webhook_id": "mock-webhook_id",
|
|
"confirm_id": confirm_id,
|
|
}
|
|
)
|
|
|
|
result = await client.receive_json()
|
|
assert not result["success"]
|
|
assert result["error"] == {
|
|
"code": "not_found",
|
|
"message": "Push notification channel not found",
|
|
}
|
|
|
|
|
|
async def test_notify_ws_not_confirming(
|
|
hass: HomeAssistant,
|
|
aioclient_mock: AiohttpClientMocker,
|
|
setup_push_receiver,
|
|
hass_ws_client: WebSocketGenerator,
|
|
) -> None:
|
|
"""Test we go via cloud when failed to confirm."""
|
|
client = await hass_ws_client(hass)
|
|
|
|
await client.send_json_auto_id(
|
|
{
|
|
"type": "mobile_app/push_notification_channel",
|
|
"webhook_id": "mock-webhook_id",
|
|
"support_confirm": True,
|
|
}
|
|
)
|
|
|
|
sub_result = await client.receive_json()
|
|
assert sub_result["success"]
|
|
|
|
await hass.services.async_call(
|
|
"notify", "mobile_app_test", {"message": "Hello world 1"}, blocking=True
|
|
)
|
|
|
|
with patch(
|
|
"homeassistant.components.mobile_app.push_notification.PUSH_CONFIRM_TIMEOUT", 0
|
|
):
|
|
await hass.services.async_call(
|
|
"notify", "mobile_app_test", {"message": "Hello world 2"}, blocking=True
|
|
)
|
|
await hass.async_block_till_done()
|
|
await hass.async_block_till_done()
|
|
|
|
# When we fail, all unconfirmed ones and failed one are sent via cloud
|
|
assert len(aioclient_mock.mock_calls) == 2
|
|
|
|
# All future ones also go via cloud
|
|
await hass.services.async_call(
|
|
"notify", "mobile_app_test", {"message": "Hello world 3"}, blocking=True
|
|
)
|
|
|
|
assert len(aioclient_mock.mock_calls) == 3
|
|
|
|
|
|
async def test_local_push_only(
|
|
hass: HomeAssistant,
|
|
hass_ws_client: WebSocketGenerator,
|
|
setup_websocket_channel_only_push,
|
|
) -> None:
|
|
"""Test a local only push registration."""
|
|
with pytest.raises(
|
|
HomeAssistantError,
|
|
match=r"Device.*websocket-push-webhook-id.*not connected to local push notifications",
|
|
):
|
|
await hass.services.async_call(
|
|
"notify",
|
|
"mobile_app_websocket_push_name",
|
|
{"message": "Not connected"},
|
|
blocking=True,
|
|
)
|
|
|
|
client = await hass_ws_client(hass)
|
|
|
|
await client.send_json_auto_id(
|
|
{
|
|
"type": "mobile_app/push_notification_channel",
|
|
"webhook_id": "websocket-push-webhook-id",
|
|
}
|
|
)
|
|
|
|
sub_result = await client.receive_json()
|
|
assert sub_result["success"]
|
|
sub_id = sub_result["id"]
|
|
|
|
await hass.services.async_call(
|
|
"notify",
|
|
"mobile_app_websocket_push_name",
|
|
{"message": "Hello world 1"},
|
|
blocking=True,
|
|
)
|
|
|
|
msg = await client.receive_json()
|
|
assert msg["id"] == sub_id
|
|
assert msg["type"] == "event"
|
|
assert msg["event"] == {"message": "Hello world 1"}
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"target", [["webhook_id_2", "mock-webhook_id", "websocket-push-webhook-id"], None]
|
|
)
|
|
async def test_notify_multiple_targets(
|
|
hass: HomeAssistant,
|
|
hass_ws_client: WebSocketGenerator,
|
|
aioclient_mock: AiohttpClientMocker,
|
|
setup_push_receiver,
|
|
setup_websocket_channel_only_push,
|
|
target: list[str] | None,
|
|
) -> None:
|
|
"""Test notify to multiple targets.
|
|
|
|
Messages will be sent to three targerts, one (with webhook id `webhook_id_2`) will be remote target
|
|
and will send the notification via HTTP request, the other two (`mock-webhook_id` and`websocket-push-webhook-id`)
|
|
will be local push only and will be sent via websocket.
|
|
"""
|
|
|
|
# Setup mock for non-local push notification target
|
|
# with webhook_id "webhook_id_2"
|
|
aioclient_mock.post(
|
|
"https://mobile-push.home-assistant.dev/push2",
|
|
json={
|
|
"rateLimits": {
|
|
"attempts": 1,
|
|
"successful": 1,
|
|
"errors": 0,
|
|
"total": 1,
|
|
"maximum": 150,
|
|
"remaining": 149,
|
|
"resetsAt": (datetime.now() + timedelta(hours=24)).strftime(
|
|
"%Y-%m-%dT%H:%M:%SZ"
|
|
),
|
|
}
|
|
},
|
|
)
|
|
|
|
client = await hass_ws_client(hass)
|
|
|
|
# Setup local push notification channels
|
|
local_push_sub_ids = []
|
|
for webhook_id in ("mock-webhook_id", "websocket-push-webhook-id"):
|
|
await client.send_json_auto_id(
|
|
{
|
|
"type": "mobile_app/push_notification_channel",
|
|
"webhook_id": webhook_id,
|
|
}
|
|
)
|
|
sub_result = await client.receive_json()
|
|
assert sub_result["success"]
|
|
local_push_sub_ids.append(sub_result["id"])
|
|
|
|
await hass.services.async_call(
|
|
"notify",
|
|
"notify",
|
|
{
|
|
"message": "Hello world",
|
|
"target": target,
|
|
},
|
|
blocking=True,
|
|
)
|
|
|
|
# Assert that the notification has been sent to the non-local push notification target
|
|
assert len(aioclient_mock.mock_calls) == 1
|
|
call = aioclient_mock.mock_calls
|
|
call_json = call[0][2]
|
|
assert call_json["push_token"] == "PUSH_TOKEN2"
|
|
assert call_json["message"] == "Hello world"
|
|
assert call_json["registration_info"]["app_id"] == "io.homeassistant.mobile_app"
|
|
assert call_json["registration_info"]["app_version"] == "1.0"
|
|
assert call_json["registration_info"]["webhook_id"] == "webhook_id_2"
|
|
|
|
# Assert that the notification has been sent to the two local push notification targets
|
|
for sub_id in local_push_sub_ids:
|
|
msg_result = await client.receive_json()
|
|
assert msg_result["event"] == {"message": "Hello world"}
|
|
msg_id = msg_result["id"]
|
|
assert msg_id == sub_id
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"target", [["webhook_id_2", "mock-webhook_id", "websocket-push-webhook-id"], None]
|
|
)
|
|
async def test_notify_multiple_targets_if_any_disconnected(
|
|
hass: HomeAssistant,
|
|
hass_ws_client: WebSocketGenerator,
|
|
aioclient_mock: AiohttpClientMocker,
|
|
setup_push_receiver,
|
|
setup_websocket_channel_only_push,
|
|
target: list[str] | None,
|
|
) -> None:
|
|
"""Notify works with disconnected targets.
|
|
|
|
Test that although one target is disconnected,
|
|
notify still works to other targets and the exception is still raised.
|
|
"""
|
|
# Setup mock for non-local push notification target
|
|
# with webhook_id "webhook_id_2"
|
|
aioclient_mock.post(
|
|
"https://mobile-push.home-assistant.dev/push2",
|
|
json={
|
|
"rateLimits": {
|
|
"attempts": 1,
|
|
"successful": 1,
|
|
"errors": 0,
|
|
"total": 1,
|
|
"maximum": 150,
|
|
"remaining": 149,
|
|
"resetsAt": (datetime.now() + timedelta(hours=24)).strftime(
|
|
"%Y-%m-%dT%H:%M:%SZ"
|
|
),
|
|
}
|
|
},
|
|
)
|
|
|
|
client = await hass_ws_client(hass)
|
|
|
|
# Setup the local push notification channel
|
|
await client.send_json_auto_id(
|
|
{
|
|
"type": "mobile_app/push_notification_channel",
|
|
"webhook_id": "mock-webhook_id",
|
|
}
|
|
)
|
|
sub_result = await client.receive_json()
|
|
assert sub_result["success"]
|
|
sub_id = sub_result["id"]
|
|
|
|
with pytest.raises(
|
|
HomeAssistantError,
|
|
match=r".*websocket-push-webhook-id.*not connected to local push notifications",
|
|
):
|
|
await hass.services.async_call(
|
|
"notify",
|
|
"notify",
|
|
{
|
|
"message": "Hello world",
|
|
"target": target,
|
|
},
|
|
blocking=True,
|
|
)
|
|
|
|
# Assert that the notification has been sent to the non-local push notification target
|
|
assert len(aioclient_mock.mock_calls) == 1
|
|
call = aioclient_mock.mock_calls
|
|
call_json = call[0][2]
|
|
assert call_json["push_token"] == "PUSH_TOKEN2"
|
|
assert call_json["message"] == "Hello world"
|
|
assert call_json["registration_info"]["app_id"] == "io.homeassistant.mobile_app"
|
|
assert call_json["registration_info"]["app_version"] == "1.0"
|
|
assert call_json["registration_info"]["webhook_id"] == "webhook_id_2"
|
|
|
|
# Assert that the notification has been sent to the local
|
|
# push notification target that has been setup
|
|
msg_result = await client.receive_json()
|
|
assert msg_result["event"] == {"message": "Hello world"}
|
|
assert msg_result["id"] == sub_id
|
|
|
|
# Check that there are no more messages to receive (timeout expected)
|
|
with pytest.raises(asyncio.TimeoutError):
|
|
await asyncio.wait_for(client.receive_json(), timeout=0.1)
|
|
|
|
|
|
@pytest.mark.usefixtures("notify_only")
|
|
async def test_notify_platform(
|
|
hass: HomeAssistant,
|
|
hass_admin_user: MockUser,
|
|
snapshot: SnapshotAssertion,
|
|
entity_registry: er.EntityRegistry,
|
|
) -> None:
|
|
"""Test setup of the Mobile app notify platform."""
|
|
config_entry = MockConfigEntry(
|
|
data={
|
|
"app_data": {
|
|
"push_token": "PUSH_TOKEN",
|
|
"push_url": "https://mobile-push.home-assistant.dev/push",
|
|
},
|
|
"app_id": "io.homeassistant.mobile_app",
|
|
"app_name": "mobile_app tests",
|
|
"app_version": "1.0",
|
|
"device_id": "4d5e6f",
|
|
"device_name": "Test",
|
|
"manufacturer": "Home Assistant",
|
|
"model": "mobile_app",
|
|
"os_name": "Linux",
|
|
"os_version": "5.0.6",
|
|
"secret": "123abc",
|
|
"supports_encryption": False,
|
|
"user_id": hass_admin_user.id,
|
|
"webhook_id": "mock-webhook_id",
|
|
},
|
|
domain=DOMAIN,
|
|
source="registration",
|
|
title="mobile_app test entry",
|
|
version=1,
|
|
)
|
|
|
|
config_entry.add_to_hass(hass)
|
|
await hass.config_entries.async_setup(config_entry.entry_id)
|
|
await hass.async_block_till_done()
|
|
|
|
assert config_entry.state is ConfigEntryState.LOADED
|
|
|
|
await snapshot_platform(hass, entity_registry, snapshot, config_entry.entry_id)
|
|
|
|
|
|
@pytest.mark.usefixtures("setup_push_receiver")
|
|
@pytest.mark.freeze_time("1970-01-01T00:00:00.000Z")
|
|
async def test_send_message(
|
|
hass: HomeAssistant,
|
|
aioclient_mock: AiohttpClientMocker,
|
|
) -> None:
|
|
"""Test sending message via notify.send_message action."""
|
|
|
|
state = hass.states.get("notify.test")
|
|
assert state
|
|
assert state.state == STATE_UNKNOWN
|
|
|
|
await hass.services.async_call(
|
|
NOTIFY_DOMAIN,
|
|
SERVICE_SEND_MESSAGE,
|
|
{
|
|
ATTR_ENTITY_ID: "notify.test",
|
|
ATTR_MESSAGE: "Hello world",
|
|
ATTR_TITLE: "test",
|
|
},
|
|
blocking=True,
|
|
)
|
|
|
|
assert len(aioclient_mock.mock_calls) == 1
|
|
call = aioclient_mock.mock_calls
|
|
|
|
call_json = call[0][2]
|
|
|
|
assert call_json["push_token"] == "PUSH_TOKEN"
|
|
assert call_json["message"] == "Hello world"
|
|
assert call_json["title"] == "test"
|
|
assert call_json["registration_info"]["app_id"] == "io.homeassistant.mobile_app"
|
|
assert call_json["registration_info"]["app_version"] == "1.0"
|
|
assert call_json["registration_info"]["webhook_id"] == "mock-webhook_id"
|
|
|
|
state = hass.states.get("notify.test")
|
|
assert state
|
|
assert state.state == "1970-01-01T00:00:00+00:00"
|
|
|
|
|
|
@pytest.mark.usefixtures("setup_websocket_channel_only_push")
|
|
@pytest.mark.freeze_time("1970-01-01T00:00:00.000Z")
|
|
async def test_send_message_local_push(
|
|
hass: HomeAssistant,
|
|
hass_ws_client: WebSocketGenerator,
|
|
) -> None:
|
|
"""Test sending message via notify.send_message action through local push."""
|
|
client = await hass_ws_client(hass)
|
|
|
|
await client.send_json_auto_id(
|
|
{
|
|
"type": "mobile_app/push_notification_channel",
|
|
"webhook_id": "websocket-push-webhook-id",
|
|
}
|
|
)
|
|
|
|
sub_result = await client.receive_json()
|
|
assert sub_result["success"]
|
|
sub_id = sub_result["id"]
|
|
|
|
await hass.services.async_call(
|
|
NOTIFY_DOMAIN,
|
|
SERVICE_SEND_MESSAGE,
|
|
{
|
|
ATTR_ENTITY_ID: "notify.websocket_push_name",
|
|
ATTR_MESSAGE: "Hello world",
|
|
ATTR_TITLE: "test",
|
|
},
|
|
blocking=True,
|
|
)
|
|
|
|
msg = await client.receive_json()
|
|
assert msg["id"] == sub_id
|
|
assert msg["type"] == "event"
|
|
assert msg["event"] == {"message": "Hello world", "title": "test"}
|
|
|
|
state = hass.states.get("notify.websocket_push_name")
|
|
assert state
|
|
assert state.state == "1970-01-01T00:00:00+00:00"
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("exc", "status", "error_msg"),
|
|
[
|
|
(
|
|
None,
|
|
HTTPStatus.TOO_MANY_REQUESTS,
|
|
"rate_limit_exceeded_sending_notification",
|
|
),
|
|
(
|
|
None,
|
|
HTTPStatus.BAD_REQUEST,
|
|
"error_sending_notification",
|
|
),
|
|
(
|
|
TimeoutError,
|
|
HTTPStatus.OK,
|
|
"timeout_sending_notification",
|
|
),
|
|
(
|
|
ClientError,
|
|
HTTPStatus.OK,
|
|
"error_sending_notification",
|
|
),
|
|
],
|
|
)
|
|
@pytest.mark.usefixtures("setup_push_receiver")
|
|
@pytest.mark.freeze_time("1970-01-01T00:00:00.000Z")
|
|
async def test_send_message_exceptions(
|
|
hass: HomeAssistant,
|
|
aioclient_mock: AiohttpClientMocker,
|
|
exc: Exception | None,
|
|
status: HTTPStatus,
|
|
error_msg: str,
|
|
) -> None:
|
|
"""Test sending message via notify.send_message action with exceptions."""
|
|
aioclient_mock.clear_requests()
|
|
aioclient_mock.post(
|
|
"https://mobile-push.home-assistant.dev/push",
|
|
json={
|
|
"message": "Unknown error",
|
|
"rateLimits": {
|
|
"attempts": 1,
|
|
"successful": 1,
|
|
"errors": 0,
|
|
"total": 1,
|
|
"maximum": 150,
|
|
"remaining": 149,
|
|
"resetsAt": "1970-01-02T00:00:00Z",
|
|
},
|
|
},
|
|
status=status,
|
|
exc=exc,
|
|
)
|
|
|
|
with pytest.raises(HomeAssistantError) as err:
|
|
await hass.services.async_call(
|
|
NOTIFY_DOMAIN,
|
|
SERVICE_SEND_MESSAGE,
|
|
{
|
|
ATTR_ENTITY_ID: "notify.test",
|
|
ATTR_MESSAGE: "Hello world",
|
|
ATTR_TITLE: "test",
|
|
},
|
|
blocking=True,
|
|
)
|
|
assert err.value.translation_key == error_msg
|
|
assert err.value.translation_placeholders == {
|
|
"device_name": "mobile_app test entry"
|
|
}
|
|
|
|
|
|
@pytest.mark.usefixtures("setup_websocket_channel_only_push")
|
|
@pytest.mark.freeze_time("1970-01-01T00:00:00.000Z")
|
|
async def test_send_message_local_push_exception(hass: HomeAssistant) -> None:
|
|
"""Test sending message via notify.send_message action through local push with exceptions."""
|
|
with pytest.raises(HomeAssistantError) as err:
|
|
await hass.services.async_call(
|
|
NOTIFY_DOMAIN,
|
|
SERVICE_SEND_MESSAGE,
|
|
{
|
|
ATTR_ENTITY_ID: "notify.websocket_push_name",
|
|
ATTR_MESSAGE: "Hello world",
|
|
ATTR_TITLE: "test",
|
|
},
|
|
blocking=True,
|
|
)
|
|
assert (
|
|
err.value.translation_key == "device_not_connected_for_local_push_notifications"
|
|
)
|
|
assert err.value.translation_placeholders == {
|
|
"device_name": "websocket push test entry"
|
|
}
|