unittest_views_basecontrollers.py 44.6 KB
Newer Older
1
# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
2
3
4
5
6
7
8
9
10
# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# This file is part of CubicWeb.
#
# CubicWeb is free software: you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free
# Software Foundation, either version 2.1 of the License, or (at your option)
# any later version.
#
11
# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
12
13
14
15
16
17
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License along
# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
18
"""cubicweb.web.views.basecontrollers unit tests"""
Adrien Di Mascio's avatar
Adrien Di Mascio committed
19

20
21
22
23
24
25
from urlparse import urlsplit, urlunsplit, urljoin
# parse_qs is deprecated in cgi and has been moved to urlparse in Python 2.6
try:
    from urlparse import parse_qs as url_parse_query
except ImportError:
    from cgi import parse_qs as url_parse_query
26
from logilab.common.testlib import unittest_main, mock_object
27
from logilab.common.decorators import monkeypatch
Adrien Di Mascio's avatar
Adrien Di Mascio committed
28

29
30
from cubicweb import Binary, NoSelectableObject, ValidationError
from cubicweb.view import STRICT_DOCTYPE
31
from cubicweb.devtools.testlib import CubicWebTC
32
from cubicweb.utils import json_dumps
Sylvain Thénault's avatar
Sylvain Thénault committed
33
from cubicweb.uilib import rql_for_eid
34
from cubicweb.web import INTERNAL_FIELD_VALUE, Redirect, RequestError, RemoteCallFailed
35
import cubicweb.server.session
36
from cubicweb.server.session import Connection as OldConnection
37
from cubicweb.entities.authobjs import CWUser
Sylvain Thénault's avatar
Sylvain Thénault committed
38
from cubicweb.web.views.autoform import get_pending_inserts, get_pending_deletes
39
40
from cubicweb.web.views.basecontrollers import JSonController, xhtmlize, jsonize
from cubicweb.web.views.ajaxcontroller import ajaxfunc, AjaxFunction
41
import cubicweb.transaction as tx
42
43
from cubicweb.server.hook import Hook, Operation
from cubicweb.predicates import is_instance
44

Sylvain Thénault's avatar
Sylvain Thénault committed
45
46
u = unicode

Sylvain Thénault's avatar
Sylvain Thénault committed
47
48
def req_form(user):
    return {'eid': [str(user.eid)],
49
            '_cw_entity_fields:%s' % user.eid: '_cw_generic_field',
Sylvain Thénault's avatar
Sylvain Thénault committed
50
51
            '__type:%s' % user.eid: user.__regid__
            }
Adrien Di Mascio's avatar
Adrien Di Mascio committed
52

53
class EditControllerTC(CubicWebTC):
Adrien Di Mascio's avatar
Adrien Di Mascio committed
54
    def setUp(self):
55
        CubicWebTC.setUp(self)
56
        self.assertIn('users', self.schema.eschema('CWGroup').get_groups('read'))
57

Adrien Di Mascio's avatar
Adrien Di Mascio committed
58
    def tearDown(self):
59
        CubicWebTC.tearDown(self)
60
        self.assertIn('users', self.schema.eschema('CWGroup').get_groups('read'))
61

Adrien Di Mascio's avatar
Adrien Di Mascio committed
62
63
64
    def test_noparam_edit(self):
        """check behaviour of this controller without any form parameter
        """
65
66
67
        with self.assertRaises(ValidationError) as cm:
            self.ctrl_publish(self.request())
        self.assertEqual(cm.exception.errors, {None: u'no selected entities'})
68

Adrien Di Mascio's avatar
Adrien Di Mascio committed
69
70
    def test_validation_unique(self):
        """test creation of two linked entities
71
        """
Adrien Di Mascio's avatar
Adrien Di Mascio committed
72
        user = self.user()
73
74
        req = self.request()
        req.form = {'eid': 'X', '__type:X': 'CWUser',
75
                    '_cw_entity_fields:X': 'login-subject,upassword-subject',
Sylvain Thénault's avatar
Sylvain Thénault committed
76
77
78
                    'login-subject:X': u'admin',
                    'upassword-subject:X': u'toto',
                    'upassword-subject-confirm:X': u'toto',
79
                    }
80
81
        with self.assertRaises(ValidationError) as cm:
            self.ctrl_publish(req)
82
        cm.exception.translate(unicode)
83
        self.assertEqual(cm.exception.errors, {'login-subject': 'the value "admin" is already used, use another one'})
Adrien Di Mascio's avatar
Adrien Di Mascio committed
84
85
86
87
88

    def test_user_editing_itself(self):
        """checking that a manager user can edit itself
        """
        user = self.user()
Sylvain Thénault's avatar
Sylvain Thénault committed
89
        basegroups = [u(eid) for eid, in self.execute('CWGroup G WHERE X in_group G, X eid %(x)s', {'x': user.eid})]
90
        groupeids = [eid for eid, in self.execute('CWGroup G WHERE G name in ("managers", "users")')]
Sylvain Thénault's avatar
Sylvain Thénault committed
91
        groups = [u(eid) for eid in groupeids]
92
        req = self.request()
Sylvain Thénault's avatar
Sylvain Thénault committed
93
        eid = u(user.eid)
94
        req.form = {
Sylvain Thénault's avatar
Sylvain Thénault committed
95
            'eid': eid, '__type:'+eid: 'CWUser',
96
            '_cw_entity_fields:'+eid: 'login-subject,firstname-subject,surname-subject,in_group-subject',
Sylvain Thénault's avatar
Sylvain Thénault committed
97
98
99
100
            'login-subject:'+eid:     u(user.login),
            'surname-subject:'+eid: u'Th\xe9nault',
            'firstname-subject:'+eid:   u'Sylvain',
            'in_group-subject:'+eid:  groups,
Adrien Di Mascio's avatar
Adrien Di Mascio committed
101
            }
