unittest_views_basecontrollers.py 29.6 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
# 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
19
20
"""cubicweb.web.views.basecontrollers unit tests

"""
Adrien Di Mascio's avatar
Adrien Di Mascio committed
21

22
from logilab.common.testlib import unittest_main, mock_object
Adrien Di Mascio's avatar
Adrien Di Mascio committed
23

24
25
from cubicweb import Binary, NoSelectableObject, ValidationError
from cubicweb.view import STRICT_DOCTYPE
26
from cubicweb.devtools.testlib import CubicWebTC
Sylvain Thénault's avatar
Sylvain Thénault committed
27
from cubicweb.uilib import rql_for_eid
28
from cubicweb.web import INTERNAL_FIELD_VALUE, Redirect, RequestError, json
29
from cubicweb.entities.authobjs import CWUser
Sylvain Thénault's avatar
Sylvain Thénault committed
30
from cubicweb.web.views.autoform import get_pending_inserts, get_pending_deletes
Sylvain Thénault's avatar
Sylvain Thénault committed
31
32
u = unicode

Sylvain Thénault's avatar
Sylvain Thénault committed
33
34
35
36
37
def req_form(user):
    return {'eid': [str(user.eid)],
            '_cw_edited_fields:%s' % user.eid: '_cw_generic_field',
            '__type:%s' % user.eid: user.__regid__
            }
Adrien Di Mascio's avatar
Adrien Di Mascio committed
38

39
class EditControllerTC(CubicWebTC):
Adrien Di Mascio's avatar
Adrien Di Mascio committed
40
    def setUp(self):
41
        CubicWebTC.setUp(self)
42
        self.failUnless('users' in self.schema.eschema('CWGroup').get_groups('read'))
43

Adrien Di Mascio's avatar
Adrien Di Mascio committed
44
    def tearDown(self):
45
        CubicWebTC.tearDown(self)
46
        self.failUnless('users' in self.schema.eschema('CWGroup').get_groups('read'))
47

Adrien Di Mascio's avatar
Adrien Di Mascio committed
48
49
50
    def test_noparam_edit(self):
        """check behaviour of this controller without any form parameter
        """
Sylvain Thénault's avatar
Sylvain Thénault committed
51
52
        ex = self.assertRaises(ValidationError, self.ctrl_publish, self.request())
        self.assertEquals(ex.errors, {None: u'no selected entities'})
53

Adrien Di Mascio's avatar
Adrien Di Mascio committed
54
55
    def test_validation_unique(self):
        """test creation of two linked entities
56
        """
Adrien Di Mascio's avatar
Adrien Di Mascio committed
57
        user = self.user()
58
59
        req = self.request()
        req.form = {'eid': 'X', '__type:X': 'CWUser',
Sylvain Thénault's avatar
Sylvain Thénault committed
60
61
62
63
                    '_cw_edited_fields:X': 'login-subject,upassword-subject',
                    'login-subject:X': u'admin',
                    'upassword-subject:X': u'toto',
                    'upassword-subject-confirm:X': u'toto',
64
                    }
Sylvain Thénault's avatar
Sylvain Thénault committed
65
        ex = self.assertRaises(ValidationError, self.ctrl_publish, req)
66
        self.assertEquals(ex.errors, {'login-subject': 'the value "admin" is already used, use another one'})
Adrien Di Mascio's avatar
Adrien Di Mascio committed
67
68
69
70
71

    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
72
        basegroups = [u(eid) for eid, in self.execute('CWGroup G WHERE X in_group G, X eid %(x)s', {'x': user.eid})]
73
        groupeids = [eid for eid, in self.execute('CWGroup G WHERE G name in ("managers", "users")')]
Sylvain Thénault's avatar
Sylvain Thénault committed
74
        groups = [u(eid) for eid in groupeids]
75
        req = self.request()
Sylvain Thénault's avatar
Sylvain Thénault committed
76
        eid = u(user.eid)
77
        req.form = {
Sylvain Thénault's avatar
Sylvain Thénault committed
78
79
80
81
82
83
            'eid': eid, '__type:'+eid: 'CWUser',
            '_cw_edited_fields:'+eid: 'login-subject,firstname-subject,surname-subject,in_group-subject',
            '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
84
            }
Sylvain Thénault's avatar
Sylvain Thénault committed
85
        path, params = self.expect_redirect_publish(req, 'edit')
86
        e = self.execute('Any X WHERE X eid %(x)s', {'x': user.eid}).get_entity(0, 0)
Sylvain Thénault's avatar
Sylvain Thénault committed
87
88
        self.assertEquals(e.firstname, u'Sylvain')
        self.assertEquals(e.surname, u'Th\xe9nault')
Adrien Di Mascio's avatar
Adrien Di Mascio committed
89
90
91
92
93
94
95
        self.assertEquals(e.login, user.login)
        self.assertEquals([g.eid for g in e.in_group], groupeids)

    def test_user_can_change_its_password(self):
        user = self.create_user('user')
        cnx = self.login('user')
        req = self.request()
Sylvain Thénault's avatar
Sylvain Thénault committed
96
        eid = u(user.eid)
