mirror of
https://github.com/caronc/apprise.git
synced 2024-11-26 01:53:10 +01:00
more tests 70% coverage now!
This commit is contained in:
parent
5a7a8a624a
commit
f8c3d35f8c
@ -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
|
||||
|
@ -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.',
|
||||
)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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']):
|
||||
|
@ -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
|
||||
|
@ -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',
|
||||
]
|
||||
|
@ -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
265
test/test_growl_plugin.py
Normal 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))
|
@ -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():
|
||||
"""
|
||||
|
@ -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):
|
||||
|
Loading…
Reference in New Issue
Block a user