102
        path, params = self.expect_redirect_handle_request(req, 'edit')
103
        e = self.execute('Any X WHERE X eid %(x)s', {'x': user.eid}).get_entity(0, 0)
104
105
106
107
        self.assertEqual(e.firstname, u'Sylvain')
        self.assertEqual(e.surname, u'Th\xe9nault')
        self.assertEqual(e.login, user.login)
        self.assertEqual([g.eid for g in e.in_group], groupeids)
Adrien Di Mascio's avatar
Adrien Di Mascio committed
108
109
110

    def test_user_can_change_its_password(self):
        req = self.request()
111
112
        user = self.create_user(req, 'user')
        cnx = self.login('user')
Sylvain Thénault's avatar
Sylvain Thénault committed
113
        eid = u(user.eid)
Adrien Di Mascio's avatar
Adrien Di Mascio committed
114
        req.form = {
Sylvain Thénault's avatar
Sylvain Thénault committed
115
116
            'eid': eid, '__maineid' : eid,
            '__type:'+eid: 'CWUser',
117
            '_cw_entity_fields:'+eid: 'upassword-subject',
Sylvain Thénault's avatar
Sylvain Thénault committed
118
119
            'upassword-subject:'+eid: 'tournicoton',
            'upassword-subject-confirm:'+eid: 'tournicoton',
Adrien Di Mascio's avatar
Adrien Di Mascio committed
120
            }
121
        path, params = self.expect_redirect_handle_request(req, 'edit')
Adrien Di Mascio's avatar
Adrien Di Mascio committed
122
        cnx.commit() # commit to check we don't get late validation error for instance
123
        self.assertEqual(path, 'cwuser/user')
124
        self.assertNotIn('vid', params)
Adrien Di Mascio's avatar
Adrien Di Mascio committed
125

Sylvain Thénault's avatar
Sylvain Thénault committed
126
    def test_user_editing_itself_no_relation(self):
Adrien Di Mascio's avatar
Adrien Di Mascio committed
127
128
129
130
        """checking we can edit an entity without specifying some required
        relations (meaning no changes)
        """
        user = self.user()
Sylvain Thénault's avatar
Sylvain Thénault committed
131
        groupeids = [g.eid for g in user.in_group]
132
        req = self.request()
Sylvain Thénault's avatar
Sylvain Thénault committed
133
        eid = u(user.eid)
134
        req.form = {
Sylvain Thénault's avatar
Sylvain Thénault committed
135
136
            'eid':       eid,
            '__type:'+eid:    'CWUser',
137
            '_cw_entity_fields:'+eid: 'login-subject,firstname-subject,surname-subject',
Sylvain Thénault's avatar
Sylvain Thénault committed
138
139
140
            'login-subject:'+eid:     u(user.login),
            'firstname-subject:'+eid: u'Th\xe9nault',
            'surname-subject:'+eid:   u'Sylvain',
Adrien Di Mascio's avatar
Adrien Di Mascio committed
141
            }
142
        path, params = self.expect_redirect_handle_request(req, 'edit')
143
        e = self.execute('Any X WHERE X eid %(x)s', {'x': user.eid}).get_entity(0, 0)
144
145
146
147
148
        self.assertEqual(e.login, user.login)
        self.assertEqual(e.firstname, u'Th\xe9nault')
        self.assertEqual(e.surname, u'Sylvain')
        self.assertEqual([g.eid for g in e.in_group], groupeids)
        self.assertEqual(e.cw_adapt_to('IWorkflowable').state, 'activated')
149
150


Adrien Di Mascio's avatar
Adrien Di Mascio committed
151
    def test_create_multiple_linked(self):
152
        gueid = self.execute('CWGroup G WHERE G name "users"')[0][0]
153
        req = self.request()
Sylvain Thénault's avatar
Sylvain Thénault committed
154
        req.form = {'eid': ['X', 'Y'], '__maineid' : 'X',
155

156
                    '__type:X': 'CWUser',
157
                    '_cw_entity_fields:X': 'login-subject,upassword-subject,surname-subject,in_group-subject',
Sylvain Thénault's avatar
Sylvain Thénault committed
158
159
160
161
                    'login-subject:X': u'adim',
                    'upassword-subject:X': u'toto', 'upassword-subject-confirm:X': u'toto',
                    'surname-subject:X': u'Di Mascio',
                    'in_group-subject:X': u(gueid),
162

163
                    '__type:Y': 'EmailAddress',
164
                    '_cw_entity_fields:Y': 'address-subject,use_email-object',
Sylvain Thénault's avatar
Sylvain Thénault committed
165
166
                    'address-subject:Y': u'dima@logilab.fr',
                    'use_email-object:Y': 'X',
167
                    }
168
        path, params = self.expect_redirect_handle_request(req, 'edit')
Adrien Di Mascio's avatar
Adrien Di Mascio committed
169
        # should be redirected on the created person
170
        self.assertEqual(path, 'cwuser/adim')
Adrien Di Mascio's avatar
Adrien Di Mascio committed
171
        e = self.execute('Any P WHERE P surname "Di Mascio"').get_entity(0, 0)
172
        self.assertEqual(e.surname, 'Di Mascio')
Adrien Di Mascio's avatar
Adrien Di Mascio committed
173
        email = e.use_email[0]
174
        self.assertEqual(email.address, 'dima@logilab.fr')
175

176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
    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
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
        # 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
218
219
220
221
222
223
        # 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)