Adrien Di Mascio's avatar
Adrien Di Mascio committed
97
        req.form = {
Sylvain Thénault's avatar
Sylvain Thénault committed
98
99
100
101
102
            'eid': eid, '__maineid' : eid,
            '__type:'+eid: 'CWUser',
            '_cw_edited_fields:'+eid: 'upassword-subject',
            'upassword-subject:'+eid: 'tournicoton',
            'upassword-subject-confirm:'+eid: 'tournicoton',
Adrien Di Mascio's avatar
Adrien Di Mascio committed
103
            }
Sylvain Thénault's avatar
Sylvain Thénault committed
104
        path, params = self.expect_redirect_publish(req, 'edit')
Adrien Di Mascio's avatar
Adrien Di Mascio committed
105
        cnx.commit() # commit to check we don't get late validation error for instance
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
106
        self.assertEquals(path, 'cwuser/user')
Adrien Di Mascio's avatar
Adrien Di Mascio committed
107
108
        self.failIf('vid' in params)

Sylvain Thénault's avatar
Sylvain Thénault committed
109
    def test_user_editing_itself_no_relation(self):
Adrien Di Mascio's avatar
Adrien Di Mascio committed
110
111
112
113
        """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
114
        groupeids = [g.eid for g in user.in_group]
115
        req = self.request()
Sylvain Thénault's avatar
Sylvain Thénault committed
116
        eid = u(user.eid)
117
        req.form = {
Sylvain Thénault's avatar
Sylvain Thénault committed
118
119
120
121
122
123
            'eid':       eid,
            '__type:'+eid:    'CWUser',
            '_cw_edited_fields:'+eid: 'login-subject,firstname-subject,surname-subject',
            '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
124
            }
Sylvain Thénault's avatar
Sylvain Thénault committed
125
        path, params = self.expect_redirect_publish(req, 'edit')
126
        e = self.execute('Any X WHERE X eid %(x)s', {'x': user.eid}).get_entity(0, 0)
Adrien Di Mascio's avatar
Adrien Di Mascio committed
127
128
129
130
        self.assertEquals(e.login, user.login)
        self.assertEquals(e.firstname, u'Th\xe9nault')
        self.assertEquals(e.surname, u'Sylvain')
        self.assertEquals([g.eid for g in e.in_group], groupeids)
131
        self.assertEquals(e.cw_adapt_to('IWorkflowable').state, 'activated')
132
133


Adrien Di Mascio's avatar
Adrien Di Mascio committed
134
    def test_create_multiple_linked(self):
135
        gueid = self.execute('CWGroup G WHERE G name "users"')[0][0]
136
        req = self.request()
Sylvain Thénault's avatar
Sylvain Thénault committed
137
        req.form = {'eid': ['X', 'Y'], '__maineid' : 'X',
138

139
                    '__type:X': 'CWUser',
Sylvain Thénault's avatar
Sylvain Thénault committed
140
141
142
143
144
                    '_cw_edited_fields:X': 'login-subject,upassword-subject,surname-subject,in_group-subject',
                    '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),
145

146
                    '__type:Y': 'EmailAddress',
Sylvain Thénault's avatar
Sylvain Thénault committed
147
148
149
                    '_cw_edited_fields:Y': 'address-subject,use_email-object',
                    'address-subject:Y': u'dima@logilab.fr',
                    'use_email-object:Y': 'X',
150
                    }
Sylvain Thénault's avatar
Sylvain Thénault committed
151
        path, params = self.expect_redirect_publish(req, 'edit')
Adrien Di Mascio's avatar
Adrien Di Mascio committed
152
        # should be redirected on the created person
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
153
        self.assertEquals(path, 'cwuser/adim')
Adrien Di Mascio's avatar
Adrien Di Mascio committed
154
155
156
157
        e = self.execute('Any P WHERE P surname "Di Mascio"').get_entity(0, 0)
        self.assertEquals(e.surname, 'Di Mascio')
        email = e.use_email[0]
        self.assertEquals(email.address, 'dima@logilab.fr')
158

Adrien Di Mascio's avatar
Adrien Di Mascio committed
159
    def test_edit_multiple_linked(self):
Sylvain Thénault's avatar
Sylvain Thénault committed
160
        peid = u(self.create_user('adim').eid)
161
        req = self.request()
Sylvain Thénault's avatar
Sylvain Thénault committed
162
        req.form = {'eid': [peid, 'Y'], '__maineid': peid,
163

Sylvain Thénault's avatar
Sylvain Thénault committed
164
165
166
                    '__type:'+peid: u'CWUser',
                    '_cw_edited_fields:'+peid: u'surname-subject',
                    'surname-subject:'+peid: u'Di Masci',
167

Sylvain Thénault's avatar
Sylvain Thénault committed
168
169
170
171
                    '__type:Y': u'EmailAddress',
                    '_cw_edited_fields:Y': u'address-subject,use_email-object',
                    'address-subject:Y': u'dima@logilab.fr',
                    'use_email-object:Y': peid,
172
                    }
Sylvain Thénault's avatar
Sylvain Thénault committed
173
        path, params = self.expect_redirect_publish(req, 'edit')
Adrien Di Mascio's avatar
Adrien Di Mascio committed
174
        # should be redirected on the created person
Sylvain Thénault's avatar
Sylvain Thénault committed
175
176
        self.assertEquals(path, 'cwuser/adim')
        e = self.execute('Any P WHERE P surname "Di Masci"').get_entity(0, 0)
Adrien Di Mascio's avatar
Adrien Di Mascio committed
177
178
        email = e.use_email[0]
        self.assertEquals(email.address, 'dima@logilab.fr')
179

Sylvain Thénault's avatar
Sylvain Thénault committed
180
        emaileid = u(email.eid)
181
        req = self.request()
Sylvain Thénault's avatar
Sylvain Thénault committed
182
183
184
185
186
187
188
189
190
191
192
193
194
        req.form = {'eid': [peid, emaileid],

                    '__type:'+peid: u'CWUser',
                    '_cw_edited_fields:'+peid: u'surname-subject',
                    'surname-subject:'+peid: u'Di Masci',

                    '__type:'+emaileid: u'EmailAddress',
                    '_cw_edited_fields:'+emaileid: u'address-subject,use_email-object',
                    'address-subject:'+emaileid: u'adim@logilab.fr',
                    'use_email-object:'+emaileid: peid,
                    }
        path, params = self.expect_redirect_publish(req, 'edit')
        email.clear_all_caches()
Adrien Di Mascio's avatar
Adrien Di Mascio committed
195
196
        self.assertEquals(email.address, 'adim@logilab.fr')

197

Adrien Di Mascio's avatar
Adrien Di Mascio committed
198
199
    def test_password_confirm(self):
        """test creation of two linked entities
