mirror of https://github.com/caronc/apprise
				
				
				
			
		
			
				
	
	
		
			384 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
			
		
		
	
	
			384 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
	
| # -*- coding: utf-8 -*-
 | |
| #
 | |
| # Copyright (C) 2019 Chris Caron <lead2gold@gmail.com>
 | |
| # All rights reserved.
 | |
| #
 | |
| # This code is licensed under the MIT License.
 | |
| #
 | |
| # Permission is hereby granted, free of charge, to any person obtaining a copy
 | |
| # of this software and associated documentation files(the "Software"), to deal
 | |
| # in the Software without restriction, including without limitation the rights
 | |
| # to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
 | |
| # copies of the Software, and to permit persons to whom the Software is
 | |
| # furnished to do so, subject to the following conditions :
 | |
| #
 | |
| # The above copyright notice and this permission notice shall be included in
 | |
| # all copies or substantial portions of the Software.
 | |
| #
 | |
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | |
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | |
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
 | |
| # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | |
| # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | |
| # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | |
| # THE SOFTWARE.
 | |
| 
 | |
| from apprise import plugins
 | |
| from apprise import NotifyType
 | |
| from apprise import Apprise
 | |
| from apprise.plugins import NotifyEmailBase
 | |
| 
 | |
| import smtplib
 | |
| import mock
 | |
| import re
 | |
| 
 | |
| 
 | |
