mirror of
https://github.com/home-assistant/core.git
synced 2026-04-02 16:36:08 +01:00
1233 lines
40 KiB
Python
1233 lines
40 KiB
Python
"""Test HTML5 notify platform."""
|
|
|
|
from collections.abc import Generator
|
|
from http import HTTPStatus
|
|
import json
|
|
from typing import Any
|
|
from unittest.mock import AsyncMock, MagicMock, Mock, mock_open, patch
|
|
|
|
from aiohttp import ClientError
|
|
from aiohttp.hdrs import AUTHORIZATION
|
|
import pytest
|
|
from pywebpush import WebPushException
|
|
from syrupy.assertion import SnapshotAssertion
|
|
|
|
from homeassistant.components.html5 import DOMAIN, notify as html5
|
|
from homeassistant.components.html5.const import (
|
|
ATTR_ACTIONS,
|
|
ATTR_BADGE,
|
|
ATTR_DIR,
|
|
ATTR_ICON,
|
|
ATTR_IMAGE,
|
|
ATTR_LANG,
|
|
ATTR_RENOTIFY,
|
|
ATTR_REQUIRE_INTERACTION,
|
|
ATTR_SILENT,
|
|
ATTR_TAG,
|
|
ATTR_TIMESTAMP,
|
|
ATTR_TTL,
|
|
ATTR_URGENCY,
|
|
ATTR_VIBRATE,
|
|
SERVICE_DISMISS,
|
|
)
|
|
from homeassistant.components.html5.notify import ATTR_ACTION, ATTR_DISMISS, DEFAULT_TTL
|
|
from homeassistant.components.html5.services import SERVICE_DISMISS_MESSAGE
|
|
from homeassistant.components.notify import (
|
|
ATTR_DATA,
|
|
ATTR_MESSAGE,
|
|
ATTR_TARGET,
|
|
ATTR_TITLE,
|
|
DOMAIN as NOTIFY_DOMAIN,
|
|
SERVICE_SEND_MESSAGE,
|
|
)
|
|
from homeassistant.config_entries import ConfigEntryState
|
|
from homeassistant.const import (
|
|
ATTR_ENTITY_ID,
|
|
STATE_UNAVAILABLE,
|
|
STATE_UNKNOWN,
|
|
Platform,
|
|
)
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.exceptions import HomeAssistantError
|
|
from homeassistant.helpers import entity_registry as er, issue_registry as ir
|
|
from homeassistant.setup import async_setup_component
|
|
|
|
from tests.common import MockConfigEntry, snapshot_platform
|
|
from tests.typing import ClientSessionGenerator
|
|
|
|
CONFIG_FILE = "file.conf"
|
|
|
|
VAPID_CONF = {
|
|
"platform": "html5",
|
|
"vapid_pub_key": (
|
|
"BJMA2gDZEkHaXRhf1fhY_"
|
|
"QbKbhVIHlSJXI0bFyo0eJXnUPOjdgycCAbj-2bMKMKNKs"
|
|
"_rM8JoSnyKGCXAY2dbONI"
|
|
),
|
|
"vapid_prv_key": "ZwPgwKpESGuGLMZYU39vKgrekrWzCijo-LsBM3CZ9-c",
|
|
"vapid_email": "someone@example.com",
|
|
}
|
|
|
|
SUBSCRIPTION_1 = {
|
|
"browser": "chrome",
|
|
"subscription": {
|
|
"endpoint": "https://googleapis.com",
|
|
"keys": {"auth": "auth", "p256dh": "p256dh"},
|
|
},
|
|
}
|
|
SUBSCRIPTION_2 = {
|
|
"browser": "firefox",
|
|
"subscription": {
|
|
"endpoint": "https://example.com",
|
|
"keys": {"auth": "bla", "p256dh": "bla"},
|
|
},
|
|
}
|
|
SUBSCRIPTION_3 = {
|
|
"browser": "chrome",
|
|
"subscription": {
|
|
"endpoint": "https://example.com/not_exist",
|
|
"keys": {"auth": "bla", "p256dh": "bla"},
|
|
},
|
|
}
|
|
SUBSCRIPTION_4 = {
|
|
"browser": "chrome",
|
|
"subscription": {
|
|
"endpoint": "https://googleapis.com",
|
|
"expirationTime": None,
|
|
"keys": {"auth": "auth", "p256dh": "p256dh"},
|
|
},
|
|
}
|
|
|
|
SUBSCRIPTION_5 = {
|
|
"browser": "chrome",
|
|
"subscription": {
|
|
"endpoint": "https://fcm.googleapis.com/fcm/send/LONG-RANDOM-KEY",
|
|
"expirationTime": None,
|
|
"keys": {"auth": "auth", "p256dh": "p256dh"},
|
|
},
|
|
}
|
|
|
|
REGISTER_URL = "/api/notify.html5"
|
|
PUBLISH_URL = "/api/notify.html5/callback"
|
|
|
|
VAPID_HEADERS = {
|
|
"Authorization": "vapid t=signed!!!",
|
|
"urgency": "normal",
|
|
"priority": "normal",
|
|
}
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def notify_only() -> Generator[None]:
|
|
"""Enable only the notify platform."""
|
|
with patch(
|
|
"homeassistant.components.html5.PLATFORMS",
|
|
[Platform.NOTIFY],
|
|
):
|
|
yield
|
|
|
|
|
|
async def test_get_service_with_no_json(hass: HomeAssistant) -> None:
|
|
"""Test empty json file."""
|
|
await async_setup_component(hass, "http", {})
|
|
m = mock_open()
|
|
with patch("homeassistant.util.json.open", m, create=True):
|
|
service = await html5.async_get_service(hass, {}, VAPID_CONF)
|
|
|
|
assert service is not None
|
|
|
|
|
|
@pytest.mark.usefixtures("mock_jwt", "mock_vapid")
|
|
@pytest.mark.freeze_time("2009-02-13T23:31:30.000Z")
|
|
async def test_dismissing_message(mock_wp: AsyncMock, hass: HomeAssistant) -> None:
|
|
"""Test dismissing message."""
|
|
await async_setup_component(hass, "http", {})
|
|
|
|
data = {"device": SUBSCRIPTION_1}
|
|
|
|
m = mock_open(read_data=json.dumps(data))
|
|
with patch("homeassistant.util.json.open", m, create=True):
|
|
service = await html5.async_get_service(hass, {}, VAPID_CONF)
|
|
service.hass = hass
|
|
|
|
assert service is not None
|
|
|
|
await service.async_dismiss(target=["device", "non_existing"], data={"tag": "test"})
|
|
|
|
mock_wp.send_async.assert_awaited_once_with(
|
|
data='{"tag": "test", "dismiss": true, "data": {"jwt": "JWT"}, "timestamp": 1234567890000}',
|
|
headers=VAPID_HEADERS,
|
|
ttl=86400,
|
|
)
|
|
|
|
|
|
@pytest.mark.usefixtures("mock_jwt", "mock_vapid", "mock_uuid")
|
|
@pytest.mark.freeze_time("2009-02-13T23:31:30.000Z")
|
|
async def test_sending_message(mock_wp: AsyncMock, hass: HomeAssistant) -> None:
|
|
"""Test sending message."""
|
|
await async_setup_component(hass, "http", {})
|
|
|
|
data = {"device": SUBSCRIPTION_1}
|
|
|
|
m = mock_open(read_data=json.dumps(data))
|
|
with patch("homeassistant.util.json.open", m, create=True):
|
|
service = await html5.async_get_service(hass, {}, VAPID_CONF)
|
|
service.hass = hass
|
|
|
|
assert service is not None
|
|
|
|
await service.async_send_message(
|
|
"Hello", target=["device", "non_existing"], data={"icon": "beer.png"}
|
|
)
|
|
|
|
mock_wp.send_async.assert_awaited_once_with(
|
|
data='{"badge": "/static/images/notification-badge.png", "body": "Hello", "data": {"url": "/", "jwt": "JWT"}, "icon": "beer.png", "tag": "12345678-1234-5678-1234-567812345678", "title": "Home Assistant", "timestamp": 1234567890000}',
|
|
headers=VAPID_HEADERS,
|
|
ttl=86400,
|
|
)
|
|
|
|
# WebPusher constructor
|
|
assert mock_wp.cls.call_args[0][0] == SUBSCRIPTION_1["subscription"]
|
|
|
|
|
|
@pytest.mark.usefixtures("mock_jwt", "mock_vapid", "mock_uuid")
|
|
@pytest.mark.freeze_time("2009-02-13T23:31:30.000Z")
|
|
async def test_fcm_key_include(mock_wp: AsyncMock, hass: HomeAssistant) -> None:
|
|
"""Test if the FCM header is included."""
|
|
await async_setup_component(hass, "http", {})
|
|
|
|
data = {"chrome": SUBSCRIPTION_5}
|
|
|
|
m = mock_open(read_data=json.dumps(data))
|
|
with patch("homeassistant.util.json.open", m, create=True):
|
|
service = await html5.async_get_service(hass, {}, VAPID_CONF)
|
|
service.hass = hass
|
|
|
|
assert service is not None
|
|
|
|
await service.async_send_message("Hello", target=["chrome"])
|
|
|
|
mock_wp.send_async.assert_awaited_once_with(
|
|
data='{"badge": "/static/images/notification-badge.png", "body": "Hello", "data": {"url": "/", "jwt": "JWT"}, "icon": "/static/icons/favicon-192x192.png", "tag": "12345678-1234-5678-1234-567812345678", "title": "Home Assistant", "timestamp": 1234567890000}',
|
|
headers=VAPID_HEADERS,
|
|
ttl=86400,
|
|
)
|
|
|
|
# WebPusher constructor
|
|
assert mock_wp.cls.call_args[0][0] == SUBSCRIPTION_5["subscription"]
|
|
|
|
|
|
@pytest.mark.usefixtures("mock_jwt", "mock_vapid", "mock_uuid")
|
|
@pytest.mark.freeze_time("2009-02-13T23:31:30.000Z")
|
|
async def test_fcm_send_with_unknown_priority(
|
|
mock_wp: AsyncMock, hass: HomeAssistant
|
|
) -> None:
|
|
"""Test if the gcm_key is only included for GCM endpoints."""
|
|
await async_setup_component(hass, "http", {})
|
|
|
|
data = {"chrome": SUBSCRIPTION_5}
|
|
|
|
m = mock_open(read_data=json.dumps(data))
|
|
with patch("homeassistant.util.json.open", m, create=True):
|
|
service = await html5.async_get_service(hass, {}, VAPID_CONF)
|
|
service.hass = hass
|
|
|
|
assert service is not None
|
|
|
|
await service.async_send_message("Hello", target=["chrome"], priority="undefined")
|
|
|
|
mock_wp.send_async.assert_awaited_once_with(
|
|
data='{"badge": "/static/images/notification-badge.png", "body": "Hello", "data": {"url": "/", "jwt": "JWT"}, "icon": "/static/icons/favicon-192x192.png", "tag": "12345678-1234-5678-1234-567812345678", "title": "Home Assistant", "timestamp": 1234567890000}',
|
|
headers=VAPID_HEADERS,
|
|
ttl=86400,
|
|
)
|
|
# WebPusher constructor
|
|
assert mock_wp.cls.call_args[0][0] == SUBSCRIPTION_5["subscription"]
|
|
|
|
|
|
@pytest.mark.usefixtures("mock_jwt", "mock_vapid", "mock_uuid")
|
|
@pytest.mark.freeze_time("2009-02-13T23:31:30.000Z")
|
|
async def test_fcm_no_targets(mock_wp: AsyncMock, hass: HomeAssistant) -> None:
|
|
"""Test if the gcm_key is only included for GCM endpoints."""
|
|
await async_setup_component(hass, "http", {})
|
|
|
|
data = {"chrome": SUBSCRIPTION_5}
|
|
|
|
m = mock_open(read_data=json.dumps(data))
|
|
with patch("homeassistant.util.json.open", m, create=True):
|
|
service = await html5.async_get_service(hass, {}, VAPID_CONF)
|
|
service.hass = hass
|
|
|
|
assert service is not None
|
|
|
|
await service.async_send_message("Hello")
|
|
|
|
mock_wp.send_async.assert_awaited_once_with(
|
|
data='{"badge": "/static/images/notification-badge.png", "body": "Hello", "data": {"url": "/", "jwt": "JWT"}, "icon": "/static/icons/favicon-192x192.png", "tag": "12345678-1234-5678-1234-567812345678", "title": "Home Assistant", "timestamp": 1234567890000}',
|
|
headers=VAPID_HEADERS,
|
|
ttl=86400,
|
|
)
|
|
# WebPusher constructor
|
|
assert mock_wp.cls.call_args[0][0] == SUBSCRIPTION_5["subscription"]
|
|
|
|
|
|
@pytest.mark.usefixtures("mock_jwt", "mock_vapid", "mock_uuid")
|
|
@pytest.mark.freeze_time("2009-02-13T23:31:30.000Z")
|
|
async def test_fcm_additional_data(mock_wp: AsyncMock, hass: HomeAssistant) -> None:
|
|
"""Test if the gcm_key is only included for GCM endpoints."""
|
|
await async_setup_component(hass, "http", {})
|
|
|
|
data = {"chrome": SUBSCRIPTION_5}
|
|
|
|
m = mock_open(read_data=json.dumps(data))
|
|
with patch("homeassistant.util.json.open", m, create=True):
|
|
service = await html5.async_get_service(hass, {}, VAPID_CONF)
|
|
service.hass = hass
|
|
|
|
assert service is not None
|
|
|
|
await service.async_send_message("Hello", data={"mykey": "myvalue"})
|
|
|
|
mock_wp.send_async.assert_awaited_once_with(
|
|
data='{"badge": "/static/images/notification-badge.png", "body": "Hello", "data": {"mykey": "myvalue", "url": "/", "jwt": "JWT"}, "icon": "/static/icons/favicon-192x192.png", "tag": "12345678-1234-5678-1234-567812345678", "title": "Home Assistant", "timestamp": 1234567890000}',
|
|
headers=VAPID_HEADERS,
|
|
ttl=86400,
|
|
)
|
|
# WebPusher constructor
|
|
assert mock_wp.cls.call_args[0][0] == SUBSCRIPTION_5["subscription"]
|
|
|
|
|
|
@pytest.mark.usefixtures("load_config")
|
|
async def test_registering_new_device_view(
|
|
hass: HomeAssistant,
|
|
hass_client: ClientSessionGenerator,
|
|
config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""Test that the HTML view works."""
|
|
await async_setup_component(hass, "http", {})
|
|
|
|
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
|
|
|
|
client = await hass_client()
|
|
|
|
with patch("homeassistant.components.html5.notify.save_json") as mock_save:
|
|
resp = await client.post(REGISTER_URL, data=json.dumps(SUBSCRIPTION_1))
|
|
|
|
assert resp.status == HTTPStatus.OK
|
|
assert len(mock_save.mock_calls) == 1
|
|
assert mock_save.mock_calls[0][1][1] == {"unnamed device": SUBSCRIPTION_1}
|
|
|
|
|
|
@pytest.mark.usefixtures("load_config")
|
|
async def test_registering_new_device_view_with_name(
|
|
hass: HomeAssistant,
|
|
hass_client: ClientSessionGenerator,
|
|
config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""Test that the HTML view works with name attribute."""
|
|
await async_setup_component(hass, "http", {})
|
|
|
|
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
|
|
|
|
client = await hass_client()
|
|
|
|
SUB_WITH_NAME = SUBSCRIPTION_1.copy()
|
|
SUB_WITH_NAME["name"] = "test device"
|
|
|
|
with patch("homeassistant.components.html5.notify.save_json") as mock_save:
|
|
resp = await client.post(REGISTER_URL, data=json.dumps(SUB_WITH_NAME))
|
|
|
|
assert resp.status == HTTPStatus.OK
|
|
assert len(mock_save.mock_calls) == 1
|
|
assert mock_save.mock_calls[0][1][1] == {"test device": SUBSCRIPTION_1}
|
|
|
|
|
|
@pytest.mark.usefixtures("load_config")
|
|
async def test_registering_new_device_expiration_view(
|
|
hass: HomeAssistant,
|
|
hass_client: ClientSessionGenerator,
|
|
config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""Test that the HTML view works."""
|
|
await async_setup_component(hass, "http", {})
|
|
|
|
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
|
|
|
|
client = await hass_client()
|
|
|
|
with patch("homeassistant.components.html5.notify.save_json") as mock_save:
|
|
resp = await client.post(REGISTER_URL, data=json.dumps(SUBSCRIPTION_4))
|
|
|
|
assert resp.status == HTTPStatus.OK
|
|
assert mock_save.mock_calls[0][1][1] == {"unnamed device": SUBSCRIPTION_4}
|
|
|
|
|
|
@pytest.mark.usefixtures("load_config")
|
|
async def test_registering_new_device_fails_view(
|
|
hass: HomeAssistant,
|
|
hass_client: ClientSessionGenerator,
|
|
config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""Test subs. are not altered when registering a new device fails."""
|
|
await async_setup_component(hass, "http", {})
|
|
|
|
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
|
|
|
|
client = await hass_client()
|
|
with patch(
|
|
"homeassistant.components.html5.notify.save_json",
|
|
side_effect=HomeAssistantError(),
|
|
):
|
|
resp = await client.post(REGISTER_URL, data=json.dumps(SUBSCRIPTION_4))
|
|
|
|
assert resp.status == HTTPStatus.INTERNAL_SERVER_ERROR
|
|
|
|
|
|
@pytest.mark.usefixtures("load_config")
|
|
async def test_registering_existing_device_view(
|
|
hass: HomeAssistant,
|
|
hass_client: ClientSessionGenerator,
|
|
config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""Test subscription is updated when registering existing device."""
|
|
await async_setup_component(hass, "http", {})
|
|
|
|
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
|
|
|
|
client = await hass_client()
|
|
|
|
with patch("homeassistant.components.html5.notify.save_json") as mock_save:
|
|
await client.post(REGISTER_URL, data=json.dumps(SUBSCRIPTION_1))
|
|
resp = await client.post(REGISTER_URL, data=json.dumps(SUBSCRIPTION_4))
|
|
|
|
assert resp.status == HTTPStatus.OK
|
|
mock_save.assert_called_with(
|
|
hass.config.path(html5.REGISTRATIONS_FILE), {"unnamed device": SUBSCRIPTION_4}
|
|
)
|
|
|
|
|
|
@pytest.mark.usefixtures("load_config")
|
|
async def test_registering_existing_device_view_with_name(
|
|
hass: HomeAssistant,
|
|
hass_client: ClientSessionGenerator,
|
|
config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""Test subscription is updated when reg'ing existing device with name."""
|
|
await async_setup_component(hass, "http", {})
|
|
|
|
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
|
|
|
|
client = await hass_client()
|
|
|
|
SUB_WITH_NAME = SUBSCRIPTION_1.copy()
|
|
SUB_WITH_NAME["name"] = "test device"
|
|
|
|
with patch("homeassistant.components.html5.notify.save_json") as mock_save:
|
|
await client.post(REGISTER_URL, data=json.dumps(SUB_WITH_NAME))
|
|
resp = await client.post(REGISTER_URL, data=json.dumps(SUBSCRIPTION_4))
|
|
|
|
assert resp.status == HTTPStatus.OK
|
|
|
|
mock_save.assert_called_with(
|
|
hass.config.path(html5.REGISTRATIONS_FILE), {"test device": SUBSCRIPTION_4}
|
|
)
|
|
|
|
|
|
@pytest.mark.usefixtures("load_config")
|
|
async def test_registering_existing_device_fails_view(
|
|
hass: HomeAssistant,
|
|
hass_client: ClientSessionGenerator,
|
|
config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""Test sub. is not updated when registering existing device fails."""
|
|
await async_setup_component(hass, "http", {})
|
|
|
|
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
|
|
|
|
client = await hass_client()
|
|
|
|
with patch("homeassistant.components.html5.notify.save_json") as mock_save:
|
|
await client.post(REGISTER_URL, data=json.dumps(SUBSCRIPTION_1))
|
|
mock_save.side_effect = HomeAssistantError
|
|
resp = await client.post(REGISTER_URL, data=json.dumps(SUBSCRIPTION_4))
|
|
|
|
assert resp.status == HTTPStatus.INTERNAL_SERVER_ERROR
|
|
|
|
|
|
@pytest.mark.usefixtures("load_config")
|
|
async def test_registering_new_device_validation(
|
|
hass: HomeAssistant,
|
|
hass_client: ClientSessionGenerator,
|
|
config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""Test various errors when registering a new device."""
|
|
await async_setup_component(hass, "http", {})
|
|
|
|
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
|
|
|
|
client = await hass_client()
|
|
|
|
resp = await client.post(
|
|
REGISTER_URL,
|
|
data=json.dumps({"browser": "invalid browser", "subscription": "sub info"}),
|
|
)
|
|
assert resp.status == HTTPStatus.BAD_REQUEST
|
|
|
|
resp = await client.post(REGISTER_URL, data=json.dumps({"browser": "chrome"}))
|
|
assert resp.status == HTTPStatus.BAD_REQUEST
|
|
|
|
with patch("homeassistant.components.html5.notify.save_json", return_value=False):
|
|
resp = await client.post(
|
|
REGISTER_URL,
|
|
data=json.dumps({"browser": "chrome", "subscription": "sub info"}),
|
|
)
|
|
assert resp.status == HTTPStatus.BAD_REQUEST
|
|
|
|
|
|
async def test_unregistering_device_view(
|
|
hass: HomeAssistant,
|
|
hass_client: ClientSessionGenerator,
|
|
config_entry: MockConfigEntry,
|
|
load_config: MagicMock,
|
|
) -> None:
|
|
"""Test that the HTML unregister view works."""
|
|
load_config.return_value = {
|
|
"some device": SUBSCRIPTION_1,
|
|
"other device": SUBSCRIPTION_2,
|
|
}
|
|
await async_setup_component(hass, "http", {})
|
|
|
|
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
|
|
|
|
client = await hass_client()
|
|
|
|
with patch("homeassistant.components.html5.notify.save_json") as mock_save:
|
|
resp = await client.delete(
|
|
REGISTER_URL,
|
|
data=json.dumps({"subscription": SUBSCRIPTION_1["subscription"]}),
|
|
)
|
|
|
|
assert resp.status == HTTPStatus.OK
|
|
assert len(mock_save.mock_calls) == 1
|
|
mock_save.assert_called_once_with(
|
|
hass.config.path(html5.REGISTRATIONS_FILE), {"other device": SUBSCRIPTION_2}
|
|
)
|
|
|
|
|
|
@pytest.mark.usefixtures("load_config")
|
|
async def test_unregister_device_view_handle_unknown_subscription(
|
|
hass: HomeAssistant,
|
|
hass_client: ClientSessionGenerator,
|
|
config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""Test that the HTML unregister view handles unknown subscriptions."""
|
|
await async_setup_component(hass, "http", {})
|
|
|
|
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
|
|
|
|
client = await hass_client()
|
|
|
|
with patch("homeassistant.components.html5.notify.save_json") as mock_save:
|
|
resp = await client.delete(
|
|
REGISTER_URL,
|
|
data=json.dumps({"subscription": SUBSCRIPTION_3["subscription"]}),
|
|
)
|
|
|
|
assert resp.status == HTTPStatus.OK, resp.response
|
|
assert len(mock_save.mock_calls) == 0
|
|
|
|
|
|
async def test_unregistering_device_view_handles_save_error(
|
|
hass: HomeAssistant,
|
|
hass_client: ClientSessionGenerator,
|
|
config_entry: MockConfigEntry,
|
|
load_config: MagicMock,
|
|
) -> None:
|
|
"""Test that the HTML unregister view handles save errors."""
|
|
load_config.return_value = {
|
|
"some device": SUBSCRIPTION_1,
|
|
"other device": SUBSCRIPTION_2,
|
|
}
|
|
await async_setup_component(hass, "http", {})
|
|
|
|
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
|
|
|
|
client = await hass_client()
|
|
|
|
with patch(
|
|
"homeassistant.components.html5.notify.save_json",
|
|
side_effect=HomeAssistantError(),
|
|
):
|
|
resp = await client.delete(
|
|
REGISTER_URL,
|
|
data=json.dumps({"subscription": SUBSCRIPTION_1["subscription"]}),
|
|
)
|
|
|
|
assert resp.status == HTTPStatus.INTERNAL_SERVER_ERROR, resp.response
|
|
|
|
|
|
@pytest.mark.usefixtures("load_config")
|
|
async def test_callback_view_no_jwt(
|
|
hass: HomeAssistant,
|
|
hass_client: ClientSessionGenerator,
|
|
config_entry: MockConfigEntry,
|
|
) -> None:
|
|
"""Test that the notification callback view works without JWT."""
|
|
await async_setup_component(hass, "http", {})
|
|
|
|
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
|
|
|
|
client = await hass_client()
|
|
resp = await client.post(
|
|
PUBLISH_URL,
|
|
data=json.dumps(
|
|
{"type": "push", "tag": "3bc28d69-0921-41f1-ac6a-7a627ba0aa72"}
|
|
),
|
|
)
|
|
|
|
assert resp.status == HTTPStatus.UNAUTHORIZED
|
|
|
|
|
|
@pytest.mark.usefixtures("mock_jwt", "mock_vapid", "mock_uuid")
|
|
@pytest.mark.freeze_time("2009-02-13T23:31:30.000Z")
|
|
async def test_callback_view_with_jwt(
|
|
hass: HomeAssistant,
|
|
hass_client: ClientSessionGenerator,
|
|
config_entry: MockConfigEntry,
|
|
load_config: MagicMock,
|
|
mock_wp: AsyncMock,
|
|
) -> None:
|
|
"""Test that the notification callback view works with JWT."""
|
|
load_config.return_value = {"device": SUBSCRIPTION_1}
|
|
await async_setup_component(hass, "http", {})
|
|
|
|
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
|
|
|
|
client = await hass_client()
|
|
|
|
await hass.services.async_call(
|
|
"notify",
|
|
"html5",
|
|
{"message": "Hello", "target": ["device"], "data": {"icon": "beer.png"}},
|
|
blocking=True,
|
|
)
|
|
|
|
mock_wp.send_async.assert_awaited_once_with(
|
|
data='{"badge": "/static/images/notification-badge.png", "body": "Hello", "data": {"url": "/", "jwt": "JWT"}, "icon": "beer.png", "tag": "12345678-1234-5678-1234-567812345678", "title": "Home Assistant", "timestamp": 1234567890000}',
|
|
headers=VAPID_HEADERS,
|
|
ttl=86400,
|
|
)
|
|
# WebPusher constructor
|
|
assert mock_wp.cls.call_args[0][0] == SUBSCRIPTION_1["subscription"]
|
|
|
|
bearer_token = "Bearer JWT"
|
|
|
|
resp = await client.post(
|
|
PUBLISH_URL, json={"type": "push"}, headers={AUTHORIZATION: bearer_token}
|
|
)
|
|
|
|
assert resp.status == HTTPStatus.OK
|
|
body = await resp.json()
|
|
assert body == {"event": "push", "status": "ok"}
|
|
|
|
|
|
@pytest.mark.usefixtures("mock_jwt", "mock_vapid", "mock_uuid")
|
|
@pytest.mark.freeze_time("2009-02-13T23:31:30.000Z")
|
|
async def test_send_fcm_without_targets(
|
|
hass: HomeAssistant,
|
|
config_entry: MockConfigEntry,
|
|
load_config: MagicMock,
|
|
mock_wp: AsyncMock,
|
|
) -> None:
|
|
"""Test that the notification is send with FCM without targets."""
|
|
load_config.return_value = {"device": SUBSCRIPTION_5}
|
|
await async_setup_component(hass, "http", {})
|
|
|
|
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 hass.services.async_call(
|
|
"notify",
|
|
"html5",
|
|
{"message": "Hello", "target": ["device"], "data": {"icon": "beer.png"}},
|
|
blocking=True,
|
|
)
|
|
|
|
mock_wp.send_async.assert_awaited_once_with(
|
|
data='{"badge": "/static/images/notification-badge.png", "body": "Hello", "data": {"url": "/", "jwt": "JWT"}, "icon": "beer.png", "tag": "12345678-1234-5678-1234-567812345678", "title": "Home Assistant", "timestamp": 1234567890000}',
|
|
headers=VAPID_HEADERS,
|
|
ttl=86400,
|
|
)
|
|
# WebPusher constructor
|
|
assert mock_wp.cls.call_args[0][0] == SUBSCRIPTION_5["subscription"]
|
|
|
|
|
|
@pytest.mark.usefixtures("mock_jwt", "mock_vapid", "mock_uuid")
|
|
@pytest.mark.freeze_time("2009-02-13T23:31:30.000Z")
|
|
async def test_send_fcm_expired(
|
|
hass: HomeAssistant,
|
|
config_entry: MockConfigEntry,
|
|
load_config: MagicMock,
|
|
mock_wp: AsyncMock,
|
|
) -> None:
|
|
"""Test that the FCM target is removed when expired."""
|
|
load_config.return_value = {"device": SUBSCRIPTION_5}
|
|
await async_setup_component(hass, "http", {})
|
|
|
|
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
|
|
mock_wp.send_async.return_value.status = 410
|
|
with (
|
|
patch("homeassistant.components.html5.notify.save_json") as mock_save,
|
|
):
|
|
await hass.services.async_call(
|
|
"notify",
|
|
"html5",
|
|
{"message": "Hello", "target": ["device"], "data": {"icon": "beer.png"}},
|
|
blocking=True,
|
|
)
|
|
# "device" should be removed when expired.
|
|
mock_save.assert_called_once_with(hass.config.path(html5.REGISTRATIONS_FILE), {})
|
|
|
|
|
|
@pytest.mark.usefixtures("mock_jwt", "mock_vapid", "mock_uuid")
|
|
@pytest.mark.freeze_time("2009-02-13T23:31:30.000Z")
|
|
async def test_send_fcm_expired_save_fails(
|
|
hass: HomeAssistant,
|
|
config_entry: MockConfigEntry,
|
|
load_config: MagicMock,
|
|
caplog: pytest.LogCaptureFixture,
|
|
mock_wp: AsyncMock,
|
|
) -> None:
|
|
"""Test that the FCM target remains after expiry if save_json fails."""
|
|
load_config.return_value = {"device": SUBSCRIPTION_5}
|
|
await async_setup_component(hass, "http", {})
|
|
|
|
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
|
|
mock_wp.send_async.return_value.status = 410
|
|
with (
|
|
patch(
|
|
"homeassistant.components.html5.notify.save_json",
|
|
side_effect=HomeAssistantError(),
|
|
),
|
|
):
|
|
await hass.services.async_call(
|
|
"notify",
|
|
"html5",
|
|
{"message": "Hello", "target": ["device"], "data": {"icon": "beer.png"}},
|
|
blocking=True,
|
|
)
|
|
# "device" should still exist if save fails.
|
|
assert "Error saving registration" in caplog.text
|
|
|
|
|
|
async def test_notify_platform(
|
|
hass: HomeAssistant,
|
|
config_entry: MockConfigEntry,
|
|
snapshot: SnapshotAssertion,
|
|
entity_registry: er.EntityRegistry,
|
|
load_config: MagicMock,
|
|
) -> None:
|
|
"""Test setup of the notify platform."""
|
|
load_config.return_value = {"my-desktop": SUBSCRIPTION_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("mock_jwt", "mock_vapid", "mock_uuid")
|
|
@pytest.mark.freeze_time("2009-02-13T23:31:30.000Z")
|
|
async def test_send_message(
|
|
hass: HomeAssistant,
|
|
config_entry: MockConfigEntry,
|
|
webpush_async: AsyncMock,
|
|
load_config: MagicMock,
|
|
) -> None:
|
|
"""Test sending a message."""
|
|
load_config.return_value = {"my-desktop": SUBSCRIPTION_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
|
|
|
|
state = hass.states.get("notify.my_desktop")
|
|
assert state
|
|
assert state.state == STATE_UNKNOWN
|
|
|
|
await hass.services.async_call(
|
|
NOTIFY_DOMAIN,
|
|
SERVICE_SEND_MESSAGE,
|
|
{
|
|
ATTR_ENTITY_ID: "notify.my_desktop",
|
|
ATTR_MESSAGE: "World",
|
|
ATTR_TITLE: "Hello",
|
|
},
|
|
blocking=True,
|
|
)
|
|
|
|
state = hass.states.get("notify.my_desktop")
|
|
assert state
|
|
assert state.state == "2009-02-13T23:31:30+00:00"
|
|
|
|
webpush_async.assert_awaited_once()
|
|
assert webpush_async.await_args
|
|
_, payload, _, _ = webpush_async.await_args.args
|
|
assert json.loads(payload) == {
|
|
"title": "Hello",
|
|
"body": "World",
|
|
"badge": "/static/images/notification-badge.png",
|
|
"icon": "/static/icons/favicon-192x192.png",
|
|
"tag": "12345678-1234-5678-1234-567812345678",
|
|
"timestamp": 1234567890000,
|
|
"data": {"jwt": "JWT"},
|
|
}
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("exception", "translation_key"),
|
|
[
|
|
(
|
|
WebPushException("", response=Mock(status=HTTPStatus.IM_A_TEAPOT)),
|
|
"request_error",
|
|
),
|
|
(
|
|
WebPushException("", response=Mock(status=HTTPStatus.GONE)),
|
|
"channel_expired",
|
|
),
|
|
(
|
|
ClientError,
|
|
"connection_error",
|
|
),
|
|
],
|
|
)
|
|
@pytest.mark.parametrize("domain", [NOTIFY_DOMAIN, DOMAIN])
|
|
@pytest.mark.usefixtures("mock_jwt", "mock_vapid", "mock_uuid")
|
|
@pytest.mark.freeze_time("2009-02-13T23:31:30.000Z")
|
|
async def test_send_message_exceptions(
|
|
hass: HomeAssistant,
|
|
config_entry: MockConfigEntry,
|
|
webpush_async: AsyncMock,
|
|
load_config: MagicMock,
|
|
exception: Exception,
|
|
translation_key: str,
|
|
domain: str,
|
|
) -> None:
|
|
"""Test sending a message with exceptions."""
|
|
load_config.return_value = {"my-desktop": SUBSCRIPTION_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
|
|
|
|
webpush_async.side_effect = exception
|
|
|
|
with pytest.raises(HomeAssistantError) as e:
|
|
await hass.services.async_call(
|
|
domain,
|
|
SERVICE_SEND_MESSAGE,
|
|
{
|
|
ATTR_ENTITY_ID: "notify.my_desktop",
|
|
ATTR_MESSAGE: "World",
|
|
ATTR_TITLE: "Hello",
|
|
},
|
|
blocking=True,
|
|
)
|
|
assert e.value.translation_key == translation_key
|
|
|
|
|
|
@pytest.mark.usefixtures("mock_jwt", "mock_vapid", "mock_uuid")
|
|
@pytest.mark.freeze_time("2009-02-13T23:31:30.000Z")
|
|
async def test_send_message_save_fails(
|
|
hass: HomeAssistant,
|
|
config_entry: MockConfigEntry,
|
|
webpush_async: AsyncMock,
|
|
load_config: MagicMock,
|
|
caplog: pytest.LogCaptureFixture,
|
|
) -> None:
|
|
"""Test sending a message with channel expired but saving registration fails."""
|
|
load_config.return_value = {"my-desktop": SUBSCRIPTION_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
|
|
|
|
webpush_async.side_effect = (
|
|
WebPushException("", response=Mock(status=HTTPStatus.GONE)),
|
|
)
|
|
with (
|
|
patch(
|
|
"homeassistant.components.html5.notify.save_json",
|
|
side_effect=HomeAssistantError,
|
|
),
|
|
pytest.raises(HomeAssistantError) as e,
|
|
):
|
|
await hass.services.async_call(
|
|
NOTIFY_DOMAIN,
|
|
SERVICE_SEND_MESSAGE,
|
|
{
|
|
ATTR_ENTITY_ID: "notify.my_desktop",
|
|
ATTR_MESSAGE: "World",
|
|
ATTR_TITLE: "Hello",
|
|
},
|
|
blocking=True,
|
|
)
|
|
assert e.value.translation_key == "channel_expired"
|
|
|
|
assert "Error saving registration" in caplog.text
|
|
|
|
|
|
@pytest.mark.usefixtures("mock_jwt", "mock_vapid", "mock_uuid")
|
|
@pytest.mark.freeze_time("2009-02-13T23:31:30.000Z")
|
|
async def test_send_message_unavailable(
|
|
hass: HomeAssistant,
|
|
config_entry: MockConfigEntry,
|
|
webpush_async: AsyncMock,
|
|
load_config: MagicMock,
|
|
) -> None:
|
|
"""Test sending a message with channel expired and entity goes unavailable."""
|
|
load_config.return_value = {"my-desktop": SUBSCRIPTION_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
|
|
|
|
webpush_async.side_effect = (
|
|
WebPushException("", response=Mock(status=HTTPStatus.GONE)),
|
|
)
|
|
with pytest.raises(HomeAssistantError) as e:
|
|
await hass.services.async_call(
|
|
NOTIFY_DOMAIN,
|
|
SERVICE_SEND_MESSAGE,
|
|
{
|
|
ATTR_ENTITY_ID: "notify.my_desktop",
|
|
ATTR_MESSAGE: "World",
|
|
ATTR_TITLE: "Hello",
|
|
},
|
|
blocking=True,
|
|
)
|
|
assert e.value.translation_key == "channel_expired"
|
|
|
|
state = hass.states.get("notify.my_desktop")
|
|
assert state
|
|
assert state.state == STATE_UNAVAILABLE
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("service_data", "expected_payload", "expected_ttl", "expected_headers"),
|
|
[
|
|
({ATTR_MESSAGE: "World"}, {"body": "World"}, DEFAULT_TTL, None),
|
|
(
|
|
{ATTR_ICON: "/static/icons/favicon-192x192.png"},
|
|
{"icon": "/static/icons/favicon-192x192.png"},
|
|
DEFAULT_TTL,
|
|
None,
|
|
),
|
|
(
|
|
{ATTR_BADGE: "/static/images/notification-badge.png"},
|
|
{"badge": "/static/images/notification-badge.png"},
|
|
DEFAULT_TTL,
|
|
None,
|
|
),
|
|
(
|
|
{ATTR_IMAGE: "/static/images/image.jpg"},
|
|
{"image": "/static/images/image.jpg"},
|
|
DEFAULT_TTL,
|
|
None,
|
|
),
|
|
({ATTR_TAG: "message-group-1"}, {"tag": "message-group-1"}, DEFAULT_TTL, None),
|
|
({ATTR_DIR: "rtl"}, {"dir": "rtl"}, DEFAULT_TTL, None),
|
|
({ATTR_RENOTIFY: True}, {"renotify": True}, DEFAULT_TTL, None),
|
|
({ATTR_SILENT: True}, {"silent": True}, DEFAULT_TTL, None),
|
|
(
|
|
{ATTR_REQUIRE_INTERACTION: True},
|
|
{"requireInteraction": True},
|
|
DEFAULT_TTL,
|
|
None,
|
|
),
|
|
(
|
|
{ATTR_VIBRATE: [200, 100, 200]},
|
|
{"vibrate": [200, 100, 200]},
|
|
DEFAULT_TTL,
|
|
None,
|
|
),
|
|
({ATTR_LANG: "es-419"}, {"lang": "es-419"}, DEFAULT_TTL, None),
|
|
({ATTR_TIMESTAMP: "1970-01-01 00:00:00"}, {"timestamp": 0}, DEFAULT_TTL, None),
|
|
({ATTR_TTL: {"days": 28}}, {}, 2419200, None),
|
|
({ATTR_TTL: {"seconds": 0}}, {}, 0, None),
|
|
(
|
|
{ATTR_URGENCY: "high"},
|
|
{},
|
|
DEFAULT_TTL,
|
|
{"Urgency": "high"},
|
|
),
|
|
(
|
|
{
|
|
ATTR_ACTIONS: [
|
|
{
|
|
ATTR_ACTION: "callback-event",
|
|
ATTR_TITLE: "Callback Event",
|
|
ATTR_ICON: "/static/icons/favicon-192x192.png",
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"actions": [
|
|
{
|
|
"action": "callback-event",
|
|
"title": "Callback Event",
|
|
"icon": "/static/icons/favicon-192x192.png",
|
|
}
|
|
]
|
|
},
|
|
DEFAULT_TTL,
|
|
None,
|
|
),
|
|
(
|
|
{ATTR_DATA: {"customKey": "customValue"}},
|
|
{"data": {"jwt": "JWT", "customKey": "customValue"}},
|
|
DEFAULT_TTL,
|
|
None,
|
|
),
|
|
],
|
|
)
|
|
@pytest.mark.usefixtures("mock_jwt", "mock_vapid", "mock_uuid")
|
|
@pytest.mark.freeze_time("2009-02-13T23:31:30.000Z")
|
|
async def test_html5_send_message(
|
|
hass: HomeAssistant,
|
|
config_entry: MockConfigEntry,
|
|
webpush_async: AsyncMock,
|
|
load_config: MagicMock,
|
|
service_data: dict[str, Any],
|
|
expected_payload: dict[str, Any],
|
|
expected_ttl: int,
|
|
expected_headers: dict[str, Any] | None,
|
|
) -> None:
|
|
"""Test sending a message via html5.send_message action."""
|
|
load_config.return_value = {"my-desktop": SUBSCRIPTION_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
|
|
|
|
state = hass.states.get("notify.my_desktop")
|
|
assert state
|
|
assert state.state == STATE_UNKNOWN
|
|
|
|
await hass.services.async_call(
|
|
DOMAIN,
|
|
SERVICE_SEND_MESSAGE,
|
|
{ATTR_ENTITY_ID: "notify.my_desktop", ATTR_TITLE: "Hello", **service_data},
|
|
blocking=True,
|
|
)
|
|
|
|
state = hass.states.get("notify.my_desktop")
|
|
assert state
|
|
assert state.state == "2009-02-13T23:31:30+00:00"
|
|
|
|
webpush_async.assert_awaited_once()
|
|
assert webpush_async.await_args
|
|
_, payload, _, _ = webpush_async.await_args.args
|
|
assert json.loads(payload) == {
|
|
"title": "Hello",
|
|
"tag": "12345678-1234-5678-1234-567812345678",
|
|
"timestamp": 1234567890000,
|
|
"data": {"jwt": "JWT"},
|
|
**expected_payload,
|
|
}
|
|
|
|
assert webpush_async.await_args.kwargs["ttl"] == expected_ttl
|
|
assert webpush_async.await_args.kwargs["headers"] == expected_headers
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("domain", "service", "service_data", "issue_id"),
|
|
[
|
|
(
|
|
NOTIFY_DOMAIN,
|
|
"html5_my_desktop",
|
|
{ATTR_MESSAGE: "Hello", ATTR_TARGET: ["my-desktop"]},
|
|
"deprecated_notify_action_notify.html5_my_desktop",
|
|
),
|
|
(
|
|
NOTIFY_DOMAIN,
|
|
DOMAIN,
|
|
{ATTR_MESSAGE: "Hello"},
|
|
"deprecated_notify_action_notify.html5",
|
|
),
|
|
(
|
|
NOTIFY_DOMAIN,
|
|
DOMAIN,
|
|
{ATTR_MESSAGE: "Hello", ATTR_TARGET: ["my-desktop", "my-phone"]},
|
|
"deprecated_notify_action_notify.html5",
|
|
),
|
|
(
|
|
DOMAIN,
|
|
SERVICE_DISMISS,
|
|
{},
|
|
"deprecated_dismiss_action",
|
|
),
|
|
],
|
|
)
|
|
@pytest.mark.usefixtures("mock_wp", "mock_jwt", "mock_vapid", "mock_uuid")
|
|
async def test_deprecation_action_call(
|
|
hass: HomeAssistant,
|
|
config_entry: MockConfigEntry,
|
|
load_config: MagicMock,
|
|
issue_registry: ir.IssueRegistry,
|
|
domain: str,
|
|
service: str,
|
|
service_data: dict[str, Any] | None,
|
|
issue_id: str,
|
|
) -> None:
|
|
"""Test deprecation action call."""
|
|
load_config.return_value = {
|
|
"my-desktop": SUBSCRIPTION_1,
|
|
"my-phone": SUBSCRIPTION_2,
|
|
}
|
|
|
|
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 hass.services.async_call(
|
|
domain,
|
|
service,
|
|
service_data,
|
|
blocking=True,
|
|
)
|
|
|
|
assert issue_registry.async_get_issue(
|
|
domain=DOMAIN,
|
|
issue_id=issue_id,
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("service_data", "expected_payload"),
|
|
[
|
|
(
|
|
{ATTR_TAG: "message-group-1"},
|
|
{ATTR_DISMISS: True, ATTR_TAG: "message-group-1"},
|
|
),
|
|
(
|
|
{},
|
|
{ATTR_DISMISS: True, ATTR_TAG: ""},
|
|
),
|
|
],
|
|
)
|
|
@pytest.mark.usefixtures("mock_jwt", "mock_vapid", "mock_uuid")
|
|
@pytest.mark.freeze_time("2009-02-13T23:31:30.000Z")
|
|
async def test_html5_dismiss_message(
|
|
hass: HomeAssistant,
|
|
config_entry: MockConfigEntry,
|
|
webpush_async: AsyncMock,
|
|
load_config: MagicMock,
|
|
service_data: dict[str, Any],
|
|
expected_payload: dict[str, Any],
|
|
) -> None:
|
|
"""Test dismissing a message via html5.dismiss_message action."""
|
|
load_config.return_value = {"my-desktop": SUBSCRIPTION_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 hass.services.async_call(
|
|
DOMAIN,
|
|
SERVICE_DISMISS_MESSAGE,
|
|
{
|
|
ATTR_ENTITY_ID: "notify.my_desktop",
|
|
**service_data,
|
|
},
|
|
blocking=True,
|
|
)
|
|
|
|
webpush_async.assert_awaited_once()
|
|
assert webpush_async.await_args
|
|
_, payload, _, _ = webpush_async.await_args.args
|
|
assert json.loads(payload) == {
|
|
"timestamp": 1234567890000,
|
|
"data": {"jwt": "JWT"},
|
|
**expected_payload,
|
|
}
|