200
        """
Adrien Di Mascio's avatar
Adrien Di Mascio committed
201
        user = self.user()
202
        req = self.request()
Sylvain Thénault's avatar
Sylvain Thénault committed
203
204
205
206
207
        req.form = {'eid': 'X',
                    '__cloned_eid:X': u(user.eid), '__type:X': 'CWUser',
                    '_cw_edited_fields:X': 'login-subject,upassword-subject',
                    'login-subject:X': u'toto',
                    'upassword-subject:X': u'toto',
208
                    }
Sylvain Thénault's avatar
Sylvain Thénault committed
209
        ex = self.assertRaises(ValidationError, self.ctrl_publish, req)
Sylvain Thénault's avatar
Sylvain Thénault committed
210
        self.assertEquals(ex.errors, {'upassword-subject': u'password and confirmation don\'t match'})
211
        req = self.request()
Sylvain Thénault's avatar
Sylvain Thénault committed
212
        req.form = {'__cloned_eid:X': u(user.eid),
213
                    'eid': 'X', '__type:X': 'CWUser',
Sylvain Thénault's avatar
Sylvain Thénault committed
214
215
216
217
                    '_cw_edited_fields:X': 'login-subject,upassword-subject',
                    'login-subject:X': u'toto',
                    'upassword-subject:X': u'toto',
                    'upassword-subject-confirm:X': u'tutu',
218
                    }
Sylvain Thénault's avatar
Sylvain Thénault committed
219
        ex = self.assertRaises(ValidationError, self.ctrl_publish, req)
Sylvain Thénault's avatar
Sylvain Thénault committed
220
        self.assertEquals(ex.errors, {'upassword-subject': u'password and confirmation don\'t match'})
Adrien Di Mascio's avatar
Adrien Di Mascio committed
221
222
223


    def test_interval_bound_constraint_success(self):
Sylvain Thénault's avatar
Sylvain Thénault committed
224
        feid = self.execute('INSERT File X: X data_name "toto.txt", X data %(data)s',
Adrien Di Mascio's avatar
Adrien Di Mascio committed
225
                            {'data': Binary('yo')})[0][0]
226
227
228
        req = self.request()
        req.form = {'eid': ['X'],
                    '__type:X': 'Salesterm',
Sylvain Thénault's avatar
Sylvain Thénault committed
229
230
231
                    '_cw_edited_fields:X': 'amount-subject,described_by_test-subject',
                    'amount-subject:X': u'-10',
                    'described_by_test-subject:X': u(feid),
232
                }
Sylvain Thénault's avatar
Sylvain Thénault committed
233
        ex = self.assertRaises(ValidationError, self.ctrl_publish, req)
234
        self.assertEquals(ex.errors, {'amount-subject': 'value must be >= 0'})
235
236
237
        req = self.request()
        req.form = {'eid': ['X'],
                    '__type:X': 'Salesterm',
Sylvain Thénault's avatar
Sylvain Thénault committed
238
239
240
                    '_cw_edited_fields:X': 'amount-subject,described_by_test-subject',
                    'amount-subject:X': u'110',
                    'described_by_test-subject:X': u(feid),
241
                    }
Sylvain Thénault's avatar
Sylvain Thénault committed
242
        ex = self.assertRaises(ValidationError, self.ctrl_publish, req)
243
        self.assertEquals(ex.errors, {'amount-subject': 'value must be <= 100'})
244
245
246
        req = self.request()
        req.form = {'eid': ['X'],
                    '__type:X': 'Salesterm',
Sylvain Thénault's avatar
Sylvain Thénault committed
247
248
249
                    '_cw_edited_fields:X': 'amount-subject,described_by_test-subject',
                    'amount-subject:X': u'10',
                    'described_by_test-subject:X': u(feid),
250
                    }
Sylvain Thénault's avatar
Sylvain Thénault committed
251
        self.expect_redirect_publish(req, 'edit')
252
        # should be redirected on the created
Adrien Di Mascio's avatar
Adrien Di Mascio committed
253
254
255
256
257
258
        #eid = params['rql'].split()[-1]
        e = self.execute('Salesterm X').get_entity(0, 0)
        self.assertEquals(e.amount, 10)

    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
259
        tmpgroup = self.request().create_entity('CWGroup', name=u"test")
Adrien Di Mascio's avatar
Adrien Di Mascio committed
260
        user = self.user()
Sylvain Thénault's avatar
Sylvain Thénault committed
261
        req = self.request(**req_form(user))
262
        req.session.data['pending_insert'] = set([(user.eid, 'in_group', tmpgroup.eid)])
Sylvain Thénault's avatar
Sylvain Thénault committed
263
        path, params = self.expect_redirect_publish(req, 'edit')
Adrien Di Mascio's avatar
Adrien Di Mascio committed
264
265
266
        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.assertUnorderedIterableEquals(usergroups, ['managers', 'test'])
Sylvain Thénault's avatar
Sylvain Thénault committed
267
        self.assertEquals(get_pending_inserts(req), [])
Adrien Di Mascio's avatar
Adrien Di Mascio committed
268
269
270
271
272


    def test_req_pending_delete(self):
        """make sure req's pending deletions are taken into account"""
        user = self.user()
