Allow unregistering a push subscription (#2921)
* Allow unregistering a push subscription
* Update frontend
* ps - HTML5 tests DRY 🍾
This commit is contained in:
parent
d2f7b3c7db
commit
7598de90cb
10 changed files with 179 additions and 44 deletions
|
@ -8,6 +8,34 @@ from werkzeug.test import EnvironBuilder
|
|||
from homeassistant.components.http import request_class
|
||||
from homeassistant.components.notify import html5
|
||||
|
||||
SUBSCRIPTION_1 = {
|
||||
'browser': 'chrome',
|
||||
'subscription': {
|
||||
'endpoint': 'https://google.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',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class TestHtml5Notify(object):
|
||||
"""Tests for HTML5 notify platform."""
|
||||
|
@ -40,13 +68,7 @@ class TestHtml5Notify(object):
|
|||
hass = MagicMock()
|
||||
|
||||
data = {
|
||||
'device': {
|
||||
'browser': 'chrome',
|
||||
'subscription': {
|
||||
'endpoint': 'https://google.com',
|
||||
'keys': {'auth': 'auth', 'p256dh': 'p256dh'}
|
||||
},
|
||||
}
|
||||
'device': SUBSCRIPTION_1
|
||||
}
|
||||
|
||||
with tempfile.NamedTemporaryFile() as fp:
|
||||
|
@ -63,10 +85,7 @@ class TestHtml5Notify(object):
|
|||
assert len(mock_wp.mock_calls) == 2
|
||||
|
||||
# WebPusher constructor
|
||||
assert mock_wp.mock_calls[0][1][0] == {'endpoint':
|
||||
'https://google.com',
|
||||
'keys': {'auth': 'auth',
|
||||
'p256dh': 'p256dh'}}
|
||||
assert mock_wp.mock_calls[0][1][0] == SUBSCRIPTION_1['subscription']
|
||||
|
||||
# Call to send
|
||||
payload = json.loads(mock_wp.mock_calls[1][1][0])
|
||||
|
@ -92,22 +111,13 @@ class TestHtml5Notify(object):
|
|||
assert view.json_path == fp.name
|
||||
assert view.registrations == {}
|
||||
|
||||
builder = EnvironBuilder(method='POST', data=json.dumps({
|
||||
'browser': 'chrome',
|
||||
'subscription': {'endpoint': 'https://google.com',
|
||||
'keys': {'auth': 'auth',
|
||||
'p256dh': 'p256dh'}},
|
||||
}))
|
||||
builder = EnvironBuilder(method='POST',
|
||||
data=json.dumps(SUBSCRIPTION_1))
|
||||
Request = request_class()
|
||||
resp = view.post(Request(builder.get_environ()))
|
||||
|
||||
expected = {
|
||||
'unnamed device': {
|
||||
'browser': 'chrome',
|
||||
'subscription': {'endpoint': 'https://google.com',
|
||||
'keys': {'auth': 'auth',
|
||||
'p256dh': 'p256dh'}},
|
||||
},
|
||||
'unnamed device': SUBSCRIPTION_1,
|
||||
}
|
||||
|
||||
assert resp.status_code == 200, resp.response
|
||||
|
@ -154,6 +164,116 @@ class TestHtml5Notify(object):
|
|||
resp = view.post(Request(builder.get_environ()))
|
||||
assert resp.status_code == 400, resp.response
|
||||
|
||||
def test_unregistering_device_view(self):
|
||||
"""Test that the HTML unregister view works."""
|
||||
hass = MagicMock()
|
||||
|
||||
config = {
|
||||
'some device': SUBSCRIPTION_1,
|
||||
'other device': SUBSCRIPTION_2,
|
||||
}
|
||||
|
||||
with tempfile.NamedTemporaryFile() as fp:
|
||||
hass.config.path.return_value = fp.name
|
||||
fp.write(json.dumps(config).encode('utf-8'))
|
||||
fp.flush()
|
||||
service = html5.get_service(hass, {})
|
||||
|
||||
assert service is not None
|
||||
|
||||
# assert hass.called
|
||||
assert len(hass.mock_calls) == 3
|
||||
|
||||
view = hass.mock_calls[1][1][0]
|
||||
assert view.json_path == fp.name
|
||||
assert view.registrations == config
|
||||
|
||||
builder = EnvironBuilder(method='DELETE', data=json.dumps({
|
||||
'subscription': SUBSCRIPTION_1['subscription'],
|
||||
}))
|
||||
Request = request_class()
|
||||
resp = view.delete(Request(builder.get_environ()))
|
||||
|
||||
config.pop('some device')
|
||||
|
||||
assert resp.status_code == 200, resp.response
|
||||
assert view.registrations == config
|
||||
with open(fp.name) as fpp:
|
||||
assert json.load(fpp) == config
|
||||
|
||||
def test_unregistering_device_view_handles_unknown_subscription(self):
|
||||
"""Test that the HTML unregister view handles unknown subscriptions."""
|
||||
hass = MagicMock()
|
||||
|
||||
config = {
|
||||
'some device': SUBSCRIPTION_1,
|
||||
'other device': SUBSCRIPTION_2,
|
||||
}
|
||||
|
||||
with tempfile.NamedTemporaryFile() as fp:
|
||||
hass.config.path.return_value = fp.name
|
||||
fp.write(json.dumps(config).encode('utf-8'))
|
||||
fp.flush()
|
||||
service = html5.get_service(hass, {})
|
||||
|
||||
assert service is not None
|
||||
|
||||
# assert hass.called
|
||||
assert len(hass.mock_calls) == 3
|
||||
|
||||
view = hass.mock_calls[1][1][0]
|
||||
assert view.json_path == fp.name
|
||||
assert view.registrations == config
|
||||
|
||||
builder = EnvironBuilder(method='DELETE', data=json.dumps({
|
||||
'subscription': SUBSCRIPTION_3['subscription']
|
||||
}))
|
||||
Request = request_class()
|
||||
resp = view.delete(Request(builder.get_environ()))
|
||||
|
||||
assert resp.status_code == 200, resp.response
|
||||
assert view.registrations == config
|
||||
with open(fp.name) as fpp:
|
||||
assert json.load(fpp) == config
|
||||
|
||||
def test_unregistering_device_view_handles_json_safe_error(self):
|
||||
"""Test that the HTML unregister view handles JSON write errors."""
|
||||
hass = MagicMock()
|
||||
|
||||
config = {
|
||||
'some device': SUBSCRIPTION_1,
|
||||
'other device': SUBSCRIPTION_2,
|
||||
}
|
||||
|
||||
with tempfile.NamedTemporaryFile() as fp:
|
||||
hass.config.path.return_value = fp.name
|
||||
fp.write(json.dumps(config).encode('utf-8'))
|
||||
fp.flush()
|
||||
service = html5.get_service(hass, {})
|
||||
|
||||
assert service is not None
|
||||
|
||||
# assert hass.called
|
||||
assert len(hass.mock_calls) == 3
|
||||
|
||||
view = hass.mock_calls[1][1][0]
|
||||
assert view.json_path == fp.name
|
||||
assert view.registrations == config
|
||||
|
||||
builder = EnvironBuilder(method='DELETE', data=json.dumps({
|
||||
'subscription': SUBSCRIPTION_1['subscription'],
|
||||
}))
|
||||
Request = request_class()
|
||||
|
||||
with patch('homeassistant.components.notify.html5._save_config',
|
||||
return_value=False):
|
||||
resp = view.delete(Request(builder.get_environ()))
|
||||
|
||||
assert resp.status_code == 500, resp.response
|
||||
assert view.registrations == config
|
||||
with open(fp.name) as fpp:
|
||||
assert json.load(fpp) == config
|
||||
|
||||
def test_callback_view_no_jwt(self):
|
||||
"""Test that the notification callback view works without JWT."""
|
||||
hass = MagicMock()
|
||||
|
@ -185,13 +305,7 @@ class TestHtml5Notify(object):
|
|||
hass = MagicMock()
|
||||
|
||||
data = {
|
||||
'device': {
|
||||
'browser': 'chrome',
|
||||
'subscription': {
|
||||
'endpoint': 'https://google.com',
|
||||
'keys': {'auth': 'auth', 'p256dh': 'p256dh'}
|
||||
},
|
||||
}
|
||||
'device': SUBSCRIPTION_1,
|
||||
}
|
||||
|
||||
with tempfile.NamedTemporaryFile() as fp:
|
||||
|
@ -211,11 +325,8 @@ class TestHtml5Notify(object):
|
|||
assert len(mock_wp.mock_calls) == 2
|
||||
|
||||
# WebPusher constructor
|
||||
assert mock_wp.mock_calls[0][1][0] == {'endpoint':
|
||||
'https://google.com',
|
||||
'keys': {'auth': 'auth',
|
||||
'p256dh':
|
||||
'p256dh'}}
|
||||
assert mock_wp.mock_calls[0][1][0] == \
|
||||
SUBSCRIPTION_1['subscription']
|
||||
|
||||
# Call to send
|
||||
push_payload = json.loads(mock_wp.mock_calls[1][1][0])
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue