From a67c40d77e7e4292ad600aa0cfccc069e463917f Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Wed, 16 Sep 2015 11:29:55 +0200 Subject: [PATCH] Add first tests on revisions --- partner_revision/models/res_partner.py | 13 +- partner_revision/tests/__init__.py | 3 + partner_revision/tests/common.py | 64 +++++++++ partner_revision/tests/test_revision_flow.py | 137 +++++++++++++++++++ 4 files changed, 214 insertions(+), 3 deletions(-) create mode 100644 partner_revision/tests/__init__.py create mode 100644 partner_revision/tests/common.py create mode 100644 partner_revision/tests/test_revision_flow.py diff --git a/partner_revision/models/res_partner.py b/partner_revision/models/res_partner.py index c3ca31e3d..0e415b05e 100644 --- a/partner_revision/models/res_partner.py +++ b/partner_revision/models/res_partner.py @@ -35,6 +35,12 @@ class ResPartner(models.Model): def _add_revision(self, values): """ Add a revision on a partner + By default, when a partner is modified by a user or by the + system, the changes are applied and a validated revision is + created. Callers which want to delegate the write of some + fields to the revision must explicitly ask for it by providing a + key ``__revision_rules`` in the environment's context. + :param values: the values being written on the partner :type values: dict @@ -51,8 +57,6 @@ class ResPartner(models.Model): if not rule: continue if field in values: - # TODO: if a change is done manually, values are always - # written but we create 'done' changes if self[field] == values[field]: # TODO handle relations, types continue @@ -61,7 +65,10 @@ class ResPartner(models.Model): 'new_value': values[field], 'field_id': rule.field_id.id, } - if rule.default_behavior == 'auto': + if not self.env.context.get('__revision_rules'): + # by default always write on partner + change['state'] = 'done' + elif rule.default_behavior == 'auto': change['state'] = 'done' elif rule.default_behavior == 'validate': change['state'] = 'draft' diff --git a/partner_revision/tests/__init__.py b/partner_revision/tests/__init__.py new file mode 100644 index 000000000..1f4968042 --- /dev/null +++ b/partner_revision/tests/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import test_revision_flow diff --git a/partner_revision/tests/common.py b/partner_revision/tests/common.py new file mode 100644 index 000000000..f42a0240d --- /dev/null +++ b/partner_revision/tests/common.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- +# +# +# Authors: Guewen Baconnier +# Copyright 2015 Camptocamp SA +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# + + +class RevisionMixin(object): + + def assert_revision(self, partner, expected_changes): + """ Check if a revision has been created according to expected values + + The partner should have no prior revision than the one created in the + test (so it has exactly 1 revision). + + The expected changes are tuples with (field, current_value, + new_value, state) + + :param partner: record of partner having a revision + :param expected_changes: contains tuples with the changes + :type expected_changes: list(tuple)) + """ + revision = self.env['res.partner.revision'].search( + [('partner_id', '=', partner.id)], + ) + self.assertEqual(len(revision), 1, + "1 revision expected, got %s" % (revision,)) + changes = revision.change_ids + missing = [] + for expected_change in expected_changes: + for change in changes: + if (change.field_id, change.current_value, change.new_value, + change.state) == expected_change: + changes -= change + break + else: + missing.append(expected_change) + message = u'' + for field, current_value, new_value, state in missing: + message += ("- field: '%s', current_value: '%s', " + "new_value: '%s', state: '%s'\n" % + (field.name, current_value, new_value, state)) + for change in changes: + message += ("+ field: '%s', current_value: '%s', " + "new_value: '%s', state: '%s'\n" % + (change.field_id.name, change.current_value, + change.new_value, change.state)) + if message: + raise AssertionError('Changes do not match\n\n:%s' % message) diff --git a/partner_revision/tests/test_revision_flow.py b/partner_revision/tests/test_revision_flow.py new file mode 100644 index 000000000..ecdb47569 --- /dev/null +++ b/partner_revision/tests/test_revision_flow.py @@ -0,0 +1,137 @@ +# -*- coding: utf-8 -*- +# +# +# Authors: Guewen Baconnier +# Copyright 2015 Camptocamp SA +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# + + +from openerp.tests import common +from .common import RevisionMixin + + +class TestRevisionFlow(RevisionMixin, common.TransactionCase): + """ Check how revision are generated and applied based on the rules. + + We do not really care about the types of the fields in this test suite, + but we have to ensure that the general revision flows work as expected. + """ + + def _setup_behavior(self): + RevisionBehavior = self.env['revision.behavior'] + partner_model_id = self.env.ref('base.model_res_partner').id + self.field_name = self.env.ref('base.field_res_partner_name') + self.field_street = self.env.ref('base.field_res_partner_street') + self.field_street2 = self.env.ref('base.field_res_partner_street2') + RevisionBehavior.create({ + 'model_id': partner_model_id, + 'field_id': self.field_name.id, + 'default_behavior': 'auto', + }) + RevisionBehavior.create({ + 'model_id': partner_model_id, + 'field_id': self.field_street.id, + 'default_behavior': 'validate', + }) + RevisionBehavior.create({ + 'model_id': partner_model_id, + 'field_id': self.field_street2.id, + 'default_behavior': 'never', + }) + + def setUp(self): + super(TestRevisionFlow, self).setUp() + self._setup_behavior() + self.partner = self.env['res.partner'].create({ + 'name': 'X', + 'street': 'street X', + 'street2': 'street2 X', + }) + + def assert_revision(self, partner, expected_changes): + """ Check if a revision has been created according to expected values + + The partner should have no prior revision than the one created in the + test (so it has exactly 1 revision). + + The expected changes are tuples with (field, current_value, + new_value, state) + + :param partner: record of partner having a revision + :param expected_changes: contains tuples with the changes + :type expected_changes: list(tuple)) + """ + revision = self.env['res.partner.revision'].search( + [('partner_id', '=', partner.id)], + ) + self.assertEqual(len(revision), 1, + "1 revision expected, got %s" % (revision,)) + changes = revision.change_ids + missing = [] + for expected_change in expected_changes: + for change in changes: + if (change.field_id, change.current_value, change.new_value, + change.state) == expected_change: + changes -= change + break + else: + missing.append(expected_change) + message = u'' + for field, current_value, new_value, state in missing: + message += ("- field: '%s', current_value: '%s', " + "new_value: '%s', state: '%s'\n" % + (field.name, current_value, new_value, state)) + for change in changes: + message += ("+ field: '%s', current_value: '%s', " + "new_value: '%s', state: '%s'\n" % + (change.field_id.name, change.current_value, + change.new_value, change.state)) + if message: + raise AssertionError('Changes do not match\n\n:%s' % message) + + def test_new_revision(self): + """ Add a new revision on a partner """ + self.partner.with_context(__revision_rules=True).write({ + 'name': 'Y', + 'street': 'street Y', + 'street2': 'street2 Y', + }) + self.assert_revision( + self.partner, + [(self.field_name, 'X', 'Y', 'done'), + (self.field_street, 'street X', 'street Y', 'draft'), + (self.field_street2, 'street2 X', 'street2 Y', 'cancel'), + ], + ) + + def test_manual_edition(self): + """ A manual edition of a partner should always be applied + + But should create a 'done' revision + """ + self.partner.write({ + 'name': 'Y', + 'street': 'street Y', + 'street2': 'street2 Y', + }) + self.assert_revision( + self.partner, + [(self.field_name, 'X', 'Y', 'done'), + (self.field_street, 'street X', 'street Y', 'done'), + (self.field_street2, 'street2 X', 'street2 Y', 'done'), + ], + )