From a54d7b42f14058ceac2246b7b87f045058e3a738 Mon Sep 17 00:00:00 2001 From: Chris Caron Date: Sun, 17 Sep 2023 15:53:18 -0400 Subject: [PATCH] Improved test cases (#137) --- apprise_api/api/tests/test_stateful_notify.py | 149 ++++++++++++++++-- .../api/tests/test_stateless_notify.py | 3 + apprise_api/gunicorn.conf.py | 4 +- 3 files changed, 144 insertions(+), 12 deletions(-) diff --git a/apprise_api/api/tests/test_stateful_notify.py b/apprise_api/api/tests/test_stateful_notify.py index 3c96d95..57e0af0 100644 --- a/apprise_api/api/tests/test_stateful_notify.py +++ b/apprise_api/api/tests/test_stateful_notify.py @@ -24,12 +24,13 @@ # THE SOFTWARE. from django.test import SimpleTestCase from django.test.utils import override_settings -from unittest.mock import patch +from unittest.mock import patch, Mock from ..forms import NotifyForm from ..utils import ConfigCache import os import re import apprise +import requests class StatefulNotifyTests(SimpleTestCase): @@ -51,8 +52,8 @@ def test_stateful_configuration_with_lock(self): response = self.client.post('/get/{}'.format(key)) assert response.status_code == 403 - @patch('apprise.Apprise.notify') - def test_stateful_configuration_io(self, mock_notify): + @patch('requests.post') + def test_stateful_configuration_io(self, mock_post): """ Test the writing, removal, writing and removal of configuration to verify it persists and is removed when expected @@ -61,16 +62,26 @@ def test_stateful_configuration_io(self, mock_notify): # our key to use key = 'test_stateful' - # Set our return value - mock_notify.return_value = True + request = Mock() + request.content = b'ok' + request.status_code = requests.codes.ok + mock_post.return_value = request + + # Monkey Patch + apprise.plugins.NotifyEmail.NotifyEmail.enabled = True # Preare our list of URLs we want to save urls = [ - 'mail=mailto://user:pass@hotmail.com', 'devops=slack://TokenA/TokenB/TokenC', 'pusbullet=pbul://tokendetails', + 'general,json=json://hostname', ] + # Monkey Patch + apprise.plugins.NotifySlack.NotifySlack.enabled = True + apprise.plugins.NotifyPushBullet.NotifyPushBullet.enabled = True + apprise.plugins.NotifyJSON.NotifyJSON.enabled = True + # For 10 iterations, repeat these tests to verify that don't change # and our saved content is not different on subsequent calls. for _ in range(10): @@ -94,6 +105,7 @@ def test_stateful_configuration_io(self, mock_notify): form_data = { 'body': '## test notifiction', 'format': apprise.NotifyFormat.MARKDOWN, + 'tag': 'general', } form = NotifyForm(data=form_data) @@ -103,10 +115,34 @@ def test_stateful_configuration_io(self, mock_notify): # self.client.post() del form.cleaned_data['attachment'] + # We sent the notification successfully response = self.client.post( '/notify/{}'.format(key), form.cleaned_data) assert response.status_code == 200 - assert mock_notify.call_count == 1 + assert mock_post.call_count == 1 + + mock_post.reset_mock() + + form_data = { + 'body': '## test notifiction', + 'format': apprise.NotifyFormat.MARKDOWN, + 'tag': 'no-on-with-this-tag', + } + + form = NotifyForm(data=form_data) + assert form.is_valid() + + # Required to prevent None from being passed into + # self.client.post() + del form.cleaned_data['attachment'] + + # No one to notify + response = self.client.post( + '/notify/{}'.format(key), form.cleaned_data) + assert response.status_code == 424 + assert mock_post.call_count == 0 + + mock_post.reset_mock() # Now empty our data response = self.client.post('/del/{}'.format(key)) @@ -117,7 +153,7 @@ def test_stateful_configuration_io(self, mock_notify): assert response.status_code == 204 # Reset our count - mock_notify.reset_mock() + mock_post.reset_mock() # Now we do a similar approach as the above except we remove the # configuration from under the application @@ -151,10 +187,103 @@ def test_stateful_configuration_io(self, mock_notify): # self.client.post() del form.cleaned_data['attachment'] + # No one to notify (no tag specified) + response = self.client.post( + '/notify/{}'.format(key), form.cleaned_data) + assert response.status_code == 424 + assert mock_post.call_count == 0 + + # Reset our configuration + mock_post.reset_mock() + + # + # Test tagging now + # + form_data = { + 'body': '## test notifiction', + 'format': apprise.NotifyFormat.MARKDOWN, + 'tag': 'general+json', + } + + form = NotifyForm(data=form_data) + assert form.is_valid() + + # Required to prevent None from being passed into + # self.client.post() + del form.cleaned_data['attachment'] + + response = self.client.post( + '/notify/{}'.format(key), form.cleaned_data) + # + (plus) not supported at this time + assert response.status_code == 400 + assert mock_post.call_count == 0 + + # Reset our configuration + mock_post.reset_mock() + + form_data = { + 'body': '## test notifiction', + 'format': apprise.NotifyFormat.MARKDOWN, + # Plus with space inbetween + 'tag': 'general + json', + } + + form = NotifyForm(data=form_data) + assert form.is_valid() + + # Required to prevent None from being passed into + # self.client.post() + del form.cleaned_data['attachment'] + + response = self.client.post( + '/notify/{}'.format(key), form.cleaned_data) + # + (plus) not supported at this time + assert response.status_code == 400 + assert mock_post.call_count == 0 + + mock_post.reset_mock() + + form_data = { + 'body': '## test notifiction', + 'format': apprise.NotifyFormat.MARKDOWN, + # Space (AND) + 'tag': 'general json', + } + + form = NotifyForm(data=form_data) + assert form.is_valid() + + # Required to prevent None from being passed into + # self.client.post() + del form.cleaned_data['attachment'] + response = self.client.post( '/notify/{}'.format(key), form.cleaned_data) assert response.status_code == 200 - assert mock_notify.call_count == 1 + assert mock_post.call_count == 1 + + mock_post.reset_mock() + + form_data = { + 'body': '## test notifiction', + 'format': apprise.NotifyFormat.MARKDOWN, + # Comma (OR) + 'tag': 'general, devops', + } + + form = NotifyForm(data=form_data) + assert form.is_valid() + + # Required to prevent None from being passed into + # self.client.post() + del form.cleaned_data['attachment'] + + response = self.client.post( + '/notify/{}'.format(key), form.cleaned_data) + assert response.status_code == 200 + + # 2 endpoints hit + assert mock_post.call_count == 2 # Now remove the file directly (as though one # removed the configuration directory) @@ -171,4 +300,4 @@ def test_stateful_configuration_io(self, mock_notify): assert response.status_code == 204 # Reset our count - mock_notify.reset_mock() + mock_post.reset_mock() diff --git a/apprise_api/api/tests/test_stateless_notify.py b/apprise_api/api/tests/test_stateless_notify.py index 0edc7ff..f595411 100644 --- a/apprise_api/api/tests/test_stateless_notify.py +++ b/apprise_api/api/tests/test_stateless_notify.py @@ -144,6 +144,9 @@ def test_stateless_notify_recursion(self, mock_notify): 'body': 'test notifiction', } + # Monkey Patch + apprise.plugins.NotifyEmail.NotifyEmail.enabled = True + # At a minimum 'body' is requred form = NotifyByUrlForm(data=form_data) assert form.is_valid() diff --git a/apprise_api/gunicorn.conf.py b/apprise_api/gunicorn.conf.py index f307eff..065b192 100644 --- a/apprise_api/gunicorn.conf.py +++ b/apprise_api/gunicorn.conf.py @@ -55,8 +55,8 @@ # Our worker type to use; over-ride the default `sync` worker_class = 'gevent' -# Get workers memory consumption under control by leveraging gunicorn worker recycling -# timeout +# Get workers memory consumption under control by leveraging gunicorn +# worker recycling timeout max_requests = 1000 max_requests_jitter = 50