224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
    def test_edit_mandatory_inlined3_object(self):
        # non regression test for #3120495. Without the fix, leads to
        # "unhashable type: 'list'" error
        req = self.request()
        cwrelation = u(req.execute('CWEType X WHERE X name "CWSource"')[0][0])
        req.form = {'eid': [cwrelation], '__maineid' : cwrelation,

                    '__type:'+cwrelation: 'CWEType',
                    '_cw_entity_fields:'+cwrelation: 'to_entity-object',
                    'to_entity-object:'+cwrelation: [9999, 9998],
                    }
        with self.session.deny_all_hooks_but():
            path, params = self.expect_redirect_handle_request(req, 'edit')
        self.assertTrue(path.startswith('cwetype/CWSource'), path)

Adrien Di Mascio's avatar
Adrien Di Mascio committed
239
    def test_edit_multiple_linked(self):
240
        req = self.request()
241
        peid = u(self.create_user(req, 'adim').eid)
Sylvain Thénault's avatar
Sylvain Thénault committed
242
        req.form = {'eid': [peid, 'Y'], '__maineid': peid,
243

Sylvain Thénault's avatar
Sylvain Thénault committed
244
                    '__type:'+peid: u'CWUser',
245
                    '_cw_entity_fields:'+peid: u'surname-subject',
Sylvain Thénault's avatar
Sylvain Thénault committed
246
                    'surname-subject:'+peid: u'Di Masci',
247

Sylvain Thénault's avatar
Sylvain Thénault committed
248
                    '__type:Y': u'EmailAddress',
249
                    '_cw_entity_fields:Y': u'address-subject,use_email-object',
Sylvain Thénault's avatar
Sylvain Thénault committed
250
251
                    'address-subject:Y': u'dima@logilab.fr',
                    'use_email-object:Y': peid,
252
                    }
253
        path, params = self.expect_redirect_handle_request(req, 'edit')
Adrien Di Mascio's avatar
Adrien Di Mascio committed
254
        # should be redirected on the created person
255
        self.assertEqual(path, 'cwuser/adim')
Sylvain Thénault's avatar
Sylvain Thénault committed
256
        e = self.execute('Any P WHERE P surname "Di Masci"').get_entity(0, 0)
Adrien Di Mascio's avatar
Adrien Di Mascio committed
257
        email = e.use_email[0]
258
        self.assertEqual(email.address, 'dima@logilab.fr')
259

Sylvain Thénault's avatar
Sylvain Thénault committed
260
        emaileid = u(email.eid)
261
        req = self.request()
Sylvain Thénault's avatar
Sylvain Thénault committed
262
263
264
        req.form = {'eid': [peid, emaileid],

                    '__type:'+peid: u'CWUser',
265
                    '_cw_entity_fields:'+peid: u'surname-subject',
Sylvain Thénault's avatar
Sylvain Thénault committed
266
267
268
                    'surname-subject:'+peid: u'Di Masci',

                    '__type:'+emaileid: u'EmailAddress',
269
                    '_cw_entity_fields:'+emaileid: u'address-subject,use_email-object',
Sylvain Thénault's avatar
Sylvain Thénault committed
270
271
272
                    'address-subject:'+emaileid: u'adim@logilab.fr',
                    'use_email-object:'+emaileid: peid,
                    }
273
        path, params = self.expect_redirect_handle_request(req, 'edit')
274
        email.cw_clear_all_caches()
275
        self.assertEqual(email.address, 'adim@logilab.fr')
Adrien Di Mascio's avatar
Adrien Di Mascio committed
276

277

Adrien Di Mascio's avatar
Adrien Di Mascio committed
278
279
    def test_password_confirm(self):
        """test creation of two linked entities
280
        """
Adrien Di Mascio's avatar
Adrien Di Mascio committed
281
        user = self.user()
282
        req = self.request()
Sylvain Thénault's avatar
Sylvain Thénault committed
283
284
        req.form = {'eid': 'X',
                    '__cloned_eid:X': u(user.eid), '__type:X': 'CWUser',
285
                    '_cw_entity_fields:X': 'login-subject,upassword-subject',
Sylvain Thénault's avatar
Sylvain Thénault committed
286
287
                    'login-subject:X': u'toto',
                    'upassword-subject:X': u'toto',
288
                    }
289
290
291
        with self.assertRaises(ValidationError) as cm:
            self.ctrl_publish(req)
        self.assertEqual(cm.exception.errors, {'upassword-subject': u'password and confirmation don\'t match'})
292
        req = self.request()
Sylvain Thénault's avatar
Sylvain Thénault committed
293
        req.form = {'__cloned_eid:X': u(user.eid),
294
                    'eid': 'X', '__type:X': 'CWUser',
295
                    '_cw_entity_fields:X': 'login-subject,upassword-subject',
Sylvain Thénault's avatar
Sylvain Thénault committed
296
297
298
                    'login-subject:X': u'toto',
                    'upassword-subject:X': u'toto',
                    'upassword-subject-confirm:X': u'tutu',
299
                    }
300
301
302
        with self.assertRaises(ValidationError) as cm:
            self.ctrl_publish(req)
        self.assertEqual(cm.exception.errors, {'upassword-subject': u'password and confirmation don\'t match'})
Adrien Di Mascio's avatar
Adrien Di Mascio committed
303
304
305


    def test_interval_bound_constraint_success(self):
Sylvain Thénault's avatar
Sylvain Thénault committed
306
        feid = self.execute('INSERT File X: X data_name "toto.txt", X data %(data)s',
Adrien Di Mascio's avatar
Adrien Di Mascio committed
307
                            {'data': Binary('yo')})[0][0]
308
        self.commit()
309
        req = self.request(rollbackfirst=True)
310
311
        req.form = {'eid': ['X'],
                    '__type:X': 'Salesterm',
312
                    '_cw_entity_fields:X': 'amount-subject,described_by_test-subject',
Sylvain Thénault's avatar
Sylvain Thénault committed
313
314
                    'amount-subject:X': u'-10',
                    'described_by_test-subject:X': u(feid),
315
                }
316
317
        with self.assertRaises(ValidationError) as cm:
            self.ctrl_publish(req)
318
        cm.exception.translate(unicode)
319
        self.assertEqual(cm.exception.errors, {'amount-subject': 'value -10 must be >= 0'})
320
        req = self.request(rollbackfirst=True)
321
322
        req.form = {'eid': ['X'],
                    '__type:X': 'Salesterm',
323
                    '_cw_entity_fields:X': 'amount-subject,described_by_test-subject',
Sylvain Thénault's avatar
Sylvain Thénault committed
324
325
                    'amount-subject:X': u'110',
                    'described_by_test-subject:X': u(feid),
326
                    }
327
328
        with self.assertRaises(ValidationError) as cm:
            self.ctrl_publish(req)
329
        cm.exception.translate(unicode)
330
        self.assertEqual(cm.exception.errors, {'amount-subject': 'value 110 must be <= 100'})
331

332
        req = self.request(rollbackfirst=True)
333
334
        req.form = {'eid': ['X'],
                    '__type:X': 'Salesterm',
335
                    '_cw_entity_fields:X': 'amount-subject,described_by_test-subject',
Sylvain Thénault's avatar
Sylvain Thénault committed
336
337
                    'amount-subject:X': u'10',
                    'described_by_test-subject:X': u(feid),
338
                    }
339
        self.expect_redirect_handle_request(req, 'edit')
340
        # should be redirected on the created
Adrien Di Mascio's avatar
Adrien Di Mascio committed
341
342
        #eid = params['rql'].split()[-1]
        e = self.execute('Salesterm X').get_entity(0, 0)
343
        self.assertEqual(e.amount, 10)
Adrien Di Mascio's avatar
Adrien Di Mascio committed
344

345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
    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'))

Adrien Di Mascio's avatar
Adrien Di Mascio committed
406
407
    def test_req_pending_insert(self):
        """make sure req's pending insertions are taken into account"""
Sylvain Thénault's avatar
Sylvain Thénault committed
408
        tmpgroup = self.request().create_entity('CWGroup', name=u"test")
Adrien Di Mascio's avatar
Adrien Di Mascio committed
409
        user = self.user()
Sylvain Thénault's avatar
Sylvain Thénault committed
410
        req = self.request(**req_form(user))
411
        req.session.data['pending_insert'] = set([(user.eid, 'in_group', tmpgroup.eid)])
412
        path, params = self.expect_redirect_handle_request(req, 'edit')
Adrien Di Mascio's avatar
Adrien Di Mascio committed
413
414
        usergroups = [gname for gname, in
                      self.execute('Any N WHERE G name N, U in_group G, U eid %(u)s', {'u': user.eid})]
415
        self.assertCountEqual(usergroups, ['managers', 'test'])
416
        self.assertEqual(get_pending_inserts(req), [])
Adrien Di Mascio's avatar
Adrien Di Mascio committed
417
418
419
420

    def test_req_pending_delete(self):
        """make sure req's pending deletions are taken into account"""
        user = self.user()
421
        groupeid = self.execute('INSERT CWGroup G: G name "test", U in_group G WHERE U eid %(x)s',
Adrien Di Mascio's avatar
Adrien Di Mascio committed
422
423
424
425
                                {'x': user.eid})[0][0]
        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
426
        self.assertCountEqual(usergroups, ['managers', 'test'])
Adrien Di Mascio's avatar
Adrien Di Mascio committed
427
        # now try to delete the relation
Sylvain Thénault's avatar
Sylvain Thénault committed
428
        req = self.request(**req_form(user))
429
        req.session.data['pending_delete'] = set([(user.eid, 'in_group', groupeid)])
430
        path, params = self.expect_redirect_handle_request(req, 'edit')
Adrien Di Mascio's avatar
Adrien Di Mascio committed
431
432
        usergroups = [gname for gname, in
                      self.execute('Any N WHERE G name N, U in_group G, U eid %(u)s', {'u': user.eid})]
433
        self.assertCountEqual(usergroups, ['managers'])
434
        self.assertEqual(get_pending_deletes(req), [])
Adrien Di Mascio's avatar
Adrien Di Mascio committed
435
436
437

    def test_redirect_apply_button(self):
        redirectrql = rql_for_eid(4012) # whatever
438
439
        req = self.request()
        req.form = {
Sylvain Thénault's avatar
Sylvain Thénault committed
440
            'eid': 'A', '__maineid' : 'A',
441
            '__type:A': 'BlogEntry', '_cw_entity_fields:A': 'content-subject,title-subject',
Sylvain Thénault's avatar
Sylvain Thénault committed
442
443
            'content-subject:A': u'"13:03:43"',
            'title-subject:A': u'huuu',
444
445
446
447
448
449
            '__redirectrql': redirectrql,
            '__redirectvid': 'primary',
            '__redirectparams': 'toto=tutu&tata=titi',
            '__form_id': 'edition',
            '__action_apply': '',
            }
450
        path, params = self.expect_redirect_handle_request(req, 'edit')
451
        self.assertTrue(path.startswith('blogentry/'))
Adrien Di Mascio's avatar
Adrien Di Mascio committed
452
        eid = path.split('/')[1]
453
        self.assertEqual(params['vid'], 'edition')
454
        self.assertNotEqual(int(eid), 4012)
455
456
457
        self.assertEqual(params['__redirectrql'], redirectrql)
        self.assertEqual(params['__redirectvid'], 'primary')
        self.assertEqual(params['__redirectparams'], 'toto=tutu&tata=titi')
Adrien Di Mascio's avatar
Adrien Di Mascio committed
458
459
460

    def test_redirect_ok_button(self):
        redirectrql = rql_for_eid(4012) # whatever
