Refactor managemails to use signals

- use signals to trigger Celery tasks to create and delete mailboxes
- add generic TestCaseWithCeleryTasks class to handle celery task tests
  in a uniform way
This commit is contained in:
Jan Dittberner 2023-02-19 13:45:30 +01:00
parent d6fc29a2b8
commit 610f8976fc
5 changed files with 114 additions and 37 deletions

View file

@ -15,3 +15,11 @@ class ManageMailsAppConfig(AppConfig):
name = "managemails" name = "managemails"
verbose_name = _("Mailboxes and Mail Addresses") verbose_name = _("Mailboxes and Mail Addresses")
def ready(self):
"""
Takes care of importing the signal handlers of the :py:mod:`userdbs`
app.
"""
import managemails.signals # NOQA

View file

@ -8,7 +8,6 @@ from model_utils.models import TimeStampedModel
from passlib.handlers.sha2_crypt import sha512_crypt from passlib.handlers.sha2_crypt import sha512_crypt
from domains.models import MailDomain from domains.models import MailDomain
from fileservertasks.tasks import create_file_mailbox, delete_file_mailbox
from osusers.models import User as OsUser from osusers.models import User as OsUser
@ -123,16 +122,6 @@ class Mailbox(ActivateAbleMixin, TimeStampedModel):
""" """
self.password = sha512_crypt.hash(password) self.password = sha512_crypt.hash(password)
def save(self, *args, **kwargs):
# TODO: refactor to use signals
create_file_mailbox.delay(self.osuser.username, self.username).get()
super(Mailbox, self).save(*args, **kwargs)
def delete(self, *args, **kwargs):
# TODO: refactor to use signals
delete_file_mailbox.delay(self.osuser.username, self.username).get()
super(Mailbox, self).delete(*args, **kwargs)
def get_mailaddresses(self): def get_mailaddresses(self):
""" """
Get a list of mail addresses assigned to this mailbox. Get a list of mail addresses assigned to this mailbox.

View file

@ -0,0 +1,62 @@
"""
This module contains the signal handlers of the :py:mod:`managemails` app.
The module starts Celery_ tasks.
.. _Celery: https://www.celeryproject.org/
"""
import logging
from django.db.models.signals import post_delete, post_save
from django.dispatch import receiver
from fileservertasks.tasks import create_file_mailbox, delete_file_mailbox
from managemails.models import Mailbox
from taskresults.models import TaskResult
_LOGGER = logging.getLogger(__name__)
@receiver(post_save, sender=Mailbox)
def handle_mailbox_created(sender, instance, created, **kwargs):
"""
Handles post creation actions on :py:class:`Mailbox <managemails.models.Mailbox>` instances.
:param sender: sender of the signal
:param instance: Mailbox instance
:param created: whether the instance has just been created
This signal handler starts a Celery_ task.
"""
if created:
taskresult = TaskResult.objects.create_task_result(
"handle_mailbox_created",
create_file_mailbox.s(instance.osuser.username, instance.username),
)
_LOGGER.info(
"Mailbox creation has been requested in task %s", taskresult.task_id
)
_LOGGER.debug("mailbox %s has been %s", instance, created and "created" or "updated")
@receiver(post_delete, sender=Mailbox)
def handle_mailbox_deleted(sender, instance, **kwargs):
"""
Handles cleanup actions to be done after deletion of a
:py:class:`Mailbox <managemails.models.Mailbox>` instance.
:param sender: sender of the signal
:param instance: Mailbox instance
This signal handler starts a Celery_ task.
"""
taskresult = TaskResult.objects.create_task_result(
"handle_mailbox_deleted",
delete_file_mailbox.s(instance.osuser.username, instance.username)
)
_LOGGER.info(
"Mailbox deletion has been requested in task %s", taskresult.task_id
)

View file

