cubicweb/server/test/unittest_migractions.py
changeset 12239 19aae64c4010
parent 12238 6ed86e0b0222
child 12512 661dd0436c01
equal deleted inserted replaced
12238:6ed86e0b0222 12239:19aae64c4010
    20 import os
    20 import os
    21 import os.path as osp
    21 import os.path as osp
    22 import sys
    22 import sys
    23 from datetime import date
    23 from datetime import date
    24 from contextlib import contextmanager
    24 from contextlib import contextmanager
    25 import tempfile
       
    26 
    25 
    27 from logilab.common import tempattr
    26 from logilab.common import tempattr
    28 
    27 
    29 from yams.constraints import UniqueConstraint
    28 from yams.constraints import UniqueConstraint
    30 
    29 
   109             yield cnx, ServerMigrationHelper(self.repo.config, migrschema,
   108             yield cnx, ServerMigrationHelper(self.repo.config, migrschema,
   110                                              repo=self.repo, cnx=cnx,
   109                                              repo=self.repo, cnx=cnx,
   111                                              interactive=False)
   110                                              interactive=False)
   112 
   111 
   113     def table_sql(self, mh, tablename):
   112     def table_sql(self, mh, tablename):
   114         result = mh.sqlexec("SELECT table_name FROM information_schema.tables WHERE LOWER(table_name)=%(table)s",
   113         result = mh.sqlexec(
   115                             {'table': tablename.lower()})
   114             "SELECT table_name FROM information_schema.tables WHERE LOWER(table_name)=%(table)s",
       
   115             {'table': tablename.lower()})
   116         if result:
   116         if result:
   117             return result[0][0]
   117             return result[0][0]
   118         return None # no such table
   118         return None  # no such table
   119 
   119 
   120     def table_schema(self, mh, tablename):
   120     def table_schema(self, mh, tablename):
   121         result = mh.sqlexec("SELECT column_name, data_type, character_maximum_length FROM information_schema.columns "
   121         result = mh.sqlexec(
   122                             "WHERE LOWER(table_name) = %(table)s", {'table': tablename.lower()})
   122             "SELECT column_name, data_type, character_maximum_length "
       
   123             "FROM information_schema.columns "
       
   124             "WHERE LOWER(table_name) = %(table)s", {'table': tablename.lower()})
   123         assert result, 'no table %s' % tablename
   125         assert result, 'no table %s' % tablename
   124         return dict((x[0], (x[1], x[2])) for x in result)
   126         return dict((x[0], (x[1], x[2])) for x in result)
   125 
   127 
   126     def table_constraints(self, mh, tablename):
   128     def table_constraints(self, mh, tablename):
   127         result = mh.sqlexec(
   129         result = mh.sqlexec(
   176             orderdict2 = dict(mh.rqlexec('Any RTN, O WHERE X name "Note", RDEF from_entity X, '
   178             orderdict2 = dict(mh.rqlexec('Any RTN, O WHERE X name "Note", RDEF from_entity X, '
   177                                          'RDEF relation_type RT, RDEF ordernum O, RT name RTN'))
   179                                          'RDEF relation_type RT, RDEF ordernum O, RT name RTN'))
   178             whateverorder = migrschema['whatever'].rdef('Note', 'Int').order
   180             whateverorder = migrschema['whatever'].rdef('Note', 'Int').order
   179             for k, v in orderdict.items():
   181             for k, v in orderdict.items():
   180                 if v >= whateverorder:
   182                 if v >= whateverorder:
   181                     orderdict[k] = v+1
   183                     orderdict[k] = v + 1
   182             orderdict['whatever'] = whateverorder
   184             orderdict['whatever'] = whateverorder
   183             self.assertDictEqual(orderdict, orderdict2)
   185             self.assertDictEqual(orderdict, orderdict2)
   184             #self.assertEqual([r.type for r in self.schema['Note'].ordered_relations()],
       
   185             #                  ['modification_date', 'creation_date', 'owned_by',
       
   186             #                   'eid', 'ecrit_par', 'inline1', 'date', 'type',
       
   187             #                   'whatever', 'date', 'in_basket'])
       
   188             # NB: commit instead of rollback make following test fail with py2.5
       
   189             #     this sounds like a pysqlite/2.5 bug (the same eid is affected to
       
   190             #     two different entities)
       
   191 
   186 
   192     def test_add_attribute_varchar(self):
   187     def test_add_attribute_varchar(self):
   193         with self.mh() as (cnx, mh):
   188         with self.mh() as (cnx, mh):
   194             self.assertNotIn('whatever', self.schema)
   189             self.assertNotIn('whatever', self.schema)
   195             cnx.create_entity('Note')
   190             cnx.create_entity('Note')
   231             self.assertIn('newstyledefaultdate', self.schema)
   226             self.assertIn('newstyledefaultdate', self.schema)
   232             self.assertEqual(self.schema['mydate'].subjects(), ('Note', ))
   227             self.assertEqual(self.schema['mydate'].subjects(), ('Note', ))
   233             self.assertEqual(self.schema['mydate'].objects(), ('Date', ))
   228             self.assertEqual(self.schema['mydate'].objects(), ('Date', ))
   234             testdate = date(2005, 12, 13)
   229             testdate = date(2005, 12, 13)
   235             eid1 = mh.rqlexec('INSERT Note N')[0][0]
   230             eid1 = mh.rqlexec('INSERT Note N')[0][0]
   236             eid2 = mh.rqlexec('INSERT Note N: N mydate %(mydate)s', {'mydate' : testdate})[0][0]
   231             eid2 = mh.rqlexec('INSERT Note N: N mydate %(mydate)s', {'mydate': testdate})[0][0]
   237             d1 = mh.rqlexec('Any D WHERE X eid %(x)s, X mydate D', {'x': eid1})[0][0]
   232             d1 = mh.rqlexec('Any D WHERE X eid %(x)s, X mydate D', {'x': eid1})[0][0]
   238             d2 = mh.rqlexec('Any D WHERE X eid %(x)s, X mydate D', {'x': eid2})[0][0]
   233             d2 = mh.rqlexec('Any D WHERE X eid %(x)s, X mydate D', {'x': eid2})[0][0]
   239             d3 = mh.rqlexec('Any D WHERE X eid %(x)s, X oldstyledefaultdate D', {'x': eid1})[0][0]
   234             d3 = mh.rqlexec('Any D WHERE X eid %(x)s, X oldstyledefaultdate D', {'x': eid1})[0][0]
   240             d4 = mh.rqlexec('Any D WHERE X eid %(x)s, X newstyledefaultdate D', {'x': eid1})[0][0]
   235             d4 = mh.rqlexec('Any D WHERE X eid %(x)s, X newstyledefaultdate D', {'x': eid1})[0][0]
   241             self.assertEqual(d1, date.today())
   236             self.assertEqual(d1, date.today())
   283             c2 = mh.rqlexec('Any C WHERE X eid %s, X civility C' % eid2)[0][0]
   278             c2 = mh.rqlexec('Any C WHERE X eid %s, X civility C' % eid2)[0][0]
   284             self.assertEqual(c2, None)
   279             self.assertEqual(c2, None)
   285 
   280 
   286     def test_workflow_actions(self):
   281     def test_workflow_actions(self):
   287         with self.mh() as (cnx, mh):
   282         with self.mh() as (cnx, mh):
   288             wf = mh.cmd_add_workflow(u'foo', ('Personne', 'Email'),
   283             mh.cmd_add_workflow(u'foo', ('Personne', 'Email'),
   289                                      ensure_workflowable=False)
   284                                 ensure_workflowable=False)
   290             for etype in ('Personne', 'Email'):
   285             for etype in ('Personne', 'Email'):
   291                 s1 = mh.rqlexec('Any N WHERE WF workflow_of ET, ET name "%s", WF name N' %
   286                 s1 = mh.rqlexec('Any N WHERE WF workflow_of ET, ET name "%s", WF name N' %
   292                                 etype)[0][0]
   287                                 etype)[0][0]
   293                 self.assertEqual(s1, "foo")
   288                 self.assertEqual(s1, "foo")
   294                 s1 = mh.rqlexec('Any N WHERE ET default_workflow WF, ET name "%s", WF name N' %
   289                 s1 = mh.rqlexec('Any N WHERE ET default_workflow WF, ET name "%s", WF name N' %
   304             self.assertIn('Old', self.schema)
   299             self.assertIn('Old', self.schema)
   305             self.assertTrue(cnx.execute('CWEType X WHERE X name "Folder2"'))
   300             self.assertTrue(cnx.execute('CWEType X WHERE X name "Folder2"'))
   306             self.assertIn('filed_under2', self.schema)
   301             self.assertIn('filed_under2', self.schema)
   307             self.assertTrue(cnx.execute('CWRType X WHERE X name "filed_under2"'))
   302             self.assertTrue(cnx.execute('CWRType X WHERE X name "filed_under2"'))
   308             self.assertEqual(sorted(str(rs) for rs in self.schema['Folder2'].subject_relations()),
   303             self.assertEqual(sorted(str(rs) for rs in self.schema['Folder2'].subject_relations()),
   309                               ['created_by', 'creation_date', 'cw_source', 'cwuri',
   304                              ['created_by', 'creation_date', 'cw_source', 'cwuri',
   310                                'description', 'description_format',
   305                               'description', 'description_format',
   311                                'eid',
   306                               'eid',
   312                                'filed_under2', 'has_text',
   307                               'filed_under2', 'has_text',
   313                                'identity', 'in_basket', 'inlined_rel', 'is', 'is_instance_of',
   308                               'identity', 'in_basket', 'inlined_rel', 'is', 'is_instance_of',
   314                                'modification_date', 'name', 'owned_by'])
   309                               'modification_date', 'name', 'owned_by'])
   315             self.assertCountEqual([str(rs) for rs in self.schema['Folder2'].object_relations()],
   310             self.assertCountEqual([str(rs) for rs in self.schema['Folder2'].object_relations()],
   316                                   ['filed_under2', 'identity', 'inlined_rel'])
   311                                   ['filed_under2', 'identity', 'inlined_rel'])
   317             # Old will be missing as it has been renamed into 'New' in the migrated
   312             # Old will be missing as it has been renamed into 'New' in the migrated
   318             # schema while New hasn't been added here.
   313             # schema while New hasn't been added here.
   319             self.assertEqual(sorted(str(e) for e in self.schema['filed_under2'].subjects()),
   314             self.assertEqual(sorted(str(e) for e in self.schema['filed_under2'].subjects()),
   320                              sorted(str(e) for e in self.schema.entities() if not e.final and e != 'Old'))
   315                              sorted(str(e) for e in self.schema.entities()
       
   316                                     if not e.final and e != 'Old'))
   321             self.assertEqual(self.schema['filed_under2'].objects(), ('Folder2',))
   317             self.assertEqual(self.schema['filed_under2'].objects(), ('Folder2',))
   322             eschema = self.schema.eschema('Folder2')
   318             eschema = self.schema.eschema('Folder2')
   323             for cstr in eschema.rdef('name').constraints:
   319             for cstr in eschema.rdef('name').constraints:
   324                 self.assertTrue(hasattr(cstr, 'eid'))
   320                 self.assertTrue(hasattr(cstr, 'eid'))
   325 
   321 
   337                 self.assertTrue(self.schema['Numeric'].final)
   333                 self.assertTrue(self.schema['Numeric'].final)
   338                 rdef = self.schema['num'].rdefs[('Location', 'Numeric')]
   334                 rdef = self.schema['num'].rdefs[('Location', 'Numeric')]
   339                 self.assertEqual(rdef.scale, 10)
   335                 self.assertEqual(rdef.scale, 10)
   340                 self.assertEqual(rdef.precision, 18)
   336                 self.assertEqual(rdef.precision, 18)
   341                 fields = self.table_schema(mh, '%sLocation' % SQL_PREFIX)
   337                 fields = self.table_schema(mh, '%sLocation' % SQL_PREFIX)
   342                 self.assertEqual(fields['%snum' % SQL_PREFIX], ('numeric', None)) # XXX
   338                 self.assertEqual(fields['%snum' % SQL_PREFIX], ('numeric', None))  # XXX
   343             finally:
   339             finally:
   344                 mh.cmd_drop_cube('fakecustomtype')
   340                 mh.cmd_drop_cube('fakecustomtype')
   345                 mh.drop_entity_type('Numeric')
   341                 mh.drop_entity_type('Numeric')
   346 
   342 
   347     def test_add_drop_entity_type(self):
   343     def test_add_drop_entity_type(self):
   352             todo = wf.add_state(u'todo', initial=True)
   348             todo = wf.add_state(u'todo', initial=True)
   353             done = wf.add_state(u'done')
   349             done = wf.add_state(u'done')
   354             wf.add_transition(u'redoit', done, todo)
   350             wf.add_transition(u'redoit', done, todo)
   355             wf.add_transition(u'markasdone', todo, done)
   351             wf.add_transition(u'markasdone', todo, done)
   356             cnx.commit()
   352             cnx.commit()
   357             eschema = self.schema.eschema('Folder2')
       
   358             mh.cmd_drop_entity_type('Folder2')
   353             mh.cmd_drop_entity_type('Folder2')
   359             self.assertNotIn('Folder2', self.schema)
   354             self.assertNotIn('Folder2', self.schema)
   360             self.assertFalse(cnx.execute('CWEType X WHERE X name "Folder2"'))
   355             self.assertFalse(cnx.execute('CWEType X WHERE X name "Folder2"'))
   361             # test automatic workflow deletion
   356             # test automatic workflow deletion
   362             self.assertFalse(cnx.execute('Workflow X WHERE NOT X workflow_of ET'))
   357             self.assertFalse(cnx.execute('Workflow X WHERE NOT X workflow_of ET'))
   402             mh.cmd_add_relation_definition('Personne', 'concerne2', 'Affaire')
   397             mh.cmd_add_relation_definition('Personne', 'concerne2', 'Affaire')
   403             self.assertEqual(self.schema['concerne2'].subjects(),
   398             self.assertEqual(self.schema['concerne2'].subjects(),
   404                              ('Personne',))
   399                              ('Personne',))
   405             self.assertEqual(self.schema['concerne2'].objects(),
   400             self.assertEqual(self.schema['concerne2'].objects(),
   406                              ('Affaire', ))
   401                              ('Affaire', ))
   407             self.assertEqual(self.schema['concerne2'].rdef('Personne', 'Affaire').cardinality,
   402             self.assertEqual(
   408                               '1*')
   403                 self.schema['concerne2'].rdef('Personne', 'Affaire').cardinality,
       
   404                 '1*')
   409             mh.cmd_add_relation_definition('Personne', 'concerne2', 'Note')
   405             mh.cmd_add_relation_definition('Personne', 'concerne2', 'Note')
   410             self.assertEqual(sorted(self.schema['concerne2'].objects()), ['Affaire', 'Note'])
   406             self.assertEqual(sorted(self.schema['concerne2'].objects()), ['Affaire', 'Note'])
   411             mh.create_entity('Personne', nom=u'tot')
   407             mh.create_entity('Personne', nom=u'tot')
   412             mh.create_entity('Affaire')
   408             mh.create_entity('Affaire')
   413             mh.rqlexec('SET X concerne2 Y WHERE X is Personne, Y is Affaire')
   409             mh.rqlexec('SET X concerne2 Y WHERE X is Personne, Y is Affaire')
   418             self.assertNotIn('concerne2', self.schema)
   414             self.assertNotIn('concerne2', self.schema)
   419 
   415 
   420     def test_drop_relation_definition_existant_rtype(self):
   416     def test_drop_relation_definition_existant_rtype(self):
   421         with self.mh() as (cnx, mh):
   417         with self.mh() as (cnx, mh):
   422             self.assertEqual(sorted(str(e) for e in self.schema['concerne'].subjects()),
   418             self.assertEqual(sorted(str(e) for e in self.schema['concerne'].subjects()),
   423                               ['Affaire', 'Personne'])
   419                              ['Affaire', 'Personne'])
   424             self.assertEqual(sorted(str(e) for e in self.schema['concerne'].objects()),
   420             self.assertEqual(sorted(str(e) for e in self.schema['concerne'].objects()),
   425                               ['Affaire', 'Division', 'Note', 'Societe', 'SubDivision'])
   421                              ['Affaire', 'Division', 'Note', 'Societe', 'SubDivision'])
   426             mh.cmd_drop_relation_definition('Personne', 'concerne', 'Affaire')
   422             mh.cmd_drop_relation_definition('Personne', 'concerne', 'Affaire')
   427             self.assertEqual(sorted(str(e) for e in self.schema['concerne'].subjects()),
   423             self.assertEqual(sorted(str(e) for e in self.schema['concerne'].subjects()),
   428                               ['Affaire'])
   424                              ['Affaire'])
   429             self.assertEqual(sorted(str(e) for e in self.schema['concerne'].objects()),
   425             self.assertEqual(sorted(str(e) for e in self.schema['concerne'].objects()),
   430                               ['Division', 'Note', 'Societe', 'SubDivision'])
   426                              ['Division', 'Note', 'Societe', 'SubDivision'])
   431             mh.cmd_add_relation_definition('Personne', 'concerne', 'Affaire')
   427             mh.cmd_add_relation_definition('Personne', 'concerne', 'Affaire')
   432             self.assertEqual(sorted(str(e) for e in self.schema['concerne'].subjects()),
   428             self.assertEqual(sorted(str(e) for e in self.schema['concerne'].subjects()),
   433                               ['Affaire', 'Personne'])
   429                              ['Affaire', 'Personne'])
   434             self.assertEqual(sorted(str(e) for e in self.schema['concerne'].objects()),
   430             self.assertEqual(sorted(str(e) for e in self.schema['concerne'].objects()),
   435                               ['Affaire', 'Division', 'Note', 'Societe', 'SubDivision'])
   431                              ['Affaire', 'Division', 'Note', 'Societe', 'SubDivision'])
   436             # trick: overwrite self.maxeid to avoid deletion of just reintroduced types
   432             # trick: overwrite self.maxeid to avoid deletion of just reintroduced types
   437             self.maxeid = cnx.execute('Any MAX(X)')[0][0]
   433             self.maxeid = cnx.execute('Any MAX(X)')[0][0]
   438 
   434 
   439     def test_drop_relation_definition_with_specialization(self):
   435     def test_drop_relation_definition_with_specialization(self):
   440         with self.mh() as (cnx, mh):
   436         with self.mh() as (cnx, mh):
   441             self.assertEqual(sorted(str(e) for e in self.schema['concerne'].subjects()),
   437             self.assertEqual(sorted(str(e) for e in self.schema['concerne'].subjects()),
   442                               ['Affaire', 'Personne'])
   438                              ['Affaire', 'Personne'])
   443             self.assertEqual(sorted(str(e) for e in self.schema['concerne'].objects()),
   439             self.assertEqual(sorted(str(e) for e in self.schema['concerne'].objects()),
   444                               ['Affaire', 'Division', 'Note', 'Societe', 'SubDivision'])
   440                              ['Affaire', 'Division', 'Note', 'Societe', 'SubDivision'])
   445             mh.cmd_drop_relation_definition('Affaire', 'concerne', 'Societe')
   441             mh.cmd_drop_relation_definition('Affaire', 'concerne', 'Societe')
   446             self.assertEqual(sorted(str(e) for e in self.schema['concerne'].subjects()),
   442             self.assertEqual(sorted(str(e) for e in self.schema['concerne'].subjects()),
   447                               ['Affaire', 'Personne'])
   443                              ['Affaire', 'Personne'])
   448             self.assertEqual(sorted(str(e) for e in self.schema['concerne'].objects()),
   444             self.assertEqual(sorted(str(e) for e in self.schema['concerne'].objects()),
   449                               ['Affaire', 'Note'])
   445                              ['Affaire', 'Note'])
   450             mh.cmd_add_relation_definition('Affaire', 'concerne', 'Societe')
   446             mh.cmd_add_relation_definition('Affaire', 'concerne', 'Societe')
   451             self.assertEqual(sorted(str(e) for e in self.schema['concerne'].subjects()),
   447             self.assertEqual(sorted(str(e) for e in self.schema['concerne'].subjects()),
   452                               ['Affaire', 'Personne'])
   448                              ['Affaire', 'Personne'])
   453             self.assertEqual(sorted(str(e) for e in self.schema['concerne'].objects()),
   449             self.assertEqual(sorted(str(e) for e in self.schema['concerne'].objects()),
   454                               ['Affaire', 'Division', 'Note', 'Societe', 'SubDivision'])
   450                              ['Affaire', 'Division', 'Note', 'Societe', 'SubDivision'])
   455             # trick: overwrite self.maxeid to avoid deletion of just reintroduced types
   451             # trick: overwrite self.maxeid to avoid deletion of just reintroduced types
   456             self.maxeid = cnx.execute('Any MAX(X)')[0][0]
   452             self.maxeid = cnx.execute('Any MAX(X)')[0][0]
   457 
   453 
   458     def test_rename_relation(self):
   454     def test_rename_relation(self):
   459         self.skipTest('implement me')
   455         self.skipTest('implement me')
   544 
   540 
   545             # test permissions synchronization ####################################
   541             # test permissions synchronization ####################################
   546             # new rql expr to add note entity
   542             # new rql expr to add note entity
   547             eexpr = self._erqlexpr_entity(cnx, 'add', 'Note')
   543             eexpr = self._erqlexpr_entity(cnx, 'add', 'Note')
   548             self.assertEqual(eexpr.expression,
   544             self.assertEqual(eexpr.expression,
   549                               'X ecrit_part PE, U in_group G, '
   545                              'X ecrit_part PE, U in_group G, '
   550                               'PE require_permission P, P name "add_note", P require_group G')
   546                              'PE require_permission P, P name "add_note", P require_group G')
   551             self.assertEqual([et.name for et in eexpr.reverse_add_permission], ['Note'])
   547             self.assertEqual([et.name for et in eexpr.reverse_add_permission], ['Note'])
   552             self.assertEqual(eexpr.reverse_read_permission, ())
   548             self.assertEqual(eexpr.reverse_read_permission, ())
   553             self.assertEqual(eexpr.reverse_delete_permission, ())
   549             self.assertEqual(eexpr.reverse_delete_permission, ())
   554             self.assertEqual(eexpr.reverse_update_permission, ())
   550             self.assertEqual(eexpr.reverse_update_permission, ())
   555             self.assertTrue(self._rrqlexpr_rset(cnx, 'add', 'para'))
   551             self.assertTrue(self._rrqlexpr_rset(cnx, 'add', 'para'))
   556             # no rqlexpr to delete para attribute
   552             # no rqlexpr to delete para attribute
   557             self.assertFalse(self._rrqlexpr_rset(cnx, 'delete', 'para'))
   553             self.assertFalse(self._rrqlexpr_rset(cnx, 'delete', 'para'))
   558             # new rql expr to add ecrit_par relation
   554             # new rql expr to add ecrit_par relation
   559             rexpr = self._rrqlexpr_entity(cnx, 'add', 'ecrit_par')
   555             rexpr = self._rrqlexpr_entity(cnx, 'add', 'ecrit_par')
   560             self.assertEqual(rexpr.expression,
   556             self.assertEqual(rexpr.expression,
   561                               'O require_permission P, P name "add_note", '
   557                              'O require_permission P, P name "add_note", '
   562                               'U in_group G, P require_group G')
   558                              'U in_group G, P require_group G')
   563             self.assertEqual([rdef.rtype.name for rdef in rexpr.reverse_add_permission], ['ecrit_par'])
   559             self.assertEqual([rdef.rtype.name for rdef in rexpr.reverse_add_permission],
       
   560                              ['ecrit_par'])
   564             self.assertEqual(rexpr.reverse_read_permission, ())
   561             self.assertEqual(rexpr.reverse_read_permission, ())
   565             self.assertEqual(rexpr.reverse_delete_permission, ())
   562             self.assertEqual(rexpr.reverse_delete_permission, ())
   566             # no more rqlexpr to delete and add travaille relation
   563             # no more rqlexpr to delete and add travaille relation
   567             self.assertFalse(self._rrqlexpr_rset(cnx, 'add', 'travaille'))
   564             self.assertFalse(self._rrqlexpr_rset(cnx, 'add', 'travaille'))
   568             self.assertFalse(self._rrqlexpr_rset(cnx, 'delete', 'travaille'))
   565             self.assertFalse(self._rrqlexpr_rset(cnx, 'delete', 'travaille'))
   588             #   * 1 update (Affaire update)
   585             #   * 1 update (Affaire update)
   589             #   * 2 new (Note add, ecrit_par add)
   586             #   * 2 new (Note add, ecrit_par add)
   590             #   * 2 implicit new for attributes (Note.para, Person.test)
   587             #   * 2 implicit new for attributes (Note.para, Person.test)
   591             # remaining orphan rql expr which should be deleted at commit (composite relation)
   588             # remaining orphan rql expr which should be deleted at commit (composite relation)
   592             # unattached expressions -> pending deletion on commit
   589             # unattached expressions -> pending deletion on commit
   593             self.assertEqual(cnx.execute('Any COUNT(X) WHERE X is RQLExpression, X exprtype "ERQLExpression",'
   590             self.assertEqual(
   594                                          'NOT ET1 read_permission X, NOT ET2 add_permission X, '
   591                 cnx.execute('Any COUNT(X) WHERE X is RQLExpression, X exprtype "ERQLExpression",'
   595                                          'NOT ET3 delete_permission X, NOT ET4 update_permission X')[0][0],
   592                             'NOT ET1 read_permission X, NOT ET2 add_permission X, '
   596                               7)
   593                             'NOT ET3 delete_permission X, NOT ET4 update_permission X')[0][0],
   597             self.assertEqual(cnx.execute('Any COUNT(X) WHERE X is RQLExpression, X exprtype "RRQLExpression",'
   594                 7)
   598                                          'NOT ET1 read_permission X, NOT ET2 add_permission X, '
   595             self.assertEqual(
   599                                          'NOT ET3 delete_permission X, NOT ET4 update_permission X')[0][0],
   596                 cnx.execute('Any COUNT(X) WHERE X is RQLExpression, X exprtype "RRQLExpression",'
   600                               2)
   597                             'NOT ET1 read_permission X, NOT ET2 add_permission X, '
       
   598                             'NOT ET3 delete_permission X, NOT ET4 update_permission X')[0][0],
       
   599                 2)
   601             # finally
   600             # finally
   602             self.assertEqual(cnx.execute('Any COUNT(X) WHERE X is RQLExpression')[0][0],
   601             self.assertEqual(cnx.execute('Any COUNT(X) WHERE X is RQLExpression')[0][0],
   603                              nbrqlexpr_start + 1 + 2 + 2 + 2)
   602                              nbrqlexpr_start + 1 + 2 + 2 + 2)
   604             cnx.commit()
   603             cnx.commit()
   605             # unique_together test
   604             # unique_together test
   606             self.assertEqual(len(self.schema.eschema('Personne')._unique_together), 1)
   605             self.assertEqual(len(self.schema.eschema('Personne')._unique_together), 1)
   607             self.assertCountEqual(self.schema.eschema('Personne')._unique_together[0],
   606             self.assertCountEqual(self.schema.eschema('Personne')._unique_together[0],
   608                                                ('nom', 'prenom', 'datenaiss'))
   607                                   ('nom', 'prenom', 'datenaiss'))
   609             rset = cnx.execute('Any C WHERE C is CWUniqueTogetherConstraint, C constraint_of ET, ET name "Personne"')
   608             rset = cnx.execute('Any C WHERE C is CWUniqueTogetherConstraint, '
       
   609                                'C constraint_of ET, ET name "Personne"')
   610             self.assertEqual(len(rset), 1)
   610             self.assertEqual(len(rset), 1)
   611             relations = [r.name for r in rset.get_entity(0, 0).relations]
   611             relations = [r.name for r in rset.get_entity(0, 0).relations]
   612             self.assertCountEqual(relations, ('nom', 'prenom', 'datenaiss'))
   612             self.assertCountEqual(relations, ('nom', 'prenom', 'datenaiss'))
   613 
   613 
   614             # serialized constraint changed
   614             # serialized constraint changed
   626         rset = self._erqlexpr_rset(cnx, action, ertype)
   626         rset = self._erqlexpr_rset(cnx, action, ertype)
   627         self.assertEqual(len(rset), 1)
   627         self.assertEqual(len(rset), 1)
   628         return rset.get_entity(0, 0)
   628         return rset.get_entity(0, 0)
   629 
   629 
   630     def _rrqlexpr_rset(self, cnx, action, ertype):
   630     def _rrqlexpr_rset(self, cnx, action, ertype):
   631         rql = 'RQLExpression X WHERE RT is CWRType, RDEF %s_permission X, RT name %%(name)s, RDEF relation_type RT' % action
   631         return cnx.execute('RQLExpression X WHERE RT is CWRType, RDEF %s_permission X, '
   632         return cnx.execute(rql, {'name': ertype})
   632                            'RT name %%(name)s, RDEF relation_type RT' % action,
       
   633                            {'name': ertype})
   633 
   634 
   634     def _rrqlexpr_entity(self, cnx, action, ertype):
   635     def _rrqlexpr_entity(self, cnx, action, ertype):
   635         rset = self._rrqlexpr_rset(cnx, action, ertype)
   636         rset = self._rrqlexpr_rset(cnx, action, ertype)
   636         self.assertEqual(len(rset), 1)
   637         self.assertEqual(len(rset), 1)
   637         return rset.get_entity(0, 0)
   638         return rset.get_entity(0, 0)
   665                 self.assertNotIn(self.config.cube_dir('file'), self.config.cubes_path())
   666                 self.assertNotIn(self.config.cube_dir('file'), self.config.cubes_path())
   666                 for ertype in ('Email', 'EmailThread', 'EmailPart', 'File',
   667                 for ertype in ('Email', 'EmailThread', 'EmailPart', 'File',
   667                                'sender', 'in_thread', 'reply_to', 'data_format'):
   668                                'sender', 'in_thread', 'reply_to', 'data_format'):
   668                     self.assertNotIn(ertype, schema)
   669                     self.assertNotIn(ertype, schema)
   669                 self.assertEqual(sorted(schema['see_also'].rdefs),
   670                 self.assertEqual(sorted(schema['see_also'].rdefs),
   670                                   sorted([('Folder', 'Folder'),
   671                                  sorted([('Folder', 'Folder'),
   671                                           ('Bookmark', 'Bookmark'),
   672                                          ('Bookmark', 'Bookmark'),
   672                                           ('Bookmark', 'Note'),
   673                                          ('Bookmark', 'Note'),
   673                                           ('Note', 'Note'),
   674                                          ('Note', 'Note'),
   674                                           ('Note', 'Bookmark')]))
   675                                          ('Note', 'Bookmark')]))
   675                 self.assertEqual(sorted(schema['see_also'].subjects()), ['Bookmark', 'Folder', 'Note'])
   676                 self.assertEqual(sorted(schema['see_also'].subjects()),
   676                 self.assertEqual(sorted(schema['see_also'].objects()), ['Bookmark', 'Folder', 'Note'])
   677                                  ['Bookmark', 'Folder', 'Note'])
   677                 self.assertEqual(cnx.execute('Any X WHERE X pkey "system.version.fakeemail"').rowcount, 0)
   678                 self.assertEqual(sorted(schema['see_also'].objects()),
   678                 self.assertEqual(cnx.execute('Any X WHERE X pkey "system.version.file"').rowcount, 0)
   679                                  ['Bookmark', 'Folder', 'Note'])
       
   680                 self.assertEqual(
       
   681                     cnx.execute('Any X WHERE X pkey "system.version.fakeemail"').rowcount,
       
   682                     0)
       
   683                 self.assertEqual(
       
   684                     cnx.execute('Any X WHERE X pkey "system.version.file"').rowcount,
       
   685                     0)
   679             finally:
   686             finally:
   680                 mh.cmd_add_cube('fakeemail')
   687                 mh.cmd_add_cube('fakeemail')
   681                 self.assertIn('fakeemail', self.config.cubes())
   688                 self.assertIn('fakeemail', self.config.cubes())
   682                 self.assertIn(self.config.cube_dir('fakeemail'), self.config.cubes_path())
   689                 self.assertIn(self.config.cube_dir('fakeemail'), self.config.cubes_path())
   683                 self.assertIn('file', self.config.cubes())
   690                 self.assertIn('file', self.config.cubes())
   689                                  sorted([('EmailThread', 'EmailThread'), ('Folder', 'Folder'),
   696                                  sorted([('EmailThread', 'EmailThread'), ('Folder', 'Folder'),
   690                                          ('Bookmark', 'Bookmark'),
   697                                          ('Bookmark', 'Bookmark'),
   691                                          ('Bookmark', 'Note'),
   698                                          ('Bookmark', 'Note'),
   692                                          ('Note', 'Note'),
   699                                          ('Note', 'Note'),
   693                                          ('Note', 'Bookmark')]))
   700                                          ('Note', 'Bookmark')]))
   694                 self.assertEqual(sorted(schema['see_also'].subjects()), ['Bookmark', 'EmailThread', 'Folder', 'Note'])
   701                 self.assertEqual(sorted(schema['see_also'].subjects()),
   695                 self.assertEqual(sorted(schema['see_also'].objects()), ['Bookmark', 'EmailThread', 'Folder', 'Note'])
   702                                  ['Bookmark', 'EmailThread', 'Folder', 'Note'])
       
   703                 self.assertEqual(sorted(schema['see_also'].objects()),
       
   704                                  ['Bookmark', 'EmailThread', 'Folder', 'Note'])
   696                 from cubes.fakeemail.__pkginfo__ import version as email_version
   705                 from cubes.fakeemail.__pkginfo__ import version as email_version
   697                 from cubes.file.__pkginfo__ import version as file_version
   706                 from cubes.file.__pkginfo__ import version as file_version
   698                 self.assertEqual(cnx.execute('Any V WHERE X value V, X pkey "system.version.fakeemail"')[0][0],
   707                 self.assertEqual(
   699                                   email_version)
   708                     cnx.execute('Any V WHERE X value V, X pkey "system.version.fakeemail"')[0][0],
   700                 self.assertEqual(cnx.execute('Any V WHERE X value V, X pkey "system.version.file"')[0][0],
   709                     email_version)
   701                                   file_version)
   710                 self.assertEqual(
   702                 # trick: overwrite self.maxeid to avoid deletion of just reintroduced
   711                     cnx.execute('Any V WHERE X value V, X pkey "system.version.file"')[0][0],
   703                 #        types (and their associated tables!)
   712                     file_version)
   704                 self.maxeid = cnx.execute('Any MAX(X)')[0][0]
       
   705                 # why this commit is necessary is unclear to me (though without it
   713                 # why this commit is necessary is unclear to me (though without it
   706                 # next test may fail complaining of missing tables
   714                 # next test may fail complaining of missing tables
   707                 cnx.commit()
   715                 cnx.commit()
   708 
   716 
   709     def test_add_drop_cube_no_deps(self):
   717     def test_add_drop_cube_no_deps(self):
   737             self.assertEqual(sorted(et.type for et in self.schema['Para'].specialized_by()),
   745             self.assertEqual(sorted(et.type for et in self.schema['Para'].specialized_by()),
   738                              ['Note'])
   746                              ['Note'])
   739             self.assertEqual(self.schema['Note'].specializes().type, 'Para')
   747             self.assertEqual(self.schema['Note'].specializes().type, 'Para')
   740             mh.cmd_add_entity_type('Text')
   748             mh.cmd_add_entity_type('Text')
   741             self.assertEqual(sorted(et.type for et in self.schema['Para'].specialized_by()),
   749             self.assertEqual(sorted(et.type for et in self.schema['Para'].specialized_by()),
   742                               ['Note', 'Text'])
   750                              ['Note', 'Text'])
   743             self.assertEqual(self.schema['Text'].specializes().type, 'Para')
   751             self.assertEqual(self.schema['Text'].specializes().type, 'Para')
   744             # test columns have been actually added
   752             # test columns have been actually added
   745             text = cnx.execute('INSERT Text X: X para "hip", X summary "hop", X newattr "momo"').get_entity(0, 0)
   753             text = cnx.create_entity('Text', para=u"hip", summary=u"hop", newattr=u"momo")
   746             note = cnx.execute('INSERT Note X: X para "hip", X shortpara "hop", X newattr "momo", X unique_id "x"').get_entity(0, 0)
   754             note = cnx.create_entity('Note', para=u"hip", shortpara=u"hop",
   747             aff = cnx.execute('INSERT Affaire X').get_entity(0, 0)
   755                                      newattr=u"momo", unique_id=u"x")
       
   756             aff = cnx.create_entity('Affaire')
   748             self.assertTrue(cnx.execute('SET X newnotinlined Y WHERE X eid %(x)s, Y eid %(y)s',
   757             self.assertTrue(cnx.execute('SET X newnotinlined Y WHERE X eid %(x)s, Y eid %(y)s',
   749                                          {'x': text.eid, 'y': aff.eid}))
   758                                         {'x': text.eid, 'y': aff.eid}))
   750             self.assertTrue(cnx.execute('SET X newnotinlined Y WHERE X eid %(x)s, Y eid %(y)s',
   759             self.assertTrue(cnx.execute('SET X newnotinlined Y WHERE X eid %(x)s, Y eid %(y)s',
   751                                          {'x': note.eid, 'y': aff.eid}))
   760                                         {'x': note.eid, 'y': aff.eid}))
   752             self.assertTrue(cnx.execute('SET X newinlined Y WHERE X eid %(x)s, Y eid %(y)s',
   761             self.assertTrue(cnx.execute('SET X newinlined Y WHERE X eid %(x)s, Y eid %(y)s',
   753                                          {'x': text.eid, 'y': aff.eid}))
   762                                         {'x': text.eid, 'y': aff.eid}))
   754             self.assertTrue(cnx.execute('SET X newinlined Y WHERE X eid %(x)s, Y eid %(y)s',
   763             self.assertTrue(cnx.execute('SET X newinlined Y WHERE X eid %(x)s, Y eid %(y)s',
   755                                          {'x': note.eid, 'y': aff.eid}))
   764                                         {'x': note.eid, 'y': aff.eid}))
   756             # XXX remove specializes by ourselves, else tearDown fails when removing
   765             # XXX remove specializes by ourselves, else tearDown fails when removing
   757             # Para because of Note inheritance. This could be fixed by putting the
   766             # Para because of Note inheritance. This could be fixed by putting the
   758             # MemSchemaCWETypeDel(session, name) operation in the
   767             # MemSchemaCWETypeDel(session, name) operation in the
   759             # after_delete_entity(CWEType) hook, since in that case the MemSchemaSpecializesDel
   768             # after_delete_entity(CWEType) hook, since in that case the MemSchemaSpecializesDel
   760             # operation would be removed before, but I'm not sure this is a desired behaviour.
   769             # operation would be removed before, but I'm not sure this is a desired behaviour.
   787             self.assertIsInstance(tel, float)
   796             self.assertIsInstance(tel, float)
   788 
   797 
   789     def test_drop_required_inlined_relation(self):
   798     def test_drop_required_inlined_relation(self):
   790         with self.mh() as (cnx, mh):
   799         with self.mh() as (cnx, mh):
   791             bob = mh.cmd_create_entity('Personne', nom=u'bob')
   800             bob = mh.cmd_create_entity('Personne', nom=u'bob')
   792             note = mh.cmd_create_entity('Note', ecrit_par=bob)
   801             mh.cmd_create_entity('Note', ecrit_par=bob)
   793             mh.commit()
   802             mh.commit()
   794             rdef = mh.fs_schema.rschema('ecrit_par').rdefs[('Note', 'Personne')]
   803             rdef = mh.fs_schema.rschema('ecrit_par').rdefs[('Note', 'Personne')]
   795             with tempattr(rdef, 'cardinality', '1*'):
   804             with tempattr(rdef, 'cardinality', '1*'):
   796                 mh.sync_schema_props_perms('ecrit_par', syncperms=False)
   805                 mh.sync_schema_props_perms('ecrit_par', syncperms=False)
   797             mh.cmd_drop_relation_type('ecrit_par')
   806             mh.cmd_drop_relation_type('ecrit_par')
   798             self.assertNotIn('%secrit_par' % SQL_PREFIX,
   807             self.assertNotIn('%secrit_par' % SQL_PREFIX,
   799                              self.table_schema(mh, '%sPersonne' % SQL_PREFIX))
   808                              self.table_schema(mh, '%sPersonne' % SQL_PREFIX))
   800 
   809 
   801     def test_drop_inlined_rdef_delete_data(self):
   810     def test_drop_inlined_rdef_delete_data(self):
   802         with self.mh() as (cnx, mh):
   811         with self.mh() as (cnx, mh):
   803             note = mh.cmd_create_entity('Note', ecrit_par=cnx.user.eid)
   812             mh.cmd_create_entity('Note', ecrit_par=cnx.user.eid)
   804             mh.commit()
   813             mh.commit()
   805             mh.drop_relation_definition('Note', 'ecrit_par', 'CWUser')
   814             mh.drop_relation_definition('Note', 'ecrit_par', 'CWUser')
   806             self.assertFalse(mh.sqlexec('SELECT * FROM cw_Note WHERE cw_ecrit_par IS NOT NULL'))
   815             self.assertFalse(mh.sqlexec('SELECT * FROM cw_Note WHERE cw_ecrit_par IS NOT NULL'))
   807 
   816 
   808     def test_storage_changed(self):
   817     def test_storage_changed(self):