web/test/unittest_views_basecontrollers.py
changeset 9402 2c48c091b6a2
parent 9020 cb87e831c183
parent 9273 f3795da61959
child 9478 2d7521881d3d
--- a/web/test/unittest_views_basecontrollers.py	Tue Jul 02 17:09:04 2013 +0200
+++ b/web/test/unittest_views_basecontrollers.py	Mon Jan 13 13:47:47 2014 +0100
@@ -1,4 +1,4 @@
-# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -39,6 +39,8 @@
 from cubicweb.web.views.basecontrollers import JSonController, xhtmlize, jsonize
 from cubicweb.web.views.ajaxcontroller import ajaxfunc, AjaxFunction
 import cubicweb.transaction as tx
+from cubicweb.server.hook import Hook, Operation
+from cubicweb.predicates import is_instance
 
 u = unicode
 
@@ -171,6 +173,54 @@
         email = e.use_email[0]
         self.assertEqual(email.address, 'dima@logilab.fr')
 
+    def test_create_mandatory_inlined(self):
+        req = self.request()
+        req.form = {'eid': ['X', 'Y'], '__maineid' : 'X',
+
+                    '__type:X': 'Salesterm',
+                    '_cw_entity_fields:X': '',
+
+                    '__type:Y': 'File',
+                    '_cw_entity_fields:Y': 'data-subject,described_by_test-object',
+                    'data-subject:Y': (u'coucou.txt', Binary('coucou')),
+                    'described_by_test-object:Y': 'X',
+                    }
+        path, params = self.expect_redirect_handle_request(req, 'edit')
+        self.assertTrue(path.startswith('salesterm/'), path)
+        eid = path.split('/')[1]
+        salesterm = req.entity_from_eid(eid)
+        # The NOT NULL constraint of mandatory relation implies that the File
+        # must be created before the Salesterm, otherwise Salesterm insertion
+        # will fail.
+        # NOTE: sqlite does have NOT NULL constraint, unlike Postgres so the
+        # insertion does not fail and we have to check dumbly that File is
+        # created before.
+        self.assertGreater(salesterm.eid, salesterm.described_by_test[0].eid)
+
+    def test_create_mandatory_inlined2(self):
+        req = self.request()
+        req.form = {'eid': ['X', 'Y'], '__maineid' : 'X',
+
+                    '__type:X': 'Salesterm',
+                    '_cw_entity_fields:X': 'described_by_test-subject',
+                    'described_by_test-subject:X': 'Y',
+
+                    '__type:Y': 'File',
+                    '_cw_entity_fields:Y': 'data-subject',
+                    'data-subject:Y': (u'coucou.txt', Binary('coucou')),
+                    }
+        path, params = self.expect_redirect_handle_request(req, 'edit')
+        self.assertTrue(path.startswith('salesterm/'), path)
+        eid = path.split('/')[1]
+        salesterm = req.entity_from_eid(eid)
+        # The NOT NULL constraint of mandatory relation implies that the File
+        # must be created before the Salesterm, otherwise Salesterm insertion
+        # will fail.
+        # NOTE: sqlite does have NOT NULL constraint, unlike Postgres so the
+        # insertion does not fail and we have to check dumbly that File is
+        # created before.
+        self.assertGreater(salesterm.eid, salesterm.described_by_test[0].eid)
+
     def test_edit_multiple_linked(self):
         req = self.request()
         peid = u(self.create_user(req, 'adim').eid)
@@ -263,6 +313,7 @@
             self.ctrl_publish(req)
         cm.exception.translate(unicode)
         self.assertEqual(cm.exception.errors, {'amount-subject': 'value 110 must be <= 100'})
