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)) |
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()) |
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): |