273
        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
274
275
276
277
278
279
                                {'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
        self.assertUnorderedIterableEquals(usergroups, ['managers', 'test'])
        # now try to delete the relation
Sylvain Thénault's avatar
Sylvain Thénault committed
280
        req = self.request(**req_form(user))
281
        req.session.data['pending_delete'] = set([(user.eid, 'in_group', groupeid)])
Sylvain Thénault's avatar
Sylvain Thénault committed
282
        path, params = self.expect_redirect_publish(req, 'edit')
Adrien Di Mascio's avatar
Adrien Di Mascio committed
283
284
285
        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.assertUnorderedIterableEquals(usergroups, ['managers'])
Sylvain Thénault's avatar
Sylvain Thénault committed
286
        self.assertEquals(get_pending_deletes(req), [])
Adrien Di Mascio's avatar
Adrien Di Mascio committed
287

Sylvain Thénault's avatar
Sylvain Thénault committed
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
    # def test_custom_attribute_handler(self):
    #     def custom_login_edit(self, formparams, value, relations):
    #         formparams['login'] = value.upper()
    #         relations.append('X login %(login)s')
    #     CWUser.custom_login_edit = custom_login_edit
    #     try:
    #         user = self.user()
    #         eid = repr(user.eid)
    #         req = self.request()
    #         req.form = {
    #             'eid': eid,
    #             '__type:'+eid:  'CWUser',
    #             'login:'+eid: u'foo',
    #             }
    #         path, params = self.expect_redirect_publish(req, 'edit')
    #         rset = self.execute('Any L WHERE X eid %(x)s, X login L', {'x': user.eid}, 'x')
    #         self.assertEquals(rset[0][0], 'FOO')
    #     finally:
    #         del CWUser.custom_login_edit
307

Adrien Di Mascio's avatar
Adrien Di Mascio committed
308
309
    def test_redirect_apply_button(self):
        redirectrql = rql_for_eid(4012) # whatever
310
311
        req = self.request()
        req.form = {
Sylvain Thénault's avatar
Sylvain Thénault committed
312
313
314
315
            'eid': 'A', '__maineid' : 'A',
            '__type:A': 'BlogEntry', '_cw_edited_fields:A': 'content-subject,title-subject',
            'content-subject:A': u'"13:03:43"',
            'title-subject:A': u'huuu',
316
317
318
319
320
321
            '__redirectrql': redirectrql,
            '__redirectvid': 'primary',
            '__redirectparams': 'toto=tutu&tata=titi',
            '__form_id': 'edition',
            '__action_apply': '',
            }
Sylvain Thénault's avatar
Sylvain Thénault committed
322
        path, params = self.expect_redirect_publish(req, 'edit')
Adrien Di Mascio's avatar
Adrien Di Mascio committed
323
324
325
326
327
328
329
330
331
332
        self.failUnless(path.startswith('blogentry/'))
        eid = path.split('/')[1]
        self.assertEquals(params['vid'], 'edition')
        self.assertNotEquals(int(eid), 4012)
        self.assertEquals(params['__redirectrql'], redirectrql)
        self.assertEquals(params['__redirectvid'], 'primary')
        self.assertEquals(params['__redirectparams'], 'toto=tutu&tata=titi')

    def test_redirect_ok_button(self):
        redirectrql = rql_for_eid(4012) # whatever
333
334
        req = self.request()
        req.form = {
Sylvain Thénault's avatar
Sylvain Thénault committed
335
336
337
338
            'eid': 'A', '__maineid' : 'A',
            '__type:A': 'BlogEntry', '_cw_edited_fields:A': 'content-subject,title-subject',
            'content-subject:A': u'"13:03:43"',
            'title-subject:A': u'huuu',
339
340
341
342
343
            '__redirectrql': redirectrql,
            '__redirectvid': 'primary',
            '__redirectparams': 'toto=tutu&tata=titi',
            '__form_id': 'edition',
            }
Sylvain Thénault's avatar
Sylvain Thénault committed
344
        path, params = self.expect_redirect_publish(req, 'edit')
Adrien Di Mascio's avatar
Adrien Di Mascio committed
345
346
347
348
349
350
351
        self.assertEquals(path, 'view')
        self.assertEquals(params['rql'], redirectrql)
        self.assertEquals(params['vid'], 'primary')
        self.assertEquals(params['tata'], 'titi')
        self.assertEquals(params['toto'], 'tutu')

    def test_redirect_delete_button(self):
352
        req = self.request()
Sylvain Thénault's avatar
Sylvain Thénault committed
353
        eid = req.create_entity('BlogEntry', title=u'hop', content=u'hop').eid
Sylvain Thénault's avatar
Sylvain Thénault committed
354
        req.form = {'eid': u(eid), '__type:%s'%eid: 'BlogEntry',
355
                    '__action_delete': ''}
Sylvain Thénault's avatar
Sylvain Thénault committed
356
        path, params = self.expect_redirect_publish(req, 'edit')
Adrien Di Mascio's avatar
Adrien Di Mascio committed
357
        self.assertEquals(path, 'blogentry')
Sylvain Thénault's avatar
Sylvain Thénault committed
358
        self.assertIn('_cwmsgid', params)
Sylvain Thénault's avatar
Sylvain Thénault committed
359
        eid = req.create_entity('EmailAddress', address=u'hop@logilab.fr').eid
Adrien Di Mascio's avatar
Adrien Di Mascio committed
360
        self.execute('SET X use_email E WHERE E eid %(e)s, X eid %(x)s',
361
                     {'x': self.session.user.eid, 'e': eid})
Adrien Di Mascio's avatar
Adrien Di Mascio committed
362
        self.commit()
Sylvain Thénault's avatar
Sylvain Thénault committed
363
        req = req
Sylvain Thénault's avatar
Sylvain Thénault committed
364
        req.form = {'eid': u(eid), '__type:%s'%eid: 'EmailAddress',
365
                    '__action_delete': ''}
Sylvain Thénault's avatar
Sylvain Thénault committed
366
        path, params = self.expect_redirect_publish(req, 'edit')
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
367
        self.assertEquals(path, 'cwuser/admin')
Sylvain Thénault's avatar
Sylvain Thénault committed
368
        self.assertIn('_cwmsgid', params)
Sylvain Thénault's avatar
Sylvain Thénault committed
369
370
        eid1 = req.create_entity('BlogEntry', title=u'hop', content=u'hop').eid
        eid2 = req.create_entity('EmailAddress', address=u'hop@logilab.fr').eid
371
        req = self.request()
Sylvain Thénault's avatar
Sylvain Thénault committed
372
        req.form = {'eid': [u(eid1), u(eid2)],
373
374
375
                    '__type:%s'%eid1: 'BlogEntry',
                    '__type:%s'%eid2: 'EmailAddress',
                    '__action_delete': ''}
Sylvain Thénault's avatar
Sylvain Thénault committed
376
        path, params = self.expect_redirect_publish(req, 'edit')
Adrien Di Mascio's avatar
Adrien Di Mascio committed
377
        self.assertEquals(path, 'view')
Sylvain Thénault's avatar
Sylvain Thénault committed
378
        self.assertIn('_cwmsgid', params)
Adrien Di Mascio's avatar
Adrien Di Mascio committed
379
380

    def test_nonregr_eetype_etype_editing(self):
381
        """non-regression test checking that a manager user can edit a CWEType entity
Adrien Di Mascio's avatar
Adrien Di Mascio committed
382
        """
383
        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
384
385
386
387
        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)