@ -1,24 +1,20 @@
""" """
This module contains tests for :py:mod:`managemails.models` This module contains tests for :py:mod:`managemails.models`
""" """
from unittest.mock import patch
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.test import TestCase, TransactionTestCase from django.test import TestCase, TransactionTestCase
from django.test.utils import override_settings from django.test.utils import override_settings
from passlib.hash import sha512_crypt from passlib.handlers.sha2_crypt import sha512_crypt
from domains.models import MailDomain from domains.models import MailDomain
from managemails.models import MailAddress, Mailbox from managemails.models import MailAddress, Mailbox
from osusers.models import User from osusers.models import User
from taskresults.tests.testutils import TestCaseWithCeleryTasks
Customer = get_user_model() Customer = get_user_model()
@override_settings( class MailboxTest(TestCaseWithCeleryTasks):
CELERY_ALWAYS_EAGER=True, CELERY_CACHE_BACKEND="memory", BROKER_BACKEND="memory"
)
class MailboxTest(TestCase):
def setUp(self): def setUp(self):
super(MailboxTest, self).setUp() super(MailboxTest, self).setUp()
self.customer = Customer.objects.create_user("test") self.customer = Customer.objects.create_user("test")
@ -35,22 +31,32 @@ class MailboxTest(TestCase):
mb.set_password("test") mb.set_password("test")
self.assertEqual(str(mb), "test") self.assertEqual(str(mb), "test")
@patch("managemails.models.create_file_mailbox") def test_save(self):
def test_save(self, create_file_mailbox_task):
user = User.objects.create_user(self.customer) user = User.objects.create_user(self.customer)
self.resetCeleryTasks()
mb = Mailbox.objects.create_mailbox(user) mb = Mailbox.objects.create_mailbox(user)
self.assertIsNotNone(mb.pk) self.assertIsNotNone(mb.pk)
create_file_mailbox_task.delay.assert_called_with(user.username, mb.username)
@patch("managemails.models.delete_file_mailbox") self.assertCeleryTasksRun([
def test_delete(self, delete_file_mailbox_task): (1, "handle_mailbox_created"),
])
def test_delete(self):
user = User.objects.create_user(self.customer) user = User.objects.create_user(self.customer)
mb = Mailbox.objects.create_mailbox(user) mb = Mailbox.objects.create_mailbox(user)
self.resetCeleryTasks()
mb.delete() mb.delete()
self.assertIsNone(mb.pk) self.assertIsNone(mb.pk)
delete_file_mailbox_task.delay.assert_called_with(user.username, mb.username)
def test_get_mailaddresses(self): self.assertCeleryTasksRun([
(1, "handle_mailbox_deleted"),
])
def test_get_mail_addresses(self):
user = User.objects.create_user(self.customer) user = User.objects.create_user(self.customer)
mb = Mailbox.objects.create_mailbox(user) mb = Mailbox.objects.create_mailbox(user)
md = MailDomain.objects.create(domain="example.org") md = MailDomain.objects.create(domain="example.org")
@ -61,10 +67,7 @@ class MailboxTest(TestCase):
self.assertIn(address, mailaddresses) self.assertIn(address, mailaddresses)
@override_settings( class MailAddressTest(TestCaseWithCeleryTasks):
CELERY_ALWAYS_EAGER=True, CELERY_CACHE_BACKEND="memory", BROKER_BACKEND="memory"
)
class MailAddressTest(TransactionTestCase):
def test__str__(self): def test__str__(self):
md = MailDomain.objects.create(domain="example.org") md = MailDomain.objects.create(domain="example.org")
ma = MailAddress.objects.create(localpart="test", domain=md) ma = MailAddress.objects.create(localpart="test", domain=md)
@ -214,10 +217,7 @@ class MailAddressTest(TransactionTestCase):
self.assertEqual(mafwds[0].target, "test2@example.org") self.assertEqual(mafwds[0].target, "test2@example.org")
@override_settings( class MailboxManagerTest(TestCaseWithCeleryTasks):
CELERY_ALWAYS_EAGER=True, CELERY_CACHE_BACKEND="memory", BROKER_BACKEND="memory"
)
class MailboxManagerTest(TransactionTestCase):
def setUp(self): def setUp(self):
super(MailboxManagerTest, self).setUp() super(MailboxManagerTest, self).setUp()
self.customer = Customer.objects.create_user("test") self.customer = Customer.objects.create_user("test")
@ -302,10 +302,7 @@ class MailboxManagerTest(TransactionTestCase):
self.assertTrue(sha512_crypt.verify("test", mailbox.password)) self.assertTrue(sha512_crypt.verify("test", mailbox.password))
@override_settings( class MailAddressMailboxTest(TestCaseWithCeleryTasks):
CELERY_ALWAYS_EAGER=True, CELERY_CACHE_BACKEND="memory", BROKER_BACKEND="memory"
)
class MailAddressMailboxTest(TestCase):
def setUp(self): def setUp(self):
super(MailAddressMailboxTest, self).setUp() super(MailAddressMailboxTest, self).setUp()
self.customer = Customer.objects.create_user("test") self.customer = Customer.objects.create_user("test")

View file

@ -0,0 +1,21 @@
from django.test import TestCase
from django.test.utils import override_settings
from taskresults.models import TaskResult
@override_settings(
CELERY_ALWAYS_EAGER=True, CELERY_CACHE_BACKEND="memory", BROKER_BACKEND="memory"
)
class TestCaseWithCeleryTasks(TestCase):
def resetCeleryTasks(self):
TaskResult.objects.all().delete()
def assertCeleryTasksRun(self, tasks):
task_results = TaskResult.objects.all()
self.assertEqual(task_results.count(), sum([t[0] for t in tasks]))
creators = [r.creator for r in task_results]
for t_count, t_creator in tasks:
self.assertEqual(creators.count(t_creator), t_count)