| TEST_URLS = (
 | |
|     ##################################
 | |
|     # NotifyEmail
 | |
|     ##################################
 | |
|     ('mailto://', {
 | |
|         'instance': None,
 | |
|     }),
 | |
|     ('mailtos://', {
 | |
|         'instance': None,
 | |
|     }),
 | |
|     ('mailto://:@/', {
 | |
|         'instance': None
 | |
|     }),
 | |
|     # No Username
 | |
|     ('mailtos://:pass@nuxref.com:567', {
 | |
|         # Can't prepare a To address using this expression
 | |
|         'exception': TypeError,
 | |
|     }),
 | |
| 
 | |
|     # Pre-Configured Email Services
 | |
|     ('mailto://user:pass@gmail.com', {
 | |
|         'instance': plugins.NotifyEmail,
 | |
|     }),
 | |
|     ('mailto://user:pass@hotmail.com', {
 | |
|         'instance': plugins.NotifyEmail,
 | |
|     }),
 | |
|     ('mailto://user:pass@live.com', {
 | |
|         'instance': plugins.NotifyEmail,
 | |
|     }),
 | |
|     ('mailto://user:pass@prontomail.com', {
 | |
|         'instance': plugins.NotifyEmail,
 | |
|     }),
 | |
|     ('mailto://user:pass@yahoo.com', {
 | |
|         'instance': plugins.NotifyEmail,
 | |
|     }),
 | |
|     ('mailto://user:pass@yahoo.ca', {
 | |
|         'instance': plugins.NotifyEmail,
 | |
|     }),
 | |
|     ('mailto://user:pass@fastmail.com', {
 | |
|         'instance': plugins.NotifyEmail,
 | |
|     }),
 | |
| 
 | |
|     # Custom Emails
 | |
|     ('mailtos://user:pass@nuxref.com:567', {
 | |
|         'instance': plugins.NotifyEmail,
 | |
|     }),
 | |
|     ('mailto://user:pass@nuxref.com:567?format=html', {
 | |
|         'instance': plugins.NotifyEmail,
 | |
|     }),
 | |
|     ('mailtos://user:pass@nuxref.com:567?to=l2g@nuxref.com', {
 | |
|         'instance': plugins.NotifyEmail,
 | |
|     }),
 | |
|     (
 | |
|         'mailtos://user:pass@example.com?smtp=smtp.example.com&timeout=5'
 | |
|         '&name=l2g&from=noreply@example.com', {
 | |
|             'instance': plugins.NotifyEmail,
 | |
|         },
 | |
|     ),
 | |
|     ('mailto://user:pass@example.com?timeout=invalid.entry', {
 | |
|         'instance': plugins.NotifyEmail,
 | |
|     }),
 | |
|     ('mailto://user:pass@example.com?timeout=invalid.entry', {
 | |
|         'instance': plugins.NotifyEmail,
 | |
|     }),
 | |
|     (
 | |
|         'mailto://user:pass@example.com:2525?user=l2g@example.com'
 | |
|         '&pass=l2g@apprise!is!Awesome', {
 | |
|             'instance': plugins.NotifyEmail,
 | |
|         },
 | |
|     ),
 | |
|     (
 | |
|         'mailto://user:pass@example.com:2525?user=l2g@example.com'
 | |
|         '&pass=l2g@apprise!is!Awesome&format=text', {
 | |
|             'instance': plugins.NotifyEmail,
 | |
|         },
 | |
|     ),
 | |
|     # No Password
 | |
|     ('mailtos://user:@nuxref.com', {
 | |
|         'instance': plugins.NotifyEmail,
 | |
|     }),
 | |
|     # Invalid From Address
 | |
|     ('mailtos://user:pass@nuxref.com?from=@', {
 | |
|         'exception': TypeError,
 | |
|     }),
 | |
|     # Invalid From Address
 | |
|     ('mailtos://nuxref.com?user=&pass=.', {
 | |
|         'exception': TypeError,
 | |
|     }),
 | |
|     # Invalid To Address
 | |
|     ('mailtos://user:pass@nuxref.com?to=@', {
 | |
|         'exception': TypeError,
 | |
|     }),
 | |
|     # Valid URL, but can't structure a proper email
 | |
|     ('mailtos://nuxref.com?user=%20!&pass=.', {
 | |
|         'exception': TypeError,
 | |
|     }),
 | |
|     # Invalid From (and To) Address
 | |
|     ('mailtos://nuxref.com?to=test', {
 | |
|         'exception': TypeError,
 | |
|     }),
 | |
|     # Invalid Secure Mode
 | |
|     ('mailtos://user:pass@example.com?mode=notamode', {
 | |
|         'exception': TypeError,
 | |
|     }),
 | |
|     # STARTTLS flag checking
 | |
|     ('mailtos://user:pass@gmail.com?mode=starttls', {
 | |
|         'instance': plugins.NotifyEmail,
 | |
|     }),
 | |
|     # SSL flag checking
 | |
|     ('mailtos://user:pass@gmail.com?mode=ssl', {
 | |
|         'instance': plugins.NotifyEmail,
 | |
|     }),
 | |
|     # Can make a To address using what we have (l2g@nuxref.com)
 | |
|     ('mailtos://nuxref.com?user=l2g&pass=.', {
 | |
|         'instance': plugins.NotifyEmail,
 | |
|     }),
 | |
|     ('mailto://user:pass@localhost:2525', {
 | |
|         'instance': plugins.NotifyEmail,
 | |
|         # Throws a series of connection and transfer exceptions when this flag
 | |
|         # is set and tests that we gracfully handle them
 | |
|         'test_smtplib_exceptions': True,
 | |
|     }),
 | |
| )
 | |
| 
 | |
| 
 | |
| @mock.patch('smtplib.SMTP')
 | |
| @mock.patch('smtplib.SMTP_SSL')
 | |
| def test_email_plugin(mock_smtp, mock_smtpssl):
 | |
|     """
 | |
|     API: NotifyEmail Plugin()
 | |
| 
 | |
|     """
 | |
| 
 | |
|     # iterate over our dictionary and test it out
 | |
|     for (url, meta) in TEST_URLS:
 | |
| 
 | |
|         # Our expected instance
 | |
|         instance = meta.get('instance', None)
 | |
| 
 | |
|         # Our expected exception
 | |
|         exception = meta.get('exception', None)
 | |
| 
 | |
|         # Our expected server objects
 | |
|         self = meta.get('self', None)
 | |
| 
 | |
|         # Our expected Query response (True, False, or exception type)
 | |
|         response = meta.get('response', True)
 | |
| 
 | |
|         test_smtplib_exceptions = meta.get(
 | |
|             'test_smtplib_exceptions', False)
 | |
| 
 | |
|         # Our mock of our socket action
 | |
|         mock_socket = mock.Mock()
 | |
|         mock_socket.starttls.return_value = True
 | |
|         mock_socket.login.return_value = True
 | |
| 
 | |
|         # Create a mock SMTP Object
 | |
