[IMP] event: improve mail scheduler tests
Make them easier to improve and modify * use a dedicated setup (allowing to add specific unit tests on test data); * move initial asserts into its own unit test (to keep other tests shorter); * use available mocks for freezetime and sql.now; Then add tests for registration emails, to check what happens for communication scheduled right at registration time. Task-3084943: Event: Improve communication scheduler scalability Part-of: odoo/odoo#155717 Co-authored-by: Stéphane Debauche <std@odoo.com>
This commit is contained in:
co-authored by
Stéphane Debauche
parent
544808d3a5
commit
80765a0c7f
@@ -8,64 +8,82 @@ from freezegun import freeze_time
|
||||
from odoo import Command
|
||||
from odoo.addons.event.tests.common import EventCase
|
||||
from odoo.addons.mail.tests.common import MockEmail
|
||||
from odoo.tests import tagged
|
||||
from odoo.tests import tagged, users
|
||||
from odoo.tools import formataddr, mute_logger
|
||||
|
||||
|
||||
@tagged('event_mail')
|
||||
@tagged('event_mail', 'post_install', '-at_install')
|
||||
class TestMailSchedule(EventCase, MockEmail):
|
||||
|
||||
@mute_logger('odoo.addons.base.models.ir_model', 'odoo.models')
|
||||
def test_event_mail_schedule(self):
|
||||
""" Test mail scheduling for events """
|
||||
self.env.company.write({
|
||||
'name': 'YourCompany',
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
|
||||
cls.env.company.write({
|
||||
'email': 'info@yourcompany.example.com',
|
||||
'name': 'YourCompany',
|
||||
})
|
||||
event_cron_id = self.env.ref('event.event_mail_scheduler')
|
||||
cls.event_cron_id = cls.env.ref('event.event_mail_scheduler')
|
||||
|
||||
# deactivate other schedulers to avoid messing with crons
|
||||
self.env['event.mail'].search([]).unlink()
|
||||
cls.env['event.mail'].search([]).unlink()
|
||||
|
||||
# freeze some datetimes, and ensure more than 1D+1H before event starts
|
||||
# to ease time-based scheduler check
|
||||
# Since `now` is used to set the `create_date` of an event and create_date
|
||||
# has often microseconds, we set it to ensure that the scheduler we still be
|
||||
# launched if scheduled_date == create_date - microseconds
|
||||
now = datetime(2021, 3, 20, 14, 30, 15, 123456)
|
||||
event_date_begin = datetime(2021, 3, 22, 8, 0, 0)
|
||||
event_date_end = datetime(2021, 3, 24, 18, 0, 0)
|
||||
cls.reference_now = datetime(2021, 3, 20, 14, 30, 15, 123456)
|
||||
cls.event_date_begin = datetime(2021, 3, 22, 8, 0, 0)
|
||||
cls.event_date_end = datetime(2021, 3, 24, 18, 0, 0)
|
||||
|
||||
with self.mock_datetime_and_now(now):
|
||||
cls.template_subscription_id = cls.env['ir.model.data']._xmlid_to_res_id('event.event_subscription')
|
||||
cls.template_reminder_id = cls.env['ir.model.data']._xmlid_to_res_id('event.event_reminder')
|
||||
|
||||
with cls.mock_datetime_and_now(cls, cls.reference_now):
|
||||
# create with admin to force create_date
|
||||
test_event = self.env['event.event'].create({
|
||||
cls.test_event = cls.env['event.event'].create({
|
||||
'name': 'TestEventMail',
|
||||
'user_id': self.user_eventmanager.id,
|
||||
'date_begin': event_date_begin,
|
||||
'date_end': event_date_end,
|
||||
'user_id': cls.user_eventmanager.id,
|
||||
'date_begin': cls.event_date_begin,
|
||||
'date_end': cls.event_date_end,
|
||||
'event_mail_ids': [
|
||||
(0, 0, { # right at subscription
|
||||
'interval_unit': 'now',
|
||||
'interval_type': 'after_sub',
|
||||
'template_ref': 'mail.template,%i' % self.env['ir.model.data']._xmlid_to_res_id('event.event_subscription')}),
|
||||
'notification_type': 'mail',
|
||||
'template_ref': f'mail.template,{cls.template_subscription_id}',
|
||||
}),
|
||||
(0, 0, { # one hour after subscription
|
||||
'interval_nbr': 1,
|
||||
'interval_unit': 'hours',
|
||||
'interval_type': 'after_sub',
|
||||
'template_ref': 'mail.template,%i' % self.env['ir.model.data']._xmlid_to_res_id('event.event_subscription')}),
|
||||
'notification_type': 'mail',
|
||||
'template_ref': f'mail.template,{cls.template_subscription_id}',
|
||||
}),
|
||||
(0, 0, { # 1 days before event
|
||||
'interval_nbr': 1,
|
||||
'interval_unit': 'days',
|
||||
'interval_type': 'before_event',
|
||||
'template_ref': 'mail.template,%i' % self.env['ir.model.data']._xmlid_to_res_id('event.event_reminder')}),
|
||||
'notification_type': 'mail',
|
||||
'template_ref': f'mail.template,{cls.template_reminder_id}',
|
||||
}),
|
||||
(0, 0, { # immediately after event
|
||||
'interval_nbr': 1,
|
||||
'interval_unit': 'hours',
|
||||
'interval_type': 'after_event',
|
||||
'template_ref': 'mail.template,%i' % self.env['ir.model.data']._xmlid_to_res_id('event.event_reminder')}),
|
||||
'notification_type': 'mail',
|
||||
'template_ref': f'mail.template,{cls.template_reminder_id}',
|
||||
}),
|
||||
]
|
||||
})
|
||||
self.assertEqual(test_event.create_date, now)
|
||||
|
||||
def test_assert_initial_values(self):
|
||||
""" Ensure base values for tests """
|
||||
test_event = self.test_event
|
||||
|
||||
# event data
|
||||
self.assertEqual(test_event.create_date, self.reference_now)
|
||||
|
||||
# check subscription scheduler
|
||||
after_sub_scheduler = self.env['event.mail'].search([('event_id', '=', test_event.id), ('interval_type', '=', 'after_sub'), ('interval_unit', '=', 'now')])
|
||||
@@ -83,19 +101,31 @@ class TestMailSchedule(EventCase, MockEmail):
|
||||
# check before event scheduler
|
||||
event_prev_scheduler = self.env['event.mail'].search([('event_id', '=', test_event.id), ('interval_type', '=', 'before_event')])
|
||||
self.assertEqual(len(event_prev_scheduler), 1, 'event: wrong scheduler creation')
|
||||
self.assertEqual(event_prev_scheduler.scheduled_date, event_date_begin + relativedelta(days=-1))
|
||||
self.assertEqual(event_prev_scheduler.scheduled_date, self.event_date_begin + relativedelta(days=-1))
|
||||
self.assertFalse(event_prev_scheduler.mail_done)
|
||||
self.assertEqual(event_prev_scheduler.mail_state, 'scheduled')
|
||||
self.assertEqual(event_prev_scheduler.mail_count_done, 0)
|
||||
# check after event scheduler
|
||||
event_next_scheduler = self.env['event.mail'].search([('event_id', '=', test_event.id), ('interval_type', '=', 'after_event')])
|
||||
self.assertEqual(len(event_next_scheduler), 1, 'event: wrong scheduler creation')
|
||||
self.assertEqual(event_next_scheduler.scheduled_date, event_date_end + relativedelta(hours=1))
|
||||
self.assertEqual(event_next_scheduler.scheduled_date, self.event_date_end + relativedelta(hours=1))
|
||||
self.assertFalse(event_next_scheduler.mail_done)
|
||||
self.assertEqual(event_next_scheduler.mail_state, 'scheduled')
|
||||
self.assertEqual(event_next_scheduler.mail_count_done, 0)
|
||||
|
||||
@mute_logger('odoo.addons.base.models.ir_model', 'odoo.models')
|
||||
@users('user_eventmanager')
|
||||
def test_event_mail_schedule(self):
|
||||
""" Test mail scheduling for events """
|
||||
# create some registrations
|
||||
test_event = self.test_event.with_env(self.env)
|
||||
now = self.reference_now
|
||||
schedulers = self.env['event.mail'].search([('event_id', '=', test_event.id)])
|
||||
after_sub_scheduler = schedulers.filtered(lambda s: s.interval_type == 'after_sub' and s.interval_unit == 'now')
|
||||
after_sub_scheduler_2 = schedulers.filtered(lambda s: s.interval_type == 'after_sub' and s.interval_unit == 'hours')
|
||||
event_prev_scheduler = schedulers.filtered(lambda s: s.interval_type == 'before_event')
|
||||
event_next_scheduler = schedulers.filtered(lambda s: s.interval_type == 'after_event')
|
||||
|
||||
with self.mock_datetime_and_now(now), self.mock_mail_gateway():
|
||||
reg1 = self.env['event.registration'].create({
|
||||
'event_id': test_event.id,
|
||||
@@ -179,9 +209,9 @@ class TestMailSchedule(EventCase, MockEmail):
|
||||
self.assertEqual(event_prev_scheduler.mail_state, 'scheduled')
|
||||
|
||||
# simulate cron running before scheduled date -> should not do anything
|
||||
now_start = event_date_begin + relativedelta(hours=-25, microsecond=654321)
|
||||
now_start = self.event_date_begin + relativedelta(hours=-25, microsecond=654321)
|
||||
with freeze_time(now_start), self.mock_mail_gateway():
|
||||
event_cron_id.method_direct_trigger()
|
||||
self.event_cron_id.method_direct_trigger()
|
||||
|
||||
self.assertFalse(event_prev_scheduler.mail_done)
|
||||
self.assertEqual(event_prev_scheduler.mail_state, 'scheduled')
|
||||
@@ -189,9 +219,9 @@ class TestMailSchedule(EventCase, MockEmail):
|
||||
self.assertEqual(len(self._new_mails), 0)
|
||||
|
||||
# execute cron to run schedulers after scheduled date
|
||||
now_start = event_date_begin + relativedelta(hours=-23, microsecond=654321)
|
||||
now_start = self.event_date_begin + relativedelta(hours=-23, microsecond=654321)
|
||||
with freeze_time(now_start), self.mock_mail_gateway():
|
||||
event_cron_id.method_direct_trigger()
|
||||
self.event_cron_id.method_direct_trigger()
|
||||
|
||||
# check that scheduler is finished
|
||||
self.assertTrue(event_prev_scheduler.mail_done, 'event: reminder scheduler should have run')
|
||||
@@ -230,7 +260,7 @@ class TestMailSchedule(EventCase, MockEmail):
|
||||
# confirm registration -> should trigger registration schedulers
|
||||
# NOTE: currently all schedulers are based on create_date
|
||||
# meaning several communications may be sent in the time time
|
||||
with freeze_time(now_start + relativedelta(hours=1)), self.mock_mail_gateway():
|
||||
with self.mock_datetime_and_now(now_start + relativedelta(hours=1)), self.mock_mail_gateway():
|
||||
reg3.action_confirm()
|
||||
|
||||
# verify that subscription scheduler was auto-executed after new registration confirmed
|
||||
@@ -263,9 +293,9 @@ class TestMailSchedule(EventCase, MockEmail):
|
||||
self.assertFalse(event_next_scheduler.mail_done)
|
||||
|
||||
# execute event reminder scheduler explicitly after its schedule date
|
||||
new_end = event_date_end + relativedelta(hours=2)
|
||||
with freeze_time(new_end), self.mock_mail_gateway():
|
||||
event_cron_id.method_direct_trigger()
|
||||
new_end = self.event_date_end + relativedelta(hours=2)
|
||||
with self.mock_datetime_and_now(new_end), self.mock_mail_gateway():
|
||||
self.event_cron_id.method_direct_trigger()
|
||||
|
||||
# check that scheduler is finished
|
||||
self.assertTrue(event_next_scheduler.mail_done, 'event: reminder scheduler should should have run')
|
||||
@@ -282,6 +312,63 @@ class TestMailSchedule(EventCase, MockEmail):
|
||||
'email_from': self.user_eventmanager.company_id.email_formatted,
|
||||
})
|
||||
|
||||
@mute_logger('odoo.addons.base.models.ir_model', 'odoo.models')
|
||||
@users('user_eventmanager')
|
||||
def test_event_mail_schedule_on_subscription(self):
|
||||
""" Test emails sent on subscription, notably to avoid bottlenecks """
|
||||
test_event = self.test_event.with_env(self.env)
|
||||
reference_now = self.reference_now
|
||||
|
||||
# remove on subscription, to create hanging registrations
|
||||
schedulers = self.env['event.mail'].search([('event_id', '=', test_event.id)])
|
||||
_sub_scheduler = schedulers.filtered(lambda s: s.interval_type == 'after_sub' and s.interval_unit == 'now')
|
||||
_sub_scheduler.unlink()
|
||||
|
||||
# consider having hanging registrations, still not processed (e.g. adding
|
||||
# a new scheduler after)
|
||||
self.env.invalidate_all()
|
||||
# com 58, event 36
|
||||
with self.assertQueryCount(61), self.mock_datetime_and_now(reference_now), \
|
||||
self.mock_mail_gateway():
|
||||
_existing = self.env['event.registration'].create([
|
||||
{
|
||||
'email': f'existing.attendee.{idx}@test.example.com',
|
||||
'event_id': test_event.id,
|
||||
'name': f'Attendee {idx}',
|
||||
} for idx in range(5)
|
||||
])
|
||||
self.assertEqual(len(self._new_mails), 0)
|
||||
self.assertEqual(self.mail_mail_create_mocked.call_count, 0)
|
||||
|
||||
# add on subscription scheduler, then new registrations ! yay ! check what
|
||||
# happens with old ones
|
||||
test_event.write({'event_mail_ids': [
|
||||
(0, 0, { # right at subscription
|
||||
'interval_unit': 'now',
|
||||
'interval_type': 'after_sub',
|
||||
'notification_type': 'mail',
|
||||
'template_ref': f'mail.template,{self.template_subscription_id}',
|
||||
}),
|
||||
]})
|
||||
self.env.invalidate_all()
|
||||
# com 148, event 134
|
||||
with self.assertQueryCount(153), \
|
||||
self.mock_datetime_and_now(reference_now + relativedelta(minutes=10)), \
|
||||
self.mock_mail_gateway():
|
||||
_new = self.env['event.registration'].create([
|
||||
{
|
||||
'email': f'new.attendee.{idx}@test.example.com',
|
||||
'event_id': test_event.id,
|
||||
'name': f'New Attendee {idx}',
|
||||
} for idx in range(2)
|
||||
])
|
||||
# self.assertEqual(len(self._new_mails), 2)
|
||||
# self.assertEqual(self.mail_mail_create_mocked.call_count, 2)
|
||||
self.assertEqual(len(self._new_mails), 7,
|
||||
'EventMail: TODO: should be limited to new registrations')
|
||||
self.assertEqual(self.mail_mail_create_mocked.call_count, 7,
|
||||
'EventMail: TODO: should create one mail / new registration')
|
||||
|
||||
@mute_logger('odoo.addons.base.models.ir_model', 'odoo.models')
|
||||
def test_unique_event_mail_ids(self):
|
||||
# create event with default event_mail_ids lines
|
||||
|
||||
@@ -7,9 +7,10 @@ from odoo import fields
|
||||
from odoo.addons.event.tests.common import EventCase
|
||||
from odoo.addons.phone_validation.tools import phone_validation
|
||||
from odoo.addons.sms.tests.common import SMSCase
|
||||
from odoo.tests import users
|
||||
from odoo.tests import tagged, users
|
||||
|
||||
|
||||
@tagged('event_mail')
|
||||
class TestSMSSchedule(EventCase, SMSCase):
|
||||
|
||||
@classmethod
|
||||
|
||||
Reference in New Issue
Block a user