|
|
|
@ -54,6 +54,15 @@ import inspect
|
|
|
|
|
import logging
|
|
|
|
|
logging.disable(logging.CRITICAL)
|
|
|
|
|
|
|
|
|
|
# Sending notifications requires the coroutines to be awaited, so we need to
|
|
|
|
|
# wrap the original function when mocking it. But don't import for Python 2.
|
|
|
|
|
if not six.PY2:
|
|
|
|
|
import apprise.py3compat.asyncio as py3aio
|
|
|
|
|
else:
|
|
|
|
|
class py3aio:
|
|
|
|
|
def notify():
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
# Attachment Directory
|
|
|
|
|
TEST_VAR_DIR = join(dirname(__file__), 'var')
|
|
|
|
|
|
|
|
|
@ -63,6 +72,25 @@ def test_apprise():
|
|
|
|
|
API: Apprise() object
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
def do_notify(server, *args, **kwargs):
|
|
|
|
|
return server.notify(*args, **kwargs)
|
|
|
|
|
|
|
|
|
|
apprise_test(do_notify)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.skipif(sys.version_info.major <= 2, reason="Requires Python 3.x+")
|
|
|
|
|
def test_apprise_async():
|
|
|
|
|
"""
|
|
|
|
|
API: Apprise() object asynchronous methods
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
def do_notify(server, *args, **kwargs):
|
|
|
|
|
return py3aio.tosync(server.async_notify(*args, **kwargs))
|
|
|
|
|
|
|
|
|
|
apprise_test(do_notify)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def apprise_test(do_notify):
|
|
|
|
|
# Caling load matix a second time which is an internal function causes it
|
|
|
|
|
# to skip over content already loaded into our matrix and thefore accesses
|
|
|
|
|
# other if/else parts of the code that aren't otherwise called
|
|
|
|
@ -153,7 +181,7 @@ def test_apprise():
|
|
|
|
|
a.clear()
|
|
|
|
|
|
|
|
|
|
# No servers to notify
|
|
|
|
|
assert a.notify(title="my title", body="my body") is False
|
|
|
|
|
assert do_notify(a, title="my title", body="my body") is False
|
|
|
|
|
|
|
|
|
|
class BadNotification(NotifyBase):
|
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
@ -202,8 +230,8 @@ def test_apprise():
|
|
|
|
|
assert len(a) == 0
|
|
|
|
|
|
|
|
|
|
# We'll fail because we've got nothing to notify
|
|
|
|
|
assert a.notify(
|
|
|
|
|
title="my title", body="my body") is False
|
|
|
|
|
assert do_notify(
|
|
|
|
|
a, title="my title", body="my body") is False
|
|
|
|
|
|
|
|
|
|
# Clear our server listings again
|
|
|
|
|
a.clear()
|
|
|
|
@ -213,50 +241,50 @@ def test_apprise():
|
|
|
|
|
|
|
|
|
|
# Bad Notification Type is still allowed as it is presumed the user
|
|
|
|
|
# know's what their doing
|
|
|
|
|
assert a.notify(
|
|
|
|
|
title="my title", body="my body", notify_type='bad') is True
|
|
|
|
|
assert do_notify(
|
|
|
|
|
a, title="my title", body="my body", notify_type='bad') is True
|
|
|
|
|
|
|
|
|
|
# No Title/Body combo's
|
|
|
|
|
assert a.notify(title=None, body=None) is False
|
|
|
|
|
assert a.notify(title='', body=None) is False
|
|
|
|
|
assert a.notify(title=None, body='') is False
|
|
|
|
|
assert do_notify(a, title=None, body=None) is False
|
|
|
|
|
assert do_notify(a, title='', body=None) is False
|
|
|
|
|
assert do_notify(a, title=None, body='') is False
|
|
|
|
|
|
|
|
|
|
# As long as one is present, we're good
|
|
|
|
|
assert a.notify(title=None, body='present') is True
|
|
|
|
|
assert a.notify(title='present', body=None) is True
|
|
|
|
|
assert a.notify(title="present", body="present") is True
|
|
|
|
|
assert do_notify(a, title=None, body='present') is True
|
|
|
|
|
assert do_notify(a, title='present', body=None) is True
|
|
|
|
|
assert do_notify(a, title="present", body="present") is True
|
|
|
|
|
|
|
|
|
|
# Send Attachment with success
|
|
|
|
|
attach = join(TEST_VAR_DIR, 'apprise-test.gif')
|
|
|
|
|
assert a.notify(
|
|
|
|
|
body='body', title='test', notify_type=NotifyType.INFO,
|
|
|
|
|
assert do_notify(
|
|
|
|
|
a, body='body', title='test', notify_type=NotifyType.INFO,
|
|
|
|
|
attach=attach) is True
|
|
|
|
|
|
|
|
|
|
# Send the attachment as an AppriseAttachment object
|
|
|
|
|
assert a.notify(
|
|
|
|
|
body='body', title='test', notify_type=NotifyType.INFO,
|
|
|
|
|
assert do_notify(
|
|
|
|
|
a, body='body', title='test', notify_type=NotifyType.INFO,
|
|
|
|
|
attach=AppriseAttachment(attach)) is True
|
|
|
|
|
|
|
|
|
|
# test a invalid attachment
|
|
|
|
|
assert a.notify(
|
|
|
|
|
body='body', title='test', notify_type=NotifyType.INFO,
|
|
|
|
|
assert do_notify(
|
|
|
|
|
a, body='body', title='test', notify_type=NotifyType.INFO,
|
|
|
|
|
attach='invalid://') is False
|
|
|
|
|
|
|
|
|
|
# Repeat the same tests above...
|
|
|
|
|
# however do it by directly accessing the object; this grants the similar
|
|
|
|
|
# results:
|
|
|
|
|
assert a[0].notify(
|
|
|
|
|
body='body', title='test', notify_type=NotifyType.INFO,
|
|
|
|
|
assert do_notify(
|
|
|
|
|
a[0], body='body', title='test', notify_type=NotifyType.INFO,
|
|
|
|
|
attach=attach) is True
|
|
|
|
|
|
|
|
|
|
# Send the attachment as an AppriseAttachment object
|
|
|
|
|
assert a[0].notify(
|
|
|
|
|
body='body', title='test', notify_type=NotifyType.INFO,
|
|
|
|
|
assert do_notify(
|
|
|
|
|
a[0], body='body', title='test', notify_type=NotifyType.INFO,
|
|
|
|
|
attach=AppriseAttachment(attach)) is True
|
|
|
|
|
|
|
|
|
|
# test a invalid attachment
|
|
|
|
|
assert a[0].notify(
|
|
|
|
|
body='body', title='test', notify_type=NotifyType.INFO,
|
|
|
|
|
assert do_notify(
|
|
|
|
|
a[0], body='body', title='test', notify_type=NotifyType.INFO,
|
|
|
|
|
attach='invalid://') is False
|
|
|
|
|
|
|
|
|
|
class ThrowNotification(NotifyBase):
|
|
|
|
@ -310,7 +338,7 @@ def test_apprise():
|
|
|
|
|
|
|
|
|
|
# Test when our notify both throws an exception and or just
|
|
|
|
|
# simply returns False
|
|
|
|
|
assert a.notify(title="present", body="present") is False
|
|
|
|
|
assert do_notify(a, title="present", body="present") is False
|
|
|
|
|
|
|
|
|
|
# Create a Notification that throws an unexected exception
|
|
|
|
|
class ThrowInstantiateNotification(NotifyBase):
|
|
|
|
@ -498,7 +526,27 @@ def test_apprise_tagging(mock_post, mock_get):
|
|
|
|
|
API: Apprise() object tagging functionality
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
def do_notify(server, *args, **kwargs):
|
|
|
|
|
return server.notify(*args, **kwargs)
|
|
|
|
|
|
|
|
|
|
apprise_tagging_test(mock_post, mock_get, do_notify)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@mock.patch('requests.get')
|
|
|
|
|
@mock.patch('requests.post')
|
|
|
|
|
@pytest.mark.skipif(sys.version_info.major <= 2, reason="Requires Python 3.x+")
|
|
|
|
|
def test_apprise_tagging_async(mock_post, mock_get):
|
|
|
|
|
"""
|
|
|
|
|
API: Apprise() object tagging functionality asynchronous methods
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
def do_notify(server, *args, **kwargs):
|
|
|
|
|
return py3aio.tosync(server.async_notify(*args, **kwargs))
|
|
|
|
|
|
|
|
|
|
apprise_tagging_test(mock_post, mock_get, do_notify)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def apprise_tagging_test(mock_post, mock_get, do_notify):
|
|
|
|
|
# A request
|
|
|
|
|
robj = mock.Mock()
|
|
|
|
|
setattr(robj, 'raw', mock.Mock())
|
|
|
|
@ -535,18 +583,19 @@ def test_apprise_tagging(mock_post, mock_get):
|
|
|
|
|
|
|
|
|
|
# notify the awesome tag; this would notify both services behind the
|
|
|
|
|
# scenes
|
|
|
|
|
assert a.notify(title="my title", body="my body", tag='awesome') is True
|
|
|
|
|
assert do_notify(
|
|
|
|
|
a, title="my title", body="my body", tag='awesome') is True
|
|
|
|
|
|
|
|
|
|
# notify all of the tags
|
|
|
|
|
assert a.notify(
|
|
|
|
|
title="my title", body="my body", tag=['awesome', 'mmost']) is True
|
|
|
|
|
assert do_notify(
|
|
|
|
|
a, title="my title", body="my body", tag=['awesome', 'mmost']) is True
|
|
|
|
|
|
|
|
|
|
# When we query against our loaded notifications for a tag that simply
|
|
|
|
|
# isn't assigned to anything, we return None. None (different then False)
|
|
|
|
|
# tells us that we litterally had nothing to query. We didn't fail...
|
|
|
|
|
# but we also didn't do anything...
|
|
|
|
|
assert a.notify(
|
|
|
|
|
title="my title", body="my body", tag='missing') is None
|
|
|
|
|
assert do_notify(
|
|
|
|
|
a, title="my title", body="my body", tag='missing') is None
|
|
|
|
|
|
|
|
|
|
# Now to test the ability to and and/or notifications
|
|
|
|
|
a = Apprise()
|
|
|
|
@ -571,20 +620,20 @@ def test_apprise_tagging(mock_post, mock_get):
|
|
|
|
|
# Matches the following only:
|
|
|
|
|
# - json://localhost/tagCD/
|
|
|
|
|
# - json://localhost/tagCDE/
|
|
|
|
|
assert a.notify(
|
|
|
|
|
title="my title", body="my body", tag=[('TagC', 'TagD')]) is True
|
|
|
|
|
assert do_notify(
|
|
|
|
|
a, title="my title", body="my body", tag=[('TagC', 'TagD')]) is True
|
|
|
|
|
|
|
|
|
|
# Expression: (TagY and TagZ) or TagX
|
|
|
|
|
# Matches nothing, None is returned in this case
|
|
|
|
|
assert a.notify(
|
|
|
|
|
title="my title", body="my body",
|
|
|
|
|
assert do_notify(
|
|
|
|
|
a, title="my title", body="my body",
|
|
|
|
|
tag=[('TagY', 'TagZ'), 'TagX']) is None
|
|
|
|
|
|
|
|
|
|
# Expression: (TagY and TagZ) or TagA
|
|
|
|
|
# Matches the following only:
|
|
|
|
|
# - json://localhost/tagAB/
|
|
|
|
|
assert a.notify(
|
|
|
|
|
title="my title", body="my body",
|
|
|
|
|
assert do_notify(
|
|
|
|
|
a, title="my title", body="my body",
|
|
|
|
|
tag=[('TagY', 'TagZ'), 'TagA']) is True
|
|
|
|
|
|
|
|
|
|
# Expression: (TagE and TagD) or TagB
|
|
|
|
@ -592,8 +641,8 @@ def test_apprise_tagging(mock_post, mock_get):
|
|
|
|
|
# - json://localhost/tagCDE/
|
|
|
|
|
# - json://localhost/tagAB/
|
|
|
|
|
# - json://localhost/tagB/
|
|
|
|
|
assert a.notify(
|
|
|
|
|
title="my title", body="my body",
|
|
|
|
|
assert do_notify(
|
|
|
|
|
a, title="my title", body="my body",
|
|
|
|
|
tag=[('TagE', 'TagD'), 'TagB']) is True
|
|
|
|
|
|
|
|
|
|
# Garbage Entries in tag field just get stripped out. the below
|
|
|
|
@ -602,8 +651,8 @@ def test_apprise_tagging(mock_post, mock_get):
|
|
|
|
|
# we fail. None is returned as a way of letting us know that we
|
|
|
|
|
# had Notifications to notify, but since none of them matched our tag
|
|
|
|
|
# none were notified.
|
|
|
|
|
assert a.notify(
|
|
|
|
|
title="my title", body="my body",
|
|
|
|
|
assert do_notify(
|
|
|
|
|
a, title="my title", body="my body",
|
|
|
|
|
tag=[(object, ), ]) is None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -1414,7 +1463,7 @@ def test_apprise_details_plugin_verification():
|
|
|
|
|
|
|
|
|
|
@pytest.mark.skipif(sys.version_info.major <= 2, reason="Requires Python 3.x+")
|
|
|
|
|
@mock.patch('requests.post')
|
|
|
|
|
@mock.patch('apprise.py3compat.asyncio.notify')
|
|
|
|
|
@mock.patch('apprise.py3compat.asyncio.notify', wraps=py3aio.notify)
|
|
|
|
|
def test_apprise_async_mode(mock_async_notify, mock_post, tmpdir):
|
|
|
|
|
"""
|
|
|
|
|
API: Apprise() async_mode tests
|
|
|
|
@ -1474,8 +1523,8 @@ def test_apprise_async_mode(mock_async_notify, mock_post, tmpdir):
|
|
|
|
|
|
|
|
|
|
# Send Notifications Syncronously
|
|
|
|
|
assert a.notify("sync") is True
|
|
|
|
|
# Verify our async code never got called
|
|
|
|
|
assert mock_async_notify.call_count == 0
|
|
|
|
|
# Verify our async code got called
|
|
|
|
|
assert mock_async_notify.call_count == 1
|
|
|
|
|
mock_async_notify.reset_mock()
|
|
|
|
|
|
|
|
|
|
# another way of looking a our false set asset configuration
|
|
|
|
|