|         mock_smtp.return_value = mock_socket
 | |
|         mock_smtpssl.return_value = mock_socket
 | |
| 
 | |
|         if test_smtplib_exceptions:
 | |
|             # Handle exception testing; first we turn the boolean flag ito
 | |
|             # a list of exceptions
 | |
|             test_smtplib_exceptions = (
 | |
|                 smtplib.SMTPHeloError(
 | |
|                     0, 'smtplib.SMTPHeloError() not handled'),
 | |
|                 smtplib.SMTPException(
 | |
|                     0, 'smtplib.SMTPException() not handled'),
 | |
|                 RuntimeError(
 | |
|                     0, 'smtplib.HTTPError() not handled'),
 | |
|                 smtplib.SMTPRecipientsRefused(
 | |
|                     'smtplib.SMTPRecipientsRefused() not handled'),
 | |
|                 smtplib.SMTPSenderRefused(
 | |
|                     0, 'smtplib.SMTPSenderRefused() not handled',
 | |
|                     'addr@example.com'),
 | |
|                 smtplib.SMTPDataError(
 | |
|                     0, 'smtplib.SMTPDataError() not handled'),
 | |
|                 smtplib.SMTPServerDisconnected(
 | |
|                     'smtplib.SMTPServerDisconnected() not handled'),
 | |
|             )
 | |
| 
 | |
|         try:
 | |
|             obj = Apprise.instantiate(url, suppress_exceptions=False)
 | |
| 
 | |
|             assert(exception is None)
 | |
| 
 | |
|             if obj is None:
 | |
|                 # We're done
 | |
|                 continue
 | |
| 
 | |
|             if instance is None:
 | |
|                 # Expected None but didn't get it
 | |
|                 print('%s instantiated %s' % (url, str(obj)))
 | |
|                 assert(False)
 | |
| 
 | |
|             assert(isinstance(obj, instance))
 | |
| 
 | |
|             if self:
 | |
|                 # Iterate over our expected entries inside of our object
 | |
|                 for key, val in self.items():
 | |
|                     # Test that our object has the desired key
 | |
|                     assert(hasattr(key, obj))
 | |
|                     assert(getattr(key, obj) == val)
 | |
| 
 | |
|             try:
 | |
|                 if test_smtplib_exceptions is False:
 | |
|                     # check that we're as expected
 | |
|                     assert obj.notify(
 | |
|                         title='test', body='body',
 | |
|                         notify_type=NotifyType.INFO) == response
 | |
| 
 | |
|                 else:
 | |
|                     for exception in test_smtplib_exceptions:
 | |
|                         mock_socket.sendmail.side_effect = exception
 | |
|                         try:
 | |
|                             assert obj.notify(
 | |
|                                 title='test', body='body',
 | |
|                                 notify_type=NotifyType.INFO) is False
 | |
| 
 | |
|                         except AssertionError:
 | |
|                             # Don't mess with these entries
 | |
|                             raise
 | |
| 
 | |
|                         except Exception as e:
 | |
|                             # We can't handle this exception type
 | |
|                             print('%s / %s' % (url, str(e)))
 | |
|                             assert False
 | |
| 
 | |
|             except AssertionError:
 | |
|                 # Don't mess with these entries
 | |
|                 raise
 | |
| 
 | |
|             except Exception as e:
 | |
|                 # Check that we were expecting this exception to happen
 | |
|                 assert isinstance(e, response)
 | |
| 
 | |
|         except AssertionError:
 | |
|             # Don't mess with these entries
 | |
|             print('%s AssertionError' % url)
 | |
|             raise
 | |
| 
 | |
|         except Exception as e:
 | |
|             # Handle our exception
 | |
|             print('%s / %s' % (url, str(e)))
 | |
|             assert(exception is not None)
 | |
|             assert(isinstance(e, exception))
 | |
| 
 | |
| 
 | |
| @mock.patch('smtplib.SMTP')
 | |
| @mock.patch('smtplib.SMTP_SSL')
 | |
| def test_webbase_lookup(mock_smtp, mock_smtpssl):
 | |
|     """
 | |
|     API: Web Based Lookup Tests
 | |
| 
 | |
|     """
 | |