388
389
        req = self.request()
        req.form = {
Sylvain Thénault's avatar
Sylvain Thénault committed
390
391
392
393
            'eid':      cwetypeeid,
            '__type:'+cwetypeeid:  'CWEType',
            '_cw_edited_fields:'+cwetypeeid: 'name-subject,final-subject,description-subject,read_permission-subject',
            'name-subject:'+cwetypeeid:     u'CWEType',
Sylvain Thénault's avatar
Sylvain Thénault committed
394
            'final-subject:'+cwetypeeid:    '',
Sylvain Thénault's avatar
Sylvain Thénault committed
395
396
            'description-subject:'+cwetypeeid:     u'users group',
            'read_permission-subject:'+cwetypeeid:  groups,
397
            }
Adrien Di Mascio's avatar
Adrien Di Mascio committed
398
        try:
Sylvain Thénault's avatar
Sylvain Thénault committed
399
            path, params = self.expect_redirect_publish(req, 'edit')
400
            e = self.execute('Any X WHERE X eid %(x)s', {'x': cwetypeeid}).get_entity(0, 0)
401
            self.assertEquals(e.name, 'CWEType')
Adrien Di Mascio's avatar
Adrien Di Mascio committed
402
403
404
            self.assertEquals(sorted(g.eid for g in e.read_permission), groupeids)
        finally:
            # restore
405
            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
406
            self.commit()
407

Adrien Di Mascio's avatar
Adrien Di Mascio committed
408
409
410
411
    def test_nonregr_strange_text_input(self):
        """non-regression test checking text input containing "13:03:43"

        this seems to be postgres (tsearch?) specific
412
        """
