core/tests/components/demo/test_notify.py

196 lines
6.9 KiB
Python
Raw Normal View History

2016-03-09 09:25:50 +00:00
"""The tests for the notify demo platform."""
2015-12-10 07:46:50 +00:00
import unittest
from unittest.mock import patch
2015-12-10 07:46:50 +00:00
import pytest
import voluptuous as vol
import homeassistant.components.demo.notify as demo
import homeassistant.components.notify as notify
from homeassistant.core import callback
Add support for simultaneous runs of Script helper (#31937) * Add tests for legacy Script helper behavior * Add Script helper if_running and run_mode options - if_running controls what happens if Script run while previous run has not completed. Can be: - error: Raise an exception - ignore: Return without doing anything (previous run continues as-is) - parallel: Start run in new task - restart: Stop previous run before starting new run - run_mode controls when call to async_run will return. Can be: - background: Returns immediately - legacy: Implements previous behavior, which is to return when done, or when suspended by delay or wait_template - blocking: Returns when run has completed - If neither is specified, default is run_mode=legacy (and if_running is not used.) Otherwise, defaults are if_running=parallel and run_mode=background. If run_mode is set to legacy then if_running must be None. - Caller may supply a logger which will be used throughout instead of default module logger. - Move Script running state into new helper classes, comprised of an abstract base class and two concrete clases, one for legacy behavior and one for new behavior. - Remove some non-async methods, as well as call_from_config which has only been used in tests. - Adjust tests accordingly. * Change per review - Change run_mode default from background to blocking. - Make sure change listener is called, even when there's an unexpected exception. - Make _ScriptRun.async_stop more graceful by using an asyncio.Event for signaling instead of simply cancelling Task. - Subclass _ScriptRun for background & blocking behavior. Also: - Fix timeouts in _ScriptRun by converting timedeltas to float seconds. - General cleanup. * Change per review 2 - Don't propagate exceptions if call from user has already returned (i.e., for background runs or legacy runs that have suspended.) - Allow user to specify if exceptions should be logged. They will still be logged regardless if exception is not propagated. - Rename _start_script_delay and _start_wait_template_delay for clarity. - Remove return value from Script.async_run. - Fix missing await. - Change call to self.is_running in Script.async_run to direct test of self._runs. * Change per review 3 and add tests - Remove Script.set_logger(). - Enhance existing tests to check all run modes. - Add tests for new features. - Fix a few minor bugs found by tests.
2020-02-24 22:56:00 +00:00
from homeassistant.helpers import discovery
from homeassistant.setup import setup_component
from tests.common import assert_setup_component, get_test_home_assistant
from tests.components.notify import common
2015-12-10 07:46:50 +00:00
2019-07-31 19:25:30 +00:00
CONFIG = {notify.DOMAIN: {"platform": "demo"}}
2016-02-14 23:08:23 +00:00
2015-12-10 07:46:50 +00:00
class TestNotifyDemo(unittest.TestCase):
2016-03-09 09:25:50 +00:00
"""Test the demo notify."""
2015-12-10 07:46:50 +00:00
def setUp(self): # pylint: disable=invalid-name
2018-08-19 20:29:08 +00:00
"""Set up things to be run when tests are started."""
2016-02-14 23:08:23 +00:00
self.hass = get_test_home_assistant()
2015-12-10 07:46:50 +00:00
self.events = []
self.calls = []
2015-12-10 07:46:50 +00:00
2016-11-04 04:58:18 +00:00
@callback
2015-12-10 07:46:50 +00:00
def record_event(event):
2016-03-09 09:25:50 +00:00
"""Record event to send notification."""
2015-12-10 07:46:50 +00:00
self.events.append(event)
self.hass.bus.listen(demo.EVENT_NOTIFY, record_event)
def tearDown(self): # pylint: disable=invalid-name
"""Stop down everything that was started."""
2015-12-10 07:46:50 +00:00
self.hass.stop()
def _setup_notify(self):
2019-04-09 20:59:15 +00:00
with assert_setup_component(1, notify.DOMAIN) as config:
assert setup_component(self.hass, notify.DOMAIN, CONFIG)
assert config[notify.DOMAIN]
self.hass.block_till_done()
def test_setup(self):
"""Test setup."""
self._setup_notify()
2019-07-31 19:25:30 +00:00
@patch("homeassistant.components.demo.notify.get_service", autospec=True)
def test_no_notify_service(self, mock_demo_get_service):
"""Test missing platform notify service instance."""
mock_demo_get_service.return_value = None
2019-07-31 19:25:30 +00:00
with self.assertLogs(
"homeassistant.components.notify", level="ERROR"
) as log_handle:
self._setup_notify()
self.hass.block_till_done()
assert mock_demo_get_service.called
2019-07-31 19:25:30 +00:00
assert log_handle.output == [
"ERROR:homeassistant.components.notify:"
"Failed to initialize notification service demo"
]
2019-07-31 19:25:30 +00:00
@patch("homeassistant.components.demo.notify.get_service", autospec=True)
def test_discover_notify(self, mock_demo_get_service):
"""Test discovery of notify demo platform."""
assert notify.DOMAIN not in self.hass.config.components
discovery.load_platform(
2019-07-31 19:25:30 +00:00
self.hass, "notify", "demo", {"test_key": "test_val"}, {"notify": {}}
)
self.hass.block_till_done()
assert notify.DOMAIN in self.hass.config.components
assert mock_demo_get_service.called
assert mock_demo_get_service.mock_calls[0][1] == (
2019-07-31 19:25:30 +00:00
self.hass,
{},
{"test_key": "test_val"},
)
2016-11-04 04:58:18 +00:00
@callback
def record_calls(self, *args):
"""Record calls."""
self.calls.append(args)
def test_sending_none_message(self):
"""Test send with None as message."""
self._setup_notify()
with pytest.raises(vol.Invalid):
common.send_message(self.hass, None)
self.hass.block_till_done()
assert len(self.events) == 0
2015-12-10 07:46:50 +00:00
def test_sending_templated_message(self):
2016-03-09 09:25:50 +00:00
"""Send a templated message."""
self._setup_notify()
2019-07-31 19:25:30 +00:00
self.hass.states.set("sensor.temperature", 10)
common.send_message(
self.hass,
"{{ states.sensor.temperature.state }}",
"{{ states.sensor.temperature.name }}",
)
self.hass.block_till_done()
2015-12-10 07:46:50 +00:00
last_event = self.events[-1]
2019-07-31 19:25:30 +00:00
assert last_event.data[notify.ATTR_TITLE] == "temperature"
assert last_event.data[notify.ATTR_MESSAGE] == "10"
2016-08-10 03:23:57 +00:00
def test_method_forwards_correct_data(self):
"""Test that all data from the service gets forwarded to service."""
self._setup_notify()
2019-07-31 19:25:30 +00:00
common.send_message(self.hass, "my message", "my title", {"hello": "world"})
self.hass.block_till_done()
assert len(self.events) == 1
2016-08-10 03:23:57 +00:00
data = self.events[0].data
assert {
2019-07-31 19:25:30 +00:00
"message": "my message",
"title": "my title",
"data": {"hello": "world"},
2016-08-10 03:23:57 +00:00
} == data
2016-09-10 14:36:55 +00:00
def test_calling_notify_from_script_loaded_from_yaml_without_title(self):
"""Test if we can call a notify from a script."""
self._setup_notify()
Add support for simultaneous runs of Script helper (#31937) * Add tests for legacy Script helper behavior * Add Script helper if_running and run_mode options - if_running controls what happens if Script run while previous run has not completed. Can be: - error: Raise an exception - ignore: Return without doing anything (previous run continues as-is) - parallel: Start run in new task - restart: Stop previous run before starting new run - run_mode controls when call to async_run will return. Can be: - background: Returns immediately - legacy: Implements previous behavior, which is to return when done, or when suspended by delay or wait_template - blocking: Returns when run has completed - If neither is specified, default is run_mode=legacy (and if_running is not used.) Otherwise, defaults are if_running=parallel and run_mode=background. If run_mode is set to legacy then if_running must be None. - Caller may supply a logger which will be used throughout instead of default module logger. - Move Script running state into new helper classes, comprised of an abstract base class and two concrete clases, one for legacy behavior and one for new behavior. - Remove some non-async methods, as well as call_from_config which has only been used in tests. - Adjust tests accordingly. * Change per review - Change run_mode default from background to blocking. - Make sure change listener is called, even when there's an unexpected exception. - Make _ScriptRun.async_stop more graceful by using an asyncio.Event for signaling instead of simply cancelling Task. - Subclass _ScriptRun for background & blocking behavior. Also: - Fix timeouts in _ScriptRun by converting timedeltas to float seconds. - General cleanup. * Change per review 2 - Don't propagate exceptions if call from user has already returned (i.e., for background runs or legacy runs that have suspended.) - Allow user to specify if exceptions should be logged. They will still be logged regardless if exception is not propagated. - Rename _start_script_delay and _start_wait_template_delay for clarity. - Remove return value from Script.async_run. - Fix missing await. - Change call to self.is_running in Script.async_run to direct test of self._runs. * Change per review 3 and add tests - Remove Script.set_logger(). - Enhance existing tests to check all run modes. - Add tests for new features. - Fix a few minor bugs found by tests.
2020-02-24 22:56:00 +00:00
step = {
2019-07-31 19:25:30 +00:00
"service": "notify.notify",
"data": {
"data": {
"push": {"sound": "US-EN-Morgan-Freeman-Roommate-Is-Arriving.wav"}
}
},
2019-07-31 19:25:30 +00:00
"data_template": {"message": "Test 123 {{ 2 + 2 }}\n"},
}
Add support for simultaneous runs of Script helper (#31937) * Add tests for legacy Script helper behavior * Add Script helper if_running and run_mode options - if_running controls what happens if Script run while previous run has not completed. Can be: - error: Raise an exception - ignore: Return without doing anything (previous run continues as-is) - parallel: Start run in new task - restart: Stop previous run before starting new run - run_mode controls when call to async_run will return. Can be: - background: Returns immediately - legacy: Implements previous behavior, which is to return when done, or when suspended by delay or wait_template - blocking: Returns when run has completed - If neither is specified, default is run_mode=legacy (and if_running is not used.) Otherwise, defaults are if_running=parallel and run_mode=background. If run_mode is set to legacy then if_running must be None. - Caller may supply a logger which will be used throughout instead of default module logger. - Move Script running state into new helper classes, comprised of an abstract base class and two concrete clases, one for legacy behavior and one for new behavior. - Remove some non-async methods, as well as call_from_config which has only been used in tests. - Adjust tests accordingly. * Change per review - Change run_mode default from background to blocking. - Make sure change listener is called, even when there's an unexpected exception. - Make _ScriptRun.async_stop more graceful by using an asyncio.Event for signaling instead of simply cancelling Task. - Subclass _ScriptRun for background & blocking behavior. Also: - Fix timeouts in _ScriptRun by converting timedeltas to float seconds. - General cleanup. * Change per review 2 - Don't propagate exceptions if call from user has already returned (i.e., for background runs or legacy runs that have suspended.) - Allow user to specify if exceptions should be logged. They will still be logged regardless if exception is not propagated. - Rename _start_script_delay and _start_wait_template_delay for clarity. - Remove return value from Script.async_run. - Fix missing await. - Change call to self.is_running in Script.async_run to direct test of self._runs. * Change per review 3 and add tests - Remove Script.set_logger(). - Enhance existing tests to check all run modes. - Add tests for new features. - Fix a few minor bugs found by tests.
2020-02-24 22:56:00 +00:00
setup_component(self.hass, "script", {"script": {"test": {"sequence": step}}})
self.hass.services.call("script", "test")
self.hass.block_till_done()
assert len(self.events) == 1
assert {
2019-07-31 19:25:30 +00:00
"message": "Test 123 4",
"data": {
"push": {"sound": "US-EN-Morgan-Freeman-Roommate-Is-Arriving.wav"}
},
2016-09-10 14:36:55 +00:00
} == self.events[0].data
def test_calling_notify_from_script_loaded_from_yaml_with_title(self):
"""Test if we can call a notify from a script."""
self._setup_notify()
Add support for simultaneous runs of Script helper (#31937) * Add tests for legacy Script helper behavior * Add Script helper if_running and run_mode options - if_running controls what happens if Script run while previous run has not completed. Can be: - error: Raise an exception - ignore: Return without doing anything (previous run continues as-is) - parallel: Start run in new task - restart: Stop previous run before starting new run - run_mode controls when call to async_run will return. Can be: - background: Returns immediately - legacy: Implements previous behavior, which is to return when done, or when suspended by delay or wait_template - blocking: Returns when run has completed - If neither is specified, default is run_mode=legacy (and if_running is not used.) Otherwise, defaults are if_running=parallel and run_mode=background. If run_mode is set to legacy then if_running must be None. - Caller may supply a logger which will be used throughout instead of default module logger. - Move Script running state into new helper classes, comprised of an abstract base class and two concrete clases, one for legacy behavior and one for new behavior. - Remove some non-async methods, as well as call_from_config which has only been used in tests. - Adjust tests accordingly. * Change per review - Change run_mode default from background to blocking. - Make sure change listener is called, even when there's an unexpected exception. - Make _ScriptRun.async_stop more graceful by using an asyncio.Event for signaling instead of simply cancelling Task. - Subclass _ScriptRun for background & blocking behavior. Also: - Fix timeouts in _ScriptRun by converting timedeltas to float seconds. - General cleanup. * Change per review 2 - Don't propagate exceptions if call from user has already returned (i.e., for background runs or legacy runs that have suspended.) - Allow user to specify if exceptions should be logged. They will still be logged regardless if exception is not propagated. - Rename _start_script_delay and _start_wait_template_delay for clarity. - Remove return value from Script.async_run. - Fix missing await. - Change call to self.is_running in Script.async_run to direct test of self._runs. * Change per review 3 and add tests - Remove Script.set_logger(). - Enhance existing tests to check all run modes. - Add tests for new features. - Fix a few minor bugs found by tests.
2020-02-24 22:56:00 +00:00
step = {
2019-07-31 19:25:30 +00:00
"service": "notify.notify",
"data": {
"data": {
"push": {"sound": "US-EN-Morgan-Freeman-Roommate-Is-Arriving.wav"}
}
},
2019-07-31 19:25:30 +00:00
"data_template": {"message": "Test 123 {{ 2 + 2 }}\n", "title": "Test"},
}
Add support for simultaneous runs of Script helper (#31937) * Add tests for legacy Script helper behavior * Add Script helper if_running and run_mode options - if_running controls what happens if Script run while previous run has not completed. Can be: - error: Raise an exception - ignore: Return without doing anything (previous run continues as-is) - parallel: Start run in new task - restart: Stop previous run before starting new run - run_mode controls when call to async_run will return. Can be: - background: Returns immediately - legacy: Implements previous behavior, which is to return when done, or when suspended by delay or wait_template - blocking: Returns when run has completed - If neither is specified, default is run_mode=legacy (and if_running is not used.) Otherwise, defaults are if_running=parallel and run_mode=background. If run_mode is set to legacy then if_running must be None. - Caller may supply a logger which will be used throughout instead of default module logger. - Move Script running state into new helper classes, comprised of an abstract base class and two concrete clases, one for legacy behavior and one for new behavior. - Remove some non-async methods, as well as call_from_config which has only been used in tests. - Adjust tests accordingly. * Change per review - Change run_mode default from background to blocking. - Make sure change listener is called, even when there's an unexpected exception. - Make _ScriptRun.async_stop more graceful by using an asyncio.Event for signaling instead of simply cancelling Task. - Subclass _ScriptRun for background & blocking behavior. Also: - Fix timeouts in _ScriptRun by converting timedeltas to float seconds. - General cleanup. * Change per review 2 - Don't propagate exceptions if call from user has already returned (i.e., for background runs or legacy runs that have suspended.) - Allow user to specify if exceptions should be logged. They will still be logged regardless if exception is not propagated. - Rename _start_script_delay and _start_wait_template_delay for clarity. - Remove return value from Script.async_run. - Fix missing await. - Change call to self.is_running in Script.async_run to direct test of self._runs. * Change per review 3 and add tests - Remove Script.set_logger(). - Enhance existing tests to check all run modes. - Add tests for new features. - Fix a few minor bugs found by tests.
2020-02-24 22:56:00 +00:00
setup_component(self.hass, "script", {"script": {"test": {"sequence": step}}})
self.hass.services.call("script", "test")
self.hass.block_till_done()
assert len(self.events) == 1
2016-09-10 14:36:55 +00:00
assert {
2019-07-31 19:25:30 +00:00
"message": "Test 123 4",
"title": "Test",
"data": {
"push": {"sound": "US-EN-Morgan-Freeman-Roommate-Is-Arriving.wav"}
},
} == self.events[0].data
def test_targets_are_services(self):
"""Test that all targets are exposed as individual services."""
self._setup_notify()
assert self.hass.services.has_service("notify", "demo") is not None
service = "demo_test_target_name"
assert self.hass.services.has_service("notify", service) is not None
def test_messages_to_targets_route(self):
"""Test message routing to specific target services."""
self._setup_notify()
self.hass.bus.listen_once("notify", self.record_calls)
2019-07-31 19:25:30 +00:00
self.hass.services.call(
"notify",
"demo_test_target_name",
{"message": "my message", "title": "my title", "data": {"hello": "world"}},
)
self.hass.block_till_done()
data = self.calls[0][0].data
assert {
2019-07-31 19:25:30 +00:00
"message": "my message",
"target": ["test target id"],
"title": "my title",
"data": {"hello": "world"},
} == data