| 
 | |
|     # Insert a test email at the head of our table
 | |
|     NotifyEmailBase.WEBBASE_LOOKUP_TABLE = (
 | |
|         (
 | |
|             # Testing URL
 | |
|             'Testing Lookup',
 | |
|             re.compile(r'^(?P<id>[^@]+)@(?P<domain>l2g\.com)$', re.I),
 | |
|             {
 | |
|                 'port': 123,
 | |
|                 'smtp_host': 'smtp.l2g.com',
 | |
|                 'secure': True,
 | |
|                 'login_type': (NotifyEmailBase.WebBaseLogin.USERID, )
 | |
|             },
 | |
|         ),
 | |
|     ) + NotifyEmailBase.WEBBASE_LOOKUP_TABLE
 | |
| 
 | |
|     obj = Apprise.instantiate(
 | |
|         'mailto://user:pass@l2g.com', suppress_exceptions=True)
 | |
| 
 | |
|     assert(isinstance(obj, plugins.NotifyEmail))
 | |
|     assert obj.to_addr == 'user@l2g.com'
 | |
|     assert obj.from_addr == 'user@l2g.com'
 | |
|     assert obj.password == 'pass'
 | |
|     assert obj.user == 'user'
 | |
|     assert obj.secure is True
 | |
|     assert obj.port == 123
 | |
|     assert obj.smtp_host == 'smtp.l2g.com'
 | |
| 
 | |
| 
 | |
| @mock.patch('smtplib.SMTP')
 | |
| def test_smtplib_init_fail(mock_smtplib):
 | |
|     """
 | |
|     API: Test exception handling when calling smtplib.SMTP()
 | |
| 
 | |
|     """
 | |
| 
 | |
|     obj = Apprise.instantiate(
 | |
|         'mailto://user:pass@gmail.com', suppress_exceptions=False)
 | |
|     assert(isinstance(obj, plugins.NotifyEmail))
 | |
| 
 | |
|     # Support Exception handling of smtplib.SMTP
 | |
|     mock_smtplib.side_effect = TypeError('Test')
 | |
| 
 | |
|     try:
 | |
|         obj.notify(
 | |
|             title='test', body='body',
 | |
|             notify_type=NotifyType.INFO)
 | |
| 
 | |
|         # We should have thrown an exception
 | |
|         assert False
 | |
| 
 | |
|     except TypeError:
 | |
|         # Exception thrown as expected
 | |
|         assert True
 | |
| 
 | |
|     except Exception:
 | |
|         # Un-Expected
 | |
|         assert False
 | |
| 
 | |
|     # A handled and expected exception
 | |
|     mock_smtplib.side_effect = smtplib.SMTPException('Test')
 | |
|     assert obj.notify(title='test', body='body',
 | |
|                       notify_type=NotifyType.INFO) is False
 | |
| 
 | |
| 
 | |
| @mock.patch('smtplib.SMTP')
 | |
| def test_smtplib_send_okay(mock_smtplib):
 | |
|     """
 | |
|     API: Test a successfully sent email
 | |
| 
 | |
|     """
 | |
| 
 | |
|     # Defaults to HTML
 | |
|     obj = Apprise.instantiate(
 | |
|         'mailto://user:pass@gmail.com', suppress_exceptions=False)
 | |
|     assert(isinstance(obj, plugins.NotifyEmail))
 | |
| 
 | |
|     # Support an email simulation where we can correctly quit
 | |
|     mock_smtplib.starttls.return_value = True
 | |
|     mock_smtplib.login.return_value = True
 | |
|     mock_smtplib.sendmail.return_value = True
 | |
|     mock_smtplib.quit.return_value = True
 | |
| 
 | |
|     assert(obj.notify(
 | |
|         title='test', body='body', notify_type=NotifyType.INFO) is True)
 | |
| 
 | |
|     # Set Text
 | |
|     obj = Apprise.instantiate(
 | |
|         'mailto://user:pass@gmail.com?format=text', suppress_exceptions=False)
 | |
|     assert(isinstance(obj, plugins.NotifyEmail))
 | |
| 
 | |
|     assert(obj.notify(
 | |
|         title='test', body='body', notify_type=NotifyType.INFO) is True)
 |