413
414
        req = self.request()
        req.form = {
Sylvain Thénault's avatar
Sylvain Thénault committed
415
416
417
418
419
            'eid': 'A', '__maineid' : 'A',
            '__type:A': 'BlogEntry', '_cw_edited_fields:A': 'title-subject,content-subject',
            'title-subject:A': u'"13:03:40"',
            'content-subject:A': u'"13:03:43"',}
        path, params = self.expect_redirect_publish(req, 'edit')
Adrien Di Mascio's avatar
Adrien Di Mascio committed
420
421
        self.failUnless(path.startswith('blogentry/'))
        eid = path.split('/')[1]
422
        e = self.execute('Any C, T WHERE C eid %(x)s, C content T', {'x': eid}).get_entity(0, 0)
Adrien Di Mascio's avatar
Adrien Di Mascio committed
423
424
425
426
427
        self.assertEquals(e.title, '"13:03:40"')
        self.assertEquals(e.content, '"13:03:43"')


    def test_nonregr_multiple_empty_email_addr(self):
428
        gueid = self.execute('CWGroup G WHERE G name "users"')[0][0]
429
430
        req = self.request()
        req.form = {'eid': ['X', 'Y'],
431

432
                    '__type:X': 'CWUser',
Sylvain Thénault's avatar
Sylvain Thénault committed
433
434
435
436
                    '_cw_edited_fields:X': 'login-subject,upassword-subject,in_group-subject',
                    'login-subject:X': u'adim',
                    'upassword-subject:X': u'toto', 'upassword-subject-confirm:X': u'toto',
                    'in_group-subject:X': `gueid`,
437

438
                    '__type:Y': 'EmailAddress',
Sylvain Thénault's avatar
Sylvain Thénault committed
439
440
441
442
                    '_cw_edited_fields:Y': 'address-subject,alias-subject,use_email-object',
                    'address-subject:Y': u'',
                    'alias-subject:Y': u'',
                    'use_email-object:Y': 'X',
443
                    }
Sylvain Thénault's avatar
Sylvain Thénault committed
444
        ex = self.assertRaises(ValidationError, self.ctrl_publish, req)
445
        self.assertEquals(ex.errors, {'address-subject': u'required field'})
Adrien Di Mascio's avatar
Adrien Di Mascio committed
446
447
448

    def test_nonregr_copy(self):
        user = self.user()
449
        req = self.request()
Sylvain Thénault's avatar
Sylvain Thénault committed
450
451
452
453
454
        req.form = {'__maineid' : 'X', 'eid': 'X',
                    '__cloned_eid:X': user.eid, '__type:X': 'CWUser',
                    '_cw_edited_fields:X': 'login-subject,upassword-subject',
                    'login-subject:X': u'toto',
                    'upassword-subject:X': u'toto', 'upassword-subject-confirm:X': u'toto',
455
                    }
Sylvain Thénault's avatar
Sylvain Thénault committed
456
        path, params = self.expect_redirect_publish(req, 'edit')
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
457
        self.assertEquals(path, 'cwuser/toto')
458
        e = self.execute('Any X WHERE X is CWUser, X login "toto"').get_entity(0, 0)
Adrien Di Mascio's avatar
Adrien Di Mascio committed
459
460
461
462
463
464
465
466
467
468
        self.assertEquals(e.login, 'toto')
        self.assertEquals(e.in_group[0].name, 'managers')


    def test_nonregr_rollback_on_validation_error(self):
        p = self.create_user("doe")
        # 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
469
            e = self.request().create_entity('EmailAddress', address=u'doe@doe.com')
Adrien Di Mascio's avatar
Adrien Di Mascio committed
470
471
            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})
472
            req = self.request()
Sylvain Thénault's avatar
Sylvain Thénault committed
473
474
475
476
477
            req.form = {'eid': 'X',
                        '__cloned_eid:X': p.eid, '__type:X': 'CWUser',
                        '_cw_edited_fields:X': 'login-subject,surname-subject',
                        'login-subject': u'dodo',
                        'surname-subject:X': u'Boom',
478
                        '__errorurl' : "whatever but required",
Sylvain Thénault's avatar
Sylvain Thénault committed
479
                        }
Adrien Di Mascio's avatar
Adrien Di Mascio committed
480
481
482
483
484
            # 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:
Sylvain Thénault's avatar
Sylvain Thénault committed
485
                self.app_publish(req, 'edit')
Adrien Di Mascio's avatar
Adrien Di Mascio committed
486
            except Redirect:
Sylvain Thénault's avatar
Sylvain Thénault committed
487
                req = self.request()
488
489
                req.form['rql'] = 'Any X WHERE X eid %s' % p.eid
                req.form['vid'] = 'copy'
Sylvain Thénault's avatar
Sylvain Thénault committed
490
                self.app_publish(req, 'view')
491
            rset = self.execute('CWUser P WHERE P surname "Boom"')
Adrien Di Mascio's avatar
Adrien Di Mascio committed
492
493
494
495
496
            self.assertEquals(len(rset), 0)
        finally:
            p.__class__.skip_copy_for = old_skips