461
462
        req = self.request()
        req.form = {
Sylvain Thénault's avatar
Sylvain Thénault committed
463
            'eid': 'A', '__maineid' : 'A',
464
            '__type:A': 'BlogEntry', '_cw_entity_fields:A': 'content-subject,title-subject',
Sylvain Thénault's avatar
Sylvain Thénault committed
465
466
            'content-subject:A': u'"13:03:43"',
            'title-subject:A': u'huuu',
467
468
469
470
471
            '__redirectrql': redirectrql,
            '__redirectvid': 'primary',
            '__redirectparams': 'toto=tutu&tata=titi',
            '__form_id': 'edition',
            }
472
        path, params = self.expect_redirect_handle_request(req, 'edit')
473
474
475
476
477
        self.assertEqual(path, 'view')
        self.assertEqual(params['rql'], redirectrql)
        self.assertEqual(params['vid'], 'primary')
        self.assertEqual(params['tata'], 'titi')
        self.assertEqual(params['toto'], 'tutu')
Adrien Di Mascio's avatar
Adrien Di Mascio committed
478
479

    def test_redirect_delete_button(self):
480
        req = self.request()
Sylvain Thénault's avatar
Sylvain Thénault committed
481
        eid = req.create_entity('BlogEntry', title=u'hop', content=u'hop').eid
Sylvain Thénault's avatar
Sylvain Thénault committed
482
        req.form = {'eid': u(eid), '__type:%s'%eid: 'BlogEntry',
483
                    '__action_delete': ''}
484
        path, params = self.expect_redirect_handle_request(req, 'edit')
485
        self.assertEqual(path, 'blogentry')
Sylvain Thénault's avatar
Sylvain Thénault committed
486
        self.assertIn('_cwmsgid', params)
Sylvain Thénault's avatar
Sylvain Thénault committed
487
        eid = req.create_entity('EmailAddress', address=u'hop@logilab.fr').eid
Adrien Di Mascio's avatar
Adrien Di Mascio committed
488
        self.execute('SET X use_email E WHERE E eid %(e)s, X eid %(x)s',
489
                     {'x': self.session.user.eid, 'e': eid})
Adrien Di Mascio's avatar
Adrien Di Mascio committed
490
        self.commit()
Sylvain Thénault's avatar
Sylvain Thénault committed
491
        req = req
Sylvain Thénault's avatar
Sylvain Thénault committed
492
        req.form = {'eid': u(eid), '__type:%s'%eid: 'EmailAddress',
493
                    '__action_delete': ''}
494
        path, params = self.expect_redirect_handle_request(req, 'edit')
495
        self.assertEqual(path, 'cwuser/admin')
Sylvain Thénault's avatar
Sylvain Thénault committed
496
        self.assertIn('_cwmsgid', params)
Sylvain Thénault's avatar
Sylvain Thénault committed
497
498
        eid1 = req.create_entity('BlogEntry', title=u'hop', content=u'hop').eid
        eid2 = req.create_entity('EmailAddress', address=u'hop@logilab.fr').eid
499
        req = self.request()
Sylvain Thénault's avatar
Sylvain Thénault committed
500
        req.form = {'eid': [u(eid1), u(eid2)],
501
502
503
                    '__type:%s'%eid1: 'BlogEntry',
                    '__type:%s'%eid2: 'EmailAddress',
                    '__action_delete': ''}
504
        path, params = self.expect_redirect_handle_request(req, 'edit')
505
        self.assertEqual(path, 'view')
Sylvain Thénault's avatar
Sylvain Thénault committed
506
        self.assertIn('_cwmsgid', params)
Adrien Di Mascio's avatar
Adrien Di Mascio committed
507

508
509
510
511
512
513
514
515
516
517
518
519
    def test_simple_copy(self):
        req = self.request()
        blog = req.create_entity('Blog', title=u'my-blog')
        blogentry = req.create_entity('BlogEntry', title=u'entry1',
                                      content=u'content1', entry_of=blog)
        req = self.request()
        req.form = {'__maineid' : 'X', 'eid': 'X',
                    '__cloned_eid:X': blogentry.eid, '__type:X': 'BlogEntry',
                    '_cw_entity_fields:X': 'title-subject,content-subject',
                    'title-subject:X': u'entry1-copy',
                    'content-subject:X': u'content1',
                    }
520
        self.expect_redirect_handle_request(req, 'edit')
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
        blogentry2 = req.find_one_entity('BlogEntry', title=u'entry1-copy')
        self.assertEqual(blogentry2.entry_of[0].eid, blog.eid)

    def test_skip_copy_for(self):
        req = self.request()
        blog = req.create_entity('Blog', title=u'my-blog')
        blogentry = req.create_entity('BlogEntry', title=u'entry1',
                                      content=u'content1', entry_of=blog)
        blogentry.__class__.cw_skip_copy_for = [('entry_of', 'subject')]
        try:
            req = self.request()
            req.form = {'__maineid' : 'X', 'eid': 'X',
                        '__cloned_eid:X': blogentry.eid, '__type:X': 'BlogEntry',
                        '_cw_entity_fields:X': 'title-subject,content-subject',
                        'title-subject:X': u'entry1-copy',
                        'content-subject:X': u'content1',
                        }
538
            self.expect_redirect_handle_request(req, 'edit')
539
540
541
542
543
544
            blogentry2 = req.find_one_entity('BlogEntry', title=u'entry1-copy')
            # entry_of should not be copied
            self.assertEqual(len(blogentry2.entry_of), 0)
        finally:
            blogentry.__class__.cw_skip_copy_for = []

Adrien Di Mascio's avatar
Adrien Di Mascio committed
545
    def test_nonregr_eetype_etype_editing(self):
