From fbcc1bf4c05440bcc55d47cd6f2f61326f54aecc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?=
Back to my amazing body
Back to my amazing body
TemplateBody {self.test_record.name}
') self.assertEqual(composer.mail_server_id, self.template.mail_server_id) self.assertEqual(composer.record_name, self.test_record.name) + self.assertEqual(FieldDatetime.from_string(composer.scheduled_date), self.reference_now + timedelta(days=2)) self.assertEqual(composer.subject, f'TemplateSubject {self.test_record.name}') else: self.assertEqual(composer.body, self.template.body_html) self.assertEqual(composer.mail_server_id, self.template.mail_server_id) self.assertFalse(composer.record_name) + self.assertEqual(composer.scheduled_date, self.template.scheduled_date) self.assertEqual(composer.subject, self.template.subject) # 3. check at create @@ -714,11 +749,13 @@ class TestComposerInternals(TestMailComposer): self.assertEqual(composer.body, f'TemplateBody {self.test_record.name}
') self.assertEqual(composer.mail_server_id, self.template.mail_server_id) self.assertEqual(composer.record_name, self.test_record.name) + self.assertEqual(FieldDatetime.from_string(composer.scheduled_date), self.reference_now + timedelta(days=2)) self.assertEqual(composer.subject, f'TemplateSubject {self.test_record.name}') else: self.assertEqual(composer.body, self.template.body_html) self.assertEqual(composer.mail_server_id, self.template.mail_server_id) self.assertFalse(composer.record_name) + self.assertEqual(composer.scheduled_date, self.template.scheduled_date) self.assertEqual(composer.subject, self.template.subject) # 4. template + user input @@ -727,6 +764,7 @@ class TestComposerInternals(TestMailComposer): 'body': 'Test Body
', 'mail_server_id': False, 'record_name': 'CustomName', + 'scheduled_date': '{{ datetime.datetime(2023, 1, 10, 10, 0, 0) }}', 'subject': 'My amazing subject', }) @@ -734,6 +772,7 @@ class TestComposerInternals(TestMailComposer): self.assertEqual(composer.body, 'Test Body
') self.assertEqual(composer.mail_server_id.id, False) self.assertEqual(composer.record_name, 'CustomName') + self.assertEqual(composer.scheduled_date, '{{ datetime.datetime(2023, 1, 10, 10, 0, 0) }}') self.assertEqual(composer.subject, 'My amazing subject') @users('employee') @@ -1015,7 +1054,7 @@ class TestComposerInternals(TestMailComposer): @tagged('mail_composer') -class TestComposerResultsComment(TestMailComposer): +class TestComposerResultsComment(TestMailComposer, CronMixinCase): """ Test global output of composer used in comment mode. Test notably notification and emails generated during this process. """ @@ -1175,14 +1214,24 @@ class TestComposerResultsComment(TestMailComposer): self.assertEqual(message.partner_ids, self.partner_1 | self.partner_2) @users('employee') - @mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail') + @mute_logger('odoo.models.unlink', 'odoo.addons.mail.models.mail_mail', 'odoo.addons.mail.models.mail_message_schedule') def test_mail_composer_wtpl_complete(self): """ Test a posting process using a complex template, holding several additional recipients and attachments. It is done in monorecord and in batch since this is now supported. This tests notifies: 2 new email_to (+ 1 duplicated), 1 email_cc, - test_records followers and partner_admin added in partner_to.""" + test_records followers and partner_admin added in partner_to. + + Global notification + * monorecord: send notifications right away (force_send=True) + * multirecord: delay notification sending (force_send=False) + + Use cases + * scheduled_date: creates mail.message.schedule (no email sent), then + scheduling send notifications with notification parameters kept + * otherwise: global behavior + """ attachment_data = self._generate_attachments_data(2, self.template._name, self.template.id) email_to_1 = 'test.to.1@test.example.com' email_to_2 = 'test.to.2@test.example.com' @@ -1200,8 +1249,14 @@ class TestComposerResultsComment(TestMailComposer): attachs = self.env['ir.attachment'].search([('name', 'in', [a['name'] for a in attachment_data])]) self.assertEqual(len(attachs), 2) - for test_records, batch in ((self.test_record, False), (self.test_records, True)): - with self.subTest(test_records=test_records, batch=batch): + for batch, scheduled_date in product( + (False, True), + (False, '{{ (object.create_date or datetime.datetime(2022, 12, 26, 18, 0, 0)) + datetime.timedelta(days=2) }}') + ): + with self.subTest(batch=batch, scheduled_date=scheduled_date): + self.template.write({'scheduled_date': scheduled_date}) + test_records = self.test_records if batch else self.test_record + # ensure initial data self.assertEqual(len(test_records.customer_id), len(test_records)) self.assertEqual(test_records.user_id, self.user_employee_2) @@ -1211,37 +1266,72 @@ class TestComposerResultsComment(TestMailComposer): composer_form = Form(self.env['mail.compose.message'].with_context( self._get_web_context(test_records, add_web=True, default_composition_mode='comment', - default_template_id=self.template.id) + default_template_id=self.template.id, + # avoid successive tests issues with followers + mail_create_nosubscribe=True, + ) )) composer = composer_form.save() self.assertFalse(composer.reply_to_force_new, 'Mail: thread-enabled models should use auto thread by default') - with self.mock_mail_gateway(mail_unlink_sent=False), self.mock_mail_app(): + + # due to scheduled_date, cron for sending notification will be used + schedule_cron_id = self.env.ref('mail.ir_cron_send_scheduled_message').id + with self.mock_mail_gateway(mail_unlink_sent=False), \ + self.mock_mail_app(), \ + freeze_time(self.reference_now), \ + self.capture_triggers(schedule_cron_id) as capt: composer._action_send_mail() - # check new partners have been created based on emails given - new_partners = self.env['res.partner'].search([ - ('email', 'in', [email_to_1, email_to_2, email_to_3, email_cc_1]) - ]) - self.assertEqual(len(new_partners), 3) - self.assertEqual(set(new_partners.mapped('email')), - set(['test.to.1@test.example.com', 'test.to.2@test.example.com', 'test.cc.1@test.example.com']) - ) - - # global outgoing: one mail.mail (all customer recipients, then all employee recipients) - # and 5 emails, and 1 inbox notification (admin) - self.assertEqual(len(self._new_mails), 2 * len(test_records), - 'Should have created 1 mail.mail per group per record') + # notification process should not have been sent + if scheduled_date: + self.assertFalse(self._new_mails) + self.assertFalse(self._mails) # monorecord: force_send notifications - if not batch: - self.assertEqual(len(self._mails), 5, 'Should have sent 5 emails, one per recipient per record') + elif not batch: self.assertEqual(self._new_mails.mapped('state'), ['sent'] * len(self._new_mails)) - # mutlirecord: use email queue + self.assertEqual(len(self._mails), 5, 'Should have sent 5 emails, one per recipient per record') + # multirecord: use email queue else: - self.assertEqual(len(self._mails), 0, 'Should have put emails in queue and not sent any emails') self.assertEqual(self._new_mails.mapped('state'), ['outgoing'] * len(self._new_mails)) + self.assertEqual(len(self._mails), 0, 'Should have put emails in queue and not sent any emails') # simulate cron sending emails self.env['mail.mail'].sudo().process_email_queue() + # notification process should not have been sent + if scheduled_date: + self.assertEqual( + capt.records.mapped('call_at'), [self.reference_now + timedelta(days=2)] * len(test_records), + msg='Should have created a cron trigger for the scheduled sending' + ) + else: + self.assertFalse(capt.records) + + # check new partners have been created based on emails given + new_partners = self.env['res.partner'].search([ + ('email', 'in', [email_to_1, email_to_2, email_to_3, email_cc_1]) + ]) + self.assertEqual(len(new_partners), 3) + self.assertEqual( + set(new_partners.mapped('email')), + set(['test.to.1@test.example.com', 'test.to.2@test.example.com', 'test.cc.1@test.example.com']) + ) + + # if scheduled_date is set: simulate cron for sending notifications + if scheduled_date: + # Send the scheduled message from the CRON + with self.mock_mail_gateway(mail_unlink_sent=False), \ + self.mock_mail_app(), \ + freeze_time(self.reference_now + timedelta(days=3)): + self.env['mail.message.schedule'].sudo()._send_notifications_cron() + + # global outgoing: one mail.mail (all customer recipients, then all employee recipients) + # and 5 emails, and 1 inbox notification (admin) + self.assertEqual(len(self._new_mails), 2 * len(test_records), + 'Should have created 1 mail.mail per group per record') + self.assertEqual(self._new_mails.mapped('state'), ['sent'] * len(self._new_mails)) + self.assertEqual(len(self._mails), 5 * len(test_records), + 'Should have sent 5 emails, one per recipient per record') + # template is sent only to partners (email_to are transformed) for test_record in test_records: message = test_record.message_ids[0] @@ -1507,6 +1597,9 @@ class TestComposerResultsMass(TestMailComposer): def test_mail_composer_wtpl_complete(self): """ Test a composer in mass mode with a quite complete template, containing notably email-based recipients and attachments. """ + # as we use the email queue, don't have failing tests due to other outgoing emails + self.env['mail.mail'].sudo().search([]).unlink() + attachment_data = self._generate_attachments_data(2, self.template._name, self.template.id) email_to_1 = 'test.to.1@test.example.com' email_to_2 = 'test.to.2@test.example.com' @@ -1534,16 +1627,25 @@ class TestComposerResultsMass(TestMailComposer): default_template_id=self.template.id) )) composer = composer_form.save() - with self.mock_mail_gateway(mail_unlink_sent=False): + with self.mock_mail_gateway(mail_unlink_sent=False), \ + freeze_time(self.reference_now): composer._action_send_mail() - new_partners = self.env['res.partner'].search([ - ('email', 'in', [email_to_1, email_to_2, email_to_3, email_cc_1]) - ]) - self.assertEqual(len(new_partners), 3) + new_partners = self.env['res.partner'].search([ + ('email', 'in', [email_to_1, email_to_2, email_to_3, email_cc_1]) + ]) + self.assertEqual(len(new_partners), 3) + + # global outgoing: emails not sent due to scheduled_date + self.assertEqual(len(self._new_mails), 2, 'Should have created 1 mail.mail per record') + self.assertEqual(len(self._mails), 0, 'Should not send emails, scheduled in the future') + self.assertEqual(self._new_mails.mapped('scheduled_date'), + [self.reference_now + timedelta(days=2)] * 2) + + # simulate cron queue at right time for sending + with freeze_time(self.reference_now + timedelta(days=2)): + self.env['mail.mail'].sudo().process_email_queue() - # global outgoing - self.assertEqual(len(self._new_mails), 2, 'Should have created 1 mail.mail per record') self.assertEqual(len(self._mails), 10, 'Should have sent 5 emails per record') # hack to use assertEmails: filtering on from/to only is not sufficient to distinguish emails