[FIX] base: tag test_add_field_valid as post_install

This test can lead to a deadlock,
when trying to drop the constraint on `ir_model_fields`
during the registry loading,
as the test opens a second cursor in addition
to the cursor used for the registry loading,
and drop a constraint on a table which is being read by
the registry loading (`ir_model_fields`), leading to the deadlock.
See screenshot in the PR for illustration.

This has been detected in the runbot nightly builds,
which were pending indefinitely because of that deadlock.
To reproduce, just init a database with `-i base`,
then restart the server with just `--test-tags .test_add_field_valid`.

To avoid to drop the constraint during the registry loading,
and hence to avoid the deadlock,
tag the test as `post_install`.
That way, the test is executed after the registry is loaded,
not during, and the constraint can be drop safely, not leading to a deadlock.

closes odoo/odoo#129660

X-original-commit: 67bc9a9e895245a94aac064e8f5a365ee6052261
Signed-off-by: Xavier Dollé (xdo) <xdo@odoo.com>
Signed-off-by: Denis Ledoux (dle) <dle@odoo.com>
This commit is contained in:
Denis Ledoux
2023-07-26 09:22:19 +02:00
parent 112bc6499a
commit 028bf8e0ec
+28 -27
View File
@@ -6,8 +6,8 @@ from psycopg2 import IntegrityError, ProgrammingError
import odoo
from odoo.exceptions import UserError, ValidationError, AccessError
from odoo.tools import mute_logger, config
from odoo.tests import common
from odoo.tools import mute_logger
from odoo.tests import common, tagged
from odoo import Command
@@ -340,7 +340,7 @@ ZeroDivisionError: division by zero""" % self.test_server_action.id
self.assertEqual(self.test_partner.date, date.today())
class TestCustomFields(common.TransactionCase):
class TestCommonCustomFields(common.TransactionCase):
MODEL = 'res.partner'
COMODEL = 'res.users'
@@ -377,6 +377,8 @@ class TestCustomFields(common.TransactionCase):
'arch': '<tree string="X"><field name="%s"/></tree>' % name,
})
class TestCustomFields(TestCommonCustomFields):
def test_create_custom(self):
""" custom field names must be start with 'x_' """
with self.assertRaises(IntegrityError), mute_logger('odoo.sql_db'):
@@ -399,30 +401,6 @@ class TestCustomFields(common.TransactionCase):
with self.assertRaises(ValidationError):
field.name = 'x_foo bar'
def test_add_field_valid(self):
""" custom field names must start with 'x_', even when bypassing the constraints
If a user bypasses all constraints to add a custom field not starting by `x_`,
it must not be loaded in the registry.
This is to forbid users to override class attributes.
"""
# For a strange reason, drop constraint will remain stuck if executed without initiating base. (even on an existing database)
# small assertion to avoid getting stuck on this test in this case.
self.assertIn('base', config['init'], 'This test can only be executed while installing base')
field = self.create_field('x_foo')
# Drop the SQL constraint, to bypass it,
# as a user could do through a SQL shell or a `cr.execute` in a server action
self.env.cr.execute("ALTER TABLE ir_model_fields DROP CONSTRAINT ir_model_fields_name_manual_field")
self.env.cr.execute("UPDATE ir_model_fields SET name = 'foo' WHERE id = %s", [field.id])
with self.assertLogs('odoo.addons.base.models.ir_model') as log_catcher:
# Trick to reload the registry. The above rename done through SQL didn't reload the registry. This will.
field.write({'field_description': 'foo'})
self.assertIn(
f'The field `{field.name}` is not defined in the `{field.model}` Python class', log_catcher.output[0]
)
def test_create_unique(self):
""" one cannot create two fields with the same name on a given model """
self.create_field('x_foo')
@@ -648,3 +626,26 @@ class TestCustomFields(common.TransactionCase):
self.assertEqual(rec1.x_sel, False)
self.assertEqual(rec2.x_sel, 'quux')
self.assertEqual(rec3.x_sel, 'baz')
@tagged('post_install', '-at_install')
class TestCustomFieldsPostInstall(TestCommonCustomFields):
def test_add_field_valid(self):
""" custom field names must start with 'x_', even when bypassing the constraints
If a user bypasses all constraints to add a custom field not starting by `x_`,
it must not be loaded in the registry.
This is to forbid users to override class attributes.
"""
field = self.create_field('x_foo')
# Drop the SQL constraint, to bypass it,
# as a user could do through a SQL shell or a `cr.execute` in a server action
self.env.cr.execute("ALTER TABLE ir_model_fields DROP CONSTRAINT ir_model_fields_name_manual_field")
self.env.cr.execute("UPDATE ir_model_fields SET name = 'foo' WHERE id = %s", [field.id])
with self.assertLogs('odoo.addons.base.models.ir_model') as log_catcher:
# Trick to reload the registry. The above rename done through SQL didn't reload the registry. This will.
field.write({'field_description': 'foo'})
self.assertIn(
f'The field `{field.name}` is not defined in the `{field.model}` Python class', log_catcher.output[0]
)