546
        """non-regression test checking that a manager user can edit a CWEType entity
Adrien Di Mascio's avatar
Adrien Di Mascio committed
547
        """
548
        groupeids = sorted(eid for eid, in self.execute('CWGroup G WHERE G name in ("managers", "users")'))
Sylvain Thénault's avatar
Sylvain Thénault committed
549
550
551
552
        groups = [u(eid) for eid in groupeids]
        cwetypeeid = self.execute('CWEType X WHERE X name "CWEType"')[0][0]
        basegroups = [u(eid) for eid, in self.execute('CWGroup G WHERE X read_permission G, X eid %(x)s', {'x': cwetypeeid})]
        cwetypeeid = u(cwetypeeid)
553
554
        req = self.request()
        req.form = {
Sylvain Thénault's avatar
Sylvain Thénault committed
555
556
            'eid':      cwetypeeid,
            '__type:'+cwetypeeid:  'CWEType',
557
            '_cw_entity_fields:'+cwetypeeid: 'name-subject,final-subject,description-subject,read_permission-subject',
Sylvain Thénault's avatar
Sylvain Thénault committed
558
            'name-subject:'+cwetypeeid:     u'CWEType',
Sylvain Thénault's avatar
Sylvain Thénault committed
559
            'final-subject:'+cwetypeeid:    '',
Sylvain Thénault's avatar
Sylvain Thénault committed
560
561
            'description-subject:'+cwetypeeid:     u'users group',
            'read_permission-subject:'+cwetypeeid:  groups,
562
            }
Adrien Di Mascio's avatar
Adrien Di Mascio committed
563
        try:
564
            path, params = self.expect_redirect_handle_request(req, 'edit')
565
            e = self.execute('Any X WHERE X eid %(x)s', {'x': cwetypeeid}).get_entity(0, 0)
566
567
            self.assertEqual(e.name, 'CWEType')
            self.assertEqual(sorted(g.eid for g in e.read_permission), groupeids)
Adrien Di Mascio's avatar
Adrien Di Mascio committed
568
569
        finally:
            # restore
570
            self.execute('SET X read_permission Y WHERE X name "CWEType", Y eid IN (%s), NOT X read_permission Y' % (','.join(basegroups)))
Adrien Di Mascio's avatar
Adrien Di Mascio committed
571
            self.commit()
572

Adrien Di Mascio's avatar
Adrien Di Mascio committed
573
574
575
576
    def test_nonregr_strange_text_input(self):
        """non-regression test checking text input containing "13:03:43"

        this seems to be postgres (tsearch?) specific
577
        """
578
579
        req = self.request()
        req.form = {
Sylvain Thénault's avatar
Sylvain Thénault committed
580
            'eid': 'A', '__maineid' : 'A',
581
            '__type:A': 'BlogEntry', '_cw_entity_fields:A': 'title-subject,content-subject',
Sylvain Thénault's avatar
Sylvain Thénault committed
582
583
            'title-subject:A': u'"13:03:40"',
            'content-subject:A': u'"13:03:43"',}
584
        path, params = self.expect_redirect_handle_request(req, 'edit')
585
        self.assertTrue(path.startswith('blogentry/'))
Adrien Di Mascio's avatar
Adrien Di Mascio committed
586
        eid = path.split('/')[1]
587
        e = self.execute('Any C, T WHERE C eid %(x)s, C content T', {'x': eid}).get_entity(0, 0)
588
589
        self.assertEqual(e.title, '"13:03:40"')
        self.assertEqual(e.content, '"13:03:43"')
Adrien Di Mascio's avatar
Adrien Di Mascio committed
590
591
592


    def test_nonregr_multiple_empty_email_addr(self):
593
        gueid = self.execute('CWGroup G WHERE G name "users"')[0][0]
594
595
        req = self.request()
        req.form = {'eid': ['X', 'Y'],
596

597
                    '__type:X': 'CWUser',
598
                    '_cw_entity_fields:X': 'login-subject,upassword-subject,in_group-subject',
Sylvain Thénault's avatar
Sylvain Thénault committed
599
600
601
                    'login-subject:X': u'adim',
                    'upassword-subject:X': u'toto', 'upassword-subject-confirm:X': u'toto',
                    'in_group-subject:X': `gueid`,
602

603
                    '__type:Y': 'EmailAddress',
604
                    '_cw_entity_fields:Y': 'address-subject,alias-subject,use_email-object',
Sylvain Thénault's avatar
Sylvain Thénault committed
605
606
607
                    'address-subject:Y': u'',
                    'alias-subject:Y': u'',
                    'use_email-object:Y': 'X',
608
                    }
609
610
611
        with self.assertRaises(ValidationError) as cm:
            self.ctrl_publish(req)
        self.assertEqual(cm.exception.errors, {'address-subject': u'required field'})
Adrien Di Mascio's avatar
Adrien Di Mascio committed
612
613
614

    def test_nonregr_copy(self):
        user = self.user()
615
        req = self.request()
Sylvain Thénault's avatar
Sylvain Thénault committed
616
617
        req.form = {'__maineid' : 'X', 'eid': 'X',
                    '__cloned_eid:X': user.eid, '__type:X': 'CWUser',
618
                    '_cw_entity_fields:X': 'login-subject,upassword-subject',
Sylvain Thénault's avatar
Sylvain Thénault committed
619
620
                    'login-subject:X': u'toto',
                    'upassword-subject:X': u'toto', 'upassword-subject-confirm:X': u'toto',
621
                    }
622
        path, params = self.expect_redirect_handle_request(req, 'edit')
623
        self.assertEqual(path, 'cwuser/toto')
624
        e = self.execute('Any X WHERE X is CWUser, X login "toto"').get_entity(0, 0)