497
class EmbedControllerTC(CubicWebTC):
Adrien Di Mascio's avatar
Adrien Di Mascio committed
498
499
500
501
502
503

    def test_nonregr_embed_publish(self):
        # This test looks a bit stupid but at least it will probably
        # fail if the controller API changes and if EmbedController is not
        # updated (which is what happened before this test)
        req = self.request()
504
        req.form['url'] = 'http://www.logilab.fr/'
505
        controller = self.vreg['controllers'].select('embed', req)
Adrien Di Mascio's avatar
Adrien Di Mascio committed
506
507
508
        result = controller.publish(rset=None)


509
class ReportBugControllerTC(CubicWebTC):
Adrien Di Mascio's avatar
Adrien Di Mascio committed
510
511

    def test_usable_by_guets(self):
512
        self.login('anon')
Sylvain Thénault's avatar
Sylvain Thénault committed
513
514
515
        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
516
517


518
class SendMailControllerTC(CubicWebTC):
Adrien Di Mascio's avatar
Adrien Di Mascio committed
519
520

    def test_not_usable_by_guets(self):
Sylvain Thénault's avatar
Sylvain Thénault committed
521
522
523
524
525
526
        self.assertRaises(NoSelectableObject,
                          self.vreg['controllers'].select, 'sendmail', self.request())
        self.vreg['controllers'].select('sendmail',
                                        self.request(subject='toto',
                                                     recipient='toto@logilab.fr',
                                                     mailbody='hop'))
Adrien Di Mascio's avatar
Adrien Di Mascio committed
527
        self.login('anon')
528
529
        self.assertRaises(NoSelectableObject,
                          self.vreg['controllers'].select, 'sendmail', self.request())
530

Adrien Di Mascio's avatar
Adrien Di Mascio committed
531
532


533
class JSONControllerTC(CubicWebTC):
Adrien Di Mascio's avatar
Adrien Di Mascio committed
534
535
536

    def ctrl(self, req=None):
        req = req or self.request(url='http://whatever.fr/')
537
        return self.vreg['controllers'].select('json', req)
Adrien Di Mascio's avatar
Adrien Di Mascio committed
538
539

    def setup_database(self):
Sylvain Thénault's avatar
Sylvain Thénault committed
540
541
542
        req = self.request()
        self.pytag = req.create_entity('Tag', name=u'python')
        self.cubicwebtag = req.create_entity('Tag', name=u'cubicweb')
Adrien Di Mascio's avatar
Adrien Di Mascio committed
543
544
545
546
547
        self.john = self.create_user(u'John')


    ## tests ##################################################################
    def test_simple_exec(self):
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
548
549
550
551
        req = self.request(rql='CWUser P WHERE P login "John"',
                           pageid='123', fname='view')
        ctrl = self.ctrl(req)
        rset = self.john.as_rset()
552
        rset.req = req
553
554
555
556
557
558
559
        source = ctrl.publish()
        self.failUnless(source.startswith('<?xml version="1.0"?>\n' + STRICT_DOCTYPE +
                                          u'<div xmlns="http://www.w3.org/1999/xhtml" xmlns:cubicweb="http://www.logilab.org/2008/cubicweb">')
                        )
        req.xhtml_browser = lambda: False
        source = ctrl.publish()
        self.failUnless(source.startswith('<div>'))
Adrien Di Mascio's avatar
Adrien Di Mascio committed
560

sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
561
562
563
564
#     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'))
#         self.assertEquals(ctrl.publish(),
565
#                           json.dumps(self.execute(rql).rows))
Adrien Di Mascio's avatar
Adrien Di Mascio committed
566
567
568

    def test_remote_add_existing_tag(self):
        self.remote_call('tag_entity', self.john.eid, ['python'])
569
570
571
572
573
574
        self.assertUnorderedIterableEquals(
            [tname for tname, in self.execute('Any N WHERE T is Tag, T name N')],
            ['python', 'cubicweb'])
        self.assertEquals(
            self.execute('Any N WHERE T tags P, P is CWUser, T name N').rows,
            [['python']])
575

Adrien Di Mascio's avatar
Adrien Di Mascio committed
576
577
    def test_remote_add_new_tag(self):
        self.remote_call('tag_entity', self.john.eid, ['javascript'])
