diff --git a/addons/crm/tests/test_crm_lead_convert_mass.py b/addons/crm/tests/test_crm_lead_convert_mass.py index c7592f9b5fd..0a3010fc3fa 100644 --- a/addons/crm/tests/test_crm_lead_convert_mass.py +++ b/addons/crm/tests/test_crm_lead_convert_mass.py @@ -167,7 +167,7 @@ class TestLeadConvertMass(crm_common.TestLeadConvertMassCommon): user_ids = self.assign_users.ids # randomness: at least 1 query - with self.assertQueryCount(user_sales_manager=1798): # crm 1503 / com 1790 / ent 1798 + with self.assertQueryCount(user_sales_manager=1800): # crm 1503 / com 1790 / ent 1798 mass_convert = self.env['crm.lead2opportunity.partner.mass'].with_context({ 'active_model': 'crm.lead', 'active_ids': test_leads.ids, diff --git a/addons/crm/tests/test_crm_lead_mail_tracking_duration.py b/addons/crm/tests/test_crm_lead_mail_tracking_duration.py index 16be8f3f217..4e750ddd472 100644 --- a/addons/crm/tests/test_crm_lead_mail_tracking_duration.py +++ b/addons/crm/tests/test_crm_lead_mail_tracking_duration.py @@ -1,7 +1,10 @@ -from odoo.addons.mail.tests.mail_tracking_duration_testing import TestMailTrackingDurationMixin +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo.addons.mail.tests.mail_tracking_duration_mixin_case import MailTrackingDurationMixinCase -class TestCrmLeadMailTrackingDuration(TestMailTrackingDurationMixin): +class TestCrmLeadMailTrackingDuration(MailTrackingDurationMixinCase): @classmethod def setUpClass(cls): diff --git a/addons/mail/tests/common.py b/addons/mail/tests/common.py index fbfa71cad4b..d8a39fad0b4 100644 --- a/addons/mail/tests/common.py +++ b/addons/mail/tests/common.py @@ -692,6 +692,13 @@ class MailCase(MockEmail): 'no_reset_password': True, } + def setUp(self): + super().setUp() + # purpose is to avoid nondeterministic tests, notably because tracking is + # accumulated and sent at flush -> we want to test only the result of a + # given test, not setup + test + self.flush_tracking() + @classmethod def _reset_mail_context(cls, record): return record.with_context( @@ -1124,27 +1131,45 @@ class MailCase(MockEmail): self.assertEqual(recipient_notif.is_read, rinfo['is_read']) self.assertEqual(recipient_notif.notification_type, rinfo['type']) - def assertTracking(self, message, data): + def assertTracking(self, message, data, strict=False): tracking_values = message.sudo().tracking_value_ids + if strict: + self.assertEqual(len(tracking_values), len(data), + 'Tracking: tracking does not match') + + suffix_mapping = { + 'boolean': 'integer', + 'char': 'char', + 'date': 'datetime', + 'datetime': 'datetime', + 'integer': 'integer', + 'float': 'float', + 'selection': 'char', + 'text': 'text', + } for field_name, value_type, old_value, new_value in data: tracking = tracking_values.filtered(lambda track: track.field.name == field_name) - self.assertEqual(len(tracking), 1) - if value_type == 'char': - self.assertEqual(tracking.old_value_char, old_value) - self.assertEqual(tracking.new_value_char, new_value) - elif value_type in ('boolean', 'integer'): - self.assertEqual(tracking.old_value_integer, old_value) - self.assertEqual(tracking.new_value_integer, new_value) - elif value_type == 'many2one': + self.assertEqual(len(tracking), 1, f'Tracking: not found for {field_name}') + msg_base = f'Tracking: {field_name} ({value_type}: ' + if value_type in suffix_mapping: + old_value_fname = f'old_value_{suffix_mapping[value_type]}' + new_value_fname = f'new_value_{suffix_mapping[value_type]}' + self.assertEqual(tracking[old_value_fname], old_value, + msg_base + f'expected {old_value}, received {tracking[old_value_fname]})') + self.assertEqual(tracking[new_value_fname], new_value, + msg_base + f'expected {new_value}, received {tracking[new_value_fname]})') + if value_type == 'many2one': self.assertEqual(tracking.old_value_integer, old_value and old_value.id or False) self.assertEqual(tracking.new_value_integer, new_value and new_value.id or False) self.assertEqual(tracking.old_value_char, old_value and old_value.display_name or '') self.assertEqual(tracking.new_value_char, new_value and new_value.display_name or '') elif value_type == 'monetary': + new_value, currency = new_value + self.assertEqual(tracking.currency_id, currency) self.assertEqual(tracking.old_value_monetary, old_value) self.assertEqual(tracking.new_value_monetary, new_value) - else: - self.assertEqual(1, 0) + if value_type not in suffix_mapping and value_type not in {'many2one', 'monetary'}: + self.assertEqual(1, 0, f'Tracking: unsupported tracking test on {value_type}') class MailCommon(common.TransactionCase, MailCase): diff --git a/addons/mail/tests/mail_tracking_duration_testing.py b/addons/mail/tests/mail_tracking_duration_mixin_case.py similarity index 98% rename from addons/mail/tests/mail_tracking_duration_testing.py rename to addons/mail/tests/mail_tracking_duration_mixin_case.py index 32290068616..ed3503537e5 100644 --- a/addons/mail/tests/mail_tracking_duration_testing.py +++ b/addons/mail/tests/mail_tracking_duration_mixin_case.py @@ -1,3 +1,6 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + from collections import defaultdict from datetime import datetime, timedelta from unittest.mock import patch @@ -5,7 +8,7 @@ from unittest.mock import patch from odoo.addons.mail.tests.common import MailCommon -class TestMailTrackingDurationMixin(MailCommon): +class MailTrackingDurationMixinCase(MailCommon): @classmethod def setUpClass(cls, tested_model, model_fields=None): diff --git a/addons/project/tests/test_project_task_mail_tracking_duration.py b/addons/project/tests/test_project_task_mail_tracking_duration.py index d06cb17ba2d..a32f8a17940 100644 --- a/addons/project/tests/test_project_task_mail_tracking_duration.py +++ b/addons/project/tests/test_project_task_mail_tracking_duration.py @@ -1,7 +1,10 @@ -from odoo.addons.mail.tests.mail_tracking_duration_testing import TestMailTrackingDurationMixin +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo.addons.mail.tests.mail_tracking_duration_mixin_case import MailTrackingDurationMixinCase -class TestProjectTaskMailTrackingDuration(TestMailTrackingDurationMixin): +class TestProjectTaskMailTrackingDuration(MailTrackingDurationMixinCase): @classmethod def setUpClass(cls): diff --git a/addons/test_mail/models/test_mail_corner_case_models.py b/addons/test_mail/models/test_mail_corner_case_models.py index eaee9705070..e088f90d4df 100644 --- a/addons/test_mail/models/test_mail_corner_case_models.py +++ b/addons/test_mail/models/test_mail_corner_case_models.py @@ -92,6 +92,29 @@ class MailTestLang(models.Model): # TRACKING MODELS # ------------------------------------------------------------ +class MailTestTrackAll(models.Model): + _name = 'mail.test.track.all' + _description = 'Test tracking on all field types' + _inherit = ['mail.thread'] + + boolean_field = fields.Boolean('Boolean', tracking=1) + char_field = fields.Char('Char', tracking=2) + company_id = fields.Many2one('res.company') + currency_id = fields.Many2one('res.currency', related='company_id.currency_id') + date_field = fields.Date('Date', tracking=3) + datetime_field = fields.Datetime('Datetime', tracking=4) + float_field = fields.Float('Float', tracking=5) + html_field = fields.Html('Html', tracking=6) + integer_field = fields.Integer('Integer', tracking=7) + many2one_field_id = fields.Many2one('res.partner', string='Many2one', tracking=8) + monetary_field = fields.Monetary('Monetary', tracking=9) + selection_field = fields.Selection( + string='Selection', + selection=[('first', 'FIRST'), ('second', 'SECOND')], + tracking=10) + text_field = fields.Text('Text', tracking=11) + + class MailTestTrackCompute(models.Model): _name = 'mail.test.track.compute' _description = "Test tracking with computed fields" @@ -112,17 +135,8 @@ class MailTestTrackMonetary(models.Model): company_currency = fields.Many2one("res.currency", string='Currency', related='company_id.currency_id', readonly=True, tracking=True) revenue = fields.Monetary('Revenue', currency_field='company_currency', tracking=True) -class MailTestMultiCompanyWithActivity(models.Model): - """ This model can be used in multi company tests with activity""" - _name = "mail.test.multi.company.with.activity" - _description = "Test Multi Company Mail With Activity" - _inherit = ["mail.thread", "mail.activity.mixin"] - name = fields.Char() - company_id = fields.Many2one("res.company") - - -class MailTestSelectionTracking(models.Model): +class MailTestTrackSelection(models.Model): """ Test tracking for selection fields """ _description = 'Test Selection Tracking' _name = 'mail.test.track.selection' @@ -132,25 +146,6 @@ class MailTestSelectionTracking(models.Model): selection_type = fields.Selection([('first', 'First'), ('second', 'Second')], tracking=True) -class MailTestTrackAll(models.Model): - _name = 'mail.test.track.all' - _description = 'Test tracking on all field types' - _inherit = ['mail.thread'] - - boolean_field = fields.Boolean('Boolean', tracking=True) - char_field = fields.Char('Char', tracking=True) - company_id = fields.Many2one('res.company') - currency_id = fields.Many2one('res.currency', related='company_id.currency_id') - date_field = fields.Date('Date', tracking=True) - datetime_field = fields.Datetime('Datetime', tracking=True) - float_field = fields.Float('Float', tracking=True) - html_field = fields.Html('Html', tracking=True) - integer_field = fields.Integer('Integer', tracking=True) - many2one_field_id = fields.Many2one('res.partner', string='Many2one', tracking=True) - monetary_field = fields.Monetary('Monetary', tracking=True) - selection_field = fields.Selection(string='Selection', selection=[['first', 'FIRST']], tracking=True) - text_field = fields.Text('Text', tracking=True) - # ------------------------------------------------------------ # OTHER # ------------------------------------------------------------ @@ -175,6 +170,16 @@ class MailTestMultiCompanyRead(models.Model): _mail_post_access = 'read' +class MailTestMultiCompanyWithActivity(models.Model): + """ This model can be used in multi company tests with activity""" + _name = "mail.test.multi.company.with.activity" + _description = "Test Multi Company Mail With Activity" + _inherit = ["mail.thread", "mail.activity.mixin"] + + name = fields.Char() + company_id = fields.Many2one("res.company") + + class MailTestNotMailThread(models.Model): """ Models not inheriting from mail.thread but using some cross models capabilities of mail. """ diff --git a/addons/test_mail/tests/__init__.py b/addons/test_mail/tests/__init__.py index 859130a5530..533bdf82dbf 100644 --- a/addons/test_mail/tests/__init__.py +++ b/addons/test_mail/tests/__init__.py @@ -17,7 +17,6 @@ from . import test_mail_thread_internals from . import test_mail_thread_mixins from . import test_mail_template from . import test_mail_template_preview -from . import test_mail_tracking_duration_mixin from . import test_message_management from . import test_message_post from . import test_message_track diff --git a/addons/test_mail/tests/test_mail_multicompany.py b/addons/test_mail/tests/test_mail_multicompany.py index eed9d754abf..09783424a26 100644 --- a/addons/test_mail/tests/test_mail_multicompany.py +++ b/addons/test_mail/tests/test_mail_multicompany.py @@ -63,7 +63,6 @@ class TestMailMCCommon(MailCommon, TestRecipients): super().setUp() # patch registry to simulate a ready environment self.patch(self.env.registry, 'ready', True) - self.flush_tracking() @tagged('multi_company') diff --git a/addons/test_mail/tests/test_mail_thread_mixins.py b/addons/test_mail/tests/test_mail_thread_mixins.py index a9221256561..e9b103845d5 100644 --- a/addons/test_mail/tests/test_mail_thread_mixins.py +++ b/addons/test_mail/tests/test_mail_thread_mixins.py @@ -3,11 +3,29 @@ from odoo import exceptions, tools from odoo.addons.mail.tests.common import MailCommon +from odoo.addons.mail.tests.mail_tracking_duration_mixin_case import MailTrackingDurationMixinCase from odoo.addons.test_mail.tests.common import TestRecipients from odoo.tests.common import tagged, users from odoo.tools import mute_logger +@tagged('mail_thread', 'mail_track') +class TestMailTrackingDurationMixin(MailTrackingDurationMixinCase): + + @classmethod + def setUpClass(cls): + super().setUpClass('mail.test.mail.tracking.duration') + + def test_mail_tracking_duration(self): + self._test_record_duration_tracking() + + def test_mail_tracking_duration_batch(self): + self._test_record_duration_tracking_batch() + + def test_queries_batch_mail_tracking_duration(self): + self._test_queries_batch_duration_tracking() + + @tagged('mail_thread', 'mail_blacklist') class TestMailThread(MailCommon, TestRecipients): diff --git a/addons/test_mail/tests/test_mail_tracking_duration_mixin.py b/addons/test_mail/tests/test_mail_tracking_duration_mixin.py deleted file mode 100644 index e5042106adc..00000000000 --- a/addons/test_mail/tests/test_mail_tracking_duration_mixin.py +++ /dev/null @@ -1,17 +0,0 @@ -from odoo.addons.mail.tests.mail_tracking_duration_testing import TestMailTrackingDurationMixin - - -class TestMailTrackingDuration(TestMailTrackingDurationMixin): - - @classmethod - def setUpClass(cls): - super().setUpClass('mail.test.mail.tracking.duration') - - def test_mail_tracking_duration(self): - self._test_record_duration_tracking() - - def test_mail_tracking_duration_batch(self): - self._test_record_duration_tracking_batch() - - def test_queries_batch_mail_tracking_duration(self): - self._test_queries_batch_duration_tracking() diff --git a/addons/test_mail/tests/test_message_post.py b/addons/test_mail/tests/test_message_post.py index c0f80e566c9..d4c396cd876 100644 --- a/addons/test_mail/tests/test_message_post.py +++ b/addons/test_mail/tests/test_message_post.py @@ -67,12 +67,7 @@ class TestMessagePostCommon(MailCommon, TestRecipients): def setUp(self): super(TestMessagePostCommon, self).setUp() - # send tracking and messages + patch registry to simulate a ready environment - # purpose is to avoid nondeterministic tests, notably because tracking is - # accumulated and sent at flush -> we want to test only the result of a - # given test, not setup + test - self.flush_tracking() - # see ``_message_auto_subscribe_notify`` + # patch registry to simulate a ready environment; see ``_message_auto_subscribe_notify`` self.patch(self.env.registry, 'ready', True) diff --git a/addons/test_mail/tests/test_message_track.py b/addons/test_mail/tests/test_message_track.py index 1a19bdbc27d..24339500a86 100644 --- a/addons/test_mail/tests/test_message_track.py +++ b/addons/test_mail/tests/test_message_track.py @@ -3,26 +3,30 @@ from unittest.mock import patch +from odoo import fields from odoo.addons.mail.tests.common import MailCommon from odoo.addons.test_mail.data.test_mail_data import MAIL_TEMPLATE -from odoo.tests.common import tagged -from odoo.tests import Form +from odoo.tests import Form, tagged, users +from odoo.tools import mute_logger @tagged('mail_track') class TestTracking(MailCommon): - def setUp(self): - super(TestTracking, self).setUp() + @classmethod + def setUpClass(cls): + super().setUpClass() - record = self.env['mail.test.ticket'].with_user(self.user_employee).with_context(self._test_context).create({ + record = cls.env['mail.test.ticket'].with_user( + cls.user_employee + ).with_context(cls._test_context).create({ 'name': 'Test', }) - self.flush_tracking() - self.record = record.with_context(mail_notrack=False) + cls.record = record.with_context(mail_notrack=False) def test_message_track_author(self): - """ Checks that the author of the log note matches the user at the time of writing""" + """ Check that the author of the log note matches the user at the time + of writing. """ with self.mock_mail_gateway(): self.record._track_set_author(self.partner_admin) self.record.write({ @@ -80,14 +84,23 @@ class TestTracking(MailCommon): self.assertEqual(len(self.record.message_ids), 4, 'Should have added one change message and one automated template') self.assertEqual(second_message.message_type, 'notification') - def test_message_track_no_tracking(self): - """ Update a set of non tracked fields -> no message, no tracking """ - self.record.write({ - 'name': 'Tracking or not', - 'count': 32, - }) + def test_message_track_multiple(self): + """ check that multiple updates generate a single tracking message """ + container = self.env['mail.test.container'].with_context(mail_create_nosubscribe=True).create({'name': 'Container'}) + self.record.name = 'Zboub' + self.record.customer_id = self.user_admin.partner_id + self.record.user_id = self.user_admin + self.record.container_id = container self.flush_tracking() - self.assertEqual(self.record.message_ids, self.env['mail.message']) + + # should have a single message with all tracked fields + self.assertEqual(len(self.record.message_ids), 1, 'should have 1 tracking message') + self.assertEqual(self.record.message_ids.author_id, self.partner_employee) + self.assertTracking(self.record.message_ids[0], [ + ('customer_id', 'many2one', False, self.user_admin.partner_id), + ('user_id', 'many2one', False, self.user_admin), + ('container_id', 'many2one', False, container), + ]) def test_message_track_no_subtype(self): """ Update some tracked fields not linked to some subtype -> message with onchange """ @@ -115,6 +128,23 @@ class TestTracking(MailCommon): [('customer_id', 'many2one', False, customer) # onchange tracked field ]) + @users('employee') + def test_message_track_no_tracking(self): + """ Update a set of non tracked fields -> no message, no tracking, or + use dedicated context key """ + record = self.record.with_env(self.env) + record.write({ + 'name': 'Tracking or not', + 'count': 32, + }) + self.flush_tracking() + self.assertFalse(record.message_ids) + + # check context key allowing to skip tracking + record.with_context(mail_notrack=True).write({'email_from': 'new.from@test.example.com'}) + self.flush_tracking() + self.assertFalse(record.message_ids) + def test_message_track_subtype(self): """ Update some tracked fields linked to some subtype -> message with onchange """ self.record.message_subscribe( @@ -144,6 +174,7 @@ class TestTracking(MailCommon): [('container_id', 'many2one', False, container) # onchange tracked field ]) + @mute_logger('odoo.addons.mail.models.mail_mail') def test_message_track_template(self): """ Update some tracked fields linked to some template -> message with onchange """ self.record.write({'mail_template': self.env.ref('test_mail.mail_test_ticket_tracking_tpl').id}) @@ -173,6 +204,7 @@ class TestTracking(MailCommon): [('customer_id', 'many2one', False, self.user_admin.partner_id) # onchange tracked field ]) + @mute_logger('odoo.addons.mail.models.mail_mail') def test_message_track_template_at_create(self): """ Create a record with tracking template on create, template should be sent.""" @@ -194,6 +226,7 @@ class TestTracking(MailCommon): # one email send due to template self.assertSentEmail(self.record.env.user.partner_id, [self.partner_admin], body='

