Correction #78
[auf_roundup.git] / test / .svn / text-base / test_cgi.py.svn-base
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 :