625
626
        self.assertEqual(e.login, 'toto')
        self.assertEqual(e.in_group[0].name, 'managers')
Adrien Di Mascio's avatar
Adrien Di Mascio committed
627
628
629


    def test_nonregr_rollback_on_validation_error(self):
630
631
        req = self.request()
        p = self.create_user(req, "doe")
Adrien Di Mascio's avatar
Adrien Di Mascio committed
632
633
634
635
        # do not try to skip 'primary_email' for this test
        old_skips = p.__class__.skip_copy_for
        p.__class__.skip_copy_for = ()
        try:
Sylvain Thénault's avatar
Sylvain Thénault committed
636
            e = self.request().create_entity('EmailAddress', address=u'doe@doe.com')
Adrien Di Mascio's avatar
Adrien Di Mascio committed
637
638
            self.execute('SET P use_email E, P primary_email E WHERE P eid %(p)s, E eid %(e)s',
                         {'p' : p.eid, 'e' : e.eid})
639
            req = self.request()
Sylvain Thénault's avatar
Sylvain Thénault committed
640
641
            req.form = {'eid': 'X',
                        '__cloned_eid:X': p.eid, '__type:X': 'CWUser',
642
                        '_cw_entity_fields:X': 'login-subject,surname-subject',
Sylvain Thénault's avatar
Sylvain Thénault committed
643
644
                        'login-subject': u'dodo',
                        'surname-subject:X': u'Boom',
645
                        '__errorurl' : "whatever but required",
Sylvain Thénault's avatar
Sylvain Thénault committed
646
                        }
Adrien Di Mascio's avatar
Adrien Di Mascio committed
647
648
649
650
651
            # try to emulate what really happens in the web application
            # 1/ validate form => EditController.publish raises a ValidationError
            #    which fires a Redirect
            # 2/ When re-publishing the copy form, the publisher implicitly commits
            try:
652
                self.app_handle_request(req, 'edit')
Adrien Di Mascio's avatar
Adrien Di Mascio committed
653
            except Redirect:
Sylvain Thénault's avatar
Sylvain Thénault committed
654
                req = self.request()
655
656
                req.form['rql'] = 'Any X WHERE X eid %s' % p.eid
                req.form['vid'] = 'copy'
657
                self.app_handle_request(req, 'view')
658
            rset = self.execute('CWUser P WHERE P surname "Boom"')
659
            self.assertEqual(len(rset), 0)
Adrien Di Mascio's avatar
Adrien Di Mascio committed
660
661
662
        finally:
            p.__class__.skip_copy_for = old_skips

663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
    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

Adrien Di Mascio's avatar
Adrien Di Mascio committed
691

692
class ReportBugControllerTC(CubicWebTC):
Adrien Di Mascio's avatar
Adrien Di Mascio committed
693

694
    def test_usable_by_guest(self):
695
        self.login('anon')
Sylvain Thénault's avatar
Sylvain Thénault committed
696
697
698
        self.assertRaises(NoSelectableObject,
                          self.vreg['controllers'].select, 'reportbug', self.request())
        self.vreg['controllers'].select('reportbug', self.request(description='hop'))
Adrien Di Mascio's avatar
Adrien Di Mascio committed
699
700


701
702
class AjaxControllerTC(CubicWebTC):
    tested_controller = 'ajax'
Adrien Di Mascio's avatar
Adrien Di Mascio committed
703
704
705

    def ctrl(self, req=None):
        req = req or self.request(url='http://whatever.fr/')
706
        return self.vreg['controllers'].select(self.tested_controller, req)
Adrien Di Mascio's avatar
Adrien Di Mascio committed
707
708

    def setup_database(self):
Sylvain Thénault's avatar
Sylvain Thénault committed
709
710
711
        req = self.request()
        self.pytag = req.create_entity('Tag', name=u'python')
        self.cubicwebtag = req.create_entity('Tag', name=u'cubicweb')
712
        self.john = self.create_user(req, u'John')
Adrien Di Mascio's avatar
Adrien Di Mascio committed
713
714
715
716


    ## tests ##################################################################
    def test_simple_exec(self):
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
717
718
719
720
        req = self.request(rql='CWUser P WHERE P login "John"',
                           pageid='123', fname='view')
        ctrl = self.ctrl(req)
        rset = self.john.as_rset()
721
        rset.req = req
722
        source = ctrl.publish()
723
        self.assertTrue(source.startswith('<div>'))
Adrien Di Mascio's avatar
Adrien Di Mascio committed
724

sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
725
726
727
#     def test_json_exec(self):
#         rql = 'Any T,N WHERE T is Tag, T name N'
#         ctrl = self.ctrl(self.request(mode='json', rql=rql, pageid='123'))
728
#         self.assertEqual(ctrl.publish(),
729
#                           json_dumps(self.execute(rql).rows))
Adrien Di Mascio's avatar
Adrien Di Mascio committed
730
731
732

    def test_remote_add_existing_tag(self):
        self.remote_call('tag_entity', self.john.eid, ['python'])
733
        self.assertCountEqual(
734
735
            [tname for tname, in self.execute('Any N WHERE T is Tag, T name N')],
            ['python', 'cubicweb'])
736
        self.assertEqual(
737
738
            self.execute('Any N WHERE T tags P, P is CWUser, T name N').rows,
            [['python']])
739

Adrien Di Mascio's avatar
Adrien Di Mascio committed
740
741
    def test_remote_add_new_tag(self):
        self.remote_call('tag_entity', self.john.eid, ['javascript'])
742
        self.assertCountEqual(
743
744
            [tname for tname, in self.execute('Any N WHERE T is Tag, T name N')],
            ['python', 'cubicweb', 'javascript'])
745
        self.assertEqual(
746
747
            self.execute('Any N WHERE T tags P, P is CWUser, T name N').rows,
            [['javascript']])
