From c40053bc28ad8a4dc3ee47e0f19b83015c44ef56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 8 Jun 2023 12:21:33 +0200 Subject: [PATCH] [IMP] test_mail: cleanup tracking tests When possible, use '@users' decorator. Rename tests to better match their main purpose, notably * test_mail_track*: mail_track specific (specific fields, display) * test_message_track*: overall behavior of 'message_track', notably subtypes, template usage, ... Add some additional tests and improve test coverage, as some field types were not covered (date, datetime, text notably). Improve currency check for monetary fields. Move 'mail.tracking.duration.mixin' tests into the file testing mixins linked to 'mail.thread', rename the Case class according to current test guidelines. Also starting from now, `MailCommon` flushes tracking automatically at setup time, allowing to remove some custom flush done in tests. That way tests are less prone to non deterministic errors. This implies some changes in execution of tests, which means some updated query counters notably. Task-3345979 (Mail: Simplify tracking model) Part-of: odoo/odoo#124182 --- .../crm/tests/test_crm_lead_convert_mass.py | 2 +- .../test_crm_lead_mail_tracking_duration.py | 7 +- addons/mail/tests/common.py | 47 ++- ...y => mail_tracking_duration_mixin_case.py} | 5 +- ...est_project_task_mail_tracking_duration.py | 7 +- .../models/test_mail_corner_case_models.py | 63 ++-- addons/test_mail/tests/__init__.py | 1 - .../test_mail/tests/test_mail_multicompany.py | 1 - .../tests/test_mail_thread_mixins.py | 18 + .../test_mail_tracking_duration_mixin.py | 17 - addons/test_mail/tests/test_message_post.py | 7 +- addons/test_mail/tests/test_message_track.py | 322 +++++++++++------- addons/test_mail/tests/test_performance.py | 1 - .../tests/test_gamification_karma.py | 2 +- 14 files changed, 301 insertions(+), 199 deletions(-) rename addons/mail/tests/{mail_tracking_duration_testing.py => mail_tracking_duration_mixin_case.py} (98%) delete mode 100644 addons/test_mail/tests/test_mail_tracking_duration_mixin.py 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