cubicweb/test/unittest_schema.py
changeset 11057 0b59724cb3f2
parent 10833 f585add0fed9
child 11164 e3fa26bd9ce0
equal deleted inserted replaced
11052:058bb3dc685f 11057:0b59724cb3f2
       
     1 # copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
       
     2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
       
     3 #
       
     4 # This file is part of CubicWeb.
       
     5 #
       
     6 # CubicWeb is free software: you can redistribute it and/or modify it under the
       
     7 # terms of the GNU Lesser General Public License as published by the Free
       
     8 # Software Foundation, either version 2.1 of the License, or (at your option)
       
     9 # any later version.
       
    10 #
       
    11 # CubicWeb is distributed in the hope that it will be useful, but WITHOUT
       
    12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
       
    13 # FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
       
    14 # details.
       
    15 #
       
    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/>.
       
    18 """unit tests for module cubicweb.schema"""
       
    19 
       
    20 import sys
       
    21 from os.path import join, isabs, basename, dirname
       
    22 
       
    23 from logilab.common.testlib import TestCase, unittest_main
       
    24 
       
    25 from rql import RQLSyntaxError
       
    26 
       
    27 from yams import ValidationError, BadSchemaDefinition
       
    28 from yams.constraints import SizeConstraint, StaticVocabularyConstraint
       
    29 from yams.buildobjs import (RelationDefinition, EntityType, RelationType,
       
    30                             Int, String, SubjectRelation, ComputedRelation)
       
    31 from yams.reader import fill_schema
       
    32 
       
    33 from cubicweb.schema import (
       
    34     CubicWebSchema, CubicWebEntitySchema, CubicWebSchemaLoader,
       
    35     RQLConstraint, RQLUniqueConstraint, RQLVocabularyConstraint,
       
    36     RQLExpression, ERQLExpression, RRQLExpression,
       
    37     normalize_expression, order_eschemas, guess_rrqlexpr_mainvars,
       
    38     build_schema_from_namespace)
       
    39 from cubicweb.devtools import TestServerConfiguration as TestConfiguration
       
    40 from cubicweb.devtools.testlib import CubicWebTC
       
    41 
       
    42 DATADIR = join(dirname(__file__), 'data')
       
    43 
       
    44 # build a dummy schema ########################################################
       
    45 
       
    46 
       
    47 PERSONNE_PERMISSIONS =  {
       
    48     'read':   ('managers', 'users', 'guests'),
       
    49     'update': ('managers', 'owners'),
       
    50     'add':    ('managers', ERQLExpression('X travaille S, S owned_by U')),
       
    51     'delete': ('managers', 'owners',),
       
    52     }
       
    53 
       
    54 CONCERNE_PERMISSIONS = {
       
    55     'read':   ('managers', 'users', 'guests'),
       
    56     'add':    ('managers', RRQLExpression('U has_update_permission S')),
       
    57     'delete': ('managers', RRQLExpression('O owned_by U')),
       
    58     }
       
    59 
       
    60 schema = CubicWebSchema('Test Schema')
       
    61 enote = schema.add_entity_type(EntityType('Note'))
       
    62 eaffaire = schema.add_entity_type(EntityType('Affaire'))
       
    63 eperson = schema.add_entity_type(EntityType('Personne', __permissions__=PERSONNE_PERMISSIONS))
       
    64 esociete = schema.add_entity_type(EntityType('Societe'))
       
    65 
       
    66 RELS = (
       
    67     # attribute relations
       
    68     ('Note date String'),
       
    69     ('Note type String'),
       
    70     ('Affaire sujet String'),
       
    71     ('Affaire ref String'),
       
    72     ('Personne nom String'),
       
    73     ('Personne prenom String'),
       
    74     ('Personne sexe String'),
       
    75     ('Personne tel Int'),
       
    76     ('Personne fax Int'),
       
    77     ('Personne datenaiss Date'),
       
    78     ('Personne promo String'),
       
    79     # real relations
       
    80     ('Personne  travaille Societe'),
       
    81     ('Personne  evaluee   Note'),
       
    82     ('Societe evaluee   Note'),
       
    83     ('Personne  concerne  Affaire'),
       
    84     ('Personne  concerne  Societe'),
       
    85     ('Affaire concerne  Societe'),
       
    86     )
       
    87 done = {}
       
    88 for rel in RELS:
       
    89     _from, _type, _to = rel.split()
       
    90     if not _type.lower() in done:
       
    91         schema.add_relation_type(RelationType(_type))
       
    92         done[_type.lower()] = True
       
    93     if _type == 'concerne':
       
    94         schema.add_relation_def(RelationDefinition(_from, _type, _to,
       
    95                                                    __permissions__=CONCERNE_PERMISSIONS))
       
    96     else:
       
    97         schema.add_relation_def(RelationDefinition(_from, _type, _to))
       
    98 
       
    99 class CubicWebSchemaTC(TestCase):
       
   100 
       
   101     def test_rql_constraints_inheritance(self):
       
   102         # isinstance(cstr, RQLVocabularyConstraint)
       
   103         # -> expected to return RQLVocabularyConstraint and RQLConstraint
       
   104         #   instances but not RQLUniqueConstraint
       
   105         #
       
   106         # isinstance(cstr, RQLConstraint)
       
   107         # -> expected to return RQLConstraint instances but not
       
   108         #    RQLVocabularyConstraint and RQLUniqueConstraint
       
   109         self.assertFalse(issubclass(RQLUniqueConstraint, RQLVocabularyConstraint))
       
   110         self.assertFalse(issubclass(RQLUniqueConstraint, RQLConstraint))
       
   111 
       
   112     def test_entity_perms(self):
       
   113         self.assertEqual(eperson.get_groups('read'), set(('managers', 'users', 'guests')))
       
   114         self.assertEqual(eperson.get_groups('update'), set(('managers', 'owners',)))
       
   115         self.assertEqual(eperson.get_groups('delete'), set(('managers', 'owners')))
       
   116         self.assertEqual(eperson.get_groups('add'), set(('managers',)))
       
   117         self.assertEqual([str(e) for e in eperson.get_rqlexprs('add')],
       
   118                          ['Any X WHERE X travaille S, S owned_by U, X eid %(x)s, U eid %(u)s'])
       
   119         eperson.set_action_permissions('read', ('managers',))
       
   120         self.assertEqual(eperson.get_groups('read'), set(('managers',)))
       
   121 
       
   122     def test_relation_perms(self):
       
   123         rconcerne = schema.rschema('concerne').rdef('Personne', 'Societe')
       
   124         self.assertEqual(rconcerne.get_groups('read'), set(('managers', 'users', 'guests')))
       
   125         self.assertEqual(rconcerne.get_groups('delete'), set(('managers',)))
       
   126         self.assertEqual(rconcerne.get_groups('add'), set(('managers', )))
       
   127         rconcerne.set_action_permissions('read', ('managers',))
       
   128         self.assertEqual(rconcerne.get_groups('read'), set(('managers',)))
       
   129         self.assertEqual([str(e) for e in rconcerne.get_rqlexprs('add')],
       
   130                          ['Any S,U WHERE U has_update_permission S, S eid %(s)s, U eid %(u)s'])
       
   131 
       
   132     def test_erqlexpression(self):
       
   133         self.assertRaises(RQLSyntaxError, ERQLExpression, '1')
       
   134         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')
       
   136         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 
       
   139     def test_rrqlexpression(self):
       
   140         self.assertRaises(Exception, RRQLExpression, '1')
       
   141         self.assertRaises(RQLSyntaxError, RRQLExpression, 'O X Y')
       
   142         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')
       
   144 
       
   145 loader = CubicWebSchemaLoader()
       
   146 config = TestConfiguration('data', apphome=DATADIR)
       
   147 config.bootstrap_cubes()
       
   148 
       
   149 class SchemaReaderClassTest(TestCase):
       
   150 
       
   151     def test_order_eschemas(self):
       
   152         schema = loader.load(config)
       
   153         self.assertEqual(order_eschemas([schema['Note'], schema['SubNote']]),
       
   154                                          [schema['Note'], schema['SubNote']])
       
   155         self.assertEqual(order_eschemas([schema['SubNote'], schema['Note']]),
       
   156                                          [schema['Note'], schema['SubNote']])
       
   157 
       
   158     def test_knownValues_load_schema(self):
       
   159         schema = loader.load(config)
       
   160         self.assertIsInstance(schema, CubicWebSchema)
       
   161         self.assertEqual(schema.name, 'data')
       
   162         entities = sorted([str(e) for e in schema.entities()])
       
   163         expected_entities = ['Ami', 'BaseTransition', 'BigInt', 'Bookmark', 'Boolean', 'Bytes', 'Card',
       
   164                              'Date', 'Datetime', 'Decimal',
       
   165                              'CWCache', 'CWComputedRType', 'CWConstraint',
       
   166                              'CWConstraintType', 'CWDataImport', 'CWEType',
       
   167                              'CWAttribute', 'CWGroup', 'EmailAddress',
       
   168                              'CWRelation', 'CWPermission', 'CWProperty', 'CWRType',
       
   169                              'CWSource', 'CWSourceHostConfig', 'CWSourceSchemaConfig',
       
   170                              'CWUniqueTogetherConstraint', 'CWUser',
       
   171                              'ExternalUri', 'FakeFile', 'Float', 'Int', 'Interval', 'Note',
       
   172                              'Password', 'Personne', 'Produit',
       
   173                              'RQLExpression', 'Reference',
       
   174                              'Service', 'Societe', 'State', 'StateFull', 'String', 'SubNote', 'SubWorkflowExitPoint',
       
   175                              'Tag', 'TZDatetime', 'TZTime', 'Time', 'Transition', 'TrInfo',
       
   176                              'Usine',
       
   177                              'Workflow', 'WorkflowTransition']
       
   178         self.assertListEqual(sorted(expected_entities), entities)
       
   179         relations = sorted([str(r) for r in schema.relations()])
       
   180         expected_relations = ['actionnaire', 'add_permission', 'address', 'alias', 'allowed_transition', 'associe',
       
   181                               'bookmarked_by', 'by_transition',
       
   182 
       
   183                               'cardinality', 'comment', 'comment_format',
       
   184                               'composite', 'condition', 'config', 'connait',
       
   185                               'constrained_by', 'constraint_of',
       
   186                               'content', 'content_format', 'contrat_exclusif',
       
   187                               'created_by', 'creation_date', 'cstrtype', 'custom_workflow',
       
   188                               'cwuri', 'cw_for_source', 'cw_import_of', 'cw_host_config_of', 'cw_schema', 'cw_source',
       
   189 
       
   190                               'data', 'data_encoding', 'data_format', 'data_name', 'default_workflow', 'defaultval', 'delete_permission',
       
   191                               'description', 'description_format', 'destination_state', 'dirige',
       
   192 
       
   193                               'ean', 'ecrit_par', 'eid', 'end_timestamp', 'evaluee', 'expression', 'exprtype', 'extra_props',
       
   194 
       
   195                               'fabrique_par', 'final', 'firstname', 'for_user', 'formula', 'fournit',
       
   196                               'from_entity', 'from_state', 'fulltext_container', 'fulltextindexed',
       
   197 
       
   198                               'has_group_permission', 'has_text',
       
   199                               'identity', 'in_group', 'in_state', 'in_synchronization', 'indexed',
       
   200                               'initial_state', 'inlined', 'internationalizable', 'is', 'is_instance_of',
       
   201 
       
   202                               'label', 'last_login_time', 'latest_retrieval', 'lieu', 'log', 'login',
       
   203 
       
   204                               'mainvars', 'match_host', 'modification_date',
       
   205 
       
   206                               'name', 'nom',
       
   207 
       
   208                               'options', 'ordernum', 'owned_by',
       
   209 
       
   210                               'parser', 'path', 'pkey', 'prefered_form', 'prenom', 'primary_email',
       
   211 
       
   212                               'read_permission', 'relation_type', 'relations', 'require_group', 'rule',
       
   213 
       
   214                               'specializes', 'start_timestamp', 'state_of', 'status', 'subworkflow', 'subworkflow_exit', 'subworkflow_state', 'surname', 'symmetric', 'synopsis',
       
   215 
       
   216                               'tags', 'timestamp', 'title', 'to_entity', 'to_state', 'transition_of', 'travaille', 'type',
       
   217 
       
   218                               'upassword', 'update_permission', 'url', 'uri', 'use_email',
       
   219 
       
   220                               'value',
       
   221 
       
   222                               'wf_info_for', 'wikiid', 'workflow_of', 'tr_count']
       
   223 
       
   224         self.assertListEqual(sorted(expected_relations), relations)
       
   225 
       
   226         eschema = schema.eschema('CWUser')
       
   227         rels = sorted(str(r) for r in eschema.subject_relations())
       
   228         self.assertListEqual(rels, ['created_by', 'creation_date', 'custom_workflow',
       
   229                                     'cw_source', 'cwuri', 'eid',
       
   230                                     'evaluee', 'firstname', 'has_group_permission',
       
   231                                     'has_text', 'identity',
       
   232                                     'in_group', 'in_state', 'is',
       
   233                                     'is_instance_of', 'last_login_time',
       
   234                                     'login', 'modification_date', 'owned_by',
       
   235                                     'primary_email', 'surname', 'upassword',
       
   236                                     'use_email'])
       
   237         rels = sorted(r.type for r in eschema.object_relations())
       
   238         self.assertListEqual(rels, ['bookmarked_by', 'created_by', 'for_user',
       
   239                                      'identity', 'owned_by', 'wf_info_for'])
       
   240         rschema = schema.rschema('relation_type')
       
   241         properties = rschema.rdef('CWAttribute', 'CWRType')
       
   242         self.assertEqual(properties.cardinality, '1*')
       
   243         constraints = properties.constraints
       
   244         self.assertEqual(len(constraints), 1, constraints)
       
   245         constraint = constraints[0]
       
   246         self.assertTrue(isinstance(constraint, RQLConstraint))
       
   247         self.assertEqual(constraint.expression, 'O final TRUE')
       
   248 
       
   249     def test_fulltext_container(self):
       
   250         schema = loader.load(config)
       
   251         self.assertIn('has_text', schema['CWUser'].subject_relations())
       
   252         self.assertNotIn('has_text', schema['EmailAddress'].subject_relations())
       
   253 
       
   254     def test_permission_settings(self):
       
   255         schema = loader.load(config)
       
   256         aschema = schema['TrInfo'].rdef('comment')
       
   257         self.assertEqual(aschema.get_groups('read'),
       
   258                           set(('managers', 'users', 'guests')))
       
   259         self.assertEqual(aschema.get_rqlexprs('read'),
       
   260                           ())
       
   261         self.assertEqual(aschema.get_groups('update'),
       
   262                           set(('managers',)))
       
   263         self.assertEqual([x.expression for x in aschema.get_rqlexprs('update')],
       
   264                           ['U has_update_permission X'])
       
   265 
       
   266     def test_nonregr_allowed_type_names(self):
       
   267         schema = CubicWebSchema('Test Schema')
       
   268         schema.add_entity_type(EntityType('NaN'))
       
   269 
       
   270     def test_relation_perm_overriding(self):
       
   271         loader = CubicWebSchemaLoader()
       
   272         config = TestConfiguration('data', apphome=join(dirname(__file__), 'data_schemareader'))
       
   273         config.bootstrap_cubes()
       
   274         schema = loader.load(config)
       
   275         rdef = next(iter(schema['in_group'].rdefs.values()))
       
   276         self.assertEqual(rdef.permissions,
       
   277                          {'read': ('managers',),
       
   278                           'add': ('managers',),
       
   279                           'delete': ('managers',)})
       
   280         rdef = next(iter(schema['cw_for_source'].rdefs.values()))
       
   281         self.assertEqual(rdef.permissions,
       
   282                          {'read': ('managers', 'users'),
       
   283                           'add': ('managers',),
       
   284                           'delete': ('managers',)})
       
   285 
       
   286     def test_computed_attribute(self):
       
   287         """Check schema finalization for computed attributes."""
       
   288         class Person(EntityType):
       
   289             salary = Int()
       
   290 
       
   291         class works_for(RelationDefinition):
       
   292             subject = 'Person'
       
   293             object  = 'Company'
       
   294             cardinality = '?*'
       
   295 
       
   296         class Company(EntityType):
       
   297             total_salary = Int(formula='Any SUM(SA) GROUPBY X WHERE '
       
   298                                        'P works_for X, P salary SA')
       
   299         good_schema = build_schema_from_namespace(vars().items())
       
   300         rdef = good_schema['Company'].rdef('total_salary')
       
   301         # ensure 'X is Company' is added to the rqlst to avoid ambiguities, see #4901163
       
   302         self.assertEqual(str(rdef.formula_select),
       
   303                          'Any SUM(SA) GROUPBY X WHERE P works_for X, P salary SA, X is Company')
       
   304         # check relation definition permissions
       
   305         self.assertEqual(rdef.permissions,
       
   306                          {'add': (), 'update': (),
       
   307                           'read': ('managers', 'users', 'guests')})
       
   308 
       
   309         class Company(EntityType):
       
   310             total_salary = String(formula='Any SUM(SA) GROUPBY X WHERE '
       
   311                                           'P works_for X, P salary SA')
       
   312 
       
   313         with self.assertRaises(BadSchemaDefinition) as exc:
       
   314             bad_schema = build_schema_from_namespace(vars().items())
       
   315 
       
   316         self.assertEqual(str(exc.exception),
       
   317                          'computed attribute total_salary on Company: '
       
   318                          'computed attribute type (Int) mismatch with '
       
   319                          'specified type (String)')
       
   320 
       
   321 
       
   322 class SchemaReaderComputedRelationAndAttributesTest(TestCase):
       
   323 
       
   324     def test_infer_computed_relation(self):
       
   325         class Person(EntityType):
       
   326             name = String()
       
   327 
       
   328         class Company(EntityType):
       
   329             name  = String()
       
   330 
       
   331         class Service(EntityType):
       
   332             name = String()
       
   333 
       
   334         class works_for(RelationDefinition):
       
   335             subject = 'Person'
       
   336             object = 'Company'
       
   337 
       
   338         class produce(RelationDefinition):
       
   339             subject = ('Person', 'Company')
       
   340             object = 'Service'
       
   341 
       
   342         class achete(RelationDefinition):
       
   343             subject = 'Person'
       
   344             object = 'Service'
       
   345 
       
   346         class produces_and_buys(ComputedRelation):
       
   347             rule = 'S produce O, S achete O'
       
   348 
       
   349         class produces_and_buys2(ComputedRelation):
       
   350             rule = 'S works_for SO, SO produce O'
       
   351 
       
   352         class reproduce(ComputedRelation):
       
   353             rule = 'S produce O'
       
   354 
       
   355         schema = build_schema_from_namespace(vars().items())
       
   356 
       
   357         # check object/subject type
       
   358         self.assertEqual([('Person','Service')],
       
   359                          list(schema['produces_and_buys'].rdefs.keys()))
       
   360         self.assertEqual([('Person','Service')],
       
   361                          list(schema['produces_and_buys2'].rdefs.keys()))
       
   362         self.assertCountEqual([('Company', 'Service'), ('Person', 'Service')],
       
   363                               list(schema['reproduce'].rdefs.keys()))
       
   364         # check relation definitions are marked infered
       
   365         rdef = schema['produces_and_buys'].rdefs[('Person','Service')]
       
   366         self.assertTrue(rdef.infered)
       
   367         # and have no add/delete permissions
       
   368         self.assertEqual(rdef.permissions,
       
   369                          {'add': (),
       
   370                           'delete': (),
       
   371                           'read': ('managers', 'users', 'guests')})
       
   372 
       
   373         class autoname(ComputedRelation):
       
   374             rule = 'S produce X, X name O'
       
   375 
       
   376         with self.assertRaises(BadSchemaDefinition) as cm:
       
   377             build_schema_from_namespace(vars().items())
       
   378         self.assertEqual(str(cm.exception), 'computed relations cannot be final')
       
   379 
       
   380 
       
   381 class BadSchemaTC(TestCase):
       
   382     def setUp(self):
       
   383         self.loader = CubicWebSchemaLoader()
       
   384         self.loader.defined = {}
       
   385         self.loader.loaded_files = []
       
   386         self.loader.post_build_callbacks = []
       
   387 
       
   388     def _test(self, schemafile, msg):
       
   389         self.loader.handle_file(join(DATADIR, schemafile))
       
   390         sch = self.loader.schemacls('toto')
       
   391         with self.assertRaises(BadSchemaDefinition) as cm:
       
   392             fill_schema(sch, self.loader.defined, False)
       
   393         self.assertEqual(str(cm.exception), msg)
       
   394 
       
   395     def test_lowered_etype(self):
       
   396         self._test('lowered_etype.py',
       
   397                    "'my_etype' is not a valid name for an entity type. It should "
       
   398                    "start with an upper cased letter and be followed by at least "
       
   399                    "a lower cased letter")
       
   400 
       
   401     def test_uppered_rtype(self):
       
   402         self._test('uppered_rtype.py',
       
   403                    "'ARelation' is not a valid name for a relation type. It should be lower cased")
       
   404 
       
   405     def test_rrqlexpr_on_etype(self):
       
   406         self._test('rrqlexpr_on_eetype.py',
       
   407                    "can't use RRQLExpression on ToTo, use an ERQLExpression")
       
   408 
       
   409     def test_erqlexpr_on_rtype(self):
       
   410         self._test('erqlexpr_on_ertype.py',
       
   411                    "can't use ERQLExpression on relation ToTo toto TuTu, use a RRQLExpression")
       
   412 
       
   413     def test_rqlexpr_on_rtype_read(self):
       
   414         self._test('rqlexpr_on_ertype_read.py',
       
   415                    "can't use rql expression for read permission of relation ToTo toto TuTu")
       
   416 
       
   417     def test_rrqlexpr_on_attr(self):
       
   418         self._test('rrqlexpr_on_attr.py',
       
   419                    "can't use RRQLExpression on attribute ToTo.attr[String], use an ERQLExpression")
       
   420 
       
   421     def test_rqlexpr_on_computedrel(self):
       
   422         self._test('rqlexpr_on_computedrel.py',
       
   423                    "can't use rql expression for read permission of relation Subject computed Object")
       
   424 
       
   425 
       
   426 class NormalizeExpressionTC(TestCase):
       
   427 
       
   428     def test(self):
       
   429         self.assertEqual(normalize_expression('X  bla Y,Y blur Z  ,  Z zigoulou   X '),
       
   430                                               'X bla Y, Y blur Z, Z zigoulou X')
       
   431         self.assertEqual(normalize_expression('X bla Y, Y name "x,y"'),
       
   432                                               'X bla Y, Y name "x,y"')
       
   433 
       
   434 
       
   435 class RQLExpressionTC(TestCase):
       
   436     def test_comparison(self):
       
   437         self.assertEqual(ERQLExpression('X is CWUser', 'X', 0),
       
   438                           ERQLExpression('X is CWUser', 'X', 0))
       
   439         self.assertNotEqual(ERQLExpression('X is CWUser', 'X', 0),
       
   440                              ERQLExpression('X is CWGroup', 'X', 0))
       
   441 
       
   442 
       
   443 class GuessRrqlExprMainVarsTC(TestCase):
       
   444     def test_exists(self):
       
   445         mainvars = guess_rrqlexpr_mainvars(normalize_expression('NOT EXISTS(O team_competition C, C level < 3, C concerns S)'))
       
   446         self.assertEqual(mainvars, set(['S', 'O']))
       
   447 
       
   448 
       
   449 class RQLConstraintTC(CubicWebTC):
       
   450     def test_user_constraint(self):
       
   451         cstr = RQLConstraint('U identity O')
       
   452         with self.admin_access.repo_cnx() as cnx:
       
   453             anoneid = cnx.execute('Any X WHERE X login "anon"')[0][0]
       
   454             self.assertRaises(ValidationError,
       
   455                               cstr.repo_check, cnx, 1, 'rel', anoneid)
       
   456             self.assertEqual(cstr.repo_check(cnx, 1, cnx.user.eid),
       
   457                              None) # no validation error, constraint checked
       
   458 
       
   459 
       
   460 class WorkflowShemaTC(CubicWebTC):
       
   461     def test_trinfo_default_format(self):
       
   462         with self.admin_access.web_request() as req:
       
   463             tr = req.user.cw_adapt_to('IWorkflowable').fire_transition('deactivate')
       
   464             self.assertEqual(tr.comment_format, 'text/plain')
       
   465 
       
   466 
       
   467 class CompositeSchemaTC(CubicWebTC):
       
   468     composites = {
       
   469         'BaseTransition': [('condition', 'BaseTransition', 'RQLExpression', 'subject')],
       
   470         'CWAttribute': [('add_permission', 'CWAttribute', 'RQLExpression', 'subject'),
       
   471                         ('constrained_by', 'CWAttribute', 'CWConstraint', 'subject'),
       
   472                         ('read_permission', 'CWAttribute', 'RQLExpression', 'subject'),
       
   473                         ('update_permission', 'CWAttribute', 'RQLExpression', 'subject')],
       
   474         'CWEType': [('add_permission', 'CWEType', 'RQLExpression', 'subject'),
       
   475                     ('constraint_of', 'CWUniqueTogetherConstraint', 'CWEType', 'object'),
       
   476                     ('cw_schema', 'CWSourceSchemaConfig', 'CWEType', 'object'),
       
   477                     ('delete_permission', 'CWEType', 'RQLExpression', 'subject'),
       
   478                     ('from_entity', 'CWAttribute', 'CWEType', 'object'),
       
   479                     ('from_entity', 'CWRelation', 'CWEType', 'object'),
       
   480                     ('read_permission', 'CWEType', 'RQLExpression', 'subject'),
       
   481                     ('to_entity', 'CWAttribute', 'CWEType', 'object'),
       
   482                     ('to_entity', 'CWRelation', 'CWEType', 'object'),
       
   483                     ('update_permission', 'CWEType', 'RQLExpression', 'subject')],
       
   484         'CWRType': [('cw_schema', 'CWSourceSchemaConfig', 'CWRType', 'object'),
       
   485                     ('relation_type', 'CWAttribute', 'CWRType', 'object'),
       
   486                     ('relation_type', 'CWRelation', 'CWRType', 'object')],
       
   487         'CWRelation': [('add_permission', 'CWRelation', 'RQLExpression', 'subject'),
       
   488                        ('constrained_by', 'CWRelation', 'CWConstraint', 'subject'),
       
   489                        ('cw_schema', 'CWSourceSchemaConfig', 'CWRelation', 'object'),
       
   490                        ('delete_permission', 'CWRelation', 'RQLExpression', 'subject'),
       
   491                        ('read_permission', 'CWRelation', 'RQLExpression', 'subject')],
       
   492         'CWComputedRType': [('read_permission', 'CWComputedRType', 'RQLExpression', 'subject')],
       
   493         'CWSource': [('cw_for_source', 'CWSourceSchemaConfig', 'CWSource', 'object'),
       
   494                      ('cw_host_config_of', 'CWSourceHostConfig', 'CWSource', 'object'),
       
   495                      ('cw_import_of', 'CWDataImport', 'CWSource', 'object'),
       
   496                      ('cw_source', 'Ami', 'CWSource', 'object'),
       
   497                      ('cw_source', 'BaseTransition', 'CWSource', 'object'),
       
   498                      ('cw_source', 'Bookmark', 'CWSource', 'object'),
       
   499                      ('cw_source', 'CWAttribute', 'CWSource', 'object'),
       
   500                      ('cw_source', 'CWCache', 'CWSource', 'object'),
       
   501                      ('cw_source', 'CWComputedRType', 'CWSource', 'object'),
       
   502                      ('cw_source', 'CWConstraint', 'CWSource', 'object'),
       
   503                      ('cw_source', 'CWConstraintType', 'CWSource', 'object'),
       
   504                      ('cw_source', 'CWDataImport', 'CWSource', 'object'),
       
   505                      ('cw_source', 'CWEType', 'CWSource', 'object'),
       
   506                      ('cw_source', 'CWGroup', 'CWSource', 'object'),
       
   507                      ('cw_source', 'CWPermission', 'CWSource', 'object'),
       
   508                      ('cw_source', 'CWProperty', 'CWSource', 'object'),
       
   509                      ('cw_source', 'CWRType', 'CWSource', 'object'),
       
   510                      ('cw_source', 'CWRelation', 'CWSource', 'object'),
       
   511                      ('cw_source', 'CWSource', 'CWSource', 'object'),
       
   512                      ('cw_source', 'CWSourceHostConfig', 'CWSource', 'object'),
       
   513                      ('cw_source', 'CWSourceSchemaConfig', 'CWSource', 'object'),
       
   514                      ('cw_source', 'CWUniqueTogetherConstraint', 'CWSource', 'object'),
       
   515                      ('cw_source', 'CWUser', 'CWSource', 'object'),
       
   516                      ('cw_source', 'Card', 'CWSource', 'object'),
       
   517                      ('cw_source', 'EmailAddress', 'CWSource', 'object'),
       
   518                      ('cw_source', 'ExternalUri', 'CWSource', 'object'),
       
   519                      ('cw_source', 'FakeFile', 'CWSource', 'object'),
       
   520                      ('cw_source', 'Note', 'CWSource', 'object'),
       
   521                      ('cw_source', 'Personne', 'CWSource', 'object'),
       
   522                      ('cw_source', 'Produit', 'CWSource', 'object'),
       
   523                      ('cw_source', 'RQLExpression', 'CWSource', 'object'),
       
   524                      ('cw_source', 'Reference', 'CWSource', 'object'),
       
   525                      ('cw_source', 'Service', 'CWSource', 'object'),
       
   526                      ('cw_source', 'Societe', 'CWSource', 'object'),
       
   527                      ('cw_source', 'State', 'CWSource', 'object'),
       
   528                      ('cw_source', 'StateFull', 'CWSource', 'object'),
       
   529                      ('cw_source', 'SubNote', 'CWSource', 'object'),
       
   530                      ('cw_source', 'SubWorkflowExitPoint', 'CWSource', 'object'),
       
   531                      ('cw_source', 'Tag', 'CWSource', 'object'),
       
   532                      ('cw_source', 'TrInfo', 'CWSource', 'object'),
       
   533                      ('cw_source', 'Transition', 'CWSource', 'object'),
       
   534                      ('cw_source', 'Usine', 'CWSource', 'object'),
       
   535                      ('cw_source', 'Workflow', 'CWSource', 'object'),
       
   536                      ('cw_source', 'WorkflowTransition', 'CWSource', 'object')],
       
   537         'CWUser': [('for_user', 'CWProperty', 'CWUser', 'object'),
       
   538                    ('use_email', 'CWUser', 'EmailAddress', 'subject'),
       
   539                    ('wf_info_for', 'TrInfo', 'CWUser', 'object')],
       
   540         'StateFull': [('wf_info_for', 'TrInfo', 'StateFull', 'object')],
       
   541         'Transition': [('condition', 'Transition', 'RQLExpression', 'subject')],
       
   542         'Workflow': [('state_of', 'State', 'Workflow', 'object'),
       
   543                      ('transition_of', 'BaseTransition', 'Workflow', 'object'),
       
   544                      ('transition_of', 'Transition', 'Workflow', 'object'),
       
   545                      ('transition_of', 'WorkflowTransition', 'Workflow', 'object')],
       
   546         'WorkflowTransition': [('condition', 'WorkflowTransition', 'RQLExpression', 'subject'),
       
   547                                ('subworkflow_exit', 'WorkflowTransition', 'SubWorkflowExitPoint', 'subject')]
       
   548     }
       
   549 
       
   550     def test_composite_entities(self):
       
   551         schema = self.vreg.schema
       
   552         self.assertEqual(sorted(self.composites),
       
   553                          [eschema.type for eschema in sorted(schema.entities())
       
   554                           if eschema.is_composite])
       
   555         for etype in self.composites:
       
   556             self.set_description('composite rdefs for %s' % etype)
       
   557             yield self.assertEqual, self.composites[etype], \
       
   558                              sorted([(r.rtype.type, r.subject.type, r.object.type, role)
       
   559                                      for r, role in schema[etype].composite_rdef_roles])
       
   560 
       
   561 
       
   562 if __name__ == '__main__':
       
   563     unittest_main()