"""The tests for the APNS component.""" import io import unittest from unittest.mock import Mock, patch, mock_open from apns2.errors import Unregistered import yaml import homeassistant.components.notify as notify from homeassistant.setup import setup_component from homeassistant.components.notify import apns from homeassistant.core import State from tests.common import assert_setup_component, get_test_home_assistant CONFIG = { notify.DOMAIN: { 'platform': 'apns', 'name': 'test_app', 'topic': 'testapp.appname', 'cert_file': 'test_app.pem' } } @patch('homeassistant.components.notify.apns.open', mock_open(), create=True) class TestApns(unittest.TestCase): """Test the APNS component.""" def setUp(self): # pylint: disable=invalid-name """Set up things to be run when tests are started.""" self.hass = get_test_home_assistant() def tearDown(self): # pylint: disable=invalid-name """Stop everything that was started.""" self.hass.stop() @patch('os.path.isfile', Mock(return_value=True)) @patch('os.access', Mock(return_value=True)) def _setup_notify(self): assert isinstance(apns.load_yaml_config_file, Mock), \ 'Found unmocked load_yaml' with assert_setup_component(1) as handle_config: assert setup_component(self.hass, notify.DOMAIN, CONFIG) assert handle_config[notify.DOMAIN] @patch('os.path.isfile', return_value=True) @patch('os.access', return_value=True) def test_apns_setup_full(self, mock_access, mock_isfile): """Test setup with all data.""" config = { 'notify': { 'platform': 'apns', 'name': 'test_app', 'sandbox': 'True', 'topic': 'testapp.appname', 'cert_file': 'test_app.pem' } } with assert_setup_component(1) as handle_config: assert setup_component(self.hass, notify.DOMAIN, config) assert handle_config[notify.DOMAIN] def test_apns_setup_missing_name(self): """Test setup with missing name.""" config = { 'notify': { 'platform': 'apns', 'topic': 'testapp.appname', 'cert_file': 'test_app.pem', } } with assert_setup_component(0) as handle_config: assert setup_component(self.hass, notify.DOMAIN, config) assert not handle_config[notify.DOMAIN] def test_apns_setup_missing_certificate(self): """Test setup with missing certificate.""" config = { 'notify': { 'platform': 'apns', 'name': 'test_app', 'topic': 'testapp.appname', } } with assert_setup_component(0) as handle_config: assert setup_component(self.hass, notify.DOMAIN, config) assert not handle_config[notify.DOMAIN] def test_apns_setup_missing_topic(self): """Test setup with missing topic.""" config = { 'notify': { 'platform': 'apns', 'name': 'test_app', 'cert_file': 'test_app.pem', } } with assert_setup_component(0) as handle_config: assert setup_component(self.hass, notify.DOMAIN, config) assert not handle_config[notify.DOMAIN] @patch('homeassistant.components.notify.apns._write_device') def test_register_new_device(self, mock_write): """Test registering a new device with a name.""" yaml_file = {5678: {'name': 'test device 2'}} written_devices = [] def fake_write(_out, device): """Fake write_device.""" written_devices.append(device) mock_write.side_effect = fake_write with patch( 'homeassistant.components.notify.apns.load_yaml_config_file', Mock(return_value=yaml_file)): self._setup_notify() assert self.hass.services.call(notify.DOMAIN, 'apns_test_app', { 'push_id': '1234', 'name': 'test device' }, blocking=True) assert len(written_devices) == 1 assert written_devices[0].name == 'test device' @patch('homeassistant.components.notify.apns._write_device') def test_register_device_without_name(self, mock_write): """Test registering a without a name.""" yaml_file = { 1234: { 'name': 'test device 1', 'tracking_device_id': 'tracking123', }, 5678: { 'name': 'test device 2', 'tracking_device_id': 'tracking456', }, } written_devices = [] def fake_write(_out, device): """Fake write_device.""" written_devices.append(device) mock_write.side_effect = fake_write with patch( 'homeassistant.components.notify.apns.load_yaml_config_file', Mock(return_value=yaml_file)): self._setup_notify() assert self.hass.services.call(notify.DOMAIN, 'apns_test_app', { 'push_id': '1234' }, blocking=True) devices = {dev.push_id: dev for dev in written_devices} test_device = devices.get('1234') assert test_device is not None assert test_device.name is None @patch('homeassistant.components.notify.apns._write_device') def test_update_existing_device(self, mock_write): """Test updating an existing device.""" yaml_file = { 1234: { 'name': 'test device 1', }, 5678: { 'name': 'test device 2', }, } written_devices = [] def fake_write(_out, device): """Fake write_device.""" written_devices.append(device) mock_write.side_effect = fake_write with patch( 'homeassistant.components.notify.apns.load_yaml_config_file', Mock(return_value=yaml_file)): self._setup_notify() assert self.hass.services.call(notify.DOMAIN, 'apns_test_app', { 'push_id': '1234', 'name': 'updated device 1' }, blocking=True) devices = {dev.push_id: dev for dev in written_devices} test_device_1 = devices.get('1234') test_device_2 = devices.get('5678') assert test_device_1 is not None assert test_device_2 is not None assert 'updated device 1' == test_device_1.name @patch('homeassistant.components.notify.apns._write_device') def test_update_existing_device_with_tracking_id(self, mock_write): """Test updating an existing device that has a tracking id.""" yaml_file = { 1234: { 'name': 'test device 1', 'tracking_device_id': 'tracking123', }, 5678: { 'name': 'test device 2', 'tracking_device_id': 'tracking456', }, } written_devices = [] def fake_write(_out, device): """Fake write_device.""" written_devices.append(device) mock_write.side_effect = fake_write with patch( 'homeassistant.components.notify.apns.load_yaml_config_file', Mock(return_value=yaml_file)): self._setup_notify() assert self.hass.services.call(notify.DOMAIN, 'apns_test_app', { 'push_id': '1234', 'name': 'updated device 1' }, blocking=True) devices = {dev.push_id: dev for dev in written_devices} test_device_1 = devices.get('1234') test_device_2 = devices.get('5678') assert test_device_1 is not None assert test_device_2 is not None assert 'tracking123' == \ test_device_1.tracking_device_id assert 'tracking456' == \ test_device_2.tracking_device_id @patch('apns2.client.APNsClient') def test_send(self, mock_client): """Test updating an existing device.""" send = mock_client.return_value.send_notification yaml_file = {1234: {'name': 'test device 1'}} with patch( 'homeassistant.components.notify.apns.load_yaml_config_file', Mock(return_value=yaml_file)): self._setup_notify() assert self.hass.services.call( 'notify', 'test_app', {'message': 'Hello', 'data': { 'badge': 1, 'sound': 'test.mp3', 'category': 'testing'}}, blocking=True) assert send.called assert 1 == len(send.mock_calls) target = send.mock_calls[0][1][0] payload = send.mock_calls[0][1][1] assert '1234' == target assert 'Hello' == payload.alert assert 1 == payload.badge assert 'test.mp3' == payload.sound assert 'testing' == payload.category @patch('apns2.client.APNsClient') def test_send_when_disabled(self, mock_client): """Test updating an existing device.""" send = mock_client.return_value.send_notification yaml_file = {1234: { 'name': 'test device 1', 'disabled': True, }} with patch( 'homeassistant.components.notify.apns.load_yaml_config_file', Mock(return_value=yaml_file)): self._setup_notify() assert self.hass.services.call( 'notify', 'test_app', {'message': 'Hello', 'data': { 'badge': 1, 'sound': 'test.mp3', 'category': 'testing'}}, blocking=True) assert not send.called @patch('apns2.client.APNsClient') def test_send_with_state(self, mock_client): """Test updating an existing device.""" send = mock_client.return_value.send_notification yaml_file = { 1234: { 'name': 'test device 1', 'tracking_device_id': 'tracking123', }, 5678: { 'name': 'test device 2', 'tracking_device_id': 'tracking456', }, } with patch( 'homeassistant.components.notify.apns.load_yaml_config_file', Mock(return_value=yaml_file)), \ patch('os.path.isfile', Mock(return_value=True)): notify_service = apns.ApnsNotificationService( self.hass, 'test_app', 'testapp.appname', False, 'test_app.pem' ) notify_service.device_state_changed_listener( 'device_tracker.tracking456', State('device_tracker.tracking456', None), State('device_tracker.tracking456', 'home')) notify_service.send_message(message='Hello', target='home') assert send.called assert 1 == len(send.mock_calls) target = send.mock_calls[0][1][0] payload = send.mock_calls[0][1][1] assert '5678' == target assert 'Hello' == payload.alert @patch('apns2.client.APNsClient') @patch('homeassistant.components.notify.apns._write_device') def test_disable_when_unregistered(self, mock_write, mock_client): """Test disabling a device when it is unregistered.""" send = mock_client.return_value.send_notification send.side_effect = Unregistered() yaml_file = { 1234: { 'name': 'test device 1', 'tracking_device_id': 'tracking123', }, 5678: { 'name': 'test device 2', 'tracking_device_id': 'tracking456', }, } written_devices = [] def fake_write(_out, device): """Fake write_device.""" written_devices.append(device) mock_write.side_effect = fake_write with patch( 'homeassistant.components.notify.apns.load_yaml_config_file', Mock(return_value=yaml_file)): self._setup_notify() assert self.hass.services.call('notify', 'test_app', {'message': 'Hello'}, blocking=True) devices = {dev.push_id: dev for dev in written_devices} test_device_1 = devices.get('1234') assert test_device_1 is not None assert test_device_1.disabled is True def test_write_device(): """Test writing device.""" out = io.StringIO() device = apns.ApnsDevice('123', 'name', 'track_id', True) apns._write_device(out, device) data = yaml.load(out.getvalue()) assert data == { 123: { 'name': 'name', 'tracking_device_id': 'track_id', 'disabled': True }, }