From ce1e8f8ace3c2961542049cc529b77a7bfde173f Mon Sep 17 00:00:00 2001 From: Florian Klien Date: Thu, 4 Oct 2018 23:34:04 +0200 Subject: [PATCH] YesssSMS handling more errors, upgrade to version 0.2.3 (#17052) * YesssSMS handling more errors, upgrade to version 0.2.1 - handling missing internet connection nicely - disabling login with non-working credentials (website locked account for 1 hour) - upgrade to new upstream version of YesssSMS * notify.yessssms tests * test requirements * flake8 fix * fixing tests, new upstream version 0.2.3 fixing tests based on requested changes, coverage * removing unmotivated print * passing exception to ConnectionError and SMSSendingError logger --- .coveragerc | 1 - homeassistant/components/notify/yessssms.py | 39 +++- requirements_all.txt | 2 +- requirements_test_all.txt | 3 + script/gen_requirements_all.py | 3 +- tests/components/notify/test_yessssms.py | 208 ++++++++++++++++++++ 6 files changed, 246 insertions(+), 10 deletions(-) create mode 100644 tests/components/notify/test_yessssms.py diff --git a/.coveragerc b/.coveragerc index 378dba5ef2a..d5296455981 100644 --- a/.coveragerc +++ b/.coveragerc @@ -630,7 +630,6 @@ omit = homeassistant/components/notify/telstra.py homeassistant/components/notify/twitter.py homeassistant/components/notify/xmpp.py - homeassistant/components/notify/yessssms.py homeassistant/components/nuimo_controller.py homeassistant/components/prometheus.py homeassistant/components/rainbird.py diff --git a/homeassistant/components/notify/yessssms.py b/homeassistant/components/notify/yessssms.py index 37a6a90a62e..e16e384ca25 100644 --- a/homeassistant/components/notify/yessssms.py +++ b/homeassistant/components/notify/yessssms.py @@ -13,7 +13,8 @@ from homeassistant.components.notify import ( from homeassistant.const import CONF_USERNAME, CONF_PASSWORD, CONF_RECIPIENT import homeassistant.helpers.config_validation as cv -REQUIREMENTS = ['YesssSMS==0.1.1b3'] + +REQUIREMENTS = ['YesssSMS==0.2.3'] _LOGGER = logging.getLogger(__name__) @@ -38,14 +39,38 @@ class YesssSMSNotificationService(BaseNotificationService): from YesssSMS import YesssSMS self.yesss = YesssSMS(username, password) self._recipient = recipient + _LOGGER.debug( + "initialized; library version: %s", self.yesss.version()) def send_message(self, message="", **kwargs): """Send a SMS message via Yesss.at's website.""" + if self.yesss.account_is_suspended(): + # only retry to login after HASS was restarted with (hopefully) + # new login data. + _LOGGER.error( + "Account is suspended, cannot send SMS. " + "Check your login data and restart Home Assistant") + return try: self.yesss.send(self._recipient, message) - except ValueError as ex: - if str(ex).startswith("YesssSMS:"): - _LOGGER.error(str(ex)) - except RuntimeError as ex: - if str(ex).startswith("YesssSMS:"): - _LOGGER.error(str(ex)) + except self.yesss.NoRecipientError as ex: + _LOGGER.error( + "You need to provide a recipient for SMS notification: %s", + ex) + except self.yesss.EmptyMessageError as ex: + _LOGGER.error( + "Cannot send empty SMS message: %s", ex) + except self.yesss.SMSSendingError as ex: + _LOGGER.error(str(ex), exc_info=ex) + except ConnectionError as ex: + _LOGGER.error( + "YesssSMS: unable to connect to yesss.at server.", + exc_info=ex) + except self.yesss.AccountSuspendedError as ex: + _LOGGER.error( + "Wrong login credentials!! Verify correct credentials and " + "restart Home Assistant: %s", ex) + except self.yesss.LoginError as ex: + _LOGGER.error("Wrong login credentials: %s", ex) + else: + _LOGGER.info("SMS sent") diff --git a/requirements_all.txt b/requirements_all.txt index 82a0571b001..fca9e771b52 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -76,7 +76,7 @@ TwitterAPI==2.5.4 WazeRouteCalculator==0.6 # homeassistant.components.notify.yessssms -YesssSMS==0.1.1b3 +YesssSMS==0.2.3 # homeassistant.components.abode abodepy==0.13.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 44d360f23ad..10b8307d337 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -24,6 +24,9 @@ HAP-python==2.2.2 # homeassistant.components.sensor.rmvtransport PyRMVtransport==0.1 +# homeassistant.components.notify.yessssms +YesssSMS==0.2.3 + # homeassistant.components.device_tracker.automatic aioautomatic==0.6.5 diff --git a/script/gen_requirements_all.py b/script/gen_requirements_all.py index c6e36c5c83e..9c0323bf5ca 100755 --- a/script/gen_requirements_all.py +++ b/script/gen_requirements_all.py @@ -104,7 +104,8 @@ TEST_REQUIREMENTS = ( 'yahoo-finance', 'pythonwhois', 'wakeonlan', - 'vultr' + 'vultr', + 'YesssSMS', ) IGNORE_PACKAGES = ( diff --git a/tests/components/notify/test_yessssms.py b/tests/components/notify/test_yessssms.py new file mode 100644 index 00000000000..42dd7be1aca --- /dev/null +++ b/tests/components/notify/test_yessssms.py @@ -0,0 +1,208 @@ +"""The tests for the notify yessssms platform.""" +import unittest +import requests_mock +from homeassistant.components.notify import yessssms + + +class TestNotifyYesssSMS(unittest.TestCase): + """Test the yessssms notify.""" + + def setUp(self): # pylint: disable=invalid-name + """Set up things to be run when tests are started.""" + login = "06641234567" + passwd = "testpasswd" + recipient = "06501234567" + self.yessssms = yessssms.YesssSMSNotificationService( + login, passwd, recipient) + + @requests_mock.Mocker() + def test_login_error(self, mock): + """Test login that fails.""" + mock.register_uri( + requests_mock.POST, + # pylint: disable=protected-access + self.yessssms.yesss._login_url, + status_code=200, + text="BlaBlaBlaLogin nicht erfolgreichBlaBla" + ) + + message = "Testing YesssSMS platform :)" + + with self.assertLogs("homeassistant.components.notify", + level='ERROR'): + self.yessssms.send_message(message) + self.assertTrue(mock.called) + self.assertEqual(mock.call_count, 1) + + def test_empty_message_error(self): + """Test for an empty SMS message error.""" + message = "" + with self.assertLogs("homeassistant.components.notify", + level='ERROR'): + self.yessssms.send_message(message) + + @requests_mock.Mocker() + def test_error_account_suspended(self, mock): + """Test login that fails after multiple attempts.""" + mock.register_uri( + 'POST', + # pylint: disable=protected-access + self.yessssms.yesss._login_url, + status_code=200, + text="BlaBlaBlaLogin nicht erfolgreichBlaBla" + ) + + message = "Testing YesssSMS platform :)" + + with self.assertLogs("homeassistant.components.notify", + level='ERROR'): + self.yessssms.send_message(message) + self.assertTrue(mock.called) + self.assertEqual(mock.call_count, 1) + + mock.register_uri( + 'POST', + # pylint: disable=protected-access + self.yessssms.yesss._login_url, + status_code=200, + text="Wegen 3 ungültigen Login-Versuchen ist Ihr Account für " + "eine Stunde gesperrt." + ) + + message = "Testing YesssSMS platform :)" + + with self.assertLogs("homeassistant.components.notify", + level='ERROR'): + self.yessssms.send_message(message) + self.assertTrue(mock.called) + self.assertEqual(mock.call_count, 2) + + def test_error_account_suspended_2(self): + """Test login that fails after multiple attempts.""" + message = "Testing YesssSMS platform :)" + # pylint: disable=protected-access + self.yessssms.yesss._suspended = True + + with self.assertLogs("homeassistant.components.notify", + level='ERROR') as context: + self.yessssms.send_message(message) + self.assertIn("Account is suspended, cannot send SMS.", + context.output[0]) + + @requests_mock.Mocker() + def test_send_message(self, mock): + """Test send message.""" + message = "Testing YesssSMS platform :)" + mock.register_uri( + 'POST', + # pylint: disable=protected-access + self.yessssms.yesss._login_url, + status_code=302, + # pylint: disable=protected-access + headers={'location': self.yessssms.yesss._kontomanager} + ) + # pylint: disable=protected-access + login = self.yessssms.yesss._logindata['login_rufnummer'] + mock.register_uri( + 'GET', + # pylint: disable=protected-access + self.yessssms.yesss._kontomanager, + status_code=200, + text="test..." + login + "" + ) + mock.register_uri( + 'POST', + # pylint: disable=protected-access + self.yessssms.yesss._websms_url, + status_code=200, + text="

Ihre SMS wurde erfolgreich verschickt!

" + ) + mock.register_uri( + 'GET', + # pylint: disable=protected-access + self.yessssms.yesss._logout_url, + status_code=200, + ) + + with self.assertLogs("homeassistant.components.notify", + level='INFO') as context: + self.yessssms.send_message(message) + self.assertIn("SMS sent", context.output[0]) + self.assertTrue(mock.called) + self.assertEqual(mock.call_count, 4) + self.assertIn(mock.last_request.scheme + "://" + + mock.last_request.hostname + + mock.last_request.path + "?" + + mock.last_request.query, + # pylint: disable=protected-access + self.yessssms.yesss._logout_url) + + def test_no_recipient_error(self): + """Test for missing/empty recipient.""" + message = "Testing YesssSMS platform :)" + # pylint: disable=protected-access + self.yessssms._recipient = "" + + with self.assertLogs("homeassistant.components.notify", + level='ERROR') as context: + self.yessssms.send_message(message) + + self.assertIn("You need to provide a recipient for SMS notification", + context.output[0]) + + @requests_mock.Mocker() + def test_sms_sending_error(self, mock): + """Test sms sending error.""" + mock.register_uri( + 'POST', + # pylint: disable=protected-access + self.yessssms.yesss._login_url, + status_code=302, + # pylint: disable=protected-access + headers={'location': self.yessssms.yesss._kontomanager} + ) + # pylint: disable=protected-access + login = self.yessssms.yesss._logindata['login_rufnummer'] + mock.register_uri( + 'GET', + # pylint: disable=protected-access + self.yessssms.yesss._kontomanager, + status_code=200, + text="test..." + login + "" + ) + mock.register_uri( + 'POST', + # pylint: disable=protected-access + self.yessssms.yesss._websms_url, + status_code=500 + ) + + message = "Testing YesssSMS platform :)" + + with self.assertLogs("homeassistant.components.notify", + level='ERROR') as context: + self.yessssms.send_message(message) + + self.assertTrue(mock.called) + self.assertEqual(mock.call_count, 3) + self.assertIn("YesssSMS: error sending SMS", context.output[0]) + + @requests_mock.Mocker() + def test_connection_error(self, mock): + """Test connection error.""" + mock.register_uri( + 'POST', + # pylint: disable=protected-access + self.yessssms.yesss._login_url, + exc=ConnectionError + ) + + message = "Testing YesssSMS platform :)" + + with self.assertLogs("homeassistant.components.notify", + level='ERROR') as context: + self.yessssms.send_message(message) + + self.assertTrue(mock.called) + self.assertEqual(mock.call_count, 1) + self.assertIn("unable to connect", context.output[0])