Hello Test

') + @mute_logger('odoo.addons.mail.models.mail_mail', 'odoo.addons.mail.models.mail_thread') def test_message_track_template_at_create_from_message(self): """Make sure records created through aliasing show the original message before the template""" # setup @@ -231,7 +264,10 @@ class TestTracking(MailCommon): } ) - def test_create_partner_from_tracking_multicompany(self): + @mute_logger('odoo.addons.mail.models.mail_mail') + def test_message_track_template_create_partner_multicompany(self): + """ Test partner created due to usage of a mail.template, triggered by + a tracking, in a multi company environment. """ company1 = self.env['res.company'].create({'name': 'company1'}) self.env.user.write({'company_ids': [(4, company1.id, False)]}) self.assertNotEqual(self.env.company, company1) @@ -266,39 +302,9 @@ class TestTracking(MailCommon): self.assertTrue(new_partner) self.assertEqual(new_partner.company_id, company1) - def test_track_invalid_selection(self): - # Test: Check that initial invalid selection values are allowed when tracking - # Create a record with an initially invalid selection value - invalid_value = 'I love writing tests!' - record = self.env['mail.test.track.selection'].create({ - 'name': 'Test Invalid Selection Values', - 'selection_type': 'first', - }) - - self.flush_tracking() - self.env.cr.execute( - """ - UPDATE mail_test_track_selection - SET selection_type = %s - WHERE id = %s - """, - [invalid_value, record.id] - ) - - record.invalidate_recordset() - - self.assertEqual(record.selection_type, invalid_value) - - # Write a valid selection value - record.selection_type = "second" - - self.flush_tracking() - self.assertTracking(record.message_ids, [ - ('selection_type', 'char', invalid_value, 'Second'), - ]) - - def test_track_template(self): - # Test: Check that default_* keys are not taken into account in _message_track_post_template + def test_message_track_template_defaults(self): + """ Check that default_* keys are not taken into account in + _message_track_post_template """ magic_code = 'Up-Up-Down-Down-Left-Right-Left-Right-Square-Triangle' mt_name_changed = self.env['mail.message.subtype'].create({ @@ -346,128 +352,185 @@ class TestTracking(MailCommon): }) test_mail_record.with_context(default_parent_id=2147483647).write({'name': magic_code}) - def test_message_track_multiple(self): - """ check that multiple updates generate a single tracking message """ - container = self.env['mail.test.container'].with_context(mail_create_nosubscribe=True).create({'name': 'Container'}) - self.record.name = 'Zboub' - self.record.customer_id = self.user_admin.partner_id - self.record.user_id = self.user_admin - self.record.container_id = container + +@tagged('mail_track') +class TestTrackingInternals(MailCommon): + + @classmethod + def setUpClass(cls): + super().setUpClass() + + cls.record = cls.env['mail.test.ticket'].with_user(cls.user_employee).create({ + 'name': 'Test', + }) + cls.test_partner = cls.env['res.partner'].create({ + 'country_id': cls.env.ref('base.be').id, + 'email': 'test.partner@test.example.com', + 'name': 'Test Partner', + 'phone': '0456001122', + }) + + @users('employee') + def test_mail_track_all_no2many(self): + test_record = self.env['mail.test.track.all'].create({ + 'company_id': self.env.company.id, + }) self.flush_tracking() + self.assertEqual(test_record.currency_id, self.env.ref('base.USD')) + messages = test_record.message_ids + today = fields.Date.today() + today_dt = fields.Datetime.to_datetime(today) + now = fields.Datetime.now() - # should have a single message with all tracked fields - self.assertEqual(len(self.record.message_ids), 1, 'should have 1 tracking message') - self.assertEqual(self.record.message_ids.author_id, self.partner_employee) - self.assertTracking(self.record.message_ids[0], [ - ('customer_id', 'many2one', False, self.user_admin.partner_id), - ('user_id', 'many2one', False, self.user_admin), - ('container_id', 'many2one', False, container), - ]) + test_record.write({ + 'boolean_field': True, + 'char_field': 'char_value', + 'date_field': today, + 'datetime_field': now, + 'float_field': 3.22, + 'html_field': '

