Fix non-thread-safe operations in html5 (#116068)

Fix non thread-safe calls in html5

https://github.com/home-assistant/core/actions/runs/8808425552/job/24177668764?pr=116055
This commit is contained in:
J. Nick Koston 2024-04-24 03:33:19 +02:00 committed by GitHub
parent b1b8b8ba00
commit 9d54aa205b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 122 additions and 114 deletions

View File

@ -165,7 +165,7 @@ HTML5_SHOWNOTIFICATION_PARAMETERS = (
) )
def get_service( async def async_get_service(
hass: HomeAssistant, hass: HomeAssistant,
config: ConfigType, config: ConfigType,
discovery_info: DiscoveryInfoType | None = None, discovery_info: DiscoveryInfoType | None = None,
@ -173,7 +173,7 @@ def get_service(
"""Get the HTML5 push notification service.""" """Get the HTML5 push notification service."""
json_path = hass.config.path(REGISTRATIONS_FILE) json_path = hass.config.path(REGISTRATIONS_FILE)
registrations = _load_config(json_path) registrations = await hass.async_add_executor_job(_load_config, json_path)
vapid_pub_key = config[ATTR_VAPID_PUB_KEY] vapid_pub_key = config[ATTR_VAPID_PUB_KEY]
vapid_prv_key = config[ATTR_VAPID_PRV_KEY] vapid_prv_key = config[ATTR_VAPID_PRV_KEY]

View File

@ -2,7 +2,7 @@
from http import HTTPStatus from http import HTTPStatus
import json import json
from unittest.mock import MagicMock, mock_open, patch from unittest.mock import mock_open, patch
from aiohttp.hdrs import AUTHORIZATION from aiohttp.hdrs import AUTHORIZATION
@ -83,166 +83,174 @@ async def mock_client(hass, hass_client, registrations=None):
return await hass_client() return await hass_client()
class TestHtml5Notify: async def test_get_service_with_no_json(hass: HomeAssistant):
"""Tests for HTML5 notify platform.""" """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)
def test_get_service_with_no_json(self): assert service is not None
"""Test empty json file."""
hass = MagicMock()
m = mock_open()
with patch("homeassistant.util.json.open", m, create=True):
service = html5.get_service(hass, VAPID_CONF)
assert service is not None @patch("homeassistant.components.html5.notify.WebPusher")
async def test_dismissing_message(mock_wp, hass: HomeAssistant):
"""Test dismissing message."""
await async_setup_component(hass, "http", {})
mock_wp().send().status_code = 201
@patch("homeassistant.components.html5.notify.WebPusher") data = {"device": SUBSCRIPTION_1}
def test_dismissing_message(self, mock_wp):
"""Test dismissing message."""
hass = MagicMock()
mock_wp().send().status_code = 201
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
m = mock_open(read_data=json.dumps(data)) assert service is not None
with patch("homeassistant.util.json.open", m, create=True):
service = html5.get_service(hass, VAPID_CONF)
assert service is not None await service.async_dismiss(target=["device", "non_existing"], data={"tag": "test"})
service.dismiss(target=["device", "non_existing"], data={"tag": "test"}) assert len(mock_wp.mock_calls) == 4
assert len(mock_wp.mock_calls) == 4 # WebPusher constructor
assert mock_wp.mock_calls[2][1][0] == SUBSCRIPTION_1["subscription"]
# WebPusher constructor # Call to send
assert mock_wp.mock_calls[2][1][0] == SUBSCRIPTION_1["subscription"] payload = json.loads(mock_wp.mock_calls[3][2]["data"])
# Call to send assert payload["dismiss"] is True
payload = json.loads(mock_wp.mock_calls[3][2]["data"]) assert payload["tag"] == "test"
assert payload["dismiss"] is True
assert payload["tag"] == "test"
@patch("homeassistant.components.html5.notify.WebPusher") @patch("homeassistant.components.html5.notify.WebPusher")
def test_sending_message(self, mock_wp): async def test_sending_message(mock_wp, hass: HomeAssistant):
"""Test sending message.""" """Test sending message."""
hass = MagicMock() await async_setup_component(hass, "http", {})
mock_wp().send().status_code = 201 mock_wp().send().status_code = 201
data = {"device": SUBSCRIPTION_1} data = {"device": SUBSCRIPTION_1}
m = mock_open(read_data=json.dumps(data)) m = mock_open(read_data=json.dumps(data))
with patch("homeassistant.util.json.open", m, create=True): with patch("homeassistant.util.json.open", m, create=True):
service = html5.get_service(hass, VAPID_CONF) service = await html5.async_get_service(hass, VAPID_CONF)
service.hass = hass
assert service is not None assert service is not None
service.send_message( await service.async_send_message(
"Hello", target=["device", "non_existing"], data={"icon": "beer.png"} "Hello", target=["device", "non_existing"], data={"icon": "beer.png"}
) )
assert len(mock_wp.mock_calls) == 4 assert len(mock_wp.mock_calls) == 4
# WebPusher constructor # WebPusher constructor
assert mock_wp.mock_calls[2][1][0] == SUBSCRIPTION_1["subscription"] assert mock_wp.mock_calls[2][1][0] == SUBSCRIPTION_1["subscription"]
# Call to send # Call to send
payload = json.loads(mock_wp.mock_calls[3][2]["data"]) payload = json.loads(mock_wp.mock_calls[3][2]["data"])
assert payload["body"] == "Hello" assert payload["body"] == "Hello"
assert payload["icon"] == "beer.png" assert payload["icon"] == "beer.png"
@patch("homeassistant.components.html5.notify.WebPusher")
def test_fcm_key_include(self, mock_wp):
"""Test if the FCM header is included."""
hass = MagicMock()
mock_wp().send().status_code = 201
data = {"chrome": SUBSCRIPTION_5} @patch("homeassistant.components.html5.notify.WebPusher")
async def test_fcm_key_include(mock_wp, hass: HomeAssistant):
"""Test if the FCM header is included."""
await async_setup_component(hass, "http", {})
mock_wp().send().status_code = 201
m = mock_open(read_data=json.dumps(data)) data = {"chrome": SUBSCRIPTION_5}
with patch("homeassistant.util.json.open", m, create=True):
service = html5.get_service(hass, VAPID_CONF)
assert service is not None 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
service.send_message("Hello", target=["chrome"]) assert service is not None
assert len(mock_wp.mock_calls) == 4 await service.async_send_message("Hello", target=["chrome"])
# WebPusher constructor
assert mock_wp.mock_calls[2][1][0] == SUBSCRIPTION_5["subscription"]
# Get the keys passed to the WebPusher's send method assert len(mock_wp.mock_calls) == 4
assert mock_wp.mock_calls[3][2]["headers"]["Authorization"] is not None # WebPusher constructor
assert mock_wp.mock_calls[2][1][0] == SUBSCRIPTION_5["subscription"]
@patch("homeassistant.components.html5.notify.WebPusher") # Get the keys passed to the WebPusher's send method
def test_fcm_send_with_unknown_priority(self, mock_wp): assert mock_wp.mock_calls[3][2]["headers"]["Authorization"] is not None
"""Test if the gcm_key is only included for GCM endpoints."""
hass = MagicMock()
mock_wp().send().status_code = 201
data = {"chrome": SUBSCRIPTION_5}
m = mock_open(read_data=json.dumps(data)) @patch("homeassistant.components.html5.notify.WebPusher")
with patch("homeassistant.util.json.open", m, create=True): async def test_fcm_send_with_unknown_priority(mock_wp, hass: HomeAssistant):
service = html5.get_service(hass, VAPID_CONF) """Test if the gcm_key is only included for GCM endpoints."""
await async_setup_component(hass, "http", {})
mock_wp().send().status_code = 201
assert service is not None data = {"chrome": SUBSCRIPTION_5}
service.send_message("Hello", target=["chrome"], priority="undefined") 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 len(mock_wp.mock_calls) == 4 assert service is not None
# WebPusher constructor
assert mock_wp.mock_calls[2][1][0] == SUBSCRIPTION_5["subscription"]
# Get the keys passed to the WebPusher's send method await service.async_send_message("Hello", target=["chrome"], priority="undefined")
assert mock_wp.mock_calls[3][2]["headers"]["priority"] == "normal"
@patch("homeassistant.components.html5.notify.WebPusher") assert len(mock_wp.mock_calls) == 4
def test_fcm_no_targets(self, mock_wp): # WebPusher constructor
"""Test if the gcm_key is only included for GCM endpoints.""" assert mock_wp.mock_calls[2][1][0] == SUBSCRIPTION_5["subscription"]
hass = MagicMock()
mock_wp().send().status_code = 201
data = {"chrome": SUBSCRIPTION_5} # Get the keys passed to the WebPusher's send method
assert mock_wp.mock_calls[3][2]["headers"]["priority"] == "normal"
m = mock_open(read_data=json.dumps(data))
with patch("homeassistant.util.json.open", m, create=True):
service = html5.get_service(hass, VAPID_CONF)
assert service is not None @patch("homeassistant.components.html5.notify.WebPusher")
async def test_fcm_no_targets(mock_wp, hass: HomeAssistant):
"""Test if the gcm_key is only included for GCM endpoints."""
await async_setup_component(hass, "http", {})
mock_wp().send().status_code = 201
service.send_message("Hello") data = {"chrome": SUBSCRIPTION_5}
assert len(mock_wp.mock_calls) == 4 m = mock_open(read_data=json.dumps(data))
# WebPusher constructor with patch("homeassistant.util.json.open", m, create=True):
assert mock_wp.mock_calls[2][1][0] == SUBSCRIPTION_5["subscription"] service = await html5.async_get_service(hass, VAPID_CONF)
service.hass = hass
# Get the keys passed to the WebPusher's send method assert service is not None
assert mock_wp.mock_calls[3][2]["headers"]["priority"] == "normal"
@patch("homeassistant.components.html5.notify.WebPusher") await service.async_send_message("Hello")
def test_fcm_additional_data(self, mock_wp):
"""Test if the gcm_key is only included for GCM endpoints."""
hass = MagicMock()
mock_wp().send().status_code = 201
data = {"chrome": SUBSCRIPTION_5} assert len(mock_wp.mock_calls) == 4
# WebPusher constructor
assert mock_wp.mock_calls[2][1][0] == SUBSCRIPTION_5["subscription"]
m = mock_open(read_data=json.dumps(data)) # Get the keys passed to the WebPusher's send method
with patch("homeassistant.util.json.open", m, create=True): assert mock_wp.mock_calls[3][2]["headers"]["priority"] == "normal"
service = html5.get_service(hass, VAPID_CONF)
assert service is not None
service.send_message("Hello", data={"mykey": "myvalue"}) @patch("homeassistant.components.html5.notify.WebPusher")
async def test_fcm_additional_data(mock_wp, hass: HomeAssistant):
"""Test if the gcm_key is only included for GCM endpoints."""
await async_setup_component(hass, "http", {})
mock_wp().send().status_code = 201
assert len(mock_wp.mock_calls) == 4 data = {"chrome": SUBSCRIPTION_5}
# WebPusher constructor
assert mock_wp.mock_calls[2][1][0] == SUBSCRIPTION_5["subscription"]
# Get the keys passed to the WebPusher's send method m = mock_open(read_data=json.dumps(data))
assert mock_wp.mock_calls[3][2]["headers"]["priority"] == "normal" 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"})
assert len(mock_wp.mock_calls) == 4
# WebPusher constructor
assert mock_wp.mock_calls[2][1][0] == SUBSCRIPTION_5["subscription"]
# Get the keys passed to the WebPusher's send method
assert mock_wp.mock_calls[3][2]["headers"]["priority"] == "normal"
async def test_registering_new_device_view( async def test_registering_new_device_view(