[CWEP002 migration] properly raise exception on (add|drop)_relation_definition for computed relation
authorLaura Médioni <laura.medioni@logilab.fr>
Thu, 28 Aug 2014 07:49:31 +0200
changeset 9961 cef58bd36f7b
parent 9960 6359f3121f3f
child 9962 64b573d54133
[CWEP002 migration] properly raise exception on (add|drop)_relation_definition for computed relation Related to #3546717
server/migractions.py
server/test/datacomputed/migratedapp/schema.py
server/test/datacomputed/schema.py
server/test/unittest_migractions.py
--- a/server/migractions.py	Fri Jun 27 12:00:17 2014 +0200
+++ b/server/migractions.py	Thu Aug 28 07:49:31 2014 +0200
@@ -1086,6 +1086,9 @@
         schema definition file
         """
         rschema = self.fs_schema.rschema(rtype)
+        if rschema.rule:
+            raise ExecutionError('Cannot add a relation definition for a '
+                                 'computed relation (%s)' % rschema)
         if not rtype in self.repo.schema:
             self.cmd_add_relation_type(rtype, addrdef=False, commit=True)
         if (subjtype, objtype) in self.repo.schema.rschema(rtype).rdefs:
@@ -1113,6 +1116,9 @@
     def cmd_drop_relation_definition(self, subjtype, rtype, objtype, commit=True):
         """unregister an existing relation definition"""
         rschema = self.repo.schema.rschema(rtype)
+        if rschema.rule:
+            raise ExecutionError('Cannot drop a relation definition for a '
+                                 'computed relation (%s)' % rschema)
         # unregister the definition from CWAttribute or CWRelation
         if rschema.final:
             etype = 'CWAttribute'
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/test/datacomputed/migratedapp/schema.py	Thu Aug 28 07:49:31 2014 +0200
@@ -0,0 +1,51 @@
+# copyright 2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of CubicWeb.
+#
+# CubicWeb is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# CubicWeb 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 Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
+
+from yams.buildobjs import EntityType, RelationDefinition, ComputedRelation
+
+
+class Employee(EntityType):
+    pass
+
+
+class employees(RelationDefinition):
+    subject = 'Company'
+    object = 'Employee'
+
+
+class associates(RelationDefinition):
+    subject = 'Company'
+    object = 'Employee'
+
+
+class works_for(ComputedRelation):
+    rule = 'O employees S, NOT EXISTS (O associates S)'
+
+
+class Company(EntityType):
+    pass
+
+
+class Note(EntityType):
+    pass
+
+
+class concerns(RelationDefinition):
+    subject = 'Note'
+    object = 'Employee'
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/test/datacomputed/schema.py	Thu Aug 28 07:49:31 2014 +0200
@@ -0,0 +1,50 @@
+# copyright 2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of CubicWeb.
+#
+# CubicWeb is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# CubicWeb 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 Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
+
+from yams.buildobjs import EntityType, RelationDefinition, ComputedRelation
+
+
+class Employee(EntityType):
+    pass
+
+
+class employees(RelationDefinition):
+    subject = 'Company'
+    object = 'Employee'
+
+
+class associates(RelationDefinition):
+    subject = 'Company'
+    object = 'Employee'
+
+
+class Company(EntityType):
+    pass
+
+
+class Note(EntityType):
+    pass
+
+
+class concerns(RelationDefinition):
+    subject = 'Note'
+    object = 'Employee'
+
+
+class notes(ComputedRelation):
+    rule = 'S employees E, O concerns E'
--- a/server/test/unittest_migractions.py	Fri Jun 27 12:00:17 2014 +0200
+++ b/server/test/unittest_migractions.py	Thu Aug 28 07:49:31 2014 +0200
@@ -18,46 +18,50 @@
 """unit tests for module cubicweb.server.migractions"""
 
 from datetime import date
-from os.path import join
+import os.path as osp
 from contextlib import contextmanager
 
 from logilab.common.testlib import unittest_main, Tags, tag
 
 from yams.constraints import UniqueConstraint
 
-from cubicweb import ConfigurationError, ValidationError
+from cubicweb import ConfigurationError, ValidationError, ExecutionError
 from cubicweb.devtools.testlib import CubicWebTC
 from cubicweb.server.sqlutils import SQL_PREFIX
 from cubicweb.server.migractions import ServerMigrationHelper
 
 import cubicweb.devtools
 
+
+HERE = osp.dirname(osp.abspath(__file__))
+
 migrschema = None
 def tearDownModule(*args):
     global migrschema
     del migrschema
     if hasattr(MigrationCommandsTC, 'origschema'):
         del MigrationCommandsTC.origschema
+    if hasattr(MigrationCommandsComputedTC, 'origschema'):
+        del MigrationCommandsComputedTC.origschema
 
-class MigrationCommandsTC(CubicWebTC):
+class MigrationTC(CubicWebTC):
 
     configcls = cubicweb.devtools.TestServerConfiguration
 
     tags = CubicWebTC.tags | Tags(('server', 'migration', 'migractions'))
 
     def _init_repo(self):
-        super(MigrationCommandsTC, self)._init_repo()
+        super(MigrationTC, self)._init_repo()
         # we have to read schema from the database to get eid for schema entities
         self.repo.set_schema(self.repo.deserialize_schema(), resetvreg=False)
         # hack to read the schema from data/migrschema
         config = self.config
-        config.appid = join('data', 'migratedapp')
-        config._apphome = self.datapath('migratedapp')
+        config.appid = osp.join(self.appid, 'migratedapp')
+        config._apphome = osp.join(HERE, config.appid)
         global migrschema
         migrschema = config.load_schema()
-        config.appid = 'data'
-        config._apphome = self.datadir
-        assert 'Folder' in migrschema
+        config.appid = self.appid
+        config._apphome = osp.join(HERE, self.appid)
 
     def setUp(self):
         CubicWebTC.setUp(self)
@@ -73,6 +77,13 @@
                                              repo=self.repo, cnx=cnx,
                                              interactive=False)
 
+
+class MigrationCommandsTC(MigrationTC):
+
+    def _init_repo(self):
+        super(MigrationCommandsTC, self)._init_repo()
+        assert 'Folder' in migrschema
+
     def test_add_attribute_bool(self):
         with self.mh() as (cnx, mh):
             self.assertNotIn('yesno', self.schema)
@@ -667,5 +678,31 @@
                                      "and name='same_as_relation'")
             self.assertTrue(same_as_sql)
 
+
+class MigrationCommandsComputedTC(MigrationTC):
+    """ Unit tests for computed relations and attributes
+    """
+    appid = 'datacomputed'
+
+    def test_computed_relation_add_relation_definition(self):
+        self.assertNotIn('works_for', self.schema)
+        with self.mh() as (cnx, mh):
+            with self.assertRaises(ExecutionError) as exc:
+                mh.cmd_add_relation_definition('Employee', 'works_for',
+                                                    'Company')
+        self.assertEqual(str(exc.exception),
+                         'Cannot add a relation definition for a computed '
+                         'relation (works_for)')
+
+    def test_computed_relation_drop_relation_definition(self):
+        self.assertIn('notes', self.schema)
+        with self.mh() as (cnx, mh):
+            with self.assertRaises(ExecutionError) as exc:
+                mh.cmd_drop_relation_definition('Company', 'notes', 'Note')
+        self.assertEqual(str(exc.exception),
+                         'Cannot drop a relation definition for a computed '
+                         'relation (notes)')
+
+
 if __name__ == '__main__':
     unittest_main()