Premiere version : mise en route du suivi.
[auf_roundup.git] / test / test_cgi.py
1 #
2 # Copyright (c) 2003 Richard Jones, rjones@ekit-inc.com
3 # This module is free software, and you may redistribute it and/or modify
4 # under the same terms as Python, so long as this copyright message and
5 # disclaimer are retained in their original form.
6 #
7 # This module is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10 #
11 # $Id: test_cgi.py,v 1.36 2008-08-07 06:12:57 richard Exp $
12
13 import unittest, os, shutil, errno, sys, difflib, cgi, re, StringIO
14
15 from roundup.cgi import client, actions, exceptions
16 from roundup.cgi.exceptions import FormError
17 from roundup.cgi.templating import HTMLItem
18 from roundup.cgi.form_parser import FormParser
19 from roundup import init, instance, password, hyperdb, date
20
21 from mocknull import MockNull
22
23 import db_test_base
24
25 NEEDS_INSTANCE = 1
26
27 class FileUpload:
28 def __init__(self, content, filename):
29 self.content = content
30 self.filename = filename
31
32 def makeForm(args):
33 form = cgi.FieldStorage()
34 for k,v in args.items():
35 if type(v) is type([]):
36 [form.list.append(cgi.MiniFieldStorage(k, x)) for x in v]
37 elif isinstance(v, FileUpload):
38 x = cgi.MiniFieldStorage(k, v.content)
39 x.filename = v.filename
40 form.list.append(x)
41 else:
42 form.list.append(cgi.MiniFieldStorage(k, v))
43 return form
44
45 cm = client.clean_message
46 class MessageTestCase(unittest.TestCase):
47 def testCleanMessageOK(self):
48 self.assertEqual(cm('<br>x<br />'), '<br>x<br />')
49 self.assertEqual(cm('<i>x</i>'), '<i>x</i>')
50 self.assertEqual(cm('<b>x</b>'), '<b>x</b>')
51 self.assertEqual(cm('<a href="y">x</a>'),
52 '<a href="y">x</a>')
53 self.assertEqual(cm('<BR>x<BR />'), '<BR>x<BR />')
54 self.assertEqual(cm('<I>x</I>'), '<I>x</I>')
55 self.assertEqual(cm('<B>x</B>'), '<B>x</B>')
56 self.assertEqual(cm('<A HREF="y">x</A>'),
57 '<A HREF="y">x</A>')
58
59 def testCleanMessageBAD(self):
60 self.assertEqual(cm('<script>x</script>'),
61 '&lt;script&gt;x&lt;/script&gt;')
62 self.assertEqual(cm('<iframe>x</iframe>'),
63 '&lt;iframe&gt;x&lt;/iframe&gt;')
64
65 class FormTestCase(unittest.TestCase):
66 def setUp(self):
67 self.dirname = '_test_cgi_form'
68 # set up and open a tracker
69 self.instance = db_test_base.setupTracker(self.dirname)
70
71 # open the database
72 self.db = self.instance.open('admin')
73 self.db.user.create(username='Chef', address='chef@bork.bork.bork',
74 realname='Bork, Chef', roles='User')
75 self.db.user.create(username='mary', address='mary@test.test',
76 roles='User', realname='Contrary, Mary')
77
78 test = self.instance.backend.Class(self.db, "test",
79 string=hyperdb.String(), number=hyperdb.Number(),
80 boolean=hyperdb.Boolean(), link=hyperdb.Link('test'),
81 multilink=hyperdb.Multilink('test'), date=hyperdb.Date(),
82 messages=hyperdb.Multilink('msg'), interval=hyperdb.Interval())
83
84 # compile the labels re
85 classes = '|'.join(self.db.classes.keys())
86 self.FV_SPECIAL = re.compile(FormParser.FV_LABELS%classes,
87 re.VERBOSE)
88
89 def parseForm(self, form, classname='test', nodeid=None):
90 cl = client.Client(self.instance, None, {'PATH_INFO':'/',
91 'REQUEST_METHOD':'POST'}, makeForm(form))
92 cl.classname = classname
93 cl.nodeid = nodeid
94 cl.language = ('en',)
95 cl.db = self.db
96 return cl.parsePropsFromForm(create=1)
97
98 def tearDown(self):
99 self.db.close()
100 try:
101 shutil.rmtree(self.dirname)
102 except OSError, error:
103 if error.errno not in (errno.ENOENT, errno.ESRCH): raise
104
105 #
106 # form label extraction
107 #
108 def tl(self, s, c, i, a, p):
109 m = self.FV_SPECIAL.match(s)
110 self.assertNotEqual(m, None)
111 d = m.groupdict()
112 self.assertEqual(d['classname'], c)
113 self.assertEqual(d['id'], i)
114 for action in 'required add remove link note file'.split():
115 if a == action:
116 self.assertNotEqual(d[action], None)
117 else:
118 self.assertEqual(d[action], None)
119 self.assertEqual(d['propname'], p)
120
121 def testLabelMatching(self):
122 self.tl('<propname>', None, None, None, '<propname>')
123 self.tl(':required', None, None, 'required', None)
124 self.tl(':confirm:<propname>', None, None, 'confirm', '<propname>')
125 self.tl(':add:<propname>', None, None, 'add', '<propname>')
126 self.tl(':remove:<propname>', None, None, 'remove', '<propname>')
127 self.tl(':link:<propname>', None, None, 'link', '<propname>')
128 self.tl('test1:<prop>', 'test', '1', None, '<prop>')
129 self.tl('test1:required', 'test', '1', 'required', None)
130 self.tl('test1:add:<prop>', 'test', '1', 'add', '<prop>')
131 self.tl('test1:remove:<prop>', 'test', '1', 'remove', '<prop>')
132 self.tl('test1:link:<prop>', 'test', '1', 'link', '<prop>')
133 self.tl('test1:confirm:<prop>', 'test', '1', 'confirm', '<prop>')
134 self.tl('test-1:<prop>', 'test', '-1', None, '<prop>')
135 self.tl('test-1:required', 'test', '-1', 'required', None)
136 self.tl('test-1:add:<prop>', 'test', '-1', 'add', '<prop>')
137 self.tl('test-1:remove:<prop>', 'test', '-1', 'remove', '<prop>')
138 self.tl('test-1:link:<prop>', 'test', '-1', 'link', '<prop>')
139 self.tl('test-1:confirm:<prop>', 'test', '-1', 'confirm', '<prop>')
140 self.tl(':note', None, None, 'note', None)
141 self.tl(':file', None, None, 'file', None)
142
143 #
144 # Empty form
145 #
146 def testNothing(self):
147 self.assertEqual(self.parseForm({}), ({('test', None): {}}, []))
148
149 def testNothingWithRequired(self):
150 self.assertRaises(FormError, self.parseForm, {':required': 'string'})
151 self.assertRaises(FormError, self.parseForm,
152 {':required': 'title,status', 'status':'1'}, 'issue')
153 self.assertRaises(FormError, self.parseForm,
154 {':required': ['title','status'], 'status':'1'}, 'issue')
155 self.assertRaises(FormError, self.parseForm,
156 {':required': 'status', 'status':''}, 'issue')
157 self.assertRaises(FormError, self.parseForm,
158 {':required': 'nosy', 'nosy':''}, 'issue')
159 self.assertRaises(FormError, self.parseForm,
160 {':required': 'msg-1@content', 'msg-1@content':''}, 'issue')
161 self.assertRaises(FormError, self.parseForm,
162 {':required': 'msg-1@content'}, 'issue')
163
164 #
165 # Nonexistant edit
166 #
167 def testEditNonexistant(self):
168 self.assertRaises(FormError, self.parseForm, {'boolean': ''},
169 'test', '1')
170
171 #
172 # String
173 #
174 def testEmptyString(self):
175 self.assertEqual(self.parseForm({'string': ''}),
176 ({('test', None): {}}, []))
177 self.assertEqual(self.parseForm({'string': ' '}),
178 ({('test', None): {}}, []))
179 self.assertRaises(FormError, self.parseForm, {'string': ['', '']})
180
181 def testSetString(self):
182 self.assertEqual(self.parseForm({'string': 'foo'}),
183 ({('test', None): {'string': 'foo'}}, []))
184 self.assertEqual(self.parseForm({'string': 'a\r\nb\r\n'}),
185 ({('test', None): {'string': 'a\nb'}}, []))
186 nodeid = self.db.issue.create(title='foo')
187 self.assertEqual(self.parseForm({'title': 'foo'}, 'issue', nodeid),
188 ({('issue', nodeid): {}}, []))
189
190 def testEmptyStringSet(self):
191 nodeid = self.db.issue.create(title='foo')
192 self.assertEqual(self.parseForm({'title': ''}, 'issue', nodeid),
193 ({('issue', nodeid): {'title': None}}, []))
194 nodeid = self.db.issue.create(title='foo')
195 self.assertEqual(self.parseForm({'title': ' '}, 'issue', nodeid),
196 ({('issue', nodeid): {'title': None}}, []))
197
198 def testStringLinkId(self):
199 self.db.status.set('1', name='2')
200 self.db.status.set('2', name='1')
201 issue = self.db.issue.create(title='i1-status1', status='1')
202 self.assertEqual(self.db.issue.get(issue,'status'),'1')
203 self.assertEqual(self.db.status.lookup('1'),'2')
204 self.assertEqual(self.db.status.lookup('2'),'1')
205 form = cgi.FieldStorage()
206 cl = client.Client(self.instance, None, {'PATH_INFO':'/'}, form)
207 cl.classname = 'issue'
208 cl.nodeid = issue
209 cl.db = self.db
210 cl.language = ('en',)
211 item = HTMLItem(cl, 'issue', issue)
212 self.assertEqual(item.status.id, '1')
213 self.assertEqual(item.status.name, '2')
214
215 def testStringMultilinkId(self):
216 id = self.db.keyword.create(name='2')
217 self.assertEqual(id,'1')
218 id = self.db.keyword.create(name='1')
219 self.assertEqual(id,'2')
220 issue = self.db.issue.create(title='i1-status1', keyword=['1'])
221 self.assertEqual(self.db.issue.get(issue,'keyword'),['1'])
222 self.assertEqual(self.db.keyword.lookup('1'),'2')
223 self.assertEqual(self.db.keyword.lookup('2'),'1')
224 form = cgi.FieldStorage()
225 cl = client.Client(self.instance, None, {'PATH_INFO':'/'}, form)
226 cl.classname = 'issue'
227 cl.nodeid = issue
228 cl.db = self.db
229 cl.language = ('en',)
230 cl.userid = '1'
231 item = HTMLItem(cl, 'issue', issue)
232 for keyword in item.keyword:
233 self.assertEqual(keyword.id, '1')
234 self.assertEqual(keyword.name, '2')
235
236 def testFileUpload(self):
237 file = FileUpload('foo', 'foo.txt')
238 self.assertEqual(self.parseForm({'content': file}, 'file'),
239 ({('file', None): {'content': 'foo', 'name': 'foo.txt',
240 'type': 'text/plain'}}, []))
241
242 def testEditFileClassAttributes(self):
243 self.assertEqual(self.parseForm({'name': 'foo.txt',
244 'type': 'application/octet-stream'},
245 'file'),
246 ({('file', None): {'name': 'foo.txt',
247 'type': 'application/octet-stream'}},[]))
248
249 #
250 # Link
251 #
252 def testEmptyLink(self):
253 self.assertEqual(self.parseForm({'link': ''}),
254 ({('test', None): {}}, []))
255 self.assertEqual(self.parseForm({'link': ' '}),
256 ({('test', None): {}}, []))
257 self.assertRaises(FormError, self.parseForm, {'link': ['', '']})
258 self.assertEqual(self.parseForm({'link': '-1'}),
259 ({('test', None): {}}, []))
260
261 def testSetLink(self):
262 self.assertEqual(self.parseForm({'status': 'unread'}, 'issue'),
263 ({('issue', None): {'status': '1'}}, []))
264 self.assertEqual(self.parseForm({'status': '1'}, 'issue'),
265 ({('issue', None): {'status': '1'}}, []))
266 nodeid = self.db.issue.create(status='unread')
267 self.assertEqual(self.parseForm({'status': 'unread'}, 'issue', nodeid),
268 ({('issue', nodeid): {}}, []))
269
270 def testUnsetLink(self):
271 nodeid = self.db.issue.create(status='unread')
272 self.assertEqual(self.parseForm({'status': '-1'}, 'issue', nodeid),
273 ({('issue', nodeid): {'status': None}}, []))
274
275 def testInvalidLinkValue(self):
276 # XXX This is not the current behaviour - should we enforce this?
277 # self.assertRaises(IndexError, self.parseForm,
278 # {'status': '4'}))
279 self.assertRaises(FormError, self.parseForm, {'link': 'frozzle'})
280 self.assertRaises(FormError, self.parseForm, {'status': 'frozzle'},
281 'issue')
282
283 #
284 # Multilink
285 #
286 def testEmptyMultilink(self):
287 self.assertEqual(self.parseForm({'nosy': ''}),
288 ({('test', None): {}}, []))
289 self.assertEqual(self.parseForm({'nosy': ' '}),
290 ({('test', None): {}}, []))
291
292 def testSetMultilink(self):
293 self.assertEqual(self.parseForm({'nosy': '1'}, 'issue'),
294 ({('issue', None): {'nosy': ['1']}}, []))
295 self.assertEqual(self.parseForm({'nosy': 'admin'}, 'issue'),
296 ({('issue', None): {'nosy': ['1']}}, []))
297 self.assertEqual(self.parseForm({'nosy': ['1','2']}, 'issue'),
298 ({('issue', None): {'nosy': ['1','2']}}, []))
299 self.assertEqual(self.parseForm({'nosy': '1,2'}, 'issue'),
300 ({('issue', None): {'nosy': ['1','2']}}, []))
301 self.assertEqual(self.parseForm({'nosy': 'admin,2'}, 'issue'),
302 ({('issue', None): {'nosy': ['1','2']}}, []))
303
304 def testMixedMultilink(self):
305 form = cgi.FieldStorage()
306 form.list.append(cgi.MiniFieldStorage('nosy', '1,2'))
307 form.list.append(cgi.MiniFieldStorage('nosy', '3'))
308 cl = client.Client(self.instance, None, {'PATH_INFO':'/'}, form)
309 cl.classname = 'issue'
310 cl.nodeid = None
311 cl.db = self.db
312 cl.language = ('en',)
313 self.assertEqual(cl.parsePropsFromForm(create=1),
314 ({('issue', None): {'nosy': ['1','2', '3']}}, []))
315
316 def testEmptyMultilinkSet(self):
317 nodeid = self.db.issue.create(nosy=['1','2'])
318 self.assertEqual(self.parseForm({'nosy': ''}, 'issue', nodeid),
319 ({('issue', nodeid): {'nosy': []}}, []))
320 nodeid = self.db.issue.create(nosy=['1','2'])
321 self.assertEqual(self.parseForm({'nosy': ' '}, 'issue', nodeid),
322 ({('issue', nodeid): {'nosy': []}}, []))
323 self.assertEqual(self.parseForm({'nosy': '1,2'}, 'issue', nodeid),
324 ({('issue', nodeid): {}}, []))
325
326 def testInvalidMultilinkValue(self):
327 # XXX This is not the current behaviour - should we enforce this?
328 # self.assertRaises(IndexError, self.parseForm,
329 # {'nosy': '4'}))
330 self.assertRaises(FormError, self.parseForm, {'nosy': 'frozzle'},
331 'issue')
332 self.assertRaises(FormError, self.parseForm, {'nosy': '1,frozzle'},
333 'issue')
334 self.assertRaises(FormError, self.parseForm, {'multilink': 'frozzle'})
335
336 def testMultilinkAdd(self):
337 nodeid = self.db.issue.create(nosy=['1'])
338 # do nothing
339 self.assertEqual(self.parseForm({':add:nosy': ''}, 'issue', nodeid),
340 ({('issue', nodeid): {}}, []))
341
342 # do something ;)
343 self.assertEqual(self.parseForm({':add:nosy': '2'}, 'issue', nodeid),
344 ({('issue', nodeid): {'nosy': ['1','2']}}, []))
345 self.assertEqual(self.parseForm({':add:nosy': '2,mary'}, 'issue',
346 nodeid), ({('issue', nodeid): {'nosy': ['1','2','4']}}, []))
347 self.assertEqual(self.parseForm({':add:nosy': ['2','3']}, 'issue',
348 nodeid), ({('issue', nodeid): {'nosy': ['1','2','3']}}, []))
349
350 def testMultilinkAddNew(self):
351 self.assertEqual(self.parseForm({':add:nosy': ['2','3']}, 'issue'),
352 ({('issue', None): {'nosy': ['2','3']}}, []))
353
354 def testMultilinkRemove(self):
355 nodeid = self.db.issue.create(nosy=['1','2'])
356 # do nothing
357 self.assertEqual(self.parseForm({':remove:nosy': ''}, 'issue', nodeid),
358 ({('issue', nodeid): {}}, []))
359
360 # do something ;)
361 self.assertEqual(self.parseForm({':remove:nosy': '1'}, 'issue',
362 nodeid), ({('issue', nodeid): {'nosy': ['2']}}, []))
363 self.assertEqual(self.parseForm({':remove:nosy': 'admin,2'},
364 'issue', nodeid), ({('issue', nodeid): {'nosy': []}}, []))
365 self.assertEqual(self.parseForm({':remove:nosy': ['1','2']},
366 'issue', nodeid), ({('issue', nodeid): {'nosy': []}}, []))
367
368 # add and remove
369 self.assertEqual(self.parseForm({':add:nosy': ['3'],
370 ':remove:nosy': ['1','2']},
371 'issue', nodeid), ({('issue', nodeid): {'nosy': ['3']}}, []))
372
373 # remove one that doesn't exist?
374 self.assertRaises(FormError, self.parseForm, {':remove:nosy': '4'},
375 'issue', nodeid)
376
377 def testMultilinkRetired(self):
378 self.db.user.retire('2')
379 self.assertEqual(self.parseForm({'nosy': ['2','3']}, 'issue'),
380 ({('issue', None): {'nosy': ['2','3']}}, []))
381 nodeid = self.db.issue.create(nosy=['1','2'])
382 self.assertEqual(self.parseForm({':remove:nosy': '2'}, 'issue',
383 nodeid), ({('issue', nodeid): {'nosy': ['1']}}, []))
384 self.assertEqual(self.parseForm({':add:nosy': '3'}, 'issue', nodeid),
385 ({('issue', nodeid): {'nosy': ['1','2','3']}}, []))
386
387 def testAddRemoveNonexistant(self):
388 self.assertRaises(FormError, self.parseForm, {':remove:foo': '2'},
389 'issue')
390 self.assertRaises(FormError, self.parseForm, {':add:foo': '2'},
391 'issue')
392
393 #
394 # Password
395 #
396 def testEmptyPassword(self):
397 self.assertEqual(self.parseForm({'password': ''}, 'user'),
398 ({('user', None): {}}, []))
399 self.assertEqual(self.parseForm({'password': ''}, 'user'),
400 ({('user', None): {}}, []))
401 self.assertRaises(FormError, self.parseForm, {'password': ['', '']},
402 'user')
403 self.assertRaises(FormError, self.parseForm, {'password': 'foo',
404 ':confirm:password': ['', '']}, 'user')
405
406 def testSetPassword(self):
407 self.assertEqual(self.parseForm({'password': 'foo',
408 ':confirm:password': 'foo'}, 'user'),
409 ({('user', None): {'password': 'foo'}}, []))
410
411 def testSetPasswordConfirmBad(self):
412 self.assertRaises(FormError, self.parseForm, {'password': 'foo'},
413 'user')
414 self.assertRaises(FormError, self.parseForm, {'password': 'foo',
415 ':confirm:password': 'bar'}, 'user')
416
417 def testEmptyPasswordNotSet(self):
418 nodeid = self.db.user.create(username='1',
419 password=password.Password('foo'))
420 self.assertEqual(self.parseForm({'password': ''}, 'user', nodeid),
421 ({('user', nodeid): {}}, []))
422 nodeid = self.db.user.create(username='2',
423 password=password.Password('foo'))
424 self.assertEqual(self.parseForm({'password': '',
425 ':confirm:password': ''}, 'user', nodeid),
426 ({('user', nodeid): {}}, []))
427
428 #
429 # Boolean
430 #
431 def testEmptyBoolean(self):
432 self.assertEqual(self.parseForm({'boolean': ''}),
433 ({('test', None): {}}, []))
434 self.assertEqual(self.parseForm({'boolean': ' '}),
435 ({('test', None): {}}, []))
436 self.assertRaises(FormError, self.parseForm, {'boolean': ['', '']})
437
438 def testSetBoolean(self):
439 self.assertEqual(self.parseForm({'boolean': 'yes'}),
440 ({('test', None): {'boolean': 1}}, []))
441 self.assertEqual(self.parseForm({'boolean': 'a\r\nb\r\n'}),
442 ({('test', None): {'boolean': 0}}, []))
443 nodeid = self.db.test.create(boolean=1)
444 self.assertEqual(self.parseForm({'boolean': 'yes'}, 'test', nodeid),
445 ({('test', nodeid): {}}, []))
446 nodeid = self.db.test.create(boolean=0)
447 self.assertEqual(self.parseForm({'boolean': 'no'}, 'test', nodeid),
448 ({('test', nodeid): {}}, []))
449
450 def testEmptyBooleanSet(self):
451 nodeid = self.db.test.create(boolean=0)
452 self.assertEqual(self.parseForm({'boolean': ''}, 'test', nodeid),
453 ({('test', nodeid): {'boolean': None}}, []))
454 nodeid = self.db.test.create(boolean=1)
455 self.assertEqual(self.parseForm({'boolean': ' '}, 'test', nodeid),
456 ({('test', nodeid): {'boolean': None}}, []))
457
458 def testRequiredBoolean(self):
459 self.assertRaises(FormError, self.parseForm, {'boolean': '',
460 ':required': 'boolean'})
461 try:
462 self.parseForm({'boolean': 'no', ':required': 'boolean'})
463 except FormError:
464 self.fail('boolean "no" raised "required missing"')
465
466 #
467 # Number
468 #
469 def testEmptyNumber(self):
470 self.assertEqual(self.parseForm({'number': ''}),
471 ({('test', None): {}}, []))
472 self.assertEqual(self.parseForm({'number': ' '}),
473 ({('test', None): {}}, []))
474 self.assertRaises(FormError, self.parseForm, {'number': ['', '']})
475
476 def testInvalidNumber(self):
477 self.assertRaises(FormError, self.parseForm, {'number': 'hi, mum!'})
478
479 def testSetNumber(self):
480 self.assertEqual(self.parseForm({'number': '1'}),
481 ({('test', None): {'number': 1}}, []))
482 self.assertEqual(self.parseForm({'number': '0'}),
483 ({('test', None): {'number': 0}}, []))
484 self.assertEqual(self.parseForm({'number': '\n0\n'}),
485 ({('test', None): {'number': 0}}, []))
486
487 def testSetNumberReplaceOne(self):
488 nodeid = self.db.test.create(number=1)
489 self.assertEqual(self.parseForm({'number': '1'}, 'test', nodeid),
490 ({('test', nodeid): {}}, []))
491 self.assertEqual(self.parseForm({'number': '0'}, 'test', nodeid),
492 ({('test', nodeid): {'number': 0}}, []))
493
494 def testSetNumberReplaceZero(self):
495 nodeid = self.db.test.create(number=0)
496 self.assertEqual(self.parseForm({'number': '0'}, 'test', nodeid),
497 ({('test', nodeid): {}}, []))
498
499 def testSetNumberReplaceNone(self):
500 nodeid = self.db.test.create()
501 self.assertEqual(self.parseForm({'number': '0'}, 'test', nodeid),
502 ({('test', nodeid): {'number': 0}}, []))
503 self.assertEqual(self.parseForm({'number': '1'}, 'test', nodeid),
504 ({('test', nodeid): {'number': 1}}, []))
505
506 def testEmptyNumberSet(self):
507 nodeid = self.db.test.create(number=0)
508 self.assertEqual(self.parseForm({'number': ''}, 'test', nodeid),
509 ({('test', nodeid): {'number': None}}, []))
510 nodeid = self.db.test.create(number=1)
511 self.assertEqual(self.parseForm({'number': ' '}, 'test', nodeid),
512 ({('test', nodeid): {'number': None}}, []))
513
514 def testRequiredNumber(self):
515 self.assertRaises(FormError, self.parseForm, {'number': '',
516 ':required': 'number'})
517 try:
518 self.parseForm({'number': '0', ':required': 'number'})
519 except FormError:
520 self.fail('number "no" raised "required missing"')
521
522 #
523 # Date
524 #
525 def testEmptyDate(self):
526 self.assertEqual(self.parseForm({'date': ''}),
527 ({('test', None): {}}, []))
528 self.assertEqual(self.parseForm({'date': ' '}),
529 ({('test', None): {}}, []))
530 self.assertRaises(FormError, self.parseForm, {'date': ['', '']})
531
532 def testInvalidDate(self):
533 self.assertRaises(FormError, self.parseForm, {'date': '12'})
534
535 def testSetDate(self):
536 self.assertEqual(self.parseForm({'date': '2003-01-01'}),
537 ({('test', None): {'date': date.Date('2003-01-01')}}, []))
538 nodeid = self.db.test.create(date=date.Date('2003-01-01'))
539 self.assertEqual(self.parseForm({'date': '2003-01-01'}, 'test',
540 nodeid), ({('test', nodeid): {}}, []))
541
542 def testEmptyDateSet(self):
543 nodeid = self.db.test.create(date=date.Date('.'))
544 self.assertEqual(self.parseForm({'date': ''}, 'test', nodeid),
545 ({('test', nodeid): {'date': None}}, []))
546 nodeid = self.db.test.create(date=date.Date('1970-01-01.00:00:00'))
547 self.assertEqual(self.parseForm({'date': ' '}, 'test', nodeid),
548 ({('test', nodeid): {'date': None}}, []))
549
550 #
551 # Test multiple items in form
552 #
553 def testMultiple(self):
554 self.assertEqual(self.parseForm({'string': 'a', 'issue-1@title': 'b'}),
555 ({('test', None): {'string': 'a'},
556 ('issue', '-1'): {'title': 'b'}
557 }, []))
558
559 def testMultipleExistingContext(self):
560 nodeid = self.db.test.create()
561 self.assertEqual(self.parseForm({'string': 'a', 'issue-1@title': 'b'},
562 'test', nodeid),({('test', nodeid): {'string': 'a'},
563 ('issue', '-1'): {'title': 'b'}}, []))
564
565 def testLinking(self):
566 self.assertEqual(self.parseForm({
567 'string': 'a',
568 'issue-1@add@nosy': '1',
569 'issue-2@link@superseder': 'issue-1',
570 }),
571 ({('test', None): {'string': 'a'},
572 ('issue', '-1'): {'nosy': ['1']},
573 },
574 [('issue', '-2', 'superseder', [('issue', '-1')])
575 ]
576 )
577 )
578
579 def testMessages(self):
580 self.assertEqual(self.parseForm({
581 'msg-1@content': 'asdf',
582 'msg-2@content': 'qwer',
583 '@link@messages': 'msg-1, msg-2'}),
584 ({('test', None): {},
585 ('msg', '-2'): {'content': 'qwer'},
586 ('msg', '-1'): {'content': 'asdf'}},
587 [('test', None, 'messages', [('msg', '-1'), ('msg', '-2')])]
588 )
589 )
590
591 def testLinkBadDesignator(self):
592 self.assertRaises(FormError, self.parseForm,
593 {'test-1@link@link': 'blah'})
594 self.assertRaises(FormError, self.parseForm,
595 {'test-1@link@link': 'issue'})
596
597 def testLinkNotLink(self):
598 self.assertRaises(FormError, self.parseForm,
599 {'test-1@link@boolean': 'issue-1'})
600 self.assertRaises(FormError, self.parseForm,
601 {'test-1@link@string': 'issue-1'})
602
603 def testBackwardsCompat(self):
604 res = self.parseForm({':note': 'spam'}, 'issue')
605 date = res[0][('msg', '-1')]['date']
606 self.assertEqual(res, ({('issue', None): {}, ('msg', '-1'):
607 {'content': 'spam', 'author': '1', 'date': date}},
608 [('issue', None, 'messages', [('msg', '-1')])]))
609 file = FileUpload('foo', 'foo.txt')
610 self.assertEqual(self.parseForm({':file': file}, 'issue'),
611 ({('issue', None): {}, ('file', '-1'): {'content': 'foo',
612 'name': 'foo.txt', 'type': 'text/plain'}},
613 [('issue', None, 'files', [('file', '-1')])]))
614
615 #
616 # SECURITY
617 #
618 # XXX test all default permissions
619 def _make_client(self, form, classname='user', nodeid='1', userid='2'):
620 cl = client.Client(self.instance, None, {'PATH_INFO':'/',
621 'REQUEST_METHOD':'POST'}, makeForm(form))
622 cl.classname = 'user'
623 if nodeid is not None:
624 cl.nodeid = nodeid
625 cl.db = self.db
626 cl.userid = userid
627 cl.language = ('en',)
628 cl.error_message = []
629 cl.template = 'item'
630 return cl
631
632 def testClassPermission(self):
633 cl = self._make_client(dict(username='bob'))
634 self.failUnlessRaises(exceptions.Unauthorised,
635 actions.EditItemAction(cl).handle)
636 cl.nodeid = '1'
637 self.assertRaises(exceptions.Unauthorised,
638 actions.EditItemAction(cl).handle)
639
640 def testCheckAndPropertyPermission(self):
641 self.db.security.permissions = {}
642 def own_record(db, userid, itemid):
643 return userid == itemid
644 p = self.db.security.addPermission(name='Edit', klass='user',
645 check=own_record, properties=("password", ))
646 self.db.security.addPermissionToRole('User', p)
647
648 cl = self._make_client(dict(username='bob'))
649 self.assertRaises(exceptions.Unauthorised,
650 actions.EditItemAction(cl).handle)
651 cl = self._make_client(dict(roles='User,Admin'), userid='4', nodeid='4')
652 self.assertRaises(exceptions.Unauthorised,
653 actions.EditItemAction(cl).handle)
654 cl = self._make_client(dict(roles='User,Admin'), userid='4')
655 self.assertRaises(exceptions.Unauthorised,
656 actions.EditItemAction(cl).handle)
657 cl = self._make_client(dict(roles='User,Admin'))
658 self.assertRaises(exceptions.Unauthorised,
659 actions.EditItemAction(cl).handle)
660 # working example, mary may change her pw
661 cl = self._make_client({'password':'ob', '@confirm@password':'ob'},
662 nodeid='4', userid='4')
663 self.assertRaises(exceptions.Redirect,
664 actions.EditItemAction(cl).handle)
665 cl = self._make_client({'password':'bob', '@confirm@password':'bob'})
666 self.failUnlessRaises(exceptions.Unauthorised,
667 actions.EditItemAction(cl).handle)
668
669 def testCreatePermission(self):
670 # this checks if we properly differentiate between create and
671 # edit permissions
672 self.db.security.permissions = {}
673 self.db.security.addRole(name='UserAdd')
674 # Don't allow roles
675 p = self.db.security.addPermission(name='Create', klass='user',
676 properties=("username", "password", "address",
677 "alternate_address", "realname", "phone", "organisation",
678 "timezone"))
679 self.db.security.addPermissionToRole('UserAdd', p)
680 # Don't allow roles *and* don't allow username
681 p = self.db.security.addPermission(name='Edit', klass='user',
682 properties=("password", "address", "alternate_address",
683 "realname", "phone", "organisation", "timezone"))
684 self.db.security.addPermissionToRole('UserAdd', p)
685 self.db.user.set('4', roles='UserAdd')
686
687 # anonymous may not
688 cl = self._make_client({'username':'new_user', 'password':'secret',
689 '@confirm@password':'secret', 'address':'new_user@bork.bork',
690 'roles':'Admin'}, nodeid=None, userid='2')
691 self.assertRaises(exceptions.Unauthorised,
692 actions.NewItemAction(cl).handle)
693 # Don't allow creating new user with roles
694 cl = self._make_client({'username':'new_user', 'password':'secret',
695 '@confirm@password':'secret', 'address':'new_user@bork.bork',
696 'roles':'Admin'}, nodeid=None, userid='4')
697 self.assertRaises(exceptions.Unauthorised,
698 actions.NewItemAction(cl).handle)
699 self.assertEqual(cl.error_message,[])
700 # this should work
701 cl = self._make_client({'username':'new_user', 'password':'secret',
702 '@confirm@password':'secret', 'address':'new_user@bork.bork'},
703 nodeid=None, userid='4')
704 self.assertRaises(exceptions.Redirect,
705 actions.NewItemAction(cl).handle)
706 self.assertEqual(cl.error_message,[])
707 # don't allow changing (my own) username (in this example)
708 cl = self._make_client(dict(username='new_user42'), userid='4')
709 self.assertRaises(exceptions.Unauthorised,
710 actions.EditItemAction(cl).handle)
711 cl = self._make_client(dict(username='new_user42'), userid='4',
712 nodeid='4')
713 self.assertRaises(exceptions.Unauthorised,
714 actions.EditItemAction(cl).handle)
715 # don't allow changing (my own) roles
716 cl = self._make_client(dict(roles='User,Admin'), userid='4',
717 nodeid='4')
718 self.assertRaises(exceptions.Unauthorised,
719 actions.EditItemAction(cl).handle)
720 cl = self._make_client(dict(roles='User,Admin'), userid='4')
721 self.assertRaises(exceptions.Unauthorised,
722 actions.EditItemAction(cl).handle)
723 cl = self._make_client(dict(roles='User,Admin'))
724 self.assertRaises(exceptions.Unauthorised,
725 actions.EditItemAction(cl).handle)
726
727 def testRoles(self):
728 cl = self._make_client({})
729 self.db.user.set('1', roles='aDmin, uSer')
730 item = HTMLItem(cl, 'user', '1')
731 self.assert_(item.hasRole('Admin'))
732 self.assert_(item.hasRole('User'))
733 self.assert_(item.hasRole('AdmiN'))
734 self.assert_(item.hasRole('UseR'))
735 self.assert_(item.hasRole('UseR','Admin'))
736 self.assert_(item.hasRole('UseR','somethingelse'))
737 self.assert_(item.hasRole('somethingelse','Admin'))
738 self.assert_(not item.hasRole('userr'))
739 self.assert_(not item.hasRole('adminn'))
740 self.assert_(not item.hasRole(''))
741 self.assert_(not item.hasRole(' '))
742 self.db.user.set('1', roles='')
743 self.assert_(not item.hasRole(''))
744
745 def testCSVExport(self):
746 cl = self._make_client({'@columns': 'id,name'}, nodeid=None,
747 userid='1')
748 cl.classname = 'status'
749 output = StringIO.StringIO()
750 cl.request = MockNull()
751 cl.request.wfile = output
752 actions.ExportCSVAction(cl).handle()
753 self.assertEquals('id,name\r\n1,unread\r\n2,deferred\r\n3,chatting\r\n'
754 '4,need-eg\r\n5,in-progress\r\n6,testing\r\n7,done-cbb\r\n'
755 '8,resolved\r\n',
756 output.getvalue())
757
758 def testCSVExportFailPermission(self):
759 cl = self._make_client({'@columns': 'id,email,password'}, nodeid=None,
760 userid='2')
761 cl.classname = 'user'
762 output = StringIO.StringIO()
763 cl.request = MockNull()
764 cl.request.wfile = output
765 self.assertRaises(exceptions.Unauthorised,
766 actions.ExportCSVAction(cl).handle)
767
768
769 def test_suite():
770 suite = unittest.TestSuite()
771
772 def test_suite():
773 suite = unittest.TestSuite()
774 suite.addTest(unittest.makeSuite(FormTestCase))
775 suite.addTest(unittest.makeSuite(MessageTestCase))
776 return suite
777
778 if __name__ == '__main__':
779 runner = unittest.TextTestRunner()
780 unittest.main(testRunner=runner)
781
782 # vim: set filetype=python sts=4 sw=4 et si :