Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[tests] Fixed tests that polluted the test environment #273

Merged
merged 1 commit into from
May 17, 2024
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
10 changes: 5 additions & 5 deletions openwisp_notifications/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
from django.db.utils import OperationalError
from django.utils import timezone

from openwisp_notifications import types
from openwisp_notifications.swapper import load_model, swapper_load_model
from openwisp_notifications.types import NOTIFICATION_TYPES
from openwisp_utils.tasks import OpenwispCeleryTask

User = get_user_model()
Expand Down Expand Up @@ -105,7 +105,7 @@ def update_superuser_notification_settings(instance_id, is_superuser, is_created
create_notification_settings(
user=user,
organizations=Organization.objects.all(),
notification_types=NOTIFICATION_TYPES.keys(),
notification_types=types.NOTIFICATION_TYPES.keys(),
)


Expand All @@ -119,7 +119,7 @@ def ns_register_unregister_notification_type(
"""

notification_types = (
[notification_type] if notification_type else NOTIFICATION_TYPES.keys()
[notification_type] if notification_type else types.NOTIFICATION_TYPES.keys()
)

organizations = Organization.objects.all()
Expand Down Expand Up @@ -166,7 +166,7 @@ def update_org_user_notificationsetting(org_user_id, user_id, org_id, is_org_adm
create_notification_settings(
user=user,
organizations=[organization],
notification_types=NOTIFICATION_TYPES.keys(),
notification_types=types.NOTIFICATION_TYPES.keys(),
)


Expand All @@ -192,7 +192,7 @@ def ns_organization_created(instance_id):
create_notification_settings(
user=user,
organizations=[organization],
notification_types=NOTIFICATION_TYPES.keys(),
notification_types=types.NOTIFICATION_TYPES.keys(),
)


Expand Down
9 changes: 4 additions & 5 deletions openwisp_notifications/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
from openwisp_notifications.swapper import load_model, swapper_load_model
from openwisp_notifications.tests.test_helpers import (
TEST_DATETIME,
mock_notification_types,
register_notification_type,
unregister_notification_type,
)
from openwisp_users.tests.test_api import AuthenticationMixin
from openwisp_users.tests.utils import TestOrganizationMixin
Expand Down Expand Up @@ -436,6 +436,7 @@ def test_list_view_notification_cache(self):
self.assertEqual(response.data['count'], number_of_notifications)

@capture_any_output()
@mock_notification_types
def test_malformed_notifications(self):
test_type = {
'verbose_name': 'Test Notification Type',
Expand Down Expand Up @@ -469,9 +470,8 @@ def test_malformed_notifications(self):
self.assertEqual(response.status_code, 404)
self.assertDictEqual(response.data, {'detail': NOT_FOUND_ERROR})

unregister_notification_type('test_type')

@capture_any_output()
@mock_notification_types
@patch('openwisp_notifications.tasks.delete_obsolete_objects.delay')
def test_obsolete_notifications_busy_worker(self, mocked_task):
"""
Expand Down Expand Up @@ -516,8 +516,6 @@ def test_obsolete_notifications_busy_worker(self, mocked_task):
self.assertEqual(response.status_code, 404)
self.assertDictEqual(response.data, {'detail': NOT_FOUND_ERROR})

unregister_notification_type('test_type')

def test_notification_setting_list_api(self):
self._create_org_user(is_admin=True)
number_of_settings = NotificationSetting.objects.filter(user=self.admin).count()
Expand Down Expand Up @@ -904,6 +902,7 @@ def test_list_ignore_obj_notification_api(self, mocked_task):
self.assertIn('object_content_type', ignore_obj_notification)
self.assertIsNone(ignore_obj_notification['valid_till'])

@capture_any_output()
@patch('openwisp_notifications.tasks.delete_notification.delay')
def test_deleted_notification_type(self, *args):
notify.send(sender=self.admin, type='default', target=self.admin)
Expand Down
40 changes: 40 additions & 0 deletions openwisp_notifications/tests/test_helpers.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
from copy import deepcopy
from datetime import datetime
from unittest.mock import patch

from django.contrib.messages.storage.fallback import FallbackStorage
from django.http import HttpRequest
from django.utils import timezone

from openwisp_notifications.swapper import load_model
from openwisp_notifications.tasks import ns_register_unregister_notification_type
from openwisp_notifications.types import NOTIFICATION_CHOICES, NOTIFICATION_TYPES
from openwisp_notifications.types import (
register_notification_type as base_register_notification_type,
)
from openwisp_notifications.types import (
unregister_notification_type as base_unregister_notification_type,
)

Notification = load_model('Notification')
NotificationSetting = load_model('NotificationSetting')

TEST_DATETIME = datetime(2020, 5, 4, 0, 0, 0, 0, timezone.get_default_timezone())
Expand Down Expand Up @@ -47,3 +51,39 @@ def unregister_notification_type(type_name):
def notification_related_object_url(obj, field, *args, **kwargs):
related_obj = getattr(obj, field)
return f'https://{related_obj}.example.com'


def mock_notification_types(func):
"""
This decorator mocks the NOTIFICATION_CHOICES and NOTIFICATION_TYPES
to prevent polluting the test environment with any notification types
registered during the test.
"""

def wrapper(*args, **kwargs):
with patch.multiple(
'openwisp_notifications.types',
NOTIFICATION_CHOICES=deepcopy(NOTIFICATION_CHOICES),
NOTIFICATION_TYPES=deepcopy(NOTIFICATION_TYPES),
):
from openwisp_notifications import types

# Here we mock the choices for model fields directly. This is
# because the choices for model fields are defined during the
# model loading phase, and any changes to the mocked
# NOTIFICATION_CHOICES don't affect the model field choices.
# So, we mock the choices directly here to ensure that any
# changes to the mocked NOTIFICATION_CHOICES reflect in the
# model field choices.
with patch.object(
Notification._meta.get_field('type'),
'choices',
types.NOTIFICATION_CHOICES,
), patch.object(
NotificationSetting._meta.get_field('type'),
'choices',
types.NOTIFICATION_CHOICES,
):
return func(*args, **kwargs)

return wrapper
26 changes: 12 additions & 14 deletions openwisp_notifications/tests/test_ignore_object_notification.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
from openwisp_notifications.swapper import load_model
from openwisp_users.tests.utils import TestOrganizationMixin

from ..types import NOTIFICATION_TYPES
from .test_helpers import register_notification_type
from ..types import get_notification_configuration
from .test_helpers import mock_notification_types, register_notification_type

IgnoreObjectNotification = load_model('IgnoreObjectNotification')
Notification = load_model('Notification')
Expand Down Expand Up @@ -46,20 +46,18 @@ def test_notification_for_disabled_object(self, mocked_task):
notify.send(sender=self.admin, type='default', target=self.obj)
self.assertEqual(Notification.objects.count(), 0)

@mock_notification_types
@patch('openwisp_notifications.tasks.delete_ignore_object_notification.apply_async')
def test_related_object_deleted(self, *args):
type_config = NOTIFICATION_TYPES['default']
with patch('openwisp_notifications.types.NOTIFICATION_TYPES', {}):
register_notification_type(
'test', type_config, models=[self.obj._meta.model]
)
IgnoreObjectNotification.objects.create(
object=self.obj,
user=self.admin,
valid_till=(timezone.now() + timezone.timedelta(days=1)),
)
self.obj.delete()
self.assertEqual(on_queryset.count(), 0)
type_config = get_notification_configuration('default')
register_notification_type('test', type_config, models=[self.obj._meta.model])
IgnoreObjectNotification.objects.create(
object=self.obj,
user=self.admin,
valid_till=(timezone.now() + timezone.timedelta(days=1)),
)
self.obj.delete()
self.assertEqual(on_queryset.count(), 0)

@patch.object(
IgnoreObjectNotification.objects, 'filter', side_effect=OperationalError
Expand Down
41 changes: 16 additions & 25 deletions openwisp_notifications/tests/test_notification_setting.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from django.core.exceptions import ImproperlyConfigured
from django.db.models.signals import post_save
from django.test import TransactionTestCase

Expand All @@ -9,10 +8,9 @@
from openwisp_notifications.tests.test_helpers import (
base_register_notification_type,
base_unregister_notification_type,
mock_notification_types,
register_notification_type,
unregister_notification_type,
)
from openwisp_notifications.types import get_notification_configuration
from openwisp_users.tests.utils import TestOrganizationMixin

test_notification_type = {
Expand All @@ -32,15 +30,7 @@

class TestNotificationSetting(TestOrganizationMixin, TransactionTestCase):
def setUp(self):
if not Organization.objects.first():
self._create_org(name='default', slug='default')

def tearDown(self):
super().tearDown()
try:
unregister_notification_type('test')
except ImproperlyConfigured:
pass
self.default_org = self._get_org('default')

def _create_staff_org_admin(self):
return self._create_org_user(user=self._create_operator(), is_admin=True)
Expand All @@ -56,6 +46,7 @@ def test_user_created(self):
self._get_user()
self.assertEqual(ns_queryset.count(), 0)

@mock_notification_types
def test_notification_type_registered(self):
register_notification_type('test', test_notification_type)
queryset = NotificationSetting.objects.filter(type='test')
Expand Down Expand Up @@ -95,6 +86,7 @@ def test_organization_user(self):
self.assertEqual(ns_queryset.filter(deleted=False).count(), 1)
self.assertEqual(ns_queryset.filter(deleted=True).count(), 1)

@mock_notification_types
def test_register_notification_org_user(self):
self._create_staff_org_admin()

Expand All @@ -103,13 +95,15 @@ def test_register_notification_org_user(self):
register_notification_type('test', test_notification_type)
self.assertEqual(queryset.count(), 1)

@mock_notification_types
def test_post_migration_handler(self):
from openwisp_notifications.types import NOTIFICATION_CHOICES

# Simulates loading of app when Django server starts
admin = self._get_admin()
org_user = self._create_staff_org_admin()
self.assertEqual(ns_queryset.count(), 3)

default_type_config = get_notification_configuration('default')
base_unregister_notification_type('default')
base_register_notification_type('test', test_notification_type)
notification_type_registered_unregistered_handler(sender=self)
Expand All @@ -119,16 +113,14 @@ def test_post_migration_handler(self):

# Notification Settings for "test" type are created
queryset = NotificationSetting.objects.filter(deleted=False)
if NotificationSetting._meta.app_label == 'sample_notifications':
self.assertEqual(queryset.count(), 6)
self.assertEqual(queryset.filter(user=admin).count(), 4)
self.assertEqual(queryset.filter(user=org_user.user).count(), 2)
else:
self.assertEqual(queryset.count(), 3)
self.assertEqual(queryset.filter(user=admin).count(), 2)
self.assertEqual(queryset.filter(user=org_user.user).count(), 1)

base_register_notification_type('default', default_type_config)
notification_types_count = len(NOTIFICATION_CHOICES)
self.assertEqual(queryset.count(), 3 * notification_types_count)
self.assertEqual(
queryset.filter(user=admin).count(), 2 * notification_types_count
)
self.assertEqual(
queryset.filter(user=org_user.user).count(), 1 * notification_types_count
)

def test_superuser_demoted_to_user(self):
admin = self._get_admin()
Expand Down Expand Up @@ -187,6 +179,7 @@ def test_multiple_org_membership(self):
OrganizationUser.objects.create(user=user, organization=test_org, is_admin=True)
self.assertEqual(ns_queryset.count(), 2)

@mock_notification_types
def test_notification_setting_full_clean(self):
test_type = {
'verbose_name': 'Test Notification Type',
Expand All @@ -207,8 +200,6 @@ def test_notification_setting_full_clean(self):
self.assertIsNone(notification_setting.email)
self.assertIsNone(notification_setting.web)

unregister_notification_type('test_type')

def test_organization_user_updated(self):
default_org = Organization.objects.first()
org_user = self._create_staff_org_admin()
Expand Down
Loading
Loading