+
         req = self.request(rollbackfirst=True)
         req.form = {'eid': ['X'],
                     '__type:X': 'Salesterm',
@@ -276,6 +327,67 @@
         e = self.execute('Salesterm X').get_entity(0, 0)
         self.assertEqual(e.amount, 10)
 
+    def test_interval_bound_constraint_validateform(self):
+        """Test the FormValidatorController controller on entity with
+        constrained attributes"""
+        feid = self.execute('INSERT File X: X data_name "toto.txt", X data %(data)s',
+                            {'data': Binary('yo')})[0][0]
+        seid = self.request().create_entity('Salesterm', amount=0, described_by_test=feid).eid
+        self.commit()
+
+        # ensure a value that violate a constraint is properly detected
+        req = self.request(rollbackfirst=True)
+        req.form = {'eid': [unicode(seid)],
+                    '__type:%s'%seid: 'Salesterm',
+                    '_cw_entity_fields:%s'%seid: 'amount-subject',
+                    'amount-subject:%s'%seid: u'-10',
+                }
+        self.assertEqual('''<script type="text/javascript">
+ window.parent.handleFormValidationResponse('entityForm', null, null, [false, [%s, {"amount-subject": "value -10 must be >= 0"}], null], null);
+</script>'''%seid, self.ctrl_publish(req, 'validateform'))
+
+        # ensure a value that comply a constraint is properly processed
+        req = self.request(rollbackfirst=True)
+        req.form = {'eid': [unicode(seid)],
+                    '__type:%s'%seid: 'Salesterm',
+                    '_cw_entity_fields:%s'%seid: 'amount-subject',
+                    'amount-subject:%s'%seid: u'20',
+                }
+        self.assertEqual('''<script type="text/javascript">
+ window.parent.handleFormValidationResponse('entityForm', null, null, [true, "http://testing.fr/cubicweb/view", null], null);
+</script>''', self.ctrl_publish(req, 'validateform'))
+        self.assertEqual(20, self.execute('Any V WHERE X amount V, X eid %(eid)s', {'eid': seid})[0][0])
+
+        req = self.request(rollbackfirst=True)
+        req.form = {'eid': ['X'],
+                    '__type:X': 'Salesterm',
+                    '_cw_entity_fields:X': 'amount-subject,described_by_test-subject',
+                    'amount-subject:X': u'0',
+                    'described_by_test-subject:X': u(feid),
+                }
+
+        # ensure a value that is modified in an operation on a modify
+        # hook works as it should (see
+        # https://www.cubicweb.org/ticket/2509729 )
+        class MyOperation(Operation):
+            def precommit_event(self):
+                self.entity.cw_set(amount=-10)
+        class ValidationErrorInOpAfterHook(Hook):
+            __regid__ = 'valerror-op-after-hook'
+            __select__ = Hook.__select__ & is_instance('Salesterm')
+            events = ('after_add_entity',)
+            def __call__(self):
+                MyOperation(self._cw, entity=self.entity)
+
+        with self.temporary_appobjects(ValidationErrorInOpAfterHook):
+            self.assertEqual('''<script type="text/javascript">
+ window.parent.handleFormValidationResponse('entityForm', null, null, [false, ["X", {"amount-subject": "value -10 must be >= 0"}], null], null);
+</script>''', self.ctrl_publish(req, 'validateform'))
+
+        self.assertEqual('''<script type="text/javascript">
+ window.parent.handleFormValidationResponse('entityForm', null, null, [true, "http://testing.fr/cubicweb/view", null], null);
+</script>''', self.ctrl_publish(req, 'validateform'))
+
     def test_req_pending_insert(self):
         """make sure req's pending insertions are taken into account"""
         tmpgroup = self.request().create_entity('CWGroup', name=u"test")
@@ -285,10 +397,9 @@
         path, params = self.expect_redirect_handle_request(req, 'edit')
         usergroups = [gname for gname, in
                       self.execute('Any N WHERE G name N, U in_group G, U eid %(u)s', {'u': user.eid})]
-        self.assertItemsEqual(usergroups, ['managers', 'test'])
+        self.assertCountEqual(usergroups, ['managers', 'test'])
         self.assertEqual(get_pending_inserts(req), [])
 
-
     def test_req_pending_delete(self):
         """make sure req's pending deletions are taken into account"""
         user = self.user()
@@ -297,14 +408,14 @@
         usergroups = [gname for gname, in
                       self.execute('Any N WHERE G name N, U in_group G, U eid %(u)s', {'u': user.eid})]
         # just make sure everything was set correctly
-        self.assertItemsEqual(usergroups, ['managers', 'test'])
+        self.assertCountEqual(usergroups, ['managers', 'test'])
         # now try to delete the relation
         req = self.request(**req_form(user))
         req.session.data['pending_delete'] = set([(user.eid, 'in_group', groupeid)])
         path, params = self.expect_redirect_handle_request(req, 'edit')
         usergroups = [gname for gname, in
                       self.execute('Any N WHERE G name N, U in_group G, U eid %(u)s', {'u': user.eid})]
-        self.assertItemsEqual(usergroups, ['managers'])
+        self.assertCountEqual(usergroups, ['managers'])
         self.assertEqual(get_pending_deletes(req), [])
 
     def test_redirect_apply_button(self):
@@ -534,6 +645,34 @@
         finally:
             p.__class__.skip_copy_for = old_skips
 
+    def test_regr_inlined_forms(self):
+        self.schema['described_by_test'].inlined = False
+        try:
+            req = self.request()
+            req.data['eidmap'] = {}
+            req.data['pending_others'] = set()
+            req.data['pending_inlined'] = {}
+            req.form = {'eid': ['X', 'Y'], '__maineid' : 'X',
+
+                        '__type:X': 'Salesterm',
+                        '_cw_entity_fields:X': 'described_by_test-subject',
+                        'described_by_test-subject:X': 'Y',
+
+                        '__type:Y': 'File',
+                        '_cw_entity_fields:Y': 'data-subject',
+                        'data-subject:Y': (u'coucou.txt', Binary('coucou')),
+                        }
+            values_by_eid = dict((eid, req.extract_entity_params(eid, minparams=2))
+                                 for eid in req.edited_eids())
+            editctrl = self.vreg['controllers'].select('edit', req)
+            # don't call publish to enforce select order
+            editctrl.errors = []
+            editctrl._to_create = {}
+            editctrl.edit_entity(values_by_eid['X']) # #3064653 raise ValidationError
+            editctrl.edit_entity(values_by_eid['Y'])
+        finally:
+            self.schema['described_by_test'].inlined = False
+
 
 class ReportBugControllerTC(CubicWebTC):
 
@@ -576,7 +715,7 @@
 
     def test_remote_add_existing_tag(self):
         self.remote_call('tag_entity', self.john.eid, ['python'])
-        self.assertItemsEqual(
+        self.assertCountEqual(
             [tname for tname, in self.execute('Any N WHERE T is Tag, T name N')],
             ['python', 'cubicweb'])
         self.assertEqual(
@@ -585,7 +724,7 @@
 
     def test_remote_add_new_tag(self):
         self.remote_call('tag_entity', self.john.eid, ['javascript'])
-        self.assertItemsEqual(
+        self.assertCountEqual(
             [tname for tname, in self.execute('Any N WHERE T is Tag, T name N')],
             ['python', 'cubicweb', 'javascript'])
         self.assertEqual(