Adrien Di Mascio's avatar
Adrien Di Mascio committed
748
749

    def test_pending_insertion(self):
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
750
        res, req = self.remote_call('add_pending_inserts', [['12', 'tags', '13']])
Sylvain Thénault's avatar
Sylvain Thénault committed
751
        deletes = get_pending_deletes(req)
752
        self.assertEqual(deletes, [])
Sylvain Thénault's avatar
Sylvain Thénault committed
753
        inserts = get_pending_inserts(req)
754
        self.assertEqual(inserts, ['12:tags:13'])
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
755
        res, req = self.remote_call('add_pending_inserts', [['12', 'tags', '14']])
Sylvain Thénault's avatar
Sylvain Thénault committed
756
        deletes = get_pending_deletes(req)
757
        self.assertEqual(deletes, [])
Sylvain Thénault's avatar
Sylvain Thénault committed
758
        inserts = get_pending_inserts(req)
759
        self.assertEqual(inserts, ['12:tags:13', '12:tags:14'])
Sylvain Thénault's avatar
Sylvain Thénault committed
760
        inserts = get_pending_inserts(req, 12)
761
        self.assertEqual(inserts, ['12:tags:13', '12:tags:14'])
Sylvain Thénault's avatar
Sylvain Thénault committed
762
        inserts = get_pending_inserts(req, 13)
763
        self.assertEqual(inserts, ['12:tags:13'])
Sylvain Thénault's avatar
Sylvain Thénault committed
764
        inserts = get_pending_inserts(req, 14)
765
        self.assertEqual(inserts, ['12:tags:14'])
Adrien Di Mascio's avatar
Adrien Di Mascio committed
766
767
768
769
        req.remove_pending_operations()

    def test_pending_deletion(self):
        res, req = self.remote_call('add_pending_delete', ['12', 'tags', '13'])
Sylvain Thénault's avatar
Sylvain Thénault committed
770
        inserts = get_pending_inserts(req)
771
        self.assertEqual(inserts, [])
Sylvain Thénault's avatar
Sylvain Thénault committed
772
        deletes = get_pending_deletes(req)
773
        self.assertEqual(deletes, ['12:tags:13'])
Adrien Di Mascio's avatar
Adrien Di Mascio committed
774
        res, req = self.remote_call('add_pending_delete', ['12', 'tags', '14'])
Sylvain Thénault's avatar
Sylvain Thénault committed
775
        inserts = get_pending_inserts(req)
776
        self.assertEqual(inserts, [])
Sylvain Thénault's avatar
Sylvain Thénault committed
777
        deletes = get_pending_deletes(req)
778
        self.assertEqual(deletes, ['12:tags:13', '12:tags:14'])
Sylvain Thénault's avatar
Sylvain Thénault committed
779
        deletes = get_pending_deletes(req, 12)
780
        self.assertEqual(deletes, ['12:tags:13', '12:tags:14'])
Sylvain Thénault's avatar
Sylvain Thénault committed
781
        deletes = get_pending_deletes(req, 13)
782
        self.assertEqual(deletes, ['12:tags:13'])
Sylvain Thénault's avatar
Sylvain Thénault committed
783
        deletes = get_pending_deletes(req, 14)
784
        self.assertEqual(deletes, ['12:tags:14'])
Adrien Di Mascio's avatar
Adrien Di Mascio committed
785
786
787
788
        req.remove_pending_operations()

    def test_remove_pending_operations(self):
        self.remote_call('add_pending_delete', ['12', 'tags', '13'])
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
789
        _, req = self.remote_call('add_pending_inserts', [['12', 'tags', '14']])
Sylvain Thénault's avatar
Sylvain Thénault committed
790
        inserts = get_pending_inserts(req)
791
        self.assertEqual(inserts, ['12:tags:14'])
Sylvain Thénault's avatar
Sylvain Thénault committed
792
        deletes = get_pending_deletes(req)
793
        self.assertEqual(deletes, ['12:tags:13'])
Adrien Di Mascio's avatar
Adrien Di Mascio committed
794
        req.remove_pending_operations()
795
796
        self.assertEqual(get_pending_deletes(req), [])
        self.assertEqual(get_pending_inserts(req), [])
797

Adrien Di Mascio's avatar
Adrien Di Mascio committed
798
799
800
801

    def test_add_inserts(self):
        res, req = self.remote_call('add_pending_inserts',
                                    [('12', 'tags', '13'), ('12', 'tags', '14')])
Sylvain Thénault's avatar
Sylvain Thénault committed
802
        inserts = get_pending_inserts(req)
803
        self.assertEqual(inserts, ['12:tags:13', '12:tags:14'])
Adrien Di Mascio's avatar
Adrien Di Mascio committed
804
        req.remove_pending_operations()
805

Adrien Di Mascio's avatar
Adrien Di Mascio committed
806
807
808

    # silly tests
    def test_external_resource(self):
809
        self.assertEqual(self.remote_call('external_resource', 'RSS_LOGO')[0],
810
                          json_dumps(self.config.uiprops['RSS_LOGO']))
Adrien Di Mascio's avatar
Adrien Di Mascio committed
811
    def test_i18n(self):
812
        self.assertEqual(self.remote_call('i18n', ['bimboom'])[0],
813
                          json_dumps(['bimboom']))
Adrien Di Mascio's avatar
Adrien Di Mascio committed
814
815

    def test_format_date(self):
816
        self.assertEqual(self.remote_call('format_date', '2007-01-01 12:00:00')[0],
817
                          json_dumps('2007/01/01'))
Adrien Di Mascio's avatar
Adrien Di Mascio committed
818

819
820
821
822
    def test_ajaxfunc_noparameter(self):
        @ajaxfunc
        def foo