diff --git a/homeassistant/components/notify/pushbullet.py b/homeassistant/components/notify/pushbullet.py index 13cb2fd579b..37edb6709a7 100644 --- a/homeassistant/components/notify/pushbullet.py +++ b/homeassistant/components/notify/pushbullet.py @@ -138,8 +138,11 @@ class PushBulletNotificationService(BaseNotificationService): filepath = data.get(ATTR_FILE) file_url = data.get(ATTR_FILE_URL) try: + email_kwargs = {} + if email: + email_kwargs['email'] = email if url: - pusher.push_link(title, url, body=message, email=email) + pusher.push_link(title, url, body=message, **email_kwargs) elif filepath: if not self.hass.config.is_allowed_path(filepath): _LOGGER.error("Filepath is not valid or allowed") @@ -149,20 +152,21 @@ class PushBulletNotificationService(BaseNotificationService): if filedata.get('file_type') == 'application/x-empty': _LOGGER.error("Can not send an empty file") return - + filedata.update(email_kwargs) pusher.push_file(title=title, body=message, - email=email, **filedata) + **filedata) elif file_url: if not file_url.startswith('http'): _LOGGER.error("URL should start with http or https") return - pusher.push_file(title=title, body=message, email=email, + pusher.push_file(title=title, body=message, file_name=file_url, file_url=file_url, file_type=(mimetypes - .guess_type(file_url)[0])) + .guess_type(file_url)[0]), + **email_kwargs) elif data_list: - pusher.push_note(title, data_list, email=email) + pusher.push_list(title, data_list, **email_kwargs) else: - pusher.push_note(title, message, email=email) + pusher.push_note(title, message, **email_kwargs) except PushError as err: _LOGGER.error("Notify failed: %s", err) diff --git a/requirements_test_all.txt b/requirements_test_all.txt index b3882a00b3b..da8187d23ff 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -116,6 +116,10 @@ pmsensor==0.4 # homeassistant.components.prometheus prometheus_client==0.1.0 +# homeassistant.components.notify.pushbullet +# homeassistant.components.sensor.pushbullet +pushbullet.py==0.11.0 + # homeassistant.components.canary py-canary==0.2.3 diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index 48d19049316..dcd201667dd 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -63,6 +63,7 @@ TEST_REQUIREMENTS = ( 'pilight', 'pmsensor', 'prometheus_client', + 'pushbullet.py', 'py-canary', 'pydispatcher', 'PyJWT', diff --git a/tests/components/notify/test_pushbullet.py b/tests/components/notify/test_pushbullet.py index ba3046e8fd7..73b9aa4fbeb 100644 --- a/tests/components/notify/test_pushbullet.py +++ b/tests/components/notify/test_pushbullet.py @@ -1,42 +1,243 @@ """The tests for the pushbullet notification platform.""" - +import json import unittest +from unittest.mock import patch + +from pushbullet import PushBullet +import requests_mock from homeassistant.setup import setup_component import homeassistant.components.notify as notify -from tests.common import assert_setup_component, get_test_home_assistant +from tests.common import ( + assert_setup_component, get_test_home_assistant, load_fixture) -class TestPushbullet(unittest.TestCase): - """Test the pushbullet notifications.""" +class TestPushBullet(unittest.TestCase): + """Tests the Pushbullet Component.""" - def setUp(self): # pylint: disable=invalid-name - """Setup things to be run when tests are started.""" + def setUp(self): + """Initialize values for this test case class.""" self.hass = get_test_home_assistant() def tearDown(self): # pylint: disable=invalid-name - """Stop down everything that was started.""" + """Stop everything that we started.""" self.hass.stop() - def test_setup(self): + @patch.object(PushBullet, '_get_data', + return_value=json.loads(load_fixture( + 'pushbullet_devices.json'))) + def test_pushbullet_config(self, mock__get_data): """Test setup.""" + config = {notify.DOMAIN: {'name': 'test', + 'platform': 'pushbullet', + 'api_key': 'MYFAKEKEY'}} with assert_setup_component(1) as handle_config: - assert setup_component(self.hass, 'notify', { - 'notify': { - 'name': 'test', - 'platform': 'pushbullet', - 'api_key': 'MYFAKEKEY', } - }) + assert setup_component(self.hass, notify.DOMAIN, config) assert handle_config[notify.DOMAIN] - def test_bad_config(self): + def test_pushbullet_config_bad(self): """Test set up the platform with bad/missing configuration.""" config = { notify.DOMAIN: { - 'name': 'test', 'platform': 'pushbullet', } } with assert_setup_component(0) as handle_config: assert setup_component(self.hass, notify.DOMAIN, config) assert not handle_config[notify.DOMAIN] + + @requests_mock.Mocker() + @patch.object(PushBullet, '_get_data', + return_value=json.loads(load_fixture( + 'pushbullet_devices.json'))) + def test_pushbullet_push_default(self, mock, mock__get_data): + """Test pushbullet push to default target.""" + config = {notify.DOMAIN: {'name': 'test', + 'platform': 'pushbullet', + 'api_key': 'MYFAKEKEY'}} + with assert_setup_component(1) as handle_config: + assert setup_component(self.hass, notify.DOMAIN, config) + assert handle_config[notify.DOMAIN] + mock.register_uri( + requests_mock.POST, + 'https://api.pushbullet.com/v2/pushes', + status_code=200, + json={'mock_response': 'Ok'} + ) + data = {'title': 'Test Title', + 'message': 'Test Message'} + self.hass.services.call(notify.DOMAIN, 'test', data) + self.hass.block_till_done() + self.assertTrue(mock.called) + self.assertEqual(mock.call_count, 1) + + expected_body = {'body': 'Test Message', + 'title': 'Test Title', + 'type': 'note'} + self.assertEqual(mock.last_request.json(), expected_body) + + @requests_mock.Mocker() + @patch.object(PushBullet, '_get_data', + return_value=json.loads(load_fixture( + 'pushbullet_devices.json'))) + def test_pushbullet_push_device(self, mock, mock__get_data): + """Test pushbullet push to default target.""" + config = {notify.DOMAIN: {'name': 'test', + 'platform': 'pushbullet', + 'api_key': 'MYFAKEKEY'}} + with assert_setup_component(1) as handle_config: + assert setup_component(self.hass, notify.DOMAIN, config) + assert handle_config[notify.DOMAIN] + mock.register_uri( + requests_mock.POST, + 'https://api.pushbullet.com/v2/pushes', + status_code=200, + json={'mock_response': 'Ok'} + ) + data = {'title': 'Test Title', + 'message': 'Test Message', + 'target': ['device/DESKTOP']} + self.hass.services.call(notify.DOMAIN, 'test', data) + self.hass.block_till_done() + self.assertTrue(mock.called) + self.assertEqual(mock.call_count, 1) + + expected_body = {'body': 'Test Message', + 'device_iden': 'identity1', + 'title': 'Test Title', + 'type': 'note'} + self.assertEqual(mock.last_request.json(), expected_body) + + @requests_mock.Mocker() + @patch.object(PushBullet, '_get_data', + return_value=json.loads(load_fixture( + 'pushbullet_devices.json'))) + def test_pushbullet_push_devices(self, mock, mock__get_data): + """Test pushbullet push to default target.""" + config = {notify.DOMAIN: {'name': 'test', + 'platform': 'pushbullet', + 'api_key': 'MYFAKEKEY'}} + with assert_setup_component(1) as handle_config: + assert setup_component(self.hass, notify.DOMAIN, config) + assert handle_config[notify.DOMAIN] + mock.register_uri( + requests_mock.POST, + 'https://api.pushbullet.com/v2/pushes', + status_code=200, + json={'mock_response': 'Ok'} + ) + data = {'title': 'Test Title', + 'message': 'Test Message', + 'target': ['device/DESKTOP', 'device/My iPhone']} + self.hass.services.call(notify.DOMAIN, 'test', data) + self.hass.block_till_done() + self.assertTrue(mock.called) + self.assertEqual(mock.call_count, 2) + self.assertEqual(len(mock.request_history), 2) + + expected_body = {'body': 'Test Message', + 'device_iden': 'identity1', + 'title': 'Test Title', + 'type': 'note'} + self.assertEqual(mock.request_history[0].json(), expected_body) + expected_body = {'body': 'Test Message', + 'device_iden': 'identity2', + 'title': 'Test Title', + 'type': 'note'} + self.assertEqual(mock.request_history[1].json(), expected_body) + + @requests_mock.Mocker() + @patch.object(PushBullet, '_get_data', + return_value=json.loads(load_fixture( + 'pushbullet_devices.json'))) + def test_pushbullet_push_email(self, mock, mock__get_data): + """Test pushbullet push to default target.""" + config = {notify.DOMAIN: {'name': 'test', + 'platform': 'pushbullet', + 'api_key': 'MYFAKEKEY'}} + with assert_setup_component(1) as handle_config: + assert setup_component(self.hass, notify.DOMAIN, config) + assert handle_config[notify.DOMAIN] + mock.register_uri( + requests_mock.POST, + 'https://api.pushbullet.com/v2/pushes', + status_code=200, + json={'mock_response': 'Ok'} + ) + data = {'title': 'Test Title', + 'message': 'Test Message', + 'target': ['email/user@host.net']} + self.hass.services.call(notify.DOMAIN, 'test', data) + self.hass.block_till_done() + self.assertTrue(mock.called) + self.assertEqual(mock.call_count, 1) + self.assertEqual(len(mock.request_history), 1) + + expected_body = {'body': 'Test Message', + 'email': 'user@host.net', + 'title': 'Test Title', + 'type': 'note'} + self.assertEqual(mock.request_history[0].json(), expected_body) + + @requests_mock.Mocker() + @patch.object(PushBullet, '_get_data', + return_value=json.loads(load_fixture( + 'pushbullet_devices.json'))) + def test_pushbullet_push_mixed(self, mock, mock__get_data): + """Test pushbullet push to default target.""" + config = {notify.DOMAIN: {'name': 'test', + 'platform': 'pushbullet', + 'api_key': 'MYFAKEKEY'}} + with assert_setup_component(1) as handle_config: + assert setup_component(self.hass, notify.DOMAIN, config) + assert handle_config[notify.DOMAIN] + mock.register_uri( + requests_mock.POST, + 'https://api.pushbullet.com/v2/pushes', + status_code=200, + json={'mock_response': 'Ok'} + ) + data = {'title': 'Test Title', + 'message': 'Test Message', + 'target': ['device/DESKTOP', 'email/user@host.net']} + self.hass.services.call(notify.DOMAIN, 'test', data) + self.hass.block_till_done() + self.assertTrue(mock.called) + self.assertEqual(mock.call_count, 2) + self.assertEqual(len(mock.request_history), 2) + + expected_body = {'body': 'Test Message', + 'device_iden': 'identity1', + 'title': 'Test Title', + 'type': 'note'} + self.assertEqual(mock.request_history[0].json(), expected_body) + expected_body = {'body': 'Test Message', + 'email': 'user@host.net', + 'title': 'Test Title', + 'type': 'note'} + self.assertEqual(mock.request_history[1].json(), expected_body) + + @requests_mock.Mocker() + @patch.object(PushBullet, '_get_data', + return_value=json.loads(load_fixture( + 'pushbullet_devices.json'))) + def test_pushbullet_push_no_file(self, mock, mock__get_data): + """Test pushbullet push to default target.""" + config = {notify.DOMAIN: {'name': 'test', + 'platform': 'pushbullet', + 'api_key': 'MYFAKEKEY'}} + with assert_setup_component(1) as handle_config: + assert setup_component(self.hass, notify.DOMAIN, config) + assert handle_config[notify.DOMAIN] + mock.register_uri( + requests_mock.POST, + 'https://api.pushbullet.com/v2/pushes', + status_code=200, + json={'mock_response': 'Ok'} + ) + data = {'title': 'Test Title', + 'message': 'Test Message', + 'target': ['device/DESKTOP', 'device/My iPhone'], + 'data': {'file': 'not_a_file'}} + assert not self.hass.services.call(notify.DOMAIN, 'test', data) + self.hass.block_till_done() diff --git a/tests/fixtures/pushbullet_devices.json b/tests/fixtures/pushbullet_devices.json new file mode 100755 index 00000000000..04f336c8adf --- /dev/null +++ b/tests/fixtures/pushbullet_devices.json @@ -0,0 +1,43 @@ +{ + "accounts": [], + "blocks": [], + "channels": [], + "chats": [], + "clients": [], + "contacts": [], + "devices": [{ + "active": true, + "iden": "identity1", + "created": 1.514520333770855e+09, + "modified": 1.5151951594363022e+09, + "type": "windows", + "kind": "windows", + "nickname": "DESKTOP", + "manufacturer": "Microsoft", + "model": "Windows 10 Home", + "app_version": 396, + "fingerprint": "{\"cpu\":\"AMD\",\"computer_name\":\"DESKTOP\"}", + "pushable": true, + "icon": "desktop", + "remote_files": "disabled" + }, { + "active": true, + "iden": "identity2", + "created": 1.5144974875448499e+09, + "modified": 1.514574792288634e+09, + "type": "ios", + "kind": "ios", + "nickname": "My iPhone", + "manufacturer": "Apple", + "model": "iPhone", + "app_version": 8646, + "push_token": "production:mytoken", + "pushable": true, + "icon": "phone" + }], + "grants": [], + "pushes": [], + "profiles": [], + "subscriptions": [], + "texts": [] +}