578
579
580
581
582
583
        self.assertUnorderedIterableEquals(
            [tname for tname, in self.execute('Any N WHERE T is Tag, T name N')],
            ['python', 'cubicweb', 'javascript'])
        self.assertEquals(
            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
584
585

    def test_pending_insertion(self):
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
586
        res, req = self.remote_call('add_pending_inserts', [['12', 'tags', '13']])
Sylvain Thénault's avatar
Sylvain Thénault committed
587
        deletes = get_pending_deletes(req)
Adrien Di Mascio's avatar
Adrien Di Mascio committed
588
        self.assertEquals(deletes, [])
Sylvain Thénault's avatar
Sylvain Thénault committed
589
        inserts = get_pending_inserts(req)
Adrien Di Mascio's avatar
Adrien Di Mascio committed
590
        self.assertEquals(inserts, ['12:tags:13'])
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
591
        res, req = self.remote_call('add_pending_inserts', [['12', 'tags', '14']])
Sylvain Thénault's avatar
Sylvain Thénault committed
592
        deletes = get_pending_deletes(req)
Adrien Di Mascio's avatar
Adrien Di Mascio committed
593
        self.assertEquals(deletes, [])
Sylvain Thénault's avatar
Sylvain Thénault committed
594
        inserts = get_pending_inserts(req)
Adrien Di Mascio's avatar
Adrien Di Mascio committed
595
        self.assertEquals(inserts, ['12:tags:13', '12:tags:14'])
Sylvain Thénault's avatar
Sylvain Thénault committed
596
        inserts = get_pending_inserts(req, 12)
Adrien Di Mascio's avatar
Adrien Di Mascio committed
597
        self.assertEquals(inserts, ['12:tags:13', '12:tags:14'])
Sylvain Thénault's avatar
Sylvain Thénault committed
598
        inserts = get_pending_inserts(req, 13)
Adrien Di Mascio's avatar
Adrien Di Mascio committed
599
        self.assertEquals(inserts, ['12:tags:13'])
Sylvain Thénault's avatar
Sylvain Thénault committed
600
        inserts = get_pending_inserts(req, 14)
Adrien Di Mascio's avatar
Adrien Di Mascio committed
601
602
603
604
605
        self.assertEquals(inserts, ['12:tags:14'])
        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
606
        inserts = get_pending_inserts(req)
Adrien Di Mascio's avatar
Adrien Di Mascio committed
607
        self.assertEquals(inserts, [])
Sylvain Thénault's avatar
Sylvain Thénault committed
608
        deletes = get_pending_deletes(req)
Adrien Di Mascio's avatar
Adrien Di Mascio committed
609
610
        self.assertEquals(deletes, ['12:tags:13'])
        res, req = self.remote_call('add_pending_delete', ['12', 'tags', '14'])
Sylvain Thénault's avatar
Sylvain Thénault committed
611
        inserts = get_pending_inserts(req)
Adrien Di Mascio's avatar
Adrien Di Mascio committed
612
        self.assertEquals(inserts, [])
Sylvain Thénault's avatar
Sylvain Thénault committed
613
        deletes = get_pending_deletes(req)
Adrien Di Mascio's avatar
Adrien Di Mascio committed
614
        self.assertEquals(deletes, ['12:tags:13', '12:tags:14'])
Sylvain Thénault's avatar
Sylvain Thénault committed
615
        deletes = get_pending_deletes(req, 12)
Adrien Di Mascio's avatar
Adrien Di Mascio committed
616
        self.assertEquals(deletes, ['12:tags:13', '12:tags:14'])
Sylvain Thénault's avatar
Sylvain Thénault committed
617
        deletes = get_pending_deletes(req, 13)
Adrien Di Mascio's avatar
Adrien Di Mascio committed
618
        self.assertEquals(deletes, ['12:tags:13'])
Sylvain Thénault's avatar
Sylvain Thénault committed
619
        deletes = get_pending_deletes(req, 14)
Adrien Di Mascio's avatar
Adrien Di Mascio committed
620
621
622
623
624
        self.assertEquals(deletes, ['12:tags:14'])
        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
625
        _, req = self.remote_call('add_pending_inserts', [['12', 'tags', '14']])
Sylvain Thénault's avatar
Sylvain Thénault committed
626
        inserts = get_pending_inserts(req)
Adrien Di Mascio's avatar
Adrien Di Mascio committed
627
        self.assertEquals(inserts, ['12:tags:14'])
Sylvain Thénault's avatar
Sylvain Thénault committed
628
        deletes = get_pending_deletes(req)
Adrien Di Mascio's avatar
Adrien Di Mascio committed
629
630
        self.assertEquals(deletes, ['12:tags:13'])
        req.remove_pending_operations()
Sylvain Thénault's avatar
Sylvain Thénault committed
631
632
        self.assertEquals(get_pending_deletes(req), [])
        self.assertEquals(get_pending_inserts(req), [])
633

Adrien Di Mascio's avatar
Adrien Di Mascio committed
634
635
636
637

    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
638
        inserts = get_pending_inserts(req)
Adrien Di Mascio's avatar
Adrien Di Mascio committed
639
640
        self.assertEquals(inserts, ['12:tags:13', '12:tags:14'])
        req.remove_pending_operations()
641

Adrien Di Mascio's avatar
Adrien Di Mascio committed
642
643
644
645

    # silly tests
    def test_external_resource(self):
        self.assertEquals(self.remote_call('external_resource', 'RSS_LOGO')[0],
646
                          json.dumps(self.config.uiprops['RSS_LOGO']))
Adrien Di Mascio's avatar
Adrien Di Mascio committed
647
648
    def test_i18n(self):
        self.assertEquals(self.remote_call('i18n', ['bimboom'])[0],
649
                          json.dumps(['bimboom']))
Adrien Di Mascio's avatar
Adrien Di Mascio committed
650
651

    def test_format_date(self):
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
652
        self.assertEquals(self.remote_call('format_date', '2007-01-01 12:00:00')[0],
653
                          json.dumps('2007/01/01'))
Adrien Di Mascio's avatar
Adrien Di Mascio committed
654
655


656
657


Adrien Di Mascio's avatar
Adrien Di Mascio committed
658
659
if __name__ == '__main__':
    unittest_main()