Html Value

', + 'integer_field': 42, + 'many2one_field_id': self.test_partner.id, + 'monetary_field': 42.42, + 'selection_field': 'first', + 'text_field': 'text_value', + }) + self.flush_tracking() + new_message = test_record.message_ids - messages + self.assertEqual(len(new_message), 1, + 'Should have generated a tracking value') + self.assertTracking( + new_message, + [ + ('boolean_field', 'boolean', 0, 1), + ('char_field', 'char', False, 'char_value'), + ('date_field', 'date', False, today_dt), + ('datetime_field', 'datetime', False, now), + ('float_field', 'float', 0, 3.22), + ('integer_field', 'integer', 0, 42), + ('many2one_field_id', 'many2one', self.env['res.partner'], self.test_partner), + ('monetary_field', 'monetary', False, (42.42, self.env.ref('base.USD'))), + ('selection_field', 'selection', '', 'FIRST'), + ('text_field', 'text', False, 'text_value'), + ], + strict=True + ) - def test_tracked_compute(self): + @users('employee') + def test_mail_track_compute(self): + """ Test tracking of computed fields """ # no tracking at creation - record = self.env['mail.test.track.compute'].create({}) + compute_record = self.env['mail.test.track.compute'].create({}) self.flush_tracking() - self.assertEqual(len(record.message_ids), 1) - self.assertEqual(len(record.message_ids[0].tracking_value_ids), 0) + self.assertEqual(len(compute_record.message_ids), 1) + self.assertEqual(len(compute_record.message_ids[0].tracking_value_ids), 0) # assign partner_id: one tracking message for the modified field and all # the stored and non-stored computed fields on the record - partner = self.env['res.partner'].create({ + partner_su = self.env['res.partner'].sudo().create({ 'name': 'Foo', 'email': 'foo@example.com', 'phone': '1234567890', }) - record.partner_id = partner + compute_record.partner_id = partner_su self.flush_tracking() - self.assertEqual(len(record.message_ids), 2) - self.assertEqual(len(record.message_ids[0].tracking_value_ids), 4) - self.assertEqual(record.message_ids.author_id, self.partner_root) - self.assertTracking(record.message_ids[0], [ - ('partner_id', 'many2one', False, partner), + self.assertEqual(len(compute_record.message_ids), 2) + self.assertEqual(len(compute_record.message_ids[0].tracking_value_ids), 4) + self.assertEqual(compute_record.message_ids.author_id, self.partner_employee) + self.assertTracking(compute_record.message_ids[0], [ + ('partner_id', 'many2one', False, partner_su), ('partner_name', 'char', False, 'Foo'), ('partner_email', 'char', False, 'foo@example.com'), ('partner_phone', 'char', False, '1234567890'), ]) # modify partner: one tracking message for the only recomputed field - partner.write({'name': 'Fool'}) + partner_su.write({'name': 'Fool'}) self.flush_tracking() - self.assertEqual(len(record.message_ids), 3) - self.assertEqual(len(record.message_ids[0].tracking_value_ids), 1) - self.assertTracking(record.message_ids[0], [ + self.assertEqual(len(compute_record.message_ids), 3) + self.assertEqual(len(compute_record.message_ids[0].tracking_value_ids), 1) + self.assertTracking(compute_record.message_ids[0], [ ('partner_name', 'char', 'Foo', 'Fool'), ]) # modify partner: one tracking message for both stored computed fields; # the non-stored computed fields have no tracking - partner.write({ + partner_su.write({ 'name': 'Bar', 'email': 'bar@example.com', 'phone': '0987654321', }) # force recomputation of 'partner_phone' to make sure it does not # generate tracking values - self.assertEqual(record.partner_phone, '0987654321') + self.assertEqual(compute_record.partner_phone, '0987654321') self.flush_tracking() - self.assertEqual(len(record.message_ids), 4) - self.assertEqual(len(record.message_ids[0].tracking_value_ids), 2) - self.assertTracking(record.message_ids[0], [ + self.assertEqual(len(compute_record.message_ids), 4) + self.assertEqual(len(compute_record.message_ids[0].tracking_value_ids), 2) + self.assertTracking(compute_record.message_ids[0], [ ('partner_name', 'char', 'Fool', 'Bar'), ('partner_email', 'char', 'foo@example.com', 'bar@example.com'), ]) -@tagged('mail_track') -class TestTrackingMonetary(MailCommon): - - def setUp(self): - super(TestTrackingMonetary, self).setUp() - - record = self.env['mail.test.track.monetary'].with_user(self.user_employee).with_context(self._test_context).create({ + @users('employee') + def test_mail_track_monetary(self): + """ Update a record with a tracked monetary field """ + monetary_record = self.env['mail.test.track.monetary'].with_user(self.user_employee).create({ 'company_id': self.user_employee.company_id.id, }) self.flush_tracking() - self.record = record.with_context(mail_notrack=False) - - def test_message_track_monetary(self): - """ Update a record with a tracked monetary field """ + self.assertEqual(len(monetary_record.message_ids), 1) # Check if the tracking value have the correct currency and values - self.record.write({ + monetary_record.write({ 'revenue': 100, }) self.flush_tracking() - self.assertEqual(len(self.record.message_ids), 1) - - self.assertTracking(self.record.message_ids[0], [ - ('revenue', 'monetary', 0, 100), + self.assertEqual(len(monetary_record.message_ids), 2) + self.assertTracking(monetary_record.message_ids[0], [ + ('revenue', 'monetary', 0, (100, self.env.company.currency_id)), ]) # Check if the tracking value have the correct currency and values after changing the value and the company - self.record.write({ + monetary_record.write({ 'revenue': 200, 'company_id': self.company_2.id, }) self.flush_tracking() - self.assertEqual(len(self.record.message_ids), 2) - - self.assertTracking(self.record.message_ids[0], [ - ('revenue', 'monetary', 100, 200), + self.assertEqual(len(monetary_record.message_ids), 3) + self.assertTracking(monetary_record.message_ids[0], [ + ('revenue', 'monetary', 100, (200, self.company_2.currency_id)), ('company_currency', 'many2one', self.user_employee.company_id.currency_id, self.company_2.currency_id) ]) - -@tagged('mail_track') -class TestTrackingInternals(MailCommon): - - def setUp(self): - super(TestTrackingInternals, self).setUp() - - record = self.env['mail.test.ticket'].with_user(self.user_employee).with_context(self._test_context).create({ - 'name': 'Test', + @users('employee') + def test_mail_track_selection_invalid(self): + """ Check that initial invalid selection values are allowed when tracking """ + # Create a record with an initially invalid selection value + invalid_value = 'I love writing tests!' + record = self.env['mail.test.track.selection'].create({ + 'name': 'Test Invalid Selection Values', + 'selection_type': 'first', }) + self.flush_tracking() - self.record = record.with_context(mail_notrack=False) + self.env.cr.execute( + """ + UPDATE mail_test_track_selection + SET selection_type = %s + WHERE id = %s + """, + [invalid_value, record.id] + ) + record.invalidate_recordset() + self.assertEqual(record.selection_type, invalid_value) + + # Write a valid selection value + record.selection_type = "second" + + self.flush_tracking() + self.assertTracking(record.message_ids, [ + ('selection_type', 'char', invalid_value, 'Second'), + ]) def test_track_groups(self): field = self.record._fields['email_from'] @@ -479,7 +542,7 @@ class TestTrackingInternals(MailCommon): msg_emp = self.record.message_ids.message_format() msg_sudo = self.record.sudo().message_ids.message_format() - tracking_values = self.env['mail.tracking.value'].search([('mail_message_id', '=', self.record.message_ids.id)]) + tracking_values = self.env['mail.tracking.value'].search([('mail_message_id', '=', self.record.message_ids[0].id)]) formattedTrackingValues = [{ 'changedField': 'Email From', 'id': tracking_values[0]['id'], @@ -497,8 +560,8 @@ class TestTrackingInternals(MailCommon): self.assertEqual(msg_emp[0].get('trackingValues'), [], "should not have protected tracking values") self.assertEqual(msg_sudo[0].get('trackingValues'), formattedTrackingValues, "should have protected tracking values") - msg_emp = self.record._notify_by_email_prepare_rendering_context(self.record.message_ids, {}) - msg_sudo = self.record.sudo()._notify_by_email_prepare_rendering_context(self.record.message_ids, {}) + msg_emp = self.record._notify_by_email_prepare_rendering_context(self.record.message_ids[0], {}) + msg_sudo = self.record.sudo()._notify_by_email_prepare_rendering_context(self.record.message_ids[0], {}) self.assertFalse(msg_emp.get('tracking_values'), "should not have protected tracking values") self.assertTrue(msg_sudo.get('tracking_values'), "should have protected tracking values") @@ -513,29 +576,36 @@ class TestTrackingInternals(MailCommon): record = record_form.save() self.assertEqual(record.name, 'TestDoNoCrash') + @users('employee') def test_track_sequence(self): """ Update some tracked fields and check that the mail.tracking.value are ordered according to their tracking_sequence""" - self.record.write({ + record = self.record.with_env(self.env) + self.assertEqual(len(record.message_ids), 1) + record.write({ 'name': 'Zboub', 'customer_id': self.user_admin.partner_id.id, 'user_id': self.user_admin.id, 'container_id': self.env['mail.test.container'].with_context(mail_create_nosubscribe=True).create({'name': 'Container'}).id }) self.flush_tracking() - self.assertEqual(len(self.record.message_ids), 1, 'should have 1 tracking message') + self.assertEqual(len(record.message_ids), 2, 'should have 1 new tracking message') - tracking_values = self.env['mail.tracking.value'].search([('mail_message_id', '=', self.record.message_ids.id)]) + tracking_values = self.env['mail.tracking.value'].sudo().search( + [('mail_message_id', '=', record.message_ids[0].id)] + ) self.assertEqual(tracking_values[0].tracking_sequence, 1) self.assertEqual(tracking_values[1].tracking_sequence, 2) self.assertEqual(tracking_values[2].tracking_sequence, 100) + @users('employee') def test_unlinked_field(self): - record_sudo = self.record.sudo() - record_sudo.write({'email_from': 'new_value'}) # create a tracking value + """ Check that removing a field removes its tracking values. """ + record = self.record.with_env(self.env) + record.write({'email_from': 'new_value'}) # create a tracking value self.flush_tracking() - self.assertEqual(len(record_sudo.message_ids.tracking_value_ids), 1) - ir_model_field = self.env['ir.model.fields'].search([ + self.assertEqual(len(record.message_ids.sudo().tracking_value_ids), 1) + ir_model_field = self.env['ir.model.fields'].sudo().search([ ('model', '=', 'mail.test.ticket'), ('name', '=', 'email_from')]) ir_model_field.with_context(_force_unlink=True).unlink() - self.assertEqual(len(record_sudo.message_ids.tracking_value_ids), 0) + self.assertEqual(len(record.message_ids.sudo().tracking_value_ids), 0) diff --git a/addons/test_mail/tests/test_performance.py b/addons/test_mail/tests/test_performance.py index 860bc5dd3b2..fe45a0cfbcc 100644 --- a/addons/test_mail/tests/test_performance.py +++ b/addons/test_mail/tests/test_performance.py @@ -56,7 +56,6 @@ class BaseMailPerformance(MailCommon, TransactionCaseWithUserDemo): super(BaseMailPerformance, self).setUp() # patch registry to simulate a ready environment self.patch(self.env.registry, 'ready', True) - self.flush_tracking() def _create_test_records(self): test_record_full = self.env['mail.test.ticket'].with_context(self._test_context).create({ diff --git a/addons/website_slides/tests/test_gamification_karma.py b/addons/website_slides/tests/test_gamification_karma.py index 951a3c06e0e..c40019f7ca8 100644 --- a/addons/website_slides/tests/test_gamification_karma.py +++ b/addons/website_slides/tests/test_gamification_karma.py @@ -128,7 +128,7 @@ class TestKarmaGain(common.SlidesCase): self.assertEqual(len(channel_partners), 4) # Set courses as completed and update karma - with self.assertQueryCount(50): # com 49 + with self.assertQueryCount(54): # com 53 channel_partners._post_completion_update_hook() computed_karma = self.channel.karma_gen_channel_finish + self.channel_2.karma_gen_channel_finish