more tests 70% coverage now!

This commit is contained in:
Chris Caron 2017-12-13 21:35:59 -05:00
parent 5a7a8a624a
commit f8c3d35f8c
11 changed files with 587 additions and 103 deletions

View File

@ -268,20 +268,11 @@ class NotifyBoxcar(NotifyBase):
access = results['host']
# Now fetch the remaining tokens
try:
secret = NotifyBase.split_path(results['fullpath'])[0]
secret = NotifyBase.split_path(results['fullpath'])[0]
except (AttributeError, IndexError):
# Force a bad value that will get caught in parsing later
secret = None
try:
recipients = ','.join(
NotifyBase.split_path(results['fullpath'])[1:])
except (AttributeError, IndexError):
# Default to not having any recipients
recipients = None
# Our recipients
recipients = ','.join(
NotifyBase.split_path(results['fullpath'])[1:])
if not (access and secret):
# If we did not recive an access and/or secret code

View File

@ -88,7 +88,7 @@ class NotifyFaast(NotifyBase):
HTTP_ERROR_MAP[r.status_code],
r.status_code))
except IndexError:
except KeyError:
self.logger.warning(
'Failed to send Faast notification '
'(error=%s).' % (
@ -96,10 +96,11 @@ class NotifyFaast(NotifyBase):
# Return; we're done
return False
else:
self.logger.info('Sent Faast notification.')
except requests.ConnectionError as e:
except requests.RequestException as e:
self.logger.warning(
'A Connection error occured sending Faast notification.',
)

View File

@ -18,10 +18,8 @@
import re
from .gntp.notifier import GrowlNotifier
from .gntp.errors import NetworkError as GrowlNetworkError
from .gntp.errors import AuthError as GrowlAuthenticationError
from .gntp import notifier
from .gntp import errors
from ..NotifyBase import NotifyBase
from ...common import NotifyImageSize
@ -31,7 +29,7 @@ GROWL_IMAGE_XY = NotifyImageSize.XY_72
# Priorities
class GrowlPriority(object):
VERY_LOW = -2
LOW = -2
MODERATE = -1
NORMAL = 0
HIGH = 1
@ -39,7 +37,7 @@ class GrowlPriority(object):
GROWL_PRIORITIES = (
GrowlPriority.VERY_LOW,
GrowlPriority.LOW,
GrowlPriority.MODERATE,
GrowlPriority.NORMAL,
GrowlPriority.HIGH,
@ -61,7 +59,7 @@ class NotifyGrowl(NotifyBase):
# Default Growl Port
default_port = 23053
def __init__(self, priority=GrowlPriority.NORMAL, version=2, **kwargs):
def __init__(self, priority=None, version=2, **kwargs):
"""
Initialize Growl Object
"""
@ -69,9 +67,6 @@ class NotifyGrowl(NotifyBase):
title_maxlen=250, body_maxlen=32768,
image_size=GROWL_IMAGE_XY, **kwargs)
# A Global flag that tracks registration
self.is_registered = False
if not self.port:
self.port = self.default_port
@ -100,27 +95,37 @@ class NotifyGrowl(NotifyBase):
payload['password'] = self.password
self.logger.debug('Growl Registration Payload: %s' % str(payload))
self.growl = GrowlNotifier(**payload)
self.growl = notifier.GrowlNotifier(**payload)
try:
self.growl.register()
# Toggle our flag
self.is_registered = True
self.logger.debug(
'Growl server registration completed successfully.'
)
except GrowlNetworkError:
except errors.NetworkError:
self.logger.warning(
'A network error occured sending Growl '
'notification to %s.' % self.host)
return
raise TypeError(
'A network error occured sending Growl '
'notification to %s.' % self.host)
except GrowlAuthenticationError:
except errors.AuthError:
self.logger.warning(
'An authentication error occured sending Growl '
'notification to %s.' % self.host)
return
raise TypeError(
'An authentication error occured sending Growl '
'notification to %s.' % self.host)
except errors.UnsupportedError:
self.logger.warning(
'An unsupported error occured sending Growl '
'notification to %s.' % self.host)
raise TypeError(
'An unsupported error occured sending Growl '
'notification to %s.' % self.host)
return
@ -129,10 +134,6 @@ class NotifyGrowl(NotifyBase):
Perform Growl Notification
"""
if not self.is_registered:
# We can't do anything
return None
# Limit results to just the first 2 line otherwise there is just to
# much content to display
body = re.split('[\r\n]+', body)
@ -158,7 +159,9 @@ class NotifyGrowl(NotifyBase):
}
self.logger.debug('Growl Payload: %s' % str(payload))
# Update icon of payload to be raw data
# Update icon of payload to be raw data; this is intentionally done
# here after we spit the debug message above (so we don't try to
# print the binary contents of an image
payload['icon'] = icon
try:
@ -174,7 +177,7 @@ class NotifyGrowl(NotifyBase):
'Growl notification sent successfully.'
)
except GrowlNetworkError as e:
except errors.BaseError as e:
# Since Growl servers listen for UDP broadcasts, it's possible
# that you will never get to this part of the code since there is
# no acknowledgement as to whether it accepted what was sent to it
@ -221,13 +224,35 @@ class NotifyGrowl(NotifyBase):
)
pass
if 'priority' in results['qsd'] and len(results['qsd']['priority']):
_map = {
'l': GrowlPriority.LOW,
'-2': GrowlPriority.LOW,
'm': GrowlPriority.MODERATE,
'-1': GrowlPriority.MODERATE,
'n': GrowlPriority.NORMAL,
'0': GrowlPriority.NORMAL,
'h': GrowlPriority.HIGH,
'1': GrowlPriority.HIGH,
'e': GrowlPriority.EMERGENCY,
'2': GrowlPriority.EMERGENCY,
}
try:
results['priority'] = \
_map[results['qsd']['priority'][0].lower()]
except KeyError:
# No priority was set
pass
# Because of the URL formatting, the password is actually where the
# username field is. For this reason, we just preform this small hack
# to make it (the URL) conform correctly. The following strips out the
# existing password entry (if exists) so that it can be swapped with
# the new one we specify.
results['user'] = None
results['password'] = results.get('user', None)
if results.get('password', None) is None:
results['password'] = results.get('user', None)
if version:
results['version'] = version

View File

@ -92,9 +92,9 @@ class NotifyJoin(NotifyBase):
self.apikey = apikey.strip()
if compat_is_basestring(devices):
self.devices = filter(bool, DEVICE_LIST_DELIM.split(
self.devices = [x for x in filter(bool, DEVICE_LIST_DELIM.split(
devices,
))
))]
elif isinstance(devices, (set, tuple, list)):
self.devices = devices
@ -103,19 +103,24 @@ class NotifyJoin(NotifyBase):
self.devices = list()
if len(self.devices) == 0:
self.logger.warning('No device(s) were specified.')
raise TypeError('No device(s) were specified.')
# Default to everyone
self.devices.append('group.all')
def notify(self, title, body, notify_type, **kwargs):
"""
Perform Join Notification
"""
# Limit results to just the first 2 line otherwise
# there is just to much content to display
body = re.split('[\r\n]+', body)
body[0] = body[0].strip('#').strip()
body = '\r\n'.join(body[0:2])
try:
# Limit results to just the first 2 line otherwise
# there is just to much content to display
body = re.split('[\r\n]+', body)
body[0] = body[0].strip('#').strip()
body = '\r\n'.join(body[0:2])
except (AttributeError, TypeError):
# body was None or not of a type string
body = ''
headers = {
'User-Agent': self.app_id,
@ -123,7 +128,7 @@ class NotifyJoin(NotifyBase):
}
# error tracking (used for function return)
has_error = False
return_status = True
# Create a copy of the devices list
devices = list(self.devices)
@ -135,7 +140,7 @@ class NotifyJoin(NotifyBase):
elif not IS_DEVICE_RE.match(device):
self.logger.warning(
"The specified device '%s' is invalid; skipping." % (
"The specified device/group '%s' is invalid; skipping." % (
device,
)
)
@ -180,7 +185,7 @@ class NotifyJoin(NotifyBase):
JOIN_HTTP_ERROR_MAP[r.status_code],
r.status_code))
except IndexError:
except KeyError:
self.logger.warning(
'Failed to send Join:%s '
'notification (error=%s).' % (
@ -189,22 +194,21 @@ class NotifyJoin(NotifyBase):
# self.logger.debug('Response Details: %s' % r.raw.read())
# Return; we're done
has_error = True
return_status = False
except requests.ConnectionError as e:
except requests.RequestException as e:
self.logger.warning(
'A Connection error occured sending Join:%s '
'notification.' % device
)
self.logger.debug('Socket Exception: %s' % str(e))
has_error = True
return_status = False
if len(devices):
# Prevent thrashing requests
self.throttle()
return has_error
return return_status
@staticmethod
def parse_url(url):
@ -220,14 +224,8 @@ class NotifyJoin(NotifyBase):
return results
# Apply our settings now
try:
devices = ' '.join(
filter(bool, NotifyBase.split_path(results['fullpath'])))
except (AttributeError, IndexError):
# Force some bad values that will get caught
# in parsing later
devices = None
devices = ' '.join(
filter(bool, NotifyBase.split_path(results['fullpath'])))
results['apikey'] = results['host']
results['devices'] = devices

View File

@ -176,13 +176,7 @@ class NotifyMatterMost(NotifyBase):
return results
# Apply our settings now
try:
authtoken = NotifyBase.split_path(results['fullpath'])[0]
except (AttributeError, IndexError):
# Force some bad values that will get caught
# in parsing later
authtoken = None
authtoken = NotifyBase.split_path(results['fullpath'])[0]
channel = None
if 'channel' in results['qsd'] and len(results['qsd']['channel']):

View File

@ -37,7 +37,7 @@ VALIDATE_APIKEY = re.compile(r'[A-Za-z0-9]{48}')
# Priorities
class NotifyMyAndroidPriority(object):
VERY_LOW = -2
LOW = -2
MODERATE = -1
NORMAL = 0
HIGH = 1
@ -45,7 +45,7 @@ class NotifyMyAndroidPriority(object):
NMA_PRIORITIES = (
NotifyMyAndroidPriority.VERY_LOW,
NotifyMyAndroidPriority.LOW,
NotifyMyAndroidPriority.MODERATE,
NotifyMyAndroidPriority.NORMAL,
NotifyMyAndroidPriority.HIGH,
@ -64,8 +64,7 @@ class NotifyMyAndroid(NotifyBase):
# Notify My Android uses the http protocol with JSON requests
notify_url = 'https://www.notifymyandroid.com/publicapi/notify'
def __init__(self, apikey, priority=NotifyMyAndroidPriority.NORMAL,
devapikey=None, **kwargs):
def __init__(self, apikey, priority=None, devapikey=None, **kwargs):
"""
Initialize Notify My Android Object
"""
@ -143,7 +142,7 @@ class NotifyMyAndroid(NotifyBase):
NMA_HTTP_ERROR_MAP[r.status_code],
r.status_code))
except IndexError:
except KeyError:
self.logger.warning(
'Failed to send NMA notification (error=%s).' % (
r.status_code))
@ -152,10 +151,10 @@ class NotifyMyAndroid(NotifyBase):
return False
else:
self.logger.debug('NMA Server Response: %s.' % r.text)
self.logger.debug('NMA Server Response: %s.' % r.raw.read())
self.logger.info('Sent NMA notification.')
except requests.ConnectionError as e:
except requests.RequestException as e:
self.logger.warning(
'A Connection error occured sending NMA notification.'
)
@ -192,6 +191,32 @@ class NotifyMyAndroid(NotifyBase):
except AttributeError:
pass
if 'priority' in results['qsd'] and len(results['qsd']['priority']):
_map = {
'l': NotifyMyAndroidPriority.LOW,
'-2': NotifyMyAndroidPriority.LOW,
'm': NotifyMyAndroidPriority.MODERATE,
'-1': NotifyMyAndroidPriority.MODERATE,
'n': NotifyMyAndroidPriority.NORMAL,
'0': NotifyMyAndroidPriority.NORMAL,
'h': NotifyMyAndroidPriority.HIGH,
'1': NotifyMyAndroidPriority.HIGH,
'e': NotifyMyAndroidPriority.EMERGENCY,
'2': NotifyMyAndroidPriority.EMERGENCY,
}
try:
results['priority'] = \
_map[results['qsd']['priority'][0].lower()]
except KeyError:
# No priority was set
pass
# Now fetch devapi if specified
devapi = NotifyBase.split_path(results['fullpath'])[0]
if devapi:
results['devapikey'] = devapi
results['apikey'] = results['host']
return results

View File

@ -23,7 +23,8 @@ from . import NotifyEmail as NotifyEmailBase
from .NotifyBoxcar import NotifyBoxcar
from .NotifyEmail import NotifyEmail
from .NotifyFaast import NotifyFaast
from .NotifyGrowl import NotifyGrowl
from .NotifyGrowl.NotifyGrowl import NotifyGrowl
from .NotifyGrowl import gntp
from .NotifyJSON import NotifyJSON
from .NotifyMyAndroid import NotifyMyAndroid
from .NotifyProwl import NotifyProwl
@ -32,14 +33,14 @@ from .NotifyPushBullet import NotifyPushBullet
from .NotifyPushover import NotifyPushover
from .NotifyRocketChat import NotifyRocketChat
from .NotifyToasty import NotifyToasty
from .NotifyTwitter import NotifyTwitter
from .NotifyTwitter.NotifyTwitter import NotifyTwitter
from .NotifyXBMC import NotifyXBMC
from .NotifyXML import NotifyXML
from .NotifySlack import NotifySlack
from .NotifyJoin import NotifyJoin
from .NotifyTelegram import NotifyTelegram
from .NotifyMatterMost import NotifyMatterMost
from .NotifyPushjet import NotifyPushjet
from .NotifyPushjet.NotifyPushjet import NotifyPushjet
from ..common import NotifyImageSize
from ..common import NOTIFY_IMAGE_SIZES
@ -59,4 +60,7 @@ __all__ = [
# NotifyEmail Base References (used for Testing)
'NotifyEmailBase',
# gntp (used for Testing)
'gntp',
]

View File

@ -24,7 +24,7 @@ import mock
import re
VALID_URLS = (
TEST_URLS = (
##################################
# NotifyEmail
##################################
@ -138,7 +138,7 @@ def test_email_plugin(mock_smtp):
"""
# iterate over our dictionary and test it out
for (url, meta) in VALID_URLS:
for (url, meta) in TEST_URLS:
# Our expected instance
instance = meta.get('instance', None)
@ -152,11 +152,6 @@ def test_email_plugin(mock_smtp):
# Our expected Query response (True, False, or exception type)
response = meta.get('response', True)
# Allow us to force the server response code to be something other then
# the defaults
smtplib_response_code = meta.get(
'smtplib_response_code', 200 if response else 404)
test_smtplib_exceptions = meta.get(
'test_smtplib_exceptions', False)
@ -168,16 +163,7 @@ def test_email_plugin(mock_smtp):
# Create a mock SMTP Object
mock_smtp.return_value = mock_socket
if test_smtplib_exceptions is False:
pass
# Handle our default response
mock_socket.sendmail.return_value = smtplib_response_code
# mock_post.return_value.status_code = smtplib_response_code
# mock_get.return_value.status_code = smtplib_response_code
# mock_post.side_effect = None
# mock_get.side_effect = None
else:
if test_smtplib_exceptions:
# Handle exception testing; first we turn the boolean flag ito
# a list of exceptions
test_smtplib_exceptions = (

265
test/test_growl_plugin.py Normal file
View File

@ -0,0 +1,265 @@
# -*- coding: utf-8 -*-
#
# NotifyGrowl - Unit Tests
#
# Copyright (C) 2017 Chris Caron <lead2gold@gmail.com>
#
# This file is part of apprise.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
from apprise import plugins
from apprise import NotifyType
from apprise import Apprise
import mock
import re
TEST_URLS = (
##################################
# NotifyGrowl
##################################
('growl://', {
'instance': None,
}),
('growl://:@/', {
'instance': None
}),
('growl://pass@growl.server', {
'instance': plugins.NotifyGrowl,
}),
('growl://ignored:pass@growl.server', {
'instance': plugins.NotifyGrowl,
}),
('growl://growl.server', {
'instance': plugins.NotifyGrowl,
# don't include an image by default
'include_image': False,
}),
('growl://growl.server?version=1', {
'instance': plugins.NotifyGrowl,
}),
# Force a failure
('growl://growl.server?version=1', {
'instance': plugins.NotifyGrowl,
'growl_response': None,
}),
('growl://growl.server?version=2', {
# don't include an image by default
'include_image': False,
'instance': plugins.NotifyGrowl,
}),
('growl://growl.server?version=2', {
# don't include an image by default
'include_image': False,
'instance': plugins.NotifyGrowl,
'growl_response': None,
}),
# Priorities
('growl://pass@growl.server?priority=low', {
'instance': plugins.NotifyGrowl,
}),
('growl://pass@growl.server?priority=moderate', {
'instance': plugins.NotifyGrowl,
}),
('growl://pass@growl.server?priority=normal', {
'instance': plugins.NotifyGrowl,
}),
('growl://pass@growl.server?priority=high', {
'instance': plugins.NotifyGrowl,
}),
('growl://pass@growl.server?priority=emergency', {
'instance': plugins.NotifyGrowl,
}),
# Invalid Priorities
('growl://pass@growl.server?priority=invalid', {
'instance': plugins.NotifyGrowl,
}),
('growl://pass@growl.server?priority=', {
'instance': plugins.NotifyGrowl,
}),
# invalid version
('growl://growl.server?version=', {
'instance': plugins.NotifyGrowl,
}),
('growl://growl.server?version=crap', {
'instance': plugins.NotifyGrowl,
}),
# Ports
('growl://growl.changeport:2000', {
'instance': plugins.NotifyGrowl,
}),
('growl://growl.garbageport:garbage', {
'instance': plugins.NotifyGrowl,
}),
('growl://growl.colon:', {
'instance': plugins.NotifyGrowl,
}),
# Exceptions
('growl://growl.exceptions01', {
'instance': plugins.NotifyGrowl,
# Throws a series of connection and transfer exceptions when this flag
# is set and tests that we gracfully handle them
'test_growl_notify_exceptions': True,
}),
('growl://growl.exceptions02', {
'instance': plugins.NotifyGrowl,
# Throws a series of connection and transfer exceptions when this flag
# is set and tests that we gracfully handle them
'test_growl_register_exceptions': True,
}),
)
@mock.patch('apprise.plugins.gntp.notifier.GrowlNotifier')
def test_growl_plugin(mock_gntp):
"""
API: NotifyGrowl 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)
# Allow us to force the server response code to be something other then
# the defaults
growl_response = meta.get(
'growl_response', True if response else False)
test_growl_notify_exceptions = meta.get(
'test_growl_notify_exceptions', False)
test_growl_register_exceptions = meta.get(
'test_growl_register_exceptions', False)
mock_notifier = mock.Mock()
mock_gntp.return_value = mock_notifier
test_growl_exceptions = (
plugins.gntp.errors.NetworkError(
0, 'gntp.ParseError() not handled'),
plugins.gntp.errors.AuthError(
0, 'gntp.AuthError() not handled'),
plugins.gntp.errors.UnsupportedError(
'gntp.UnsupportedError() not handled'),
)
if test_growl_notify_exceptions is True:
# Store oure exceptions
test_growl_notify_exceptions = test_growl_exceptions
elif test_growl_register_exceptions is True:
# Store oure exceptions
test_growl_register_exceptions = test_growl_exceptions
for exception in test_growl_register_exceptions:
mock_notifier.register.side_effect = exception
try:
obj = Apprise.instantiate(url, suppress_exceptions=False)
except TypeError:
# This is the response we expect
assert True
except Exception as e:
# We can't handle this exception type
print('%s / %s' % (url, str(e)))
assert False
# We're done this part of the test
continue
else:
# Store our response
mock_notifier.notify.return_value = growl_response
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_growl_notify_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_growl_notify_exceptions:
mock_notifier.notify.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))

View File

@ -111,6 +111,12 @@ def test_notify_base():
# Test is_hostname
assert NotifyBase.is_hostname('example.com') is True
# Test quote
assert NotifyBase.unquote('%20') == ' '
assert NotifyBase.quote(' ') == '%20'
assert NotifyBase.unquote(None) == ''
assert NotifyBase.quote(None) == ''
def test_notify_base_urls():
"""

View File

@ -87,6 +87,103 @@ TEST_URLS = (
'test_requests_exceptions': True,
}),
##################################
# NotifyFaast
##################################
('faast://', {
'instance': None,
}),
# Auth Token specified
('faast://%s' % ('a' * 32), {
'instance': plugins.NotifyFaast,
}),
('faast://%s' % ('a' * 32), {
'instance': plugins.NotifyFaast,
# don't include an image by default
'include_image': False,
}),
('faast://:@/', {
'instance': None,
}),
('faast://%s' % ('a' * 32), {
'instance': plugins.NotifyFaast,
# force a failure
'response': False,
'requests_response_code': requests.codes.internal_server_error,
}),
('faast://%s' % ('a' * 32), {
'instance': plugins.NotifyFaast,
# throw a bizzare code forcing us to fail to look it up
'response': False,
'requests_response_code': 999,
}),
('faast://%s' % ('a' * 32), {
'instance': plugins.NotifyFaast,
# Throws a series of connection and transfer exceptions when this flag
# is set and tests that we gracfully handle them
'test_requests_exceptions': True,
}),
##################################
# NotifyJoin
##################################
('join://', {
'instance': None,
}),
# APIkey; no device
('join://%s' % ('a' * 32), {
'instance': plugins.NotifyJoin,
}),
# Invalid APIKey
('join://%s' % ('a' * 24), {
'instance': None,
# Missing a channel
'exception': TypeError,
}),
# APIKey + device
('join://%s/%s' % ('a' * 32, 'd' * 32), {
'instance': plugins.NotifyJoin,
# don't include an image by default
'include_image': False,
}),
# APIKey + 2 devices
('join://%s/%s/%s' % ('a' * 32, 'd' * 32, 'e' * 32), {
'instance': plugins.NotifyJoin,
# don't include an image by default
'include_image': False,
}),
# APIKey + 1 device and 1 group
('join://%s/%s/%s' % ('a' * 32, 'd' * 32, 'group.chrome'), {
'instance': plugins.NotifyJoin,
}),
# APIKey + bad device
('join://%s/%s' % ('a' * 32, 'd' * 10), {
'instance': plugins.NotifyJoin,
}),
# APIKey + bad url
('join://:@/', {
'instance': None,
}),
('join://%s' % ('a' * 32), {
'instance': plugins.NotifyJoin,
# force a failure
'response': False,
'requests_response_code': requests.codes.internal_server_error,
}),
('join://%s' % ('a' * 32), {
'instance': plugins.NotifyJoin,
# throw a bizzare code forcing us to fail to look it up
'response': False,
'requests_response_code': 999,
}),
# apikey = a
('join://%s' % ('a' * 32), {
'instance': plugins.NotifyJoin,
# Throws a series of connection and transfer exceptions when this flag
# is set and tests that we gracfully handle them
'test_requests_exceptions': True,
}),
##################################
# NotifyJSON
##################################
@ -271,6 +368,56 @@ TEST_URLS = (
'test_requests_exceptions': True,
}),
##################################
# NotifyMyAndroid
##################################
('nma://', {
'instance': None,
}),
# APIkey; no device
('nma://%s' % ('a' * 48), {
'instance': plugins.NotifyMyAndroid,
}),
# Invalid APIKey
('nma://%s' % ('a' * 24), {
'instance': None,
# Missing a channel
'exception': TypeError,
}),
# APIKey
('nma://%s' % ('a' * 48), {
'instance': plugins.NotifyMyAndroid,
# don't include an image by default
'include_image': False,
}),
# APIKey + with image
('nma://%s' % ('a' * 48), {
'instance': plugins.NotifyMyAndroid,
}),
# bad url
('nma://:@/', {
'instance': None,
}),
('nma://%s' % ('a' * 48), {
'instance': plugins.NotifyMyAndroid,
# force a failure
'response': False,
'requests_response_code': requests.codes.internal_server_error,
}),
('nma://%s' % ('a' * 48), {
'instance': plugins.NotifyMyAndroid,
# throw a bizzare code forcing us to fail to look it up
'response': False,
'requests_response_code': 999,
}),
# apikey = a
('nma://%s' % ('a' * 48), {
'instance': plugins.NotifyMyAndroid,
# Throws a series of connection and transfer exceptions when this flag
# is set and tests that we gracfully handle them
'test_requests_exceptions': True,
}),
##################################
# NotifySlack
##################################
@ -498,8 +645,14 @@ def test_rest_plugins(mock_post, mock_get):
test_requests_exceptions = meta.get(
'test_requests_exceptions', False)
mock_get.return_value = requests.Request()
mock_post.return_value = requests.Request()
# A request
robj = mock.Mock()
setattr(robj, 'raw', mock.Mock())
# Allow raw.read() calls
robj.raw.read.return_value = ''
mock_get.return_value = robj
mock_post.return_value = robj
if test_requests_exceptions is False:
# Handle our default response
mock_post.return_value.status_code = requests_response_code
@ -527,10 +680,13 @@ def test_rest_plugins(mock_post, mock_get):
obj = Apprise.instantiate(
url, asset=asset, suppress_exceptions=False)
assert(exception is None)
# Make sure we weren't expecting an exception and just didn't get
# one.
assert exception is None
if obj is None:
# We're done
# We're done (assuming this is what we were expecting)
assert instance is None
continue
if instance is None:
@ -646,11 +802,44 @@ def test_notify_boxcar_plugin(mock_post, mock_get):
mock_post.return_value = requests.Request()
mock_post.return_value.status_code = requests.codes.created
mock_get.return_value.status_code = requests.codes.created
# Test notifications without a body or a title
p = plugins.NotifyBoxcar(access=access, secret=secret, recipients=None)
p.notify(body=None, title=None, notify_type=NotifyType.INFO) is True
@mock.patch('requests.get')
@mock.patch('requests.post')
def test_notify_join_plugin(mock_post, mock_get):
"""
API: NotifyJoin() Extra Checks
"""
# Generate some generic message types
device = 'A' * 32
group = 'group.chrome'
apikey = 'a' * 32
# Initializes the plugin with devices set to a string
plugins.NotifyJoin(apikey=apikey, devices=group)
# Initializes the plugin with devices set to None
plugins.NotifyJoin(apikey=apikey, devices=None)
# Initializes the plugin with devices set to a set
p = plugins.NotifyJoin(apikey=apikey, devices=[group, device])
# Prepare our mock responses
mock_get.return_value = requests.Request()
mock_post.return_value = requests.Request()
mock_post.return_value.status_code = requests.codes.created
mock_get.return_value.status_code = requests.codes.created
# Test notifications without a body or a title; nothing to send
# so we return False
p.notify(body=None, title=None, notify_type=NotifyType.INFO) is False
@mock.patch('requests.get')
@mock.patch('requests.post')
def test_notify_slack_plugin(mock_post, mock_get):