diff --git a/addons/mail/tests/common.py b/addons/mail/tests/common.py index 6e6172b3835..48dcb88d976 100644 --- a/addons/mail/tests/common.py +++ b/addons/mail/tests/common.py @@ -230,14 +230,36 @@ class MockEmail(common.BaseCase, MockSmtplibCase): raise AssertionError('sent mail not found for email_to %s' % (email_to)) return sent_email - def _find_mail_mail_wid(self, mail_id): + def _filter_mail(self, status=None, mail_message=None, author=None): + """ Filter mail generated during mock, based on common parameters + + :param status: state of mail.mail. If not void use it to filter mail.mail + record; + :param mail_message: optional check/filter on mail_message_id field aka + a ``mail.message`` record; + :param author: optional check/filter on author_id field aka a ``res.partner`` + record; + """ + filtered = self._new_mails.env['mail.mail'] + for mail in self._new_mails: + if status is not None and mail.state != status: + continue + if mail_message is not None and mail.mail_message_id != mail_message: + continue + if author is not None and mail.author_id != author: + continue + filtered += mail + return filtered + + def _find_mail_mail_wid(self, mail_id, status=None, mail_message=None, author=None): """ Find a ``mail.mail`` record based on a given ID (used notably when having mail ID in mailing traces). :return mail: a ``mail.mail`` record generated during the mock and matching given ID; """ - for mail in self._new_mails: + filtered = self._filter_mail(status=status, mail_message=mail_message, author=author) + for mail in filtered: if mail.id == mail_id: break else: @@ -250,23 +272,12 @@ class MockEmail(common.BaseCase, MockSmtplibCase): :param recipients: a ``res.partner`` recordset Check all of them are in mail recipients to find the right mail.mail record; - :param status: state of mail.mail. If not void use it to filter mail.mail - record; - :param mail_message: optional check/filter on mail_message_id field aka - a ``mail.message`` record; - :param author: optional check/filter on author_id field aka a ``res.partner`` - record; :return mail: a ``mail.mail`` record generated during the mock and matching given parameters and filters; """ - for mail in self._new_mails: - if author is not None and mail.author_id != author: - continue - if mail_message is not None and mail.mail_message_id != mail_message: - continue - if status and mail.state != status: - continue + filtered = self._filter_mail(status=status, mail_message=mail_message, author=author) + for mail in filtered: if all(p in mail.recipient_ids for p in recipients): break else: @@ -279,35 +290,25 @@ class MockEmail(common.BaseCase, MockSmtplibCase): :param email_to: either matching mail.email_to value, either a mail sent to a single recipient whose email is email_to; - :param status: state of mail.mail. If not void use it to filter mail.mail - record; - :param mail_message: optional check/filter on mail_message_id field aka - a ``mail.message`` record; - :param author: optional check/filter on author_id field aka a ``res.partner`` - record; :return mail: a ``mail.mail`` record generated during the mock and matching given parameters and filters; """ - for mail in self._new_mails: - if author is not None and mail.author_id != author: - continue - if mail_message is not None and mail.mail_message_id != mail_message: - continue - if status and mail.state != status: - continue + filtered = self._filter_mail(status=status, mail_message=mail_message, author=author) + for mail in filtered: if (mail.email_to == email_to and not mail.recipient_ids) or (not mail.email_to and mail.recipient_ids.email == email_to): break else: raise AssertionError('mail.mail not found for email_to %s / status %s in %s' % (email_to, status, repr([m.email_to for m in self._new_mails]))) return mail - def _find_mail_mail_wrecord(self, record): + def _find_mail_mail_wrecord(self, record, status=None, mail_message=None, author=None): """ Find a mail.mail record based on model / res_id of a record. :return mail: a ``mail.mail`` record generated during the mock; """ - for mail in self._new_mails: + filtered = self._filter_mail(status=status, mail_message=mail_message, author=author) + for mail in filtered: if mail.model == record._name and mail.res_id == record.id: break else: @@ -464,15 +465,18 @@ class MockEmail(common.BaseCase, MockSmtplibCase): either a partner record; :param values: dictionary of additional values to check email content; """ - base_expected = {} - for fname in ['author_id', 'reply_to', - 'subject', 'body', 'body_content', 'body_alternative_content', - 'references', 'references_content', - 'attachments']: - if fname in values: - base_expected[fname] = values[fname] + direct_check = ['body_alternative', 'email_from', 'references', 'reply_to', 'subject'] + content_check = ['body_alternative_content', 'body_content', 'references_content'] + other_check = ['attachments', 'body', 'attachments_info'] + + expected = {} + for fname in direct_check + content_check + other_check: + if fname in values: + expected[fname] = values[fname] + unknown = set(values.keys()) - set(direct_check + content_check + other_check) + if unknown: + raise NotImplementedError('Unsupported %s' % ', '.join(unknown)) - expected = dict(base_expected) if isinstance(author, self.env['res.partner'].__class__): expected['email_from'] = formataddr((author.name, author.email)) else: @@ -493,7 +497,7 @@ class MockEmail(common.BaseCase, MockSmtplibCase): debug_info = '-'.join('From: %s-To: %s' % (mail['email_from'], mail['email_to']) for mail in self._mails) if not bool(sent_mail) else '' self.assertTrue(bool(sent_mail), 'Expected mail from %s to %s not found in %s' % (expected['email_from'], expected['email_to'], debug_info)) - for val in ['reply_to', 'subject', 'references']: + for val in direct_check: if val in expected: self.assertEqual(expected[val], sent_mail[val], 'Value for %s: expected %s, received %s' % (val, expected[val], sent_mail[val])) if 'attachments' in expected: @@ -501,22 +505,18 @@ class MockEmail(common.BaseCase, MockSmtplibCase): sorted(expected['attachments']), sorted(sent_mail['attachments']), 'Value for %s: expected %s, received %s' % ('attachments', expected['attachments'], sent_mail['attachments']) ) - if 'attachments_info' in values: + if 'attachments_info' in expected: attachments = sent_mail['attachments'] - for attachment_info in values['attachments_info']: + for attachment_info in expected['attachments_info']: attachment = next(attach for attach in attachments if attach[0] == attachment_info['name']) if attachment_info.get('raw'): self.assertEqual(attachment[1], attachment_info['raw']) if attachment_info.get('type'): self.assertEqual(attachment[2], attachment_info['type']) - self.assertEqual(len(values['attachments_info']), len(attachments)) - for val in ['body', 'body_alternative']: - if val in expected: - self.assertHtmlEqual( - expected[val], sent_mail[val], - 'Value for %s: expected %s, received %s' % (val, expected[val], sent_mail[val]) - ) - for val in ['body_content', 'body_alternative_content', 'references_content']: + self.assertEqual(len(expected['attachments_info']), len(attachments)) + if 'body' in expected: + self.assertHtmlEqual(expected['body'], sent_mail['body'], 'Value for %s: expected %s, received %s' % ('body', expected['body'], sent_mail['body'])) + for val in content_check: if val in expected: self.assertIn( expected[val], sent_mail[val[:-8]], @@ -761,7 +761,7 @@ class MailCase(MockEmail): # check notifications and prepare assert data email_groups = defaultdict(list) - mail_groups = {'failure': []} + mail_groups = {'failure': [], 'outgoing': []} for recipient in message_info['notif']: partner, ntype, ngroup, nstatus = recipient['partner'], recipient['type'], recipient.get('group'), recipient.get('status', 'sent') nis_read, ncheck_send = recipient.get('is_read', False if recipient['type'] == 'inbox' else True), recipient.get('check_send', True) @@ -788,6 +788,12 @@ class MailCase(MockEmail): if nstatus == 'sent': if ncheck_send: email_groups[ngroup].append(partner) + # when force_send is False notably, notifications are ready and emails outgoing + elif nstatus == 'ready': + mail_groups['outgoing'].append(partner) + if ncheck_send: + email_groups[ngroup].append(partner) + # canceled: currently nothing checked elif nstatus == 'exception': mail_groups['failure'].append(partner) if ncheck_send: @@ -813,27 +819,26 @@ class MailCase(MockEmail): for recipients in email_groups.values(): partners = self.env['res.partner'].sudo().concat(*recipients) if all(p in mail_groups['failure'] for p in partners): - if not self.mail_unlink_sent: - self.assertMailMail(partners, 'exception', - author=message.author_id, - mail_message=message) - else: - for recipient in partners: - self.assertSentEmail(message.author_id, [recipient]) + mail_status = 'exception' + elif all(p in mail_groups['outgoing'] for p in partners): + mail_status = 'outgoing' else: - if not self.mail_unlink_sent: - self.assertMailMail( - partners, 'sent', - author=message.author_id if message.author_id else message.email_from, - mail_message=message, - email_values=email_values, + mail_status = 'sent' + if not self.mail_unlink_sent: + self.assertMailMail( + partners, mail_status, + author=message.author_id if message.author_id else message.email_from, + mail_message=message, + email_values=email_values, + ) + else: + for recipient in partners: + self.assertSentEmail( + message.author_id if message.author_id else message.email_from, + [recipient], + **email_values ) - else: - for recipient in partners: - self.assertSentEmail(message.author_id if message.author_id else message.email_from, - [recipient], - **email_values - ) + if not any(p for recipients in email_groups.values() for p in recipients): self.assertNoMail(partners, mail_message=message, author=message.author_id) diff --git a/addons/test_mail/tests/test_mail_composer.py b/addons/test_mail/tests/test_mail_composer.py index ba416aef8c6..ead685ad71e 100644 --- a/addons/test_mail/tests/test_mail_composer.py +++ b/addons/test_mail/tests/test_mail_composer.py @@ -867,7 +867,6 @@ class TestComposerResultsComment(TestMailComposer): email_values={ 'body_content': 'TemplateBody %s' % self.test_record.name, 'email_from': self.test_record.user_id.email_formatted, # set by template - 'mail_server_id': self.mail_server_domain, 'subject': 'TemplateSubject %s' % self.test_record.name, 'attachments_info': [ {'name': 'AttFileName_00.txt', 'raw': b'AttContent_00', 'type': 'text/plain'}, @@ -875,7 +874,9 @@ class TestComposerResultsComment(TestMailComposer): {'name': 'TestReport for %s.html' % self.test_record.name, 'type': 'text/plain'}, ] }, - fields_values={}, + fields_values={ + 'mail_server_id': self.mail_server_domain, + }, ) self.assertMailMail(self.test_record.customer_id + new_partners, 'sent', mail_message=message, @@ -883,7 +884,6 @@ class TestComposerResultsComment(TestMailComposer): email_values={ 'body_content': 'TemplateBody %s' % self.test_record.name, 'email_from': self.test_record.user_id.email_formatted, # set by template - 'mail_server_id': self.mail_server_domain, 'subject': 'TemplateSubject %s' % self.test_record.name, 'attachments_info': [ {'name': 'AttFileName_00.txt', 'raw': b'AttContent_00', 'type': 'text/plain'}, @@ -891,7 +891,9 @@ class TestComposerResultsComment(TestMailComposer): {'name': 'TestReport for %s.html' % self.test_record.name, 'type': 'text/plain'}, ] }, - fields_values={}, + fields_values={ + 'mail_server_id': self.mail_server_domain, + }, ) # message is posted and notified admin @@ -1081,14 +1083,16 @@ class TestComposerResultsMass(TestMailComposer): email_values={ 'body_content': 'TemplateBody %s' % record.name, 'email_from': self.partner_employee_2.email_formatted, - 'mail_server_id': self.mail_server_global.id, 'subject': 'TemplateSubject %s' % record.name, 'attachments_info': [ {'name': 'AttFileName_00.txt', 'raw': b'AttContent_00', 'type': 'text/plain'}, {'name': 'AttFileName_01.txt', 'raw': b'AttContent_01', 'type': 'text/plain'}, {'name': 'TestReport for %s.html' % record.name, 'type': 'text/plain'}, ] - } + }, + fields_values={ + 'mail_server_id': self.mail_server_domain, + }, ) @users('employee') @@ -1113,9 +1117,9 @@ class TestComposerResultsMass(TestMailComposer): self.assertEqual(len(self._mails), 2, 'Should have sent 1 email per record') for record in self.test_records: - # template is sent directly using customer field - self.assertSentEmail(self.partner_employee_2.email_formatted, record.customer_id, - author_id=self.partner_employee) + # template is sent directly using customer field, even if author is partner_employee + self.assertSentEmail(self.partner_employee_2.email_formatted, + record.customer_id) # 2: active_domain not taken into account if use_active_domain is False composer_form = Form(self.env['mail.compose.message'].with_context(