cubicweb/test/unittest_schema.py
changeset 11348 70337ad23145
parent 11279 e4f11ef1face
child 11681 b23d58050076
equal deleted inserted replaced
11347:b4dcfd734686 11348:70337ad23145
     1 # copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
     1 # copyright 2003-2016 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
     2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
     2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
     3 #
     3 #
     4 # This file is part of CubicWeb.
     4 # This file is part of CubicWeb.
     5 #
     5 #
     6 # CubicWeb is free software: you can redistribute it and/or modify it under the
     6 # CubicWeb is free software: you can redistribute it and/or modify it under the
    15 #
    15 #
    16 # You should have received a copy of the GNU Lesser General Public License along
    16 # You should have received a copy of the GNU Lesser General Public License along
    17 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
    17 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
    18 """unit tests for module cubicweb.schema"""
    18 """unit tests for module cubicweb.schema"""
    19 
    19 
    20 import sys
    20 from os.path import join, dirname
    21 from os.path import join, isabs, basename, dirname
       
    22 
    21 
    23 from logilab.common.testlib import TestCase, unittest_main
    22 from logilab.common.testlib import TestCase, unittest_main
    24 
    23 
    25 from rql import RQLSyntaxError
    24 from rql import RQLSyntaxError
    26 
    25 
    27 from yams import ValidationError, BadSchemaDefinition
    26 from yams import ValidationError, BadSchemaDefinition
    28 from yams.constraints import SizeConstraint, StaticVocabularyConstraint
       
    29 from yams.buildobjs import (RelationDefinition, EntityType, RelationType,
    27 from yams.buildobjs import (RelationDefinition, EntityType, RelationType,
    30                             Int, String, SubjectRelation, ComputedRelation)
    28                             Int, String, ComputedRelation)
    31 from yams.reader import fill_schema
    29 from yams.reader import fill_schema
    32 
    30 
    33 from cubicweb.schema import (
    31 from cubicweb.schema import (
    34     CubicWebSchema, CubicWebEntitySchema, CubicWebSchemaLoader,
    32     CubicWebSchema, CubicWebSchemaLoader,
    35     RQLConstraint, RQLUniqueConstraint, RQLVocabularyConstraint,
    33     RQLConstraint, RQLUniqueConstraint, RQLVocabularyConstraint,
    36     RQLExpression, ERQLExpression, RRQLExpression,
    34     ERQLExpression, RRQLExpression,
    37     normalize_expression, order_eschemas, guess_rrqlexpr_mainvars,
    35     normalize_expression, order_eschemas, guess_rrqlexpr_mainvars,
    38     build_schema_from_namespace)
    36     build_schema_from_namespace)
    39 from cubicweb.devtools import TestServerConfiguration as TestConfiguration
    37 from cubicweb.devtools import TestServerConfiguration as TestConfiguration
    40 from cubicweb.devtools.testlib import CubicWebTC
    38 from cubicweb.devtools.testlib import CubicWebTC
    41 
    39 
    42 DATADIR = join(dirname(__file__), 'data')
    40 DATADIR = join(dirname(__file__), 'data')
    43 
    41 
    44 # build a dummy schema ########################################################
    42 # build a dummy schema ########################################################
    45 
    43 
    46 
    44 
    47 PERSONNE_PERMISSIONS =  {
    45 PERSONNE_PERMISSIONS = {
    48     'read':   ('managers', 'users', 'guests'),
    46     'read': ('managers', 'users', 'guests'),
    49     'update': ('managers', 'owners'),
    47     'update': ('managers', 'owners'),
    50     'add':    ('managers', ERQLExpression('X travaille S, S owned_by U')),
    48     'add': ('managers', ERQLExpression('X travaille S, S owned_by U')),
    51     'delete': ('managers', 'owners',),
    49     'delete': ('managers', 'owners',),
    52     }
    50 }
    53 
    51 
    54 CONCERNE_PERMISSIONS = {
    52 CONCERNE_PERMISSIONS = {
    55     'read':   ('managers', 'users', 'guests'),
    53     'read': ('managers', 'users', 'guests'),
    56     'add':    ('managers', RRQLExpression('U has_update_permission S')),
    54     'add': ('managers', RRQLExpression('U has_update_permission S')),
    57     'delete': ('managers', RRQLExpression('O owned_by U')),
    55     'delete': ('managers', RRQLExpression('O owned_by U')),
    58     }
    56 }
    59 
    57 
    60 schema = CubicWebSchema('Test Schema')
    58 schema = CubicWebSchema('Test Schema')
    61 enote = schema.add_entity_type(EntityType('Note'))
    59 enote = schema.add_entity_type(EntityType('Note'))
    62 eaffaire = schema.add_entity_type(EntityType('Affaire'))
    60 eaffaire = schema.add_entity_type(EntityType('Affaire'))
    63 eperson = schema.add_entity_type(EntityType('Personne', __permissions__=PERSONNE_PERMISSIONS))
    61 eperson = schema.add_entity_type(EntityType('Personne', __permissions__=PERSONNE_PERMISSIONS))
    81     ('Personne  evaluee   Note'),
    79     ('Personne  evaluee   Note'),
    82     ('Societe evaluee   Note'),
    80     ('Societe evaluee   Note'),
    83     ('Personne  concerne  Affaire'),
    81     ('Personne  concerne  Affaire'),
    84     ('Personne  concerne  Societe'),
    82     ('Personne  concerne  Societe'),
    85     ('Affaire concerne  Societe'),
    83     ('Affaire concerne  Societe'),
    86     )
    84 )
    87 done = {}
    85 done = {}
    88 for rel in RELS:
    86 for rel in RELS:
    89     _from, _type, _to = rel.split()
    87     _from, _type, _to = rel.split()
    90     if not _type.lower() in done:
    88     if not _type.lower() in done:
    91         schema.add_relation_type(RelationType(_type))
    89         schema.add_relation_type(RelationType(_type))
    94         schema.add_relation_def(RelationDefinition(_from, _type, _to,
    92         schema.add_relation_def(RelationDefinition(_from, _type, _to,
    95                                                    __permissions__=CONCERNE_PERMISSIONS))
    93                                                    __permissions__=CONCERNE_PERMISSIONS))
    96     else:
    94     else:
    97         schema.add_relation_def(RelationDefinition(_from, _type, _to))
    95         schema.add_relation_def(RelationDefinition(_from, _type, _to))
    98 
    96 
       
    97 
    99 class CubicWebSchemaTC(TestCase):
    98 class CubicWebSchemaTC(TestCase):
   100 
    99 
   101     def test_rql_constraints_inheritance(self):
   100     def test_rql_constraints_inheritance(self):
   102         # isinstance(cstr, RQLVocabularyConstraint)
   101         # isinstance(cstr, RQLVocabularyConstraint)
   103         # -> expected to return RQLVocabularyConstraint and RQLConstraint
   102         # -> expected to return RQLVocabularyConstraint and RQLConstraint
   130                          ['Any S,U WHERE U has_update_permission S, S eid %(s)s, U eid %(u)s'])
   129                          ['Any S,U WHERE U has_update_permission S, S eid %(s)s, U eid %(u)s'])
   131 
   130 
   132     def test_erqlexpression(self):
   131     def test_erqlexpression(self):
   133         self.assertRaises(RQLSyntaxError, ERQLExpression, '1')
   132         self.assertRaises(RQLSyntaxError, ERQLExpression, '1')
   134         expr = ERQLExpression('X travaille S, S owned_by U')
   133         expr = ERQLExpression('X travaille S, S owned_by U')
   135         self.assertEqual(str(expr), 'Any X WHERE X travaille S, S owned_by U, X eid %(x)s, U eid %(u)s')
   134         self.assertEqual(
       
   135             str(expr),
       
   136             'Any X WHERE X travaille S, S owned_by U, X eid %(x)s, U eid %(u)s')
   136         expr = ERQLExpression('X foo S, S bar U, X baz XE, S quux SE HAVING XE > SE')
   137         expr = ERQLExpression('X foo S, S bar U, X baz XE, S quux SE HAVING XE > SE')
   137         self.assertEqual(str(expr), 'Any X WHERE X foo S, S bar U, X baz XE, S quux SE, X eid %(x)s, U eid %(u)s HAVING XE > SE')
   138         self.assertEqual(
       
   139             str(expr),
       
   140             'Any X WHERE X foo S, S bar U, X baz XE, S quux SE, X eid %(x)s, '
       
   141             'U eid %(u)s HAVING XE > SE')
   138 
   142 
   139     def test_rrqlexpression(self):
   143     def test_rrqlexpression(self):
   140         self.assertRaises(Exception, RRQLExpression, '1')
   144         self.assertRaises(Exception, RRQLExpression, '1')
   141         self.assertRaises(RQLSyntaxError, RRQLExpression, 'O X Y')
   145         self.assertRaises(RQLSyntaxError, RRQLExpression, 'O X Y')
   142         expr = RRQLExpression('U has_update_permission O')
   146         expr = RRQLExpression('U has_update_permission O')
   143         self.assertEqual(str(expr), 'Any O,U WHERE U has_update_permission O, O eid %(o)s, U eid %(u)s')
   147         self.assertEqual(
       
   148             str(expr),
       
   149             'Any O,U WHERE U has_update_permission O, O eid %(o)s, U eid %(u)s')
       
   150 
   144 
   151 
   145 loader = CubicWebSchemaLoader()
   152 loader = CubicWebSchemaLoader()
   146 config = TestConfiguration('data', __file__)
   153 config = TestConfiguration('data', __file__)
   147 config.bootstrap_cubes()
   154 config.bootstrap_cubes()
   148 
   155 
       
   156 
   149 class SchemaReaderClassTest(TestCase):
   157 class SchemaReaderClassTest(TestCase):
   150 
   158 
   151     def test_order_eschemas(self):
   159     def test_order_eschemas(self):
   152         schema = loader.load(config)
   160         schema = loader.load(config)
   153         self.assertEqual(order_eschemas([schema['Note'], schema['SubNote']]),
   161         self.assertEqual(order_eschemas([schema['Note'], schema['SubNote']]),
   154                                          [schema['Note'], schema['SubNote']])
   162                          [schema['Note'], schema['SubNote']])
   155         self.assertEqual(order_eschemas([schema['SubNote'], schema['Note']]),
   163         self.assertEqual(order_eschemas([schema['SubNote'], schema['Note']]),
   156                                          [schema['Note'], schema['SubNote']])
   164                          [schema['Note'], schema['SubNote']])
   157 
   165 
   158     def test_knownValues_load_schema(self):
   166     def test_knownValues_load_schema(self):
   159         schema = loader.load(config)
   167         schema = loader.load(config)
   160         self.assertIsInstance(schema, CubicWebSchema)
   168         self.assertIsInstance(schema, CubicWebSchema)
   161         self.assertEqual(schema.name, 'data')
   169         self.assertEqual(schema.name, 'data')
   162         entities = sorted([str(e) for e in schema.entities()])
   170         entities = sorted([str(e) for e in schema.entities()])
   163         expected_entities = ['Ami', 'BaseTransition', 'BigInt', 'Bookmark', 'Boolean', 'Bytes', 'Card',
   171         expected_entities = [
   164                              'Date', 'Datetime', 'Decimal',
   172             'Ami', 'BaseTransition', 'BigInt', 'Bookmark', 'Boolean', 'Bytes', 'Card',
   165                              'CWCache', 'CWComputedRType', 'CWConstraint',
   173             'Date', 'Datetime', 'Decimal',
   166                              'CWConstraintType', 'CWDataImport', 'CWEType',
   174             'CWCache', 'CWComputedRType', 'CWConstraint',
   167                              'CWAttribute', 'CWGroup', 'EmailAddress',
   175             'CWConstraintType', 'CWDataImport', 'CWEType',
   168                              'CWRelation', 'CWPermission', 'CWProperty', 'CWRType',
   176             'CWAttribute', 'CWGroup', 'EmailAddress',
   169                              'CWSource', 'CWSourceHostConfig', 'CWSourceSchemaConfig',
   177             'CWRelation', 'CWPermission', 'CWProperty', 'CWRType',
   170                              'CWUniqueTogetherConstraint', 'CWUser',
   178             'CWSource', 'CWSourceHostConfig', 'CWSourceSchemaConfig',
   171                              'ExternalUri', 'FakeFile', 'Float', 'Int', 'Interval', 'Note',
   179             'CWUniqueTogetherConstraint', 'CWUser',
   172                              'Password', 'Personne', 'Produit',
   180             'ExternalUri', 'FakeFile', 'Float', 'Int', 'Interval', 'Note',
   173                              'RQLExpression', 'Reference',
   181             'Password', 'Personne', 'Produit',
   174                              'Service', 'Societe', 'State', 'StateFull', 'String', 'SubNote', 'SubWorkflowExitPoint',
   182             'RQLExpression', 'Reference',
   175                              'Tag', 'TZDatetime', 'TZTime', 'Time', 'Transition', 'TrInfo',
   183             'Service', 'Societe', 'State', 'StateFull', 'String', 'SubNote', 'SubWorkflowExitPoint',
   176                              'Usine',
   184             'Tag', 'TZDatetime', 'TZTime', 'Time', 'Transition', 'TrInfo',
   177                              'Workflow', 'WorkflowTransition']
   185             'Usine',
       
   186             'Workflow', 'WorkflowTransition',
       
   187         ]
   178         self.assertListEqual(sorted(expected_entities), entities)
   188         self.assertListEqual(sorted(expected_entities), entities)
   179         relations = sorted([str(r) for r in schema.relations()])
   189         relations = sorted([str(r) for r in schema.relations()])
   180         expected_relations = ['actionnaire', 'add_permission', 'address', 'alias', 'allowed_transition', 'associe',
   190         expected_relations = [
   181                               'bookmarked_by', 'by_transition', 'buddies',
   191             'actionnaire', 'add_permission', 'address', 'alias', 'allowed_transition', 'associe',
   182 
   192             'bookmarked_by', 'by_transition', 'buddies',
   183                               'cardinality', 'comment', 'comment_format',
   193 
   184                               'composite', 'condition', 'config', 'connait',
   194             'cardinality', 'comment', 'comment_format',
   185                               'constrained_by', 'constraint_of',
   195             'composite', 'condition', 'config', 'connait',
   186                               'content', 'content_format', 'contrat_exclusif',
   196             'constrained_by', 'constraint_of',
   187                               'created_by', 'creation_date', 'cstrtype', 'custom_workflow',
   197             'content', 'content_format', 'contrat_exclusif',
   188                               'cwuri', 'cw_for_source', 'cw_import_of', 'cw_host_config_of', 'cw_schema', 'cw_source',
   198             'created_by', 'creation_date', 'cstrtype', 'custom_workflow',
   189 
   199             'cwuri', 'cw_for_source', 'cw_import_of', 'cw_host_config_of', 'cw_schema', 'cw_source',
   190                               'data', 'data_encoding', 'data_format', 'data_name', 'default_workflow', 'defaultval', 'delete_permission',
   200 
   191                               'description', 'description_format', 'destination_state', 'dirige',
   201             'data', 'data_encoding', 'data_format', 'data_name', 'default_workflow', 'defaultval',
   192 
   202             'delete_permission', 'description', 'description_format', 'destination_state',
   193                               'ean', 'ecrit_par', 'eid', 'end_timestamp', 'evaluee', 'expression', 'exprtype', 'extra_props',
   203             'dirige',
   194 
   204 
   195                               'fabrique_par', 'final', 'firstname', 'for_user', 'formula', 'fournit',
   205             'ean', 'ecrit_par', 'eid', 'end_timestamp', 'evaluee', 'expression', 'exprtype',
   196                               'from_entity', 'from_state', 'fulltext_container', 'fulltextindexed',
   206             'extra_props',
   197 
   207 
   198                               'has_group_permission', 'has_text',
   208             'fabrique_par', 'final', 'firstname', 'for_user', 'formula', 'fournit',
   199                               'identity', 'in_group', 'in_state', 'in_synchronization', 'indexed',
   209             'from_entity', 'from_state', 'fulltext_container', 'fulltextindexed',
   200                               'initial_state', 'inlined', 'internationalizable', 'is', 'is_instance_of',
   210 
   201 
   211             'has_group_permission', 'has_text',
   202                               'label', 'last_login_time', 'latest_retrieval', 'lieu', 'log', 'login',
   212             'identity', 'in_group', 'in_state', 'in_synchronization', 'indexed',
   203 
   213             'initial_state', 'inlined', 'internationalizable', 'is', 'is_instance_of',
   204                               'mainvars', 'match_host', 'modification_date',
   214 
   205 
   215             'label', 'last_login_time', 'latest_retrieval', 'lieu', 'log', 'login',
   206                               'name', 'nom',
   216 
   207 
   217             'mainvars', 'match_host', 'modification_date',
   208                               'options', 'ordernum', 'owned_by',
   218 
   209 
   219             'name', 'nom',
   210                               'parser', 'path', 'pkey', 'prefered_form', 'prenom', 'primary_email',
   220 
   211 
   221             'options', 'ordernum', 'owned_by',
   212                               'read_permission', 'relation_type', 'relations', 'require_group', 'rule',
   222 
   213 
   223             'parser', 'path', 'pkey', 'prefered_form', 'prenom', 'primary_email',
   214                               'specializes', 'start_timestamp', 'state_of', 'status', 'subworkflow', 'subworkflow_exit', 'subworkflow_state', 'surname', 'symmetric', 'synopsis',
   224 
   215 
   225             'read_permission', 'relation_type', 'relations', 'require_group', 'rule',
   216                               'tags', 'timestamp', 'title', 'to_entity', 'to_state', 'transition_of', 'travaille', 'type',
   226 
   217 
   227             'specializes', 'start_timestamp', 'state_of', 'status', 'subworkflow',
   218                               'upassword', 'update_permission', 'url', 'uri', 'use_email',
   228             'subworkflow_exit', 'subworkflow_state', 'surname', 'symmetric', 'synopsis',
   219 
   229 
   220                               'value',
   230             'tags', 'timestamp', 'title', 'to_entity', 'to_state', 'transition_of', 'travaille',
   221 
   231             'type',
   222                               'wf_info_for', 'wikiid', 'workflow_of', 'tr_count']
   232 
       
   233             'upassword', 'update_permission', 'url', 'uri', 'use_email',
       
   234 
       
   235             'value',
       
   236 
       
   237             'wf_info_for', 'wikiid', 'workflow_of', 'tr_count',
       
   238         ]
   223 
   239 
   224         self.assertListEqual(sorted(expected_relations), relations)
   240         self.assertListEqual(sorted(expected_relations), relations)
   225 
   241 
   226         eschema = schema.eschema('CWUser')
   242         eschema = schema.eschema('CWUser')
   227         rels = sorted(str(r) for r in eschema.subject_relations())
   243         rels = sorted(str(r) for r in eschema.subject_relations())
   234                                     'login', 'modification_date', 'owned_by',
   250                                     'login', 'modification_date', 'owned_by',
   235                                     'primary_email', 'surname', 'upassword',
   251                                     'primary_email', 'surname', 'upassword',
   236                                     'use_email'])
   252                                     'use_email'])
   237         rels = sorted(r.type for r in eschema.object_relations())
   253         rels = sorted(r.type for r in eschema.object_relations())
   238         self.assertListEqual(rels, ['bookmarked_by', 'buddies', 'created_by', 'for_user',
   254         self.assertListEqual(rels, ['bookmarked_by', 'buddies', 'created_by', 'for_user',
   239                                      'identity', 'owned_by', 'wf_info_for'])
   255                                     'identity', 'owned_by', 'wf_info_for'])
   240         rschema = schema.rschema('relation_type')
   256         rschema = schema.rschema('relation_type')
   241         properties = rschema.rdef('CWAttribute', 'CWRType')
   257         properties = rschema.rdef('CWAttribute', 'CWRType')
   242         self.assertEqual(properties.cardinality, '1*')
   258         self.assertEqual(properties.cardinality, '1*')
   243         constraints = properties.constraints
   259         constraints = properties.constraints
   244         self.assertEqual(len(constraints), 1, constraints)
   260         self.assertEqual(len(constraints), 1, constraints)
   253 
   269 
   254     def test_permission_settings(self):
   270     def test_permission_settings(self):
   255         schema = loader.load(config)
   271         schema = loader.load(config)
   256         aschema = schema['TrInfo'].rdef('comment')
   272         aschema = schema['TrInfo'].rdef('comment')
   257         self.assertEqual(aschema.get_groups('read'),
   273         self.assertEqual(aschema.get_groups('read'),
   258                           set(('managers', 'users', 'guests')))
   274                          set(('managers', 'users', 'guests')))
   259         self.assertEqual(aschema.get_rqlexprs('read'),
   275         self.assertEqual(aschema.get_rqlexprs('read'),
   260                           ())
   276                          ())
   261         self.assertEqual(aschema.get_groups('update'),
   277         self.assertEqual(aschema.get_groups('update'),
   262                           set(('managers',)))
   278                          set(('managers',)))
   263         self.assertEqual([x.expression for x in aschema.get_rqlexprs('update')],
   279         self.assertEqual([x.expression for x in aschema.get_rqlexprs('update')],
   264                           ['U has_update_permission X'])
   280                          ['U has_update_permission X'])
   265 
   281 
   266     def test_nonregr_allowed_type_names(self):
   282     def test_nonregr_allowed_type_names(self):
   267         schema = CubicWebSchema('Test Schema')
   283         schema = CubicWebSchema('Test Schema')
   268         schema.add_entity_type(EntityType('NaN'))
   284         schema.add_entity_type(EntityType('NaN'))
   269 
   285 
   288         class Person(EntityType):
   304         class Person(EntityType):
   289             salary = Int()
   305             salary = Int()
   290 
   306 
   291         class works_for(RelationDefinition):
   307         class works_for(RelationDefinition):
   292             subject = 'Person'
   308             subject = 'Person'
   293             object  = 'Company'
   309             object = 'Company'
   294             cardinality = '?*'
   310             cardinality = '?*'
   295 
   311 
   296         class Company(EntityType):
   312         class Company(EntityType):
   297             total_salary = Int(formula='Any SUM(SA) GROUPBY X WHERE '
   313             total_salary = Int(formula='Any SUM(SA) GROUPBY X WHERE P works_for X, P salary SA')
   298                                        'P works_for X, P salary SA')
   314 
   299         good_schema = build_schema_from_namespace(vars().items())
   315         good_schema = build_schema_from_namespace(vars().items())
   300         rdef = good_schema['Company'].rdef('total_salary')
   316         rdef = good_schema['Company'].rdef('total_salary')
   301         # ensure 'X is Company' is added to the rqlst to avoid ambiguities, see #4901163
   317         # ensure 'X is Company' is added to the rqlst to avoid ambiguities, see #4901163
   302         self.assertEqual(str(rdef.formula_select),
   318         self.assertEqual(str(rdef.formula_select),
   303                          'Any SUM(SA) GROUPBY X WHERE P works_for X, P salary SA, X is Company')
   319                          'Any SUM(SA) GROUPBY X WHERE P works_for X, P salary SA, X is Company')
   304         # check relation definition permissions
   320         # check relation definition permissions
   305         self.assertEqual(rdef.permissions,
   321         self.assertEqual(rdef.permissions,
   306                          {'add': (), 'update': (),
   322                          {'add': (), 'update': (),
   307                           'read': ('managers', 'users', 'guests')})
   323                           'read': ('managers', 'users', 'guests')})
   308 
   324 
   309         class Company(EntityType):
   325         class Company(EntityType):  # noqa
   310             total_salary = String(formula='Any SUM(SA) GROUPBY X WHERE '
   326             total_salary = String(formula='Any SUM(SA) GROUPBY X WHERE '
   311                                           'P works_for X, P salary SA')
   327                                           'P works_for X, P salary SA')
   312 
   328 
   313         with self.assertRaises(BadSchemaDefinition) as exc:
   329         with self.assertRaises(BadSchemaDefinition) as exc:
   314             bad_schema = build_schema_from_namespace(vars().items())
   330             build_schema_from_namespace(vars().items())
   315 
   331 
   316         self.assertEqual(str(exc.exception),
   332         self.assertEqual(str(exc.exception),
   317                          'computed attribute total_salary on Company: '
   333                          'computed attribute total_salary on Company: '
   318                          'computed attribute type (Int) mismatch with '
   334                          'computed attribute type (Int) mismatch with '
   319                          'specified type (String)')
   335                          'specified type (String)')
   324     def test_infer_computed_relation(self):
   340     def test_infer_computed_relation(self):
   325         class Person(EntityType):
   341         class Person(EntityType):
   326             name = String()
   342             name = String()
   327 
   343 
   328         class Company(EntityType):
   344         class Company(EntityType):
   329             name  = String()
   345             name = String()
   330 
   346 
   331         class Service(EntityType):
   347         class Service(EntityType):
   332             name = String()
   348             name = String()
   333 
   349 
   334         class works_for(RelationDefinition):
   350         class works_for(RelationDefinition):
   353             rule = 'S produce O'
   369             rule = 'S produce O'
   354 
   370 
   355         schema = build_schema_from_namespace(vars().items())
   371         schema = build_schema_from_namespace(vars().items())
   356 
   372 
   357         # check object/subject type
   373         # check object/subject type
   358         self.assertEqual([('Person','Service')],
   374         self.assertEqual([('Person', 'Service')],
   359                          list(schema['produces_and_buys'].rdefs.keys()))
   375                          list(schema['produces_and_buys'].rdefs.keys()))
   360         self.assertEqual([('Person','Service')],
   376         self.assertEqual([('Person', 'Service')],
   361                          list(schema['produces_and_buys2'].rdefs.keys()))
   377                          list(schema['produces_and_buys2'].rdefs.keys()))
   362         self.assertCountEqual([('Company', 'Service'), ('Person', 'Service')],
   378         self.assertCountEqual([('Company', 'Service'), ('Person', 'Service')],
   363                               list(schema['reproduce'].rdefs.keys()))
   379                               list(schema['reproduce'].rdefs.keys()))
   364         # check relation definitions are marked infered
   380         # check relation definitions are marked infered
   365         rdef = schema['produces_and_buys'].rdefs[('Person','Service')]
   381         rdef = schema['produces_and_buys'].rdefs[('Person', 'Service')]
   366         self.assertTrue(rdef.infered)
   382         self.assertTrue(rdef.infered)
   367         # and have no add/delete permissions
   383         # and have no add/delete permissions
   368         self.assertEqual(rdef.permissions,
   384         self.assertEqual(rdef.permissions,
   369                          {'add': (),
   385                          {'add': (),
   370                           'delete': (),
   386                           'delete': (),
   417     def test_rrqlexpr_on_attr(self):
   433     def test_rrqlexpr_on_attr(self):
   418         self._test('rrqlexpr_on_attr.py',
   434         self._test('rrqlexpr_on_attr.py',
   419                    "can't use RRQLExpression on attribute ToTo.attr[String], use an ERQLExpression")
   435                    "can't use RRQLExpression on attribute ToTo.attr[String], use an ERQLExpression")
   420 
   436 
   421     def test_rqlexpr_on_computedrel(self):
   437     def test_rqlexpr_on_computedrel(self):
   422         self._test('rqlexpr_on_computedrel.py',
   438         self._test(
   423                    "can't use rql expression for read permission of relation Subject computed Object")
   439             'rqlexpr_on_computedrel.py',
       
   440             "can't use rql expression for read permission of relation Subject computed Object")
   424 
   441 
   425 
   442 
   426 class NormalizeExpressionTC(TestCase):
   443 class NormalizeExpressionTC(TestCase):
   427 
   444 
   428     def test(self):
   445     def test(self):
   429         self.assertEqual(normalize_expression('X  bla Y,Y blur Z  ,  Z zigoulou   X '),
   446         self.assertEqual(normalize_expression('X  bla Y,Y blur Z  ,  Z zigoulou   X '),
   430                                               'X bla Y, Y blur Z, Z zigoulou X')
   447                          'X bla Y, Y blur Z, Z zigoulou X')
   431         self.assertEqual(normalize_expression('X bla Y, Y name "x,y"'),
   448         self.assertEqual(normalize_expression('X bla Y, Y name "x,y"'),
   432                                               'X bla Y, Y name "x,y"')
   449                          'X bla Y, Y name "x,y"')
   433 
   450 
   434 
   451 
   435 class RQLExpressionTC(TestCase):
   452 class RQLExpressionTC(TestCase):
   436     def test_comparison(self):
   453     def test_comparison(self):
   437         self.assertEqual(ERQLExpression('X is CWUser', 'X', 0),
   454         self.assertEqual(ERQLExpression('X is CWUser', 'X', 0),
   438                           ERQLExpression('X is CWUser', 'X', 0))
   455                          ERQLExpression('X is CWUser', 'X', 0))
   439         self.assertNotEqual(ERQLExpression('X is CWUser', 'X', 0),
   456         self.assertNotEqual(ERQLExpression('X is CWUser', 'X', 0),
   440                              ERQLExpression('X is CWGroup', 'X', 0))
   457                             ERQLExpression('X is CWGroup', 'X', 0))
   441 
   458 
   442 
   459 
   443 class GuessRrqlExprMainVarsTC(TestCase):
   460 class GuessRrqlExprMainVarsTC(TestCase):
   444     def test_exists(self):
   461     def test_exists(self):
   445         mainvars = guess_rrqlexpr_mainvars(normalize_expression('NOT EXISTS(O team_competition C, C level < 3, C concerns S)'))
   462         mainvars = guess_rrqlexpr_mainvars(normalize_expression(
       
   463             'NOT EXISTS(O team_competition C, C level < 3, C concerns S)'))
   446         self.assertEqual(mainvars, set(['S', 'O']))
   464         self.assertEqual(mainvars, set(['S', 'O']))
   447 
   465 
   448 
   466 
   449 class RQLConstraintTC(CubicWebTC):
   467 class RQLConstraintTC(CubicWebTC):
   450     def test_user_constraint(self):
   468     def test_user_constraint(self):
   452         with self.admin_access.repo_cnx() as cnx:
   470         with self.admin_access.repo_cnx() as cnx:
   453             anoneid = cnx.execute('Any X WHERE X login "anon"')[0][0]
   471             anoneid = cnx.execute('Any X WHERE X login "anon"')[0][0]
   454             self.assertRaises(ValidationError,
   472             self.assertRaises(ValidationError,
   455                               cstr.repo_check, cnx, 1, 'rel', anoneid)
   473                               cstr.repo_check, cnx, 1, 'rel', anoneid)
   456             self.assertEqual(cstr.repo_check(cnx, 1, cnx.user.eid),
   474             self.assertEqual(cstr.repo_check(cnx, 1, cnx.user.eid),
   457                              None) # no validation error, constraint checked
   475                              None)  # no validation error, constraint checked
   458 
   476 
   459 
   477 
   460 class WorkflowShemaTC(CubicWebTC):
   478 class WorkflowShemaTC(CubicWebTC):
   461     def test_trinfo_default_format(self):
   479     def test_trinfo_default_format(self):
   462         with self.admin_access.web_request() as req:
   480         with self.admin_access.web_request() as req:
   541         'Transition': [('condition', 'Transition', 'RQLExpression', 'subject')],
   559         'Transition': [('condition', 'Transition', 'RQLExpression', 'subject')],
   542         'Workflow': [('state_of', 'State', 'Workflow', 'object'),
   560         'Workflow': [('state_of', 'State', 'Workflow', 'object'),
   543                      ('transition_of', 'BaseTransition', 'Workflow', 'object'),
   561                      ('transition_of', 'BaseTransition', 'Workflow', 'object'),
   544                      ('transition_of', 'Transition', 'Workflow', 'object'),
   562                      ('transition_of', 'Transition', 'Workflow', 'object'),
   545                      ('transition_of', 'WorkflowTransition', 'Workflow', 'object')],
   563                      ('transition_of', 'WorkflowTransition', 'Workflow', 'object')],
   546         'WorkflowTransition': [('condition', 'WorkflowTransition', 'RQLExpression', 'subject'),
   564         'WorkflowTransition': [
   547                                ('subworkflow_exit', 'WorkflowTransition', 'SubWorkflowExitPoint', 'subject')]
   565             ('condition', 'WorkflowTransition', 'RQLExpression', 'subject'),
       
   566             ('subworkflow_exit', 'WorkflowTransition', 'SubWorkflowExitPoint', 'subject')
       
   567         ]
   548     }
   568     }
   549 
   569 
   550     def test_composite_entities(self):
   570     def test_composite_entities(self):
   551         schema = self.vreg.schema
   571         schema = self.vreg.schema
   552         self.assertEqual(sorted(self.composites),
   572         self.assertEqual(sorted(self.composites),