[MERGE][IMP] mail, event: add test for batch emailing from template

PURPOSE

Mail: add tests for MailTemplate send_mail

Several flows use MailTemplate.send_mail() in batch, notably event email
scheduler which sends emails to event attendee in batch. This commit adds
tests around 'send_mail' method of MailTemplate model

  * add tests for batch: it is currently not supported hence using a loop but
    batch is going to be added soon, allowing to test the batch version works
    as intended;
  * add query counters, notably for batch mode and when dynamic reports are
    involved in templates;

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.

LINKS

Part of Task-3764894: Event: Allow using cron triggers for communication
Part of Task-3764891: Mail: Batch-ize MailTemplate send_mail
Part of Task-3164278: Mail: Batch send: ensure limit, avoid force
Part of Task-3084943: Event: Improve communication scheduler scalability

closes odoo/odoo#155717

Related: odoo/enterprise#57731
Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
This commit is contained in:
Odoo's Mergebot
2024-02-28 19:49:12 +00:00
committed by Odoo Online
8 changed files with 367 additions and 127 deletions
+123 -40
View File
@@ -8,63 +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, users
from odoo.tools import formataddr, mute_logger
@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 freeze_time(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',
'create_date': now,
'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')])
@@ -82,28 +101,38 @@ 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
with freeze_time(now), self.mock_mail_gateway():
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({
'create_date': now,
'event_id': test_event.id,
'name': 'Reg1',
'email': 'reg1@example.com',
})
reg2 = self.env['event.registration'].create({
'create_date': now,
'event_id': test_event.id,
'name': 'Reg2',
'email': 'reg2@example.com',
@@ -180,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')
@@ -190,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')
@@ -211,9 +240,8 @@ class TestMailSchedule(EventCase, MockEmail):
# NEW REGISTRATION EFFECT ON SCHEDULERS
# --------------------------------------------------
with freeze_time(now_start), self.mock_mail_gateway():
with self.mock_datetime_and_now(now_start), self.mock_mail_gateway():
reg3 = self.env['event.registration'].create({
'create_date': now_start,
'event_id': test_event.id,
'name': 'Reg3',
'email': 'reg3@example.com',
@@ -232,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
@@ -265,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')
@@ -284,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
@@ -353,7 +438,7 @@ class TestMailSchedule(EventCase, MockEmail):
event_date_begin = datetime(2023, 7, 26, 8, 0, 0)
event_date_end = datetime(2023, 7, 28, 18, 0, 0)
with freeze_time(now):
with self.mock_datetime_and_now(now):
test_event = self.env['event.event'].with_user(self.user_eventmanager).create({
'name': 'TestEventMail',
'date_begin': event_date_begin,
@@ -377,15 +462,13 @@ class TestMailSchedule(EventCase, MockEmail):
event_prev_scheduler = self.env['event.mail'].search([('event_id', '=', test_event.id), ('interval_type', '=', 'before_event')])
with freeze_time(now), self.mock_mail_gateway():
with self.mock_datetime_and_now(now), self.mock_mail_gateway():
self.env['event.registration'].create({
'create_date': now,
'event_id': test_event.id,
'name': 'Reg1',
'email': 'reg1@example.com',
})
self.env['event.registration'].create({
'create_date': now,
'event_id': test_event.id,
'name': 'Reg2',
'email': 'reg2@example.com',
+2 -1
View File
@@ -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
+9 -8
View File
@@ -226,14 +226,15 @@ class TestEventFullCommon(EventCrmCase, TestSalesCommon, MockVisitor):
'is_published': True,
}
cls.test_event = cls.env['event.event'].create({
'name': 'Test Event',
'date_begin': datetime.now() + timedelta(days=1),
'date_end': datetime.now() + timedelta(days=5),
'date_tz': 'Europe/Brussels',
'event_type_id': cls.test_event_type.id,
'is_published': True,
})
with cls.mock_datetime_and_now(cls, cls.reference_now):
cls.test_event = cls.env['event.event'].create({
'name': 'Test Event',
'date_begin': datetime.now() + timedelta(days=1),
'date_end': datetime.now() + timedelta(days=5),
'date_tz': 'Europe/Brussels',
'event_type_id': cls.test_event_type.id,
'is_published': True,
})
# update post-synchronize data
ticket_1 = cls.test_event.event_ticket_ids.filtered(lambda t: t.name == 'Ticket1')
ticket_2 = cls.test_event.event_ticket_ids.filtered(lambda t: t.name == 'Ticket2')
@@ -77,9 +77,6 @@ class TestEventEvent(TestEventFullCommon):
@freeze_time('2021-12-01 11:00:00')
@users('event_user')
def test_event_seats_and_schedulers(self):
now = datetime.now() # used to force create_date, as sql is not wrapped by freeze gun
self.env.cr._now = now
test_event = self.env['event.event'].browse(self.test_event.ids)
ticket_1 = test_event.event_ticket_ids.filtered(lambda ticket: ticket.name == 'Ticket1')
ticket_2 = test_event.event_ticket_ids.filtered(lambda ticket: ticket.name == 'Ticket2')
@@ -93,14 +90,15 @@ class TestEventEvent(TestEventFullCommon):
self.assertFalse(ticket_2.sale_available)
# make 9 registrations (let 1 on ticket)
with self.mock_mail_gateway():
with self.mock_datetime_and_now(self.reference_now), \
self.mock_mail_gateway():
self.env['event.registration'].create([
{'create_date': now,
'email': 'test.customer.%02d@test.example.com' % x,
'phone': '04560011%02d' % x,
'event_id': test_event.id,
'event_ticket_id': ticket_1.id,
'name': 'Customer %d' % x,
{
'email': 'test.customer.%02d@test.example.com' % x,
'phone': '04560011%02d' % x,
'event_id': test_event.id,
'event_ticket_id': ticket_1.id,
'name': 'Customer %d' % x,
}
for x in range(0, 9)
])
@@ -113,27 +111,29 @@ class TestEventEvent(TestEventFullCommon):
self.assertEqual(ticket_2.seats_available, 0)
# prevent registration due to ticket limit
with self.assertRaises(exceptions.ValidationError):
with self.mock_datetime_and_now(self.reference_now), \
self.assertRaises(exceptions.ValidationError):
self.env['event.registration'].create([
{'create_date': now,
'email': 'additional.customer.%02d@test.example.com' % x,
'phone': '04560011%02d' % x,
'event_id': test_event.id,
'event_ticket_id': ticket_1.id,
'name': 'Additional Customer %d' % x,
{
'email': 'additional.customer.%02d@test.example.com' % x,
'phone': '04560011%02d' % x,
'event_id': test_event.id,
'event_ticket_id': ticket_1.id,
'name': 'Additional Customer %d' % x,
}
for x in range(0, 2)
])
# make 20 registrations (on free ticket)
with self.mock_mail_gateway():
with self.mock_datetime_and_now(self.reference_now), \
self.mock_mail_gateway():
self.env['event.registration'].create([
{'create_date': now,
'email': 'other.customer.%02d@test.example.com' % x,
'phone': '04560011%02d' % x,
'event_id': test_event.id,
'event_ticket_id': ticket_2.id,
'name': 'Other Customer %d' % x,
{
'email': 'other.customer.%02d@test.example.com' % x,
'phone': '04560011%02d' % x,
'event_id': test_event.id,
'event_ticket_id': ticket_2.id,
'name': 'Other Customer %d' % x,
}
for x in range(0, 20)
])
@@ -144,14 +144,15 @@ class TestEventEvent(TestEventFullCommon):
self.assertEqual(ticket_2.seats_available, 0)
# prevent registration due to event limit
with self.assertRaises(exceptions.ValidationError):
with self.mock_datetime_and_now(self.reference_now), \
self.assertRaises(exceptions.ValidationError):
self.env['event.registration'].create([
{'create_date': now,
'email': 'additional.customer.%02d@test.example.com' % x,
'phone': '04560011%02d' % x,
'event_id': test_event.id,
'event_ticket_id': ticket_2.id,
'name': 'Additional Customer %d' % x,
{
'email': 'additional.customer.%02d@test.example.com' % x,
'phone': '04560011%02d' % x,
'event_id': test_event.id,
'event_ticket_id': ticket_2.id,
'name': 'Additional Customer %d' % x,
}
for x in range(0, 2)
])
@@ -3,11 +3,13 @@
from odoo.addons.test_event_full.tests.common import TestWEventCommon
from odoo.exceptions import ValidationError
from odoo.tests import tagged
@tagged('event_mail')
class TestTemplateRefModel(TestWEventCommon):
def test_template_ref_model_constraint(self):
test_cases = [
('mail', 'mail.template', True),
('mail', 'sms.template', False),
+176 -24
View File
@@ -116,50 +116,202 @@ class TestMailTemplate(TestMailTemplateCommon):
self.assertEqual(mail.body_html, body_result)
self.assertEqual(mail.body, body_result)
@tagged('mail_template', 'multi_lang')
@tagged('mail_template', 'multi_lang', 'post_install', '-at_install')
class TestMailTemplateLanguages(TestMailTemplateCommon):
@classmethod
def setUpClass(cls):
""" Create lang-based records and templates, to test batch and performances
with language involved. """
super().setUpClass()
# use test notification layout
cls.test_template.write({
'email_layout_xmlid': 'mail.test_layout',
})
# double record, one in each lang
cls.test_records = cls.test_record + cls.env['mail.test.lang'].create({
'email_from': 'ignasse.es@example.com',
'lang': 'es_ES',
'name': 'Test Record 2',
})
# pure batch, 100 records
cls.test_records_batch, test_partners = cls._create_records_for_batch(
'mail.test.lang', 100,
)
test_partners[:50].lang = 'es_ES'
# have a template with dynamic templates to check impact
cls.test_template_wreports = cls.test_template.copy({
'email_layout_xmlid': 'mail.test_layout',
})
cls.test_reports = cls.env['ir.actions.report'].create([
{
'name': f'Test Report on {cls.test_record._name}',
'model': cls.test_record._name,
'print_report_name': "f'TestReport for {object.name}'",
'report_type': 'qweb-pdf',
'report_name': 'test_mail.mail_test_ticket_test_template',
}, {
'name': f'Test Report 2 on {cls.test_record._name}',
'model': cls.test_record._name,
'print_report_name': "f'TestReport2 for {object.name}'",
'report_type': 'qweb-pdf',
'report_name': 'test_mail.mail_test_ticket_test_template_2',
}
])
cls.test_template_wreports.report_template_ids = cls.test_reports
cls.env.flush_all()
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_template_send_email(self):
mail_id = self.test_template.send_mail(self.test_record.id)
mail = self.env['mail.mail'].sudo().browse(mail_id)
""" Test 'send_email' on template on a given record, used notably as
contextual action. """
self.env.invalidate_all()
with self.with_user(self.user_employee.login), self.assertQueryCount(28): # test_mail: 28
mail_id = self.test_template.with_env(self.env).send_mail(self.test_record.id)
mail = self.env['mail.mail'].sudo().browse(mail_id)
self.assertEqual(sorted(mail.attachment_ids.mapped('name')), ['first.txt', 'second.txt'])
self.assertEqual(mail.body_html,
f'<body><p>EnglishBody for {self.test_record.name}</p> English Layout for Lang Chatter Model</body>')
self.assertEqual(mail.email_cc, self.test_template.email_cc)
self.assertEqual(mail.email_to, self.test_template.email_to)
self.assertEqual(mail.recipient_ids, self.partner_2 | self.user_admin.partner_id)
self.assertEqual(mail.subject, 'EnglishSubject for %s' % self.test_record.name)
self.assertEqual(mail.subject, f'EnglishSubject for {self.test_record.name}')
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_template_send_email_nolayout(self):
""" Test without layout, just to check impact """
self.test_template.email_layout_xmlid = False
self.env.invalidate_all()
with self.with_user(self.user_employee.login), self.assertQueryCount(21): # test_mail: 21
mail_id = self.test_template.with_env(self.env).send_mail(self.test_record.id)
mail = self.env['mail.mail'].sudo().browse(mail_id)
self.assertEqual(sorted(mail.attachment_ids.mapped('name')), ['first.txt', 'second.txt'])
self.assertEqual(mail.body_html,
f'<p>EnglishBody for {self.test_record.name}</p>')
self.assertEqual(mail.email_cc, self.test_template.email_cc)
self.assertEqual(mail.email_to, self.test_template.email_to)
self.assertEqual(mail.recipient_ids, self.partner_2 | self.user_admin.partner_id)
self.assertEqual(mail.subject, f'EnglishSubject for {self.test_record.name}')
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_template_send_email_batch(self):
""" Test 'send_email' on template in batch """
self.env.invalidate_all()
mails = self.env['mail.mail'].sudo()
with self.with_user(self.user_employee.login), self.assertQueryCount(934): # test_mail: 934
template = self.test_template.with_env(self.env)
for record in self.test_records_batch:
mails += mails.browse(template.send_mail(record.id))
self.assertEqual(len(mails), 100)
for idx, (mail, record) in enumerate(zip(mails, self.test_records_batch)):
self.assertEqual(sorted(mail.attachment_ids.mapped('name')), ['first.txt', 'second.txt'])
self.assertEqual(mail.email_cc, self.test_template.email_cc)
self.assertEqual(mail.email_to, self.test_template.email_to)
self.assertEqual(mail.recipient_ids, self.partner_2 | self.user_admin.partner_id)
if idx >= 50:
self.assertEqual(mail.subject, f'EnglishSubject for {record.name}')
else:
self.assertEqual(mail.subject, f'SpanishSubject for {record.name}')
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_template_send_email_wreport(self):
""" Test 'send_email' on template on a given record, used notably as
contextual action, with dynamic reports involved """
self.env.invalidate_all()
with self.with_user(self.user_employee.login), self.assertQueryCount(107): # test_mail: 106
mail_id = self.test_template_wreports.with_env(self.env).send_mail(self.test_record.id)
mail = self.env['mail.mail'].sudo().browse(mail_id)
self.assertEqual(
sorted(mail.attachment_ids.mapped('name')),
[f'TestReport for {self.test_record.name}.html', f'TestReport2 for {self.test_record.name}.html', 'first.txt', 'second.txt']
)
self.assertEqual(mail.recipient_ids, self.partner_2 | self.user_admin.partner_id)
self.assertEqual(mail.subject, f'EnglishSubject for {self.test_record.name}')
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_template_send_email_wreport_batch(self):
""" Test 'send_email' on template in batch with dynamic reports """
self.env.invalidate_all()
mails = self.env['mail.mail'].sudo()
with self.with_user(self.user_employee.login), self.assertQueryCount(1645): # test_mail: 1645
template = self.test_template_wreports.with_env(self.env)
for record in self.test_records_batch:
mails += mails.browse(template.send_mail(record.id))
self.assertEqual(len(mails), 100)
for idx, (mail, record) in enumerate(zip(mails, self.test_records_batch)):
self.assertEqual(
sorted(mail.attachment_ids.mapped('name')),
[f'TestReport for {record.name}.html', f'TestReport2 for {record.name}.html', 'first.txt', 'second.txt']
)
self.assertEqual(mail.email_cc, self.test_template.email_cc)
self.assertEqual(mail.email_to, self.test_template.email_to)
self.assertEqual(mail.recipient_ids, self.partner_2 | self.user_admin.partner_id)
if idx >= 50:
self.assertEqual(mail.subject, f'EnglishSubject for {record.name}')
self.assertEqual(mail.body_html,
f'<body><p>EnglishBody for {record.name}</p> English Layout for Lang Chatter Model</body>')
else:
self.assertEqual(mail.subject, f'SpanishSubject for {record.name}')
self.assertEqual(mail.body_html,
f'<body><p>SpanishBody for {record.name}</p> Spanish Layout para Spanish Model Description</body>')
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_template_translation_lang(self):
""" Test template rendering using lang defined directly on the record """
test_record = self.test_record.with_env(self.env)
test_record.write({
'lang': 'es_ES',
})
test_template = self.test_template.with_env(self.env)
test_template.write({
'email_layout_xmlid': 'mail.test_layout',
})
mail_id = test_template.send_mail(test_record.id)
mail = self.env['mail.mail'].sudo().browse(mail_id)
self.assertEqual(mail.body_html,
'<body><p>SpanishBody for %s</p> Spanish Layout para Spanish Model Description</body>' % self.test_record.name)
self.assertEqual(mail.subject, 'SpanishSubject for %s' % self.test_record.name)
f'<body><p>SpanishBody for {self.test_record.name}</p> Spanish Layout para Spanish Model Description</body>')
self.assertEqual(mail.subject, f'SpanishSubject for {self.test_record.name}')
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_template_translation_partner_lang(self):
test_record = self.env['mail.test.lang'].browse(self.test_record.ids)
customer = self.env['res.partner'].create({
'email': 'robert.carlos@test.example.com',
'lang': 'es_ES',
'name': 'Roberto Carlos',
})
test_record.write({
'customer_id': customer.id,
})
test_template = self.env['mail.template'].browse(self.test_template.ids)
""" Test template rendering using lang defined on a sub-record aka
'partner_id.lang' """
test_records = self.env['mail.test.lang'].browse(self.test_records.ids)
customers = self.env['res.partner'].create([
{
'email': 'roberto.carlos@test.example.com',
'lang': 'es_ES',
'name': 'Roberto Carlos',
}, {
'email': 'rob.charly@test.example.com',
'lang': 'en_US',
'name': 'Rob Charly',
}
])
test_records[0].write({'customer_id': customers[0].id})
test_records[1].write({'customer_id': customers[1].id})
mail_id = test_template.send_mail(test_record.id, email_layout_xmlid='mail.test_layout')
mail = self.env['mail.mail'].sudo().browse(mail_id)
self.assertEqual(mail.body_html,
'<body><p>SpanishBody for %s</p> Spanish Layout para Spanish Model Description</body>' % self.test_record.name)
self.assertEqual(mail.subject, 'SpanishSubject for %s' % self.test_record.name)
self.env.invalidate_all()
mails = self.env['mail.mail'].sudo()
with self.with_user(self.user_employee.login), self.assertQueryCount(52):
template = self.test_template.with_env(self.env)
for record in self.test_records:
mails += mails.browse(
template.send_mail(record.id, email_layout_xmlid='mail.test_layout')
)
self.assertEqual(mails[0].body_html,
f'<body><p>SpanishBody for {test_records[0].name}</p> Spanish Layout para Spanish Model Description</body>')
self.assertEqual(mails[0].subject, f'SpanishSubject for {test_records[0].name}')
self.assertEqual(mails[1].body_html,
f'<body><p>EnglishBody for {test_records[1].name}</p> English Layout for Lang Chatter Model</body>')
self.assertEqual(mails[1].subject, f'EnglishSubject for {test_records[1].name}')
+21 -21
View File
@@ -286,11 +286,11 @@ class TestBaseAPIPerformance(BaseMailPerformance):
# voip module read activity_type during create leading to one less query in enterprise on action_feedback
_category = activity.activity_type_id.category
with self.assertQueryCount(admin=13, employee=13):
with self.assertQueryCount(admin=12, employee=12):
activity.action_feedback(feedback='Zizisse Done !')
@warmup
def test_adv_activity_mixin_batched(self):
def test_activity_mixin_batched(self):
records = self.env['mail.test.activity'].create([{'name': 'Test'}] * 10)
MailActivity = self.env['mail.activity'].with_context({
'default_res_model': 'mail.test.activity',
@@ -304,7 +304,7 @@ class TestBaseAPIPerformance(BaseMailPerformance):
} for record in records])
self.env.invalidate_all()
with self.assertQueryCount(3):
with self.assertQueryCount(2):
records.mapped('activity_date_deadline')
@users('admin', 'employee')
@@ -321,7 +321,7 @@ class TestBaseAPIPerformance(BaseMailPerformance):
record.write({'name': 'Dupe write'})
with self.assertQueryCount(admin=14, employee=14):
with self.assertQueryCount(admin=13, employee=13):
record.action_close('Dupe feedback')
self.assertEqual(record.activity_ids, self.env['mail.activity'])
@@ -347,7 +347,7 @@ class TestBaseAPIPerformance(BaseMailPerformance):
record.write({'name': 'Dupe write'})
with self.assertQueryCount(admin=17, employee=17): # com+tm 16/16
with self.assertQueryCount(admin=15, employee=15): # com+tm 14/14
record.action_close('Dupe feedback', attachment_ids=attachments.ids)
# notifications
@@ -373,7 +373,7 @@ class TestBaseAPIPerformance(BaseMailPerformance):
'partner_ids': [(4, customer_id)],
})
with self.assertQueryCount(admin=39, employee=39): # com 35/35
with self.assertQueryCount(admin=39, employee=39):
composer._action_send_mail()
@users('admin', 'employee')
@@ -394,7 +394,7 @@ class TestBaseAPIPerformance(BaseMailPerformance):
'partner_ids': [(4, customer.id)],
})
with self.assertQueryCount(admin=40, employee=40): # com 35/35
with self.assertQueryCount(admin=40, employee=40):
composer._action_send_mail()
@users('admin', 'employee')
@@ -418,7 +418,7 @@ class TestBaseAPIPerformance(BaseMailPerformance):
composer_form.attachment_ids.add(attachment)
composer = composer_form.save()
with self.assertQueryCount(admin=55, employee=55): # tm+com 53/53
with self.assertQueryCount(admin=55, employee=55): # tm+com 54/54
composer._action_send_mail()
# notifications
@@ -472,7 +472,7 @@ class TestBaseAPIPerformance(BaseMailPerformance):
test_record, test_template = self._create_test_records()
test_template.write({'attachment_ids': [(5, 0)]})
with self.assertQueryCount(admin=27, employee=27): # tm 15/15 / com 25/25
with self.assertQueryCount(admin=27, employee=27): # tm 16/16 / com 26/26
composer = self.env['mail.compose.message'].with_context({
'default_composition_mode': 'comment',
'default_model': test_record._name,
@@ -496,7 +496,7 @@ class TestBaseAPIPerformance(BaseMailPerformance):
def test_mail_composer_w_template_attachments(self):
test_record, test_template = self._create_test_records()
with self.assertQueryCount(admin=28, employee=28): # tm 16/16 / com 26/26
with self.assertQueryCount(admin=28, employee=28): # tm 17/17 / com 27/27
composer = self.env['mail.compose.message'].with_context({
'default_composition_mode': 'comment',
'default_model': test_record._name,
@@ -525,7 +525,7 @@ class TestBaseAPIPerformance(BaseMailPerformance):
test_template.write({'attachment_ids': [(5, 0)]})
customer = self.env['res.partner'].browse(self.customer.ids)
with self.assertQueryCount(admin=34, employee=34): # tm 22/22 / com 32/32
with self.assertQueryCount(admin=34, employee=34): # tm 23/23 / com 33/33
composer_form = Form(
self.env['mail.compose.message'].with_context({
'default_composition_mode': 'comment',
@@ -555,7 +555,7 @@ class TestBaseAPIPerformance(BaseMailPerformance):
test_record, test_template = self._create_test_records()
customer = self.env['res.partner'].browse(self.customer.ids)
with self.assertQueryCount(admin=34, employee=34): # tm 22/22 / com 32/32
with self.assertQueryCount(admin=34, employee=34): # tm 23/23 / com 32/32
composer_form = Form(
self.env['mail.compose.message'].with_context({
'default_composition_mode': 'comment',
@@ -878,7 +878,7 @@ class TestMailAPIPerformance(BaseMailPerformance):
record = self.container.with_user(self.env.user)
# about 20 (19?) queries per additional customer group
with self.assertQueryCount(admin=61, employee=60): # com 55/54
with self.assertQueryCount(admin=60, employee=59):
record.message_post(
body=Markup('<p>Test Post Performances</p>'),
message_type='comment',
@@ -896,7 +896,7 @@ class TestMailAPIPerformance(BaseMailPerformance):
template = self.env.ref('test_mail.mail_test_container_tpl')
# about 20 (19 ?) queries per additional customer group
with self.assertQueryCount(admin=70, employee=69): # com 62/61
with self.assertQueryCount(admin=69, employee=68):
record.message_post_with_source(
template,
message_type='comment',
@@ -920,7 +920,7 @@ class TestMailAPIPerformance(BaseMailPerformance):
'default_template_id': test_template.id,
}).create({})
with self.assertQueryCount(admin=151, employee=151): # com 141/141
with self.assertQueryCount(admin=151, employee=151):
messages_as_sudo = test_records.message_post_with_source(
'test_mail.mail_template_simple_test',
render_values={'partner': self.user_test.partner_id},
@@ -1006,7 +1006,7 @@ class TestMailAPIPerformance(BaseMailPerformance):
customer_id = self.customer.id
user_id = self.user_portal.id
with self.assertQueryCount(admin=102, employee=102):
with self.assertQueryCount(admin=101, employee=101):
rec = self.env['mail.test.ticket'].create({
'name': 'Test',
'container_id': container_id,
@@ -1035,7 +1035,7 @@ class TestMailAPIPerformance(BaseMailPerformance):
rec1 = rec.with_context(active_test=False) # to see inactive records
self.assertEqual(rec1.message_partner_ids, self.user_portal.partner_id | self.env.user.partner_id)
self.assertEqual(len(rec1.message_ids), 1)
with self.assertQueryCount(admin=63, employee=63):
with self.assertQueryCount(admin=62, employee=62):
rec.write({
'name': 'Test2',
'container_id': self.container.id,
@@ -1072,7 +1072,7 @@ class TestMailAPIPerformance(BaseMailPerformance):
rec1 = rec.with_context(active_test=False) # to see inactive records
self.assertEqual(rec1.message_partner_ids, self.user_portal.partner_id | self.env.user.partner_id)
with self.assertQueryCount(admin=70, employee=70):
with self.assertQueryCount(admin=69, employee=69):
rec.write({
'name': 'Test2',
'container_id': container_id,
@@ -1264,7 +1264,7 @@ class TestMailFormattersPerformance(BaseMailPerformance):
"""
messages_all = self.messages_all.with_env(self.env)
with self.assertQueryCount(employee=26): # test_mail: 25
with self.assertQueryCount(employee=26):
res = messages_all.message_format()
self.assertEqual(len(res), 2*2)
@@ -1277,7 +1277,7 @@ class TestMailFormattersPerformance(BaseMailPerformance):
def test_message_format_single(self):
message = self.messages_all[0].with_env(self.env)
with self.assertQueryCount(employee=23): # test_mail: 22
with self.assertQueryCount(employee=23):
res = message.message_format()
self.assertEqual(len(res), 1)
@@ -1382,7 +1382,7 @@ class TestPerformance(BaseMailPerformance):
attachments = self.env['ir.attachment'].with_user(self.env.user).create(self.test_attachments_vals)
# enable_logging = self.cr._enable_logging() if self.warm else nullcontext()
# with self.assertQueryCount(employee=63), enable_logging:
with self.assertQueryCount(employee=69): # com 63
with self.assertQueryCount(employee=68):
record_container.with_context({}).message_post(
body=Markup('<p>Test body <img src="cid:cid1"> <img src="cid:cid2"></p>'),
subject='Test Subject',
@@ -91,7 +91,7 @@ class TestMailPerformance(FullBaseMailPerformance):
record_ticket = self.env['mail.test.ticket.mc'].browse(self.record_ticket.ids)
attachments = self.env['ir.attachment'].create(self.test_attachments_vals)
with self.assertQueryCount(employee=101): # test_mail_full: 95
with self.assertQueryCount(employee=99): # test_mail_full: 95
new_message = record_ticket.message_post(
attachment_ids=attachments.ids,
body=Markup('<p>Test Content</p>'),