Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/1965.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added check on startup for if an Apprise-derived medium is activated without the Apprise package itself being installed.
11 changes: 11 additions & 0 deletions src/argus/notificationprofile/signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from typing import TYPE_CHECKING

from django.contrib.auth import get_user_model
from django.core.exceptions import ImproperlyConfigured
from django.db.utils import ProgrammingError

from argus.notificationprofile.media import EMAIL_DESTINATION_SLUG, send_notifications_to_users
Expand Down Expand Up @@ -34,6 +35,16 @@ def sync_media(sender, **kwargs):
Check if all media in Media has a respective class"""

from .media import MEDIA_CLASSES_DICT
from .media.base import Apprise, AppriseMedium

if Apprise is None:
missing = sorted(cls.__name__ for cls in MEDIA_CLASSES_DICT.values() if issubclass(cls, AppriseMedium))
if missing:
raise ImproperlyConfigured(
"MEDIA_PLUGINS lists media that require the 'apprise' package, which is not installed: "
f'{", ".join(missing)}. Install the "apprise" extra (`pip install argus-server[apprise]`) '
"or remove these media from MEDIA_PLUGINS."
)

apps = kwargs["apps"]
try:
Expand Down
9 changes: 9 additions & 0 deletions src/argus/site/settings/dev.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@

INSTALLED_APPS += ["django_extensions"] # noqa: F405

# Mock to bypass Apprise check for development.
try:
import apprise # noqa: F401
except ImportError:
import sys
from unittest.mock import MagicMock

sys.modules["apprise"] = MagicMock()

# Paths to plugins
MEDIA_PLUGINS = [
"argus.notificationprofile.media.email.EmailNotification",
Expand Down
10 changes: 10 additions & 0 deletions src/argus/site/settings/test_CI.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,16 @@
pass
MIDDLEWARE = middleware

# Hack to bypass Apprise check for running tests.
# Remove as soon as the real Apprise library is included in the test environment.
try:
import apprise # noqa: F401
except ImportError:
import sys
from unittest.mock import MagicMock

sys.modules["apprise"] = MagicMock()

# Paths to plugins
MEDIA_PLUGINS = [
"argus.notificationprofile.media.email.EmailNotification",
Expand Down
34 changes: 32 additions & 2 deletions tests/notificationprofile/test_signals.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,38 @@
from unittest.mock import Mock, patch

from django.test import SimpleTestCase, tag
from django.apps import apps
from django.core.exceptions import ImproperlyConfigured
from django.test import SimpleTestCase, TestCase, tag

from argus.notificationprofile.signals import task_background_send_notification
from argus.notificationprofile.media.base import AppriseMedium
from argus.notificationprofile.media.email import EmailNotification
from argus.notificationprofile.media.slack import SlackNotification
from argus.notificationprofile.signals import sync_media, task_background_send_notification


@tag("integration", "signal")
class SyncMediaAppriseAvailabilityTests(TestCase):
@patch("argus.notificationprofile.media.base.Apprise", object())
def test_when_apprise_is_available_should_not_raise(self):
sync_media(None, apps=apps)

@patch("argus.notificationprofile.media.base.Apprise", None)
@patch("argus.notificationprofile.media.MEDIA_CLASSES_DICT", {"email": EmailNotification})
def test_when_apprise_is_missing_and_no_apprise_derived_medium_is_configured_should_not_raise(self):
sync_media(None, apps=apps)

@patch("argus.notificationprofile.media.base.Apprise", None)
@patch(
"argus.notificationprofile.media.MEDIA_CLASSES_DICT",
{"slack": SlackNotification, "apprise": AppriseMedium, "email": EmailNotification},
)
def test_when_apprise_is_missing_should_raise_with_only_apprise_derived_medium(self):
with self.assertRaises(ImproperlyConfigured) as cm:
sync_media(None)

self.assertIn("SlackNotification", str(cm.exception))
self.assertIn("AppriseMedium, SlackNotification", str(cm.exception))
self.assertNotIn("EmailNotification", str(cm.exception))


@tag("unit", "signal")
Expand Down
Loading