From b68617ca40d6f9cd20948e216dda99545c527115 Mon Sep 17 00:00:00 2001 From: Stephane Mangin Date: Tue, 12 Jan 2021 14:28:34 +0100 Subject: [PATCH] [MIG] database_cleanup: Migration to 14.0 --- database_cleanup/README.rst | 62 +++++-- .../{__openerp__.py => __manifest__.py} | 5 +- database_cleanup/i18n/en.po | 2 +- database_cleanup/models/create_indexes.py | 9 +- database_cleanup/models/purge_columns.py | 6 +- database_cleanup/models/purge_data.py | 3 +- database_cleanup/models/purge_menus.py | 7 +- database_cleanup/models/purge_models.py | 5 +- database_cleanup/models/purge_modules.py | 3 +- database_cleanup/models/purge_properties.py | 36 ++-- database_cleanup/models/purge_tables.py | 4 +- database_cleanup/models/purge_wizard.py | 6 +- database_cleanup/readme/CONTRIBUTORS.rst | 3 + database_cleanup/readme/DESCRIPTION.rst | 9 + database_cleanup/readme/USAGE.rst | 10 ++ database_cleanup/security/ir.model.access.csv | 17 ++ database_cleanup/tests/__init__.py | 11 +- database_cleanup/tests/common.py | 24 +++ database_cleanup/tests/test_create_indexes.py | 20 +++ .../tests/test_database_cleanup.py | 154 ------------------ database_cleanup/tests/test_purge_columns.py | 39 +++++ database_cleanup/tests/test_purge_data.py | 26 +++ database_cleanup/tests/test_purge_menus.py | 34 ++++ database_cleanup/tests/test_purge_models.py | 40 +++++ database_cleanup/tests/test_purge_modules.py | 35 ++++ .../tests/test_purge_properties.py | 39 +++++ database_cleanup/tests/test_purge_tables.py | 22 +++ database_cleanup/views/purge_menus.xml | 2 +- 28 files changed, 411 insertions(+), 222 deletions(-) rename database_cleanup/{__openerp__.py => __manifest__.py} (78%) create mode 100644 database_cleanup/readme/CONTRIBUTORS.rst create mode 100644 database_cleanup/readme/DESCRIPTION.rst create mode 100644 database_cleanup/readme/USAGE.rst create mode 100644 database_cleanup/security/ir.model.access.csv create mode 100644 database_cleanup/tests/common.py create mode 100644 database_cleanup/tests/test_create_indexes.py delete mode 100644 database_cleanup/tests/test_database_cleanup.py create mode 100644 database_cleanup/tests/test_purge_columns.py create mode 100644 database_cleanup/tests/test_purge_data.py create mode 100644 database_cleanup/tests/test_purge_menus.py create mode 100644 database_cleanup/tests/test_purge_models.py create mode 100644 database_cleanup/tests/test_purge_modules.py create mode 100644 database_cleanup/tests/test_purge_properties.py create mode 100644 database_cleanup/tests/test_purge_tables.py diff --git a/database_cleanup/README.rst b/database_cleanup/README.rst index 32fc9cbb8e3..79ea892c397 100644 --- a/database_cleanup/README.rst +++ b/database_cleanup/README.rst @@ -1,10 +1,29 @@ -.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg +=================== +Base Technical User +=================== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--tools-lightgray.png?logo=github + :target: https://github.com/OCA/server-tools/tree/14.0/database_cleanup + :alt: OCA/server-tools +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/server-tools-14-0/server-tools-14-0-database_cleanup + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/149/14.0 + :alt: Try me on Runbot -================ -Database cleanup -================ +|badge1| |badge2| |badge3| |badge4| |badge5| Clean your Odoo database from remnants of modules, models, columns and tables left by uninstalled modules (prior to 7.0) or a homebrew database @@ -16,6 +35,11 @@ with the technical details of the Odoo data model of *all* the modules that have ever been installed on your database, and do not purge any module, model, column or table if you do not know exactly what you are doing. +**Table of contents** + +.. contents:: + :local: + Usage ===== @@ -28,42 +52,46 @@ in one big step (if you are *really* confident). .. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas :alt: Try me on Runbot - :target: https://runbot.odoo-community.org/runbot/149/11.0 + :target: https://runbot.odoo-community.org/runbot/149/14.0 Bug Tracker =========== -Bugs are tracked on `GitHub Issues `_. +Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. -If you spotted it first, help us smashing it by providing a detailed and welcomed feedback. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. Credits ======= -Images ------- +Authors +~~~~~~~ -* Odoo Community Association: `Icon `_. +* Therp BV Contributors ------------- +~~~~~~~~~~~~ * Stefan Rijnhart * Holger Brunn +* Stéphane Mangin -Do not contact contributors directly about help with questions or problems concerning this addon, but use the `community mailing list `_ or the `appropriate specialized mailinglist `_ for help, and the bug tracker linked in `Bug Tracker`_ above for technical issues. +Maintainers +~~~~~~~~~~~ -Maintainer ----------- +This module is maintained by the OCA. .. image:: https://odoo-community.org/logo.png :alt: Odoo Community Association :target: https://odoo-community.org -This module is maintained by the OCA. - OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use. -To contribute to this module, please visit https://odoo-community.org. +This module is part of the `OCA/server-tools `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/database_cleanup/__openerp__.py b/database_cleanup/__manifest__.py similarity index 78% rename from database_cleanup/__openerp__.py rename to database_cleanup/__manifest__.py index 24600036ea1..bd8e0362981 100644 --- a/database_cleanup/__openerp__.py +++ b/database_cleanup/__manifest__.py @@ -1,9 +1,11 @@ # Copyright 2014-2016 Therp BV +# Copyright 2021 Camptocamp SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { "name": "Database cleanup", - "version": "12.0.1.0.1", + "version": "14.0.1.0.0", "author": "Therp BV,Odoo Community Association (OCA)", + "website": "https://github.com/OCA/server-tools", "depends": ["base"], "license": "AGPL-3", "category": "Tools", @@ -18,6 +20,7 @@ "views/create_indexes.xml", "views/purge_properties.xml", "views/menu.xml", + "security/ir.model.access.csv", ], "installable": True, } diff --git a/database_cleanup/i18n/en.po b/database_cleanup/i18n/en.po index c668b3647c6..ce94cd872c3 100644 --- a/database_cleanup/i18n/en.po +++ b/database_cleanup/i18n/en.po @@ -457,7 +457,7 @@ msgstr "Purge obsolete modules" #, fuzzy #| msgid "Purge obsolete models" msgid "Purge obsolete properties" -msgstr "Purge obsolete models" +msgstr "Purge obsolete properties" #. module: database_cleanup #: model:ir.ui.menu,name:database_cleanup.menu_purge_tables diff --git a/database_cleanup/models/create_indexes.py b/database_cleanup/models/create_indexes.py index 669d8051fd7..7ed78deca39 100644 --- a/database_cleanup/models/create_indexes.py +++ b/database_cleanup/models/create_indexes.py @@ -1,7 +1,8 @@ # Copyright 2017 Therp BV +# Copyright 2021 Camptocamp # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # pylint: disable=consider-merging-classes-inherited -from odoo import api, fields, models +from odoo import fields, models from ..identifier_adapter import IdentifierAdapter @@ -9,14 +10,13 @@ class CreateIndexesLine(models.TransientModel): _inherit = "cleanup.purge.line" _name = "cleanup.create_indexes.line" + _description = "Cleanup Create Indexes line" purged = fields.Boolean("Created") wizard_id = fields.Many2one("cleanup.create_indexes.wizard") field_id = fields.Many2one("ir.model.fields", required=True) - @api.multi def purge(self): - tables = set() for field in self.mapped("field_id"): model = self.env[field.model] name = "{}_{}_index".format(model._table, field.name) @@ -28,8 +28,6 @@ def purge(self): IdentifierAdapter(field.name), ), ) - tables.add(model._table) - for table in tables: self.env.cr.execute("analyze %s", (IdentifierAdapter(model._table),)) self.write( { @@ -48,7 +46,6 @@ class CreateIndexesWizard(models.TransientModel): "wizard_id", ) - @api.multi def find(self): res = list() for field in self.env["ir.model.fields"].search( diff --git a/database_cleanup/models/purge_columns.py b/database_cleanup/models/purge_columns.py index db198c6a6c9..8ccb0cf1530 100644 --- a/database_cleanup/models/purge_columns.py +++ b/database_cleanup/models/purge_columns.py @@ -1,4 +1,5 @@ # Copyright 2014-2016 Therp BV +# Copyright 2021 Camptocamp # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # pylint: disable=consider-merging-classes-inherited from odoo import _, api, fields, models @@ -10,14 +11,13 @@ class CleanupPurgeLineColumn(models.TransientModel): _inherit = "cleanup.purge.line" _name = "cleanup.purge.line.column" - _description = "Purge Column Wizard Lines" + _description = "Cleanup Purge Line Column" model_id = fields.Many2one("ir.model", "Model", required=True, ondelete="CASCADE") wizard_id = fields.Many2one( "cleanup.purge.wizard.column", "Purge Wizard", readonly=True ) - @api.multi def purge(self): """ Unlink columns upon manual confirmation. @@ -123,7 +123,7 @@ def find(self): model_pool ) - for table, model_spec in table2model.items(): + for _table, model_spec in table2model.items(): for column in self.get_orphaned_columns(model_spec[1]): res.append((0, 0, {"name": column, "model_id": model_spec[0]})) if not res: diff --git a/database_cleanup/models/purge_data.py b/database_cleanup/models/purge_data.py index e25dc79f1b4..42bb0bafab8 100644 --- a/database_cleanup/models/purge_data.py +++ b/database_cleanup/models/purge_data.py @@ -1,4 +1,5 @@ # Copyright 2014-2016 Therp BV +# Copyright 2021 Camptocamp # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from odoo import _, api, fields, models from odoo.exceptions import UserError @@ -9,13 +10,13 @@ class CleanupPurgeLineData(models.TransientModel): _inherit = "cleanup.purge.line" _name = "cleanup.purge.line.data" + _description = "Cleanup Purge Line Data" data_id = fields.Many2one("ir.model.data", "Data entry") wizard_id = fields.Many2one( "cleanup.purge.wizard.data", "Purge Wizard", readonly=True ) - @api.multi def purge(self): """Unlink data entries upon manual confirmation.""" if self: diff --git a/database_cleanup/models/purge_menus.py b/database_cleanup/models/purge_menus.py index de41cdccfb2..f73d918de9b 100644 --- a/database_cleanup/models/purge_menus.py +++ b/database_cleanup/models/purge_menus.py @@ -1,4 +1,5 @@ # Copyright 2014-2016 Therp BV +# Copyright 2021 Camptocamp # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # pylint: disable=consider-merging-classes-inherited from odoo import _, api, fields, models @@ -8,13 +9,13 @@ class CleanupPurgeLineMenu(models.TransientModel): _inherit = "cleanup.purge.line" _name = "cleanup.purge.line.menu" + _description = "Cleanup Purge Line Menu" wizard_id = fields.Many2one( "cleanup.purge.wizard.menu", "Purge Wizard", readonly=True ) menu_id = fields.Many2one("ir.ui.menu", "Menu entry") - @api.multi def purge(self): """Unlink menu entries upon manual confirmation.""" if self: @@ -47,9 +48,7 @@ def find(self): ): if menu.action.type != "ir.actions.act_window": continue - if (menu.action.res_model and menu.action.res_model not in self.env) or ( - menu.action.src_model and menu.action.src_model not in self.env - ): + if menu.action.res_model and menu.action.res_model not in self.env: res.append( ( 0, diff --git a/database_cleanup/models/purge_models.py b/database_cleanup/models/purge_models.py index f20f5dfc261..26548e3d251 100644 --- a/database_cleanup/models/purge_models.py +++ b/database_cleanup/models/purge_models.py @@ -1,4 +1,5 @@ # Copyright 2014-2016 Therp BV +# Copyright 2021 Camptocamp # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # pylint: disable=consider-merging-classes-inherited from odoo import _, api, fields, models @@ -25,7 +26,6 @@ def _inherited_models(self): class IrModelFields(models.Model): _inherit = "ir.model.fields" - @api.multi def _prepare_update(self): """this function crashes for undefined models""" existing = self.filtered(lambda x: x.model in self.env) @@ -35,13 +35,12 @@ def _prepare_update(self): class CleanupPurgeLineModel(models.TransientModel): _inherit = "cleanup.purge.line" _name = "cleanup.purge.line.model" - _description = "Purge models" + _description = "Cleanup Purge Line Model" wizard_id = fields.Many2one( "cleanup.purge.wizard.model", "Purge Wizard", readonly=True ) - @api.multi def purge(self): """ Unlink models upon manual confirmation. diff --git a/database_cleanup/models/purge_modules.py b/database_cleanup/models/purge_modules.py index 278badd5fd5..f085b237487 100644 --- a/database_cleanup/models/purge_modules.py +++ b/database_cleanup/models/purge_modules.py @@ -1,4 +1,5 @@ # Copyright 2014-2016 Therp BV +# Copyright 2021 Camptocamp # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # pylint: disable=consider-merging-classes-inherited from odoo import _, api, fields, models @@ -33,12 +34,12 @@ def _module_data_uninstall(self, modules_to_remove): class CleanupPurgeLineModule(models.TransientModel): _inherit = "cleanup.purge.line" _name = "cleanup.purge.line.module" + _description = "Cleanup Purge Line Module" wizard_id = fields.Many2one( "cleanup.purge.wizard.module", "Purge Wizard", readonly=True ) - @api.multi def purge(self): """ Uninstall modules upon manual confirmation, then reload diff --git a/database_cleanup/models/purge_properties.py b/database_cleanup/models/purge_properties.py index b79e8fec81c..aa118dd35e9 100644 --- a/database_cleanup/models/purge_properties.py +++ b/database_cleanup/models/purge_properties.py @@ -1,18 +1,19 @@ # Copyright 2017 Therp BV +# Copyright 2021 Camptocamp # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # pylint: disable=consider-merging-classes-inherited from odoo import api, fields, models -REASON_DUPLICATE = 1 -REASON_DEFAULT = 2 -REASON_DEFAULT_FALSE = 3 -REASON_UNKNOWN_MODEL = 4 +REASON_DUPLICATE = "REASON_DUPLICATE" +REASON_DEFAULT = "REASON_DEFAULT" +REASON_DEFAULT_FALSE = "REASON_DEFAULT_FALSE" +REASON_UNKNOWN_MODEL = "REASON_UNKNOWN_MODEL" class CleanupPurgeLineProperty(models.TransientModel): _inherit = "cleanup.purge.line" _name = "cleanup.purge.line.property" - _description = "Purge properties" + _description = "Cleanup Purge Line Property" wizard_id = fields.Many2one( "cleanup.purge.wizard.property", "Purge Wizard", readonly=True @@ -27,7 +28,6 @@ class CleanupPurgeLineProperty(models.TransientModel): ] ) - @api.multi def purge(self): """Delete properties""" self.write({"purged": True}) @@ -58,12 +58,7 @@ def find(self): except KeyError: result.append( { - "name": "%s@%s: %s" - % ( - prop.name, - prop.res_id, - value, - ), + "name": "{}@{}: {}".format(prop.name, prop.res_id, value), "property_id": prop.id, "reason": REASON_UNKNOWN_MODEL, } @@ -72,12 +67,7 @@ def find(self): if not value: result.append( { - "name": "%s@%s: %s" - % ( - prop.name, - prop.res_id, - value, - ), + "name": "{}@{}: {}".format(prop.name, prop.res_id, value), "property_id": prop.id, "reason": REASON_DEFAULT_FALSE, } @@ -127,8 +117,9 @@ def find(self): for redundant_property in self.env["ir.property"].search(domain): result.append( { - "name": "%s@%s: %s" - % (prop.name, redundant_property.res_id, prop.get_by_record()), + "name": "{}@{}: {}".format( + prop.name, redundant_property.res_id, prop.get_by_record() + ), "property_id": redundant_property.id, "reason": REASON_DEFAULT, } @@ -148,8 +139,9 @@ def find(self): for prop in self.env["ir.property"].search([("id", "in", ids)])[1:]: result.append( { - "name": "%s@%s: %s" - % (prop.name, prop.res_id, prop.get_by_record()), + "name": "{}@{}: {}".format( + prop.name, prop.res_id, prop.get_by_record() + ), "property_id": prop.id, "reason": REASON_DUPLICATE, } diff --git a/database_cleanup/models/purge_tables.py b/database_cleanup/models/purge_tables.py index 58086b67d02..cde69fd8799 100644 --- a/database_cleanup/models/purge_tables.py +++ b/database_cleanup/models/purge_tables.py @@ -1,4 +1,5 @@ # Copyright 2014-2016 Therp BV +# Copyright 2021 Camptocamp # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # pylint: disable=consider-merging-classes-inherited from odoo import _, api, fields, models @@ -10,13 +11,12 @@ class CleanupPurgeLineTable(models.TransientModel): _inherit = "cleanup.purge.line" _name = "cleanup.purge.line.table" - _description = "Purge tables wizard lines" + _description = "Cleanup Purge Line Table" wizard_id = fields.Many2one( "cleanup.purge.wizard.table", "Purge Wizard", readonly=True ) - @api.multi def purge(self): """ Unlink tables upon manual confirmation. diff --git a/database_cleanup/models/purge_wizard.py b/database_cleanup/models/purge_wizard.py index eb07fd47ece..2a6eb8bcef8 100644 --- a/database_cleanup/models/purge_wizard.py +++ b/database_cleanup/models/purge_wizard.py @@ -1,4 +1,5 @@ # Copyright 2014-2016 Therp BV +# Copyright 2021 Camptocamp # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # pylint: disable=consider-merging-classes-inherited @@ -21,7 +22,6 @@ class CleanupPurgeLine(models.AbstractModel): logger = logging.getLogger("odoo.addons.database_cleanup") - @api.multi def purge(self): raise NotImplementedError @@ -46,11 +46,9 @@ def default_get(self, fields_list): res["purge_line_ids"] = self.find() return res - @api.multi def find(self): raise NotImplementedError - @api.multi def purge_all(self): self.mapped("purge_line_ids").purge() return True @@ -70,7 +68,6 @@ def get_wizard_action(self): }, } - @api.multi def select_lines(self): return { "type": "ir.actions.act_window", @@ -80,7 +77,6 @@ def select_lines(self): "domain": [("wizard_id", "in", self.ids)], } - @api.multi def name_get(self): return [(this.id, self._description) for this in self] diff --git a/database_cleanup/readme/CONTRIBUTORS.rst b/database_cleanup/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000000..347fe7519e1 --- /dev/null +++ b/database_cleanup/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* Stefan Rijnhart +* Holger Brunn +* Stéphane Mangin diff --git a/database_cleanup/readme/DESCRIPTION.rst b/database_cleanup/readme/DESCRIPTION.rst new file mode 100644 index 00000000000..1656dca9721 --- /dev/null +++ b/database_cleanup/readme/DESCRIPTION.rst @@ -0,0 +1,9 @@ +Clean your Odoo database from remnants of modules, models, columns and +tables left by uninstalled modules (prior to 7.0) or a homebrew database +upgrade to a new major version of Odoo. + +Caution! This module is potentially harmful and can *easily* destroy the +integrity of your data. Do not use if you are not entirely comfortable +with the technical details of the Odoo data model of *all* the modules +that have ever been installed on your database, and do not purge any module, +model, column or table if you do not know exactly what you are doing. diff --git a/database_cleanup/readme/USAGE.rst b/database_cleanup/readme/USAGE.rst new file mode 100644 index 00000000000..f97ea497393 --- /dev/null +++ b/database_cleanup/readme/USAGE.rst @@ -0,0 +1,10 @@ +After installation of this module, go to the Settings menu -> Technical -> +Database cleanup. This menu is only available to members of the *Access Rights* +group. Go through the modules, models, columns and tables +entries under this menu (in that order) and find out if there is orphaned data +in your database. You can either delete entries by line, or sweep all entries +in one big step (if you are *really* confident). + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/149/11.0 diff --git a/database_cleanup/security/ir.model.access.csv b/database_cleanup/security/ir.model.access.csv new file mode 100644 index 00000000000..dde616bd074 --- /dev/null +++ b/database_cleanup/security/ir.model.access.csv @@ -0,0 +1,17 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_cleanup_create_indexes_line,access_cleanup_create_indexes_line,model_cleanup_create_indexes_line,base.group_user,1,1,1,1 +access_cleanup_create_indexes_wizard,access_cleanup_create_indexes_wizard,model_cleanup_create_indexes_wizard,base.group_user,1,1,1,1 +access_cleanup_purge_line_module,access_cleanup_purge_line_module,model_cleanup_purge_line_module,base.group_user,1,1,1,1 +access_cleanup_purge_wizard_module,access_cleanup_purge_wizard_module,model_cleanup_purge_wizard_module,base.group_user,1,1,1,1 +access_cleanup_purge_line_model,access_cleanup_purge_line_model,model_cleanup_purge_line_model,base.group_user,1,1,1,1 +access_cleanup_purge_wizard_model,access_cleanup_purge_wizard_model,model_cleanup_purge_wizard_model,base.group_user,1,1,1,1 +access_cleanup_purge_line_column,access_cleanup_purge_line_column,model_cleanup_purge_line_column,base.group_user,1,1,1,1 +access_cleanup_purge_wizard_column,access_cleanup_purge_wizard_column,model_cleanup_purge_wizard_column,base.group_user,1,1,1,1 +access_cleanup_purge_line_table,access_cleanup_purge_line_table,model_cleanup_purge_line_table,base.group_user,1,1,1,1 +access_cleanup_purge_wizard_table,access_cleanup_purge_wizard_table,model_cleanup_purge_wizard_table,base.group_user,1,1,1,1 +access_cleanup_purge_line_data,access_cleanup_purge_line_data,model_cleanup_purge_line_data,base.group_user,1,1,1,1 +access_cleanup_purge_wizard_data,access_cleanup_purge_wizard_data,model_cleanup_purge_wizard_data,base.group_user,1,1,1,1 +access_cleanup_purge_line_menu,access_cleanup_purge_line_menu,model_cleanup_purge_line_menu,base.group_user,1,1,1,1 +access_cleanup_purge_wizard_menu,access_cleanup_purge_wizard_menu,model_cleanup_purge_wizard_menu,base.group_user,1,1,1,1 +access_cleanup_purge_line_property,access_cleanup_purge_line_property,model_cleanup_purge_line_property,base.group_user,1,1,1,1 +access_cleanup_purge_wizard_property,access_cleanup_purge_wizard_property,model_cleanup_purge_wizard_property,base.group_user,1,1,1,1 diff --git a/database_cleanup/tests/__init__.py b/database_cleanup/tests/__init__.py index 298a513e25a..b5658779060 100644 --- a/database_cleanup/tests/__init__.py +++ b/database_cleanup/tests/__init__.py @@ -1,3 +1,12 @@ # Copyright 2016 Therp BV +# Copyright 2021 Camptocamp # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from . import test_database_cleanup +from . import common +from . import test_create_indexes +from . import test_purge_columns +from . import test_purge_data +from . import test_purge_menus +from . import test_purge_models +from . import test_purge_modules +from . import test_purge_properties +from . import test_purge_tables diff --git a/database_cleanup/tests/common.py b/database_cleanup/tests/common.py new file mode 100644 index 00000000000..df9b1700785 --- /dev/null +++ b/database_cleanup/tests/common.py @@ -0,0 +1,24 @@ +# Copyright 2021 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo.modules.registry import Registry +from odoo.tests import TransactionCase +from odoo.tests.common import tagged + + +# Use post_install to get all models loaded more info: odoo/odoo#13458 +@tagged("post_install", "-at_install") +class Common(TransactionCase): + def setUp(self): + super(Common, self).setUp() + # this reloads our registry, and we don't want to run tests twice + # we also need the original registry for further tests, so save a + # reference to it + self.original_registry = Registry.registries[self.env.cr.dbname] + + def tearDown(self): + super(Common, self).tearDown() + # Force rollback to avoid unstable test database + self.env.cr.rollback() + # reset afterwards + Registry.registries[self.env.cr.dbname] = self.original_registry diff --git a/database_cleanup/tests/test_create_indexes.py b/database_cleanup/tests/test_create_indexes.py new file mode 100644 index 00000000000..4bfe6f2e756 --- /dev/null +++ b/database_cleanup/tests/test_create_indexes.py @@ -0,0 +1,20 @@ +# Copyright 2021 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from .common import Common + + +class TestCreateIndexesLine(Common): + def setUp(self): + super(TestCreateIndexesLine, self).setUp() + # delete some index and check if our module recreated it + self.env.cr.execute("drop index res_partner_name_index") + + def test_deleted_index(self): + wizard = self.env["cleanup.create_indexes.wizard"].create({}) + wizard.purge_all() + self.env.cr.execute( + "select indexname from pg_indexes where " + "indexname='res_partner_name_index' and tablename='res_partner' " + ) + self.assertEqual(self.env.cr.rowcount, 1) diff --git a/database_cleanup/tests/test_database_cleanup.py b/database_cleanup/tests/test_database_cleanup.py deleted file mode 100644 index e9ae5765725..00000000000 --- a/database_cleanup/tests/test_database_cleanup.py +++ /dev/null @@ -1,154 +0,0 @@ -# Copyright 2016 Therp BV -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from psycopg2 import ProgrammingError - -from odoo.modules.registry import Registry -from odoo.tests.common import TransactionCase, at_install, post_install -from odoo.tools import config, mute_logger - - -# Use post_install to get all models loaded more info: odoo/odoo#13458 -@at_install(False) -@post_install(True) -class TestDatabaseCleanup(TransactionCase): - def setUp(self): - super(TestDatabaseCleanup, self).setUp() - self.module = None - self.model = None - # Create one property for tests - self.env["ir.property"].create( - { - "fields_id": self.env.ref("base.field_res_partner__name").id, - "type": "char", - "value_text": "My default partner name", - } - ) - - def test_database_cleanup(self): - # delete some index and check if our module recreated it - self.env.cr.execute("drop index res_partner_name_index") - create_indexes = self.env["cleanup.create_indexes.wizard"].create({}) - create_indexes.purge_all() - self.env.cr.execute( - "select indexname from pg_indexes " - "where indexname='res_partner_name_index' and " - "tablename='res_partner'" - ) - self.assertEqual(self.env.cr.rowcount, 1) - # duplicate a property - duplicate_property = self.env["ir.property"].search([], limit=1).copy() - purge_property = self.env["cleanup.purge.wizard.property"].create({}) - purge_property.purge_all() - self.assertFalse(duplicate_property.exists()) - # create an orphaned column - self.env.cr.execute( - "alter table res_partner add column database_cleanup_test int" - ) - # We need use a model that is not blocked (Avoid use res.users) - partner_model = self.env["ir.model"].search( - [("model", "=", "res.partner")], limit=1 - ) - purge_columns = self.env["cleanup.purge.wizard.column"].create( - { - "purge_line_ids": [ - ( - 0, - 0, - {"model_id": partner_model.id, "name": "database_cleanup_test"}, - ) - ] - } - ) - purge_columns.purge_all() - # must be removed by the wizard - with self.assertRaises(ProgrammingError): - with self.env.registry.cursor() as cr: - with mute_logger("odoo.sql_db"): - cr.execute("select database_cleanup_test from res_partner") - - # create a data entry pointing nowhere - self.env.cr.execute("select max(id) + 1 from res_users") - self.env["ir.model.data"].create( - { - "module": "database_cleanup", - "name": "test_no_data_entry", - "model": "res.users", - "res_id": self.env.cr.fetchone()[0], - } - ) - purge_data = self.env["cleanup.purge.wizard.data"].create({}) - purge_data.purge_all() - # must be removed by the wizard - with self.assertRaises(ValueError): - self.env.ref("database_cleanup.test_no_data_entry") - - # create a nonexistent model - self.model = self.env["ir.model"].create( - { - "name": "Database cleanup test model", - "model": "x_database.cleanup.test.model", - } - ) - self.env.cr.execute( - "insert into ir_attachment (name, res_model, res_id, type) values " - "('test attachment', 'database.cleanup.test.model', 42, 'binary')" - ) - self.env.registry.models.pop("x_database.cleanup.test.model") - purge_models = self.env["cleanup.purge.wizard.model"].create({}) - purge_models.purge_all() - # must be removed by the wizard - self.assertFalse( - self.env["ir.model"].search( - [ - ("model", "=", "x_database.cleanup.test.model"), - ] - ) - ) - - # create a nonexistent module - self.module = self.env["ir.module.module"].create( - { - "name": "database_cleanup_test", - "state": "to upgrade", - } - ) - purge_modules = self.env["cleanup.purge.wizard.module"].create({}) - # this reloads our registry, and we don't want to run tests twice - # we also need the original registry for further tests, so save a - # reference to it - original_registry = Registry.registries[self.env.cr.dbname] - config.options["test_enable"] = False - purge_modules.purge_all() - config.options["test_enable"] = True - # must be removed by the wizard - self.assertFalse( - self.env["ir.module.module"].search( - [ - ("name", "=", "database_cleanup_test"), - ] - ) - ) - # reset afterwards - Registry.registries[self.env.cr.dbname] = original_registry - - # create an orphaned table - self.env.cr.execute("create table database_cleanup_test (test int)") - purge_tables = self.env["cleanup.purge.wizard.table"].create({}) - purge_tables.purge_all() - with self.assertRaises(ProgrammingError): - with self.env.registry.cursor() as cr: - with mute_logger("odoo.sql_db"): - cr.execute("select * from database_cleanup_test") - - def tearDown(self): - super(TestDatabaseCleanup, self).tearDown() - with self.registry.cursor() as cr2: - # Release blocked tables with pending deletes - self.env.cr.rollback() - if self.module: - cr2.execute( - "DELETE FROM ir_module_module WHERE id=%s", (self.module.id,) - ) - if self.model: - cr2.execute("DELETE FROM ir_model WHERE id=%s", (self.model.id,)) - cr2.commit() diff --git a/database_cleanup/tests/test_purge_columns.py b/database_cleanup/tests/test_purge_columns.py new file mode 100644 index 00000000000..c4b06a5a793 --- /dev/null +++ b/database_cleanup/tests/test_purge_columns.py @@ -0,0 +1,39 @@ +# Copyright 2021 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from psycopg2 import ProgrammingError + +from odoo.tools import mute_logger + +from .common import Common + + +class TestCleanupPurgeLineColumn(Common): + def setUp(self): + super(TestCleanupPurgeLineColumn, self).setUp() + # create an orphaned column + self.env.cr.execute( + "alter table res_partner add column database_cleanup_test int" + ) + + def test_empty_column(self): + # We need use a model that is not blocked (Avoid use res.users) + partner_model = self.env["ir.model"].search( + [("model", "=", "res.partner")], limit=1 + ) + wizard = self.env["cleanup.purge.wizard.column"].create( + { + "purge_line_ids": [ + ( + 0, + 0, + {"model_id": partner_model.id, "name": "database_cleanup_test"}, + ) + ] + } + ) + wizard.purge_all() + # must be removed by the wizard + with self.assertRaises(ProgrammingError): + with self.env.registry.cursor() as cr: + with mute_logger("odoo.sql_db"): + cr.execute("select database_cleanup_test from res_partner") diff --git a/database_cleanup/tests/test_purge_data.py b/database_cleanup/tests/test_purge_data.py new file mode 100644 index 00000000000..e979f13dc66 --- /dev/null +++ b/database_cleanup/tests/test_purge_data.py @@ -0,0 +1,26 @@ +# Copyright 2021 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from .common import Common + + +class TestCleanupPurgeLineData(Common): + def setUp(self): + super(TestCleanupPurgeLineData, self).setUp() + # create a data entry pointing nowhere + self.env.cr.execute("select max(id) + 1 from res_users") + self.env["ir.model.data"].create( + { + "module": "database_cleanup", + "name": "test_no_data_entry", + "model": "res.users", + "res_id": self.env.cr.fetchone()[0], + } + ) + + def test_pointing_nowhere(self): + wizard = self.env["cleanup.purge.wizard.data"].create({}) + wizard.purge_all() + # must be removed by the wizard + with self.assertRaises(ValueError): + self.env.ref("database_cleanup.test_no_data_entry") diff --git a/database_cleanup/tests/test_purge_menus.py b/database_cleanup/tests/test_purge_menus.py new file mode 100644 index 00000000000..4b5d26e99ca --- /dev/null +++ b/database_cleanup/tests/test_purge_menus.py @@ -0,0 +1,34 @@ +# Copyright 2021 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from .common import Common + + +class TestCleanupPurgeLineMenu(Common): + def setUp(self): + super(TestCleanupPurgeLineMenu, self).setUp() + # create a new empty menu + self.menu = self.env["ir.ui.menu"].create({"name": "database_cleanup_test"}) + + def test_empty_menu(self): + wizard = self.env["cleanup.purge.wizard.menu"].create( + { + "purge_line_ids": [ + ( + 0, + 0, + { + "menu_id": self.menu.id, + }, + ) + ] + } + ) + wizard.purge_all() + self.assertFalse( + self.env["ir.ui.menu"].search( + [ + ("name", "=", "database_cleanup_test"), + ] + ) + ) diff --git a/database_cleanup/tests/test_purge_models.py b/database_cleanup/tests/test_purge_models.py new file mode 100644 index 00000000000..3e79f8f7af8 --- /dev/null +++ b/database_cleanup/tests/test_purge_models.py @@ -0,0 +1,40 @@ +# Copyright 2021 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from .common import Common + + +class TestCleanupPurgeLineColumn(Common): + def setUp(self): + super(TestCleanupPurgeLineColumn, self).setUp() + # create a nonexistent model + self.model_name = "x_database.cleanup.test.model" + self.model_values = { + "name": "Database cleanup test model", + "model": self.model_name, + } + self.model = self.env["ir.model"].create(self.model_values) + self.env.cr.execute( + "insert into ir_attachment (name, res_model, res_id, type) values " + "('test attachment', %s, 42, 'binary')", + [self.model_name], + ) + self.env.registry.models.pop(self.model_name) + + def tearDown(self): + """ We recreate the model to avoid registry Exception at loading """ + super(TestCleanupPurgeLineColumn, self).tearDown() + # FIXME: issue origin is not clear but it must be addressed. + self.model = self.env["ir.model"].create(self.model_values) + + def test_empty_model(self): + wizard = self.env["cleanup.purge.wizard.model"].create({}) + wizard.purge_all() + # must be removed by the wizard + self.assertFalse( + self.env["ir.model"].search( + [ + ("model", "=", self.model_name), + ] + ) + ) diff --git a/database_cleanup/tests/test_purge_modules.py b/database_cleanup/tests/test_purge_modules.py new file mode 100644 index 00000000000..3c8778a48a7 --- /dev/null +++ b/database_cleanup/tests/test_purge_modules.py @@ -0,0 +1,35 @@ +# Copyright 2021 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +import threading + +from odoo.tools import config + +from .common import Common + + +class TestCleanupPurgeLineModule(Common): + def setUp(self): + super(TestCleanupPurgeLineModule, self).setUp() + # create a nonexistent module + self.module = self.env["ir.module.module"].create( + { + "name": "database_cleanup_test", + "state": "to upgrade", + } + ) + + def test_remove_to_upgrade_module(self): + wizard = self.env["cleanup.purge.wizard.module"].create({}) + config.options["test_enable"] = False # Maybe useless now ?! + self.patch(threading.currentThread(), "testing", False) + wizard.purge_all() + config.options["test_enable"] = True # Maybe useless now ?! + self.patch(threading.currentThread(), "testing", True) + # must be removed by the wizard + self.assertFalse( + self.env["ir.module.module"].search( + [ + ("name", "=", "database_cleanup_test"), + ] + ) + ) diff --git a/database_cleanup/tests/test_purge_properties.py b/database_cleanup/tests/test_purge_properties.py new file mode 100644 index 00000000000..589aecc4f81 --- /dev/null +++ b/database_cleanup/tests/test_purge_properties.py @@ -0,0 +1,39 @@ +# Copyright 2021 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from .common import Common + + +class TestCleanupPurgeLineProperty(Common): + def setUp(self): + super(TestCleanupPurgeLineProperty, self).setUp() + # Create one property for tests + self.partner_name_field_id = self.env["ir.model.fields"].search( + [("name", "=", "name"), ("model_id.model", "=", "res.partner")], limit=1 + ) + + def test_property_to_not_removed(self): + self.property = self.env["ir.property"].create( + { + "fields_id": self.partner_name_field_id.id, + "type": "char", + "value_text": "My default partner name", + "res_id": False, + } + ) + wizard = self.env["cleanup.purge.wizard.property"].create({}) + wizard.purge_all() + self.assertTrue(self.property.exists()) + + def test_property_no_value(self): + self.property = self.env["ir.property"].create( + { + "fields_id": self.partner_name_field_id.id, + "type": "char", + "value_text": False, + "res_id": False, + } + ) + wizard = self.env["cleanup.purge.wizard.property"].create({}) + wizard.purge_all() + self.assertFalse(self.property.exists()) diff --git a/database_cleanup/tests/test_purge_tables.py b/database_cleanup/tests/test_purge_tables.py new file mode 100644 index 00000000000..bf21177dffa --- /dev/null +++ b/database_cleanup/tests/test_purge_tables.py @@ -0,0 +1,22 @@ +# Copyright 2021 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from psycopg2 import ProgrammingError + +from odoo.tools import mute_logger + +from .common import Common + + +class TestCleanupPurgeLineTable(Common): + def setUp(self): + super(TestCleanupPurgeLineTable, self).setUp() + # create an orphaned table + self.env.cr.execute("create table database_cleanup_test (test int)") + + def test_empty_table(self): + wizard = self.env["cleanup.purge.wizard.table"].create({}) + wizard.purge_all() + with self.assertRaises(ProgrammingError): + with self.env.registry.cursor() as cr: + with mute_logger("odoo.sql_db"): + cr.execute("select * from database_cleanup_test") diff --git a/database_cleanup/views/purge_menus.xml b/database_cleanup/views/purge_menus.xml index 8b6286c0f85..2209e7f71fa 100644 --- a/database_cleanup/views/purge_menus.xml +++ b/database_cleanup/views/purge_menus.xml @@ -15,7 +15,7 @@ code - action = env.get('cleanup.purge.wizard.menu').get_wizard_action() + action = env.get('cleanup.purge.wizard').get_wizard_action()