Code

Finished implementation of session and one-time-key stores for RDBMS
[roundup.git] / test / db_test_base.py
1 #
2 # Copyright (c) 2001 Bizar Software Pty Ltd (http://www.bizarsoftware.com.au/)
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 # IN NO EVENT SHALL BIZAR SOFTWARE PTY LTD BE LIABLE TO ANY PARTY FOR
8 # DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING
9 # OUT OF THE USE OF THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE
10 # POSSIBILITY OF SUCH DAMAGE.
11 #
12 # BIZAR SOFTWARE PTY LTD SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
13 # BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
14 # FOR A PARTICULAR PURPOSE.  THE CODE PROVIDED HEREUNDER IS ON AN "AS IS"
15 # BASIS, AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE,
16 # SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
17
18 # $Id: db_test_base.py,v 1.17 2004-03-18 01:58:46 richard Exp $ 
20 import unittest, os, shutil, errno, imp, sys, time, pprint
22 from roundup.hyperdb import String, Password, Link, Multilink, Date, \
23     Interval, DatabaseError, Boolean, Number, Node
24 from roundup import date, password
25 from roundup import init
26 from roundup.indexer import Indexer
28 def setupSchema(db, create, module):
29     status = module.Class(db, "status", name=String())
30     status.setkey("name")
31     user = module.Class(db, "user", username=String(), password=Password(),
32         assignable=Boolean(), age=Number(), roles=String())
33     user.setkey("username")
34     file = module.FileClass(db, "file", name=String(), type=String(),
35         comment=String(indexme="yes"), fooz=Password())
36     issue = module.IssueClass(db, "issue", title=String(indexme="yes"),
37         status=Link("status"), nosy=Multilink("user"), deadline=Date(),
38         foo=Interval(), files=Multilink("file"), assignedto=Link('user'))
39     stuff = module.Class(db, "stuff", stuff=String())
40     session = module.Class(db, 'session', title=String())
41     session.disableJournalling()
42     db.post_init()
43     if create:
44         user.create(username="admin", roles='Admin',
45             password=password.Password('sekrit'))
46         user.create(username="fred", roles='User',
47             password=password.Password('sekrit'))
48         status.create(name="unread")
49         status.create(name="in-progress")
50         status.create(name="testing")
51         status.create(name="resolved")
52     db.commit()
54 class MyTestCase(unittest.TestCase):
55     def tearDown(self):
56         if hasattr(self, 'db'):
57             self.db.close()
58         if os.path.exists(config.DATABASE):
59             shutil.rmtree(config.DATABASE)
61 class config:
62     DATABASE='_test_dir'
63     MAILHOST = 'localhost'
64     MAIL_DOMAIN = 'fill.me.in.'
65     TRACKER_NAME = 'Roundup issue tracker'
66     TRACKER_EMAIL = 'issue_tracker@%s'%MAIL_DOMAIN
67     TRACKER_WEB = 'http://some.useful.url/'
68     ADMIN_EMAIL = 'roundup-admin@%s'%MAIL_DOMAIN
69     FILTER_POSITION = 'bottom'      # one of 'top', 'bottom', 'top and bottom'
70     ANONYMOUS_ACCESS = 'deny'       # either 'deny' or 'allow'
71     ANONYMOUS_REGISTER = 'deny'     # either 'deny' or 'allow'
72     MESSAGES_TO_AUTHOR = 'no'       # either 'yes' or 'no'
73     EMAIL_SIGNATURE_POSITION = 'bottom'
76 class DBTest(MyTestCase):
77     def setUp(self):
78         # remove previous test, ignore errors
79         if os.path.exists(config.DATABASE):
80             shutil.rmtree(config.DATABASE)
81         os.makedirs(config.DATABASE + '/files')
82         self.db = self.module.Database(config, 'admin')
83         setupSchema(self.db, 1, self.module)
85     def testRefresh(self):
86         self.db.refresh_database()
88     #
89     # automatic properties (well, the two easy ones anyway)
90     #
91     def testCreatorProperty(self):
92         id1 = self.db.issue.create()
93         self.db.commit()
94         self.db.close()
95         self.db = self.module.Database(config, 'fred')
96         setupSchema(self.db, 0, self.module)
97         i = self.db.issue
98         id2 = i.create()
99         self.assertNotEqual(id1, id2)
100         self.assertNotEqual(i.get(id1, 'creator'), i.get(id2, 'creator'))
102     def testActorProperty(self):
103         id1 = self.db.issue.create()
104         self.db.commit()
105         self.db.close()
106         self.db = self.module.Database(config, 'fred')
107         setupSchema(self.db, 0, self.module)
108         i = self.db.issue
109         i.set(id1, title='asfasd')
110         self.assertNotEqual(i.get(id1, 'creator'), i.get(id1, 'actor'))
112     #
113     # basic operations
114     #
115     def testIDGeneration(self):
116         id1 = self.db.issue.create(title="spam", status='1')
117         id2 = self.db.issue.create(title="eggs", status='2')
118         self.assertNotEqual(id1, id2)
120     def testEmptySet(self):
121         id1 = self.db.issue.create(title="spam", status='1')
122         self.db.issue.set(id1)
124     def testStringChange(self):
125         for commit in (0,1):
126             # test set & retrieve
127             nid = self.db.issue.create(title="spam", status='1')
128             self.assertEqual(self.db.issue.get(nid, 'title'), 'spam')
130             # change and make sure we retrieve the correct value
131             self.db.issue.set(nid, title='eggs')
132             if commit: self.db.commit()
133             self.assertEqual(self.db.issue.get(nid, 'title'), 'eggs')
135     def testStringUnset(self):
136         for commit in (0,1):
137             nid = self.db.issue.create(title="spam", status='1')
138             if commit: self.db.commit()
139             self.assertEqual(self.db.issue.get(nid, 'title'), 'spam')
140             # make sure we can unset
141             self.db.issue.set(nid, title=None)
142             if commit: self.db.commit()
143             self.assertEqual(self.db.issue.get(nid, "title"), None)
145     def testLinkChange(self):
146         self.assertRaises(IndexError, self.db.issue.create, title="spam",
147             status='100')
148         for commit in (0,1):
149             nid = self.db.issue.create(title="spam", status='1')
150             if commit: self.db.commit()
151             self.assertEqual(self.db.issue.get(nid, "status"), '1')
152             self.db.issue.set(nid, status='2')
153             if commit: self.db.commit()
154             self.assertEqual(self.db.issue.get(nid, "status"), '2')
156     def testLinkUnset(self):
157         for commit in (0,1):
158             nid = self.db.issue.create(title="spam", status='1')
159             if commit: self.db.commit()
160             self.db.issue.set(nid, status=None)
161             if commit: self.db.commit()
162             self.assertEqual(self.db.issue.get(nid, "status"), None)
164     def testMultilinkChange(self):
165         for commit in (0,1):
166             self.assertRaises(IndexError, self.db.issue.create, title="spam",
167                 nosy=['foo%s'%commit])
168             u1 = self.db.user.create(username='foo%s'%commit)
169             u2 = self.db.user.create(username='bar%s'%commit)
170             nid = self.db.issue.create(title="spam", nosy=[u1])
171             if commit: self.db.commit()
172             self.assertEqual(self.db.issue.get(nid, "nosy"), [u1])
173             self.db.issue.set(nid, nosy=[])
174             if commit: self.db.commit()
175             self.assertEqual(self.db.issue.get(nid, "nosy"), [])
176             self.db.issue.set(nid, nosy=[u1,u2])
177             if commit: self.db.commit()
178             self.assertEqual(self.db.issue.get(nid, "nosy"), [u1,u2])
180     def testDateChange(self):
181         self.assertRaises(TypeError, self.db.issue.create, 
182             title='spam', deadline=1)
183         for commit in (0,1):
184             nid = self.db.issue.create(title="spam", status='1')
185             self.assertRaises(TypeError, self.db.issue.set, nid, deadline=1)
186             a = self.db.issue.get(nid, "deadline")
187             if commit: self.db.commit()
188             self.db.issue.set(nid, deadline=date.Date())
189             b = self.db.issue.get(nid, "deadline")
190             if commit: self.db.commit()
191             self.assertNotEqual(a, b)
192             self.assertNotEqual(b, date.Date('1970-1-1 00:00:00'))
194     def testDateUnset(self):
195         for commit in (0,1):
196             nid = self.db.issue.create(title="spam", status='1')
197             self.db.issue.set(nid, deadline=date.Date())
198             if commit: self.db.commit()
199             self.assertNotEqual(self.db.issue.get(nid, "deadline"), None)
200             self.db.issue.set(nid, deadline=None)
201             if commit: self.db.commit()
202             self.assertEqual(self.db.issue.get(nid, "deadline"), None)
204     def testIntervalChange(self):
205         self.assertRaises(TypeError, self.db.issue.create, 
206             title='spam', foo=1)
207         for commit in (0,1):
208             nid = self.db.issue.create(title="spam", status='1')
209             self.assertRaises(TypeError, self.db.issue.set, nid, foo=1)
210             if commit: self.db.commit()
211             a = self.db.issue.get(nid, "foo")
212             i = date.Interval('-1d')
213             self.db.issue.set(nid, foo=i)
214             if commit: self.db.commit()
215             self.assertNotEqual(self.db.issue.get(nid, "foo"), a)
216             self.assertEqual(i, self.db.issue.get(nid, "foo"))
217             j = date.Interval('1y')
218             self.db.issue.set(nid, foo=j)
219             if commit: self.db.commit()
220             self.assertNotEqual(self.db.issue.get(nid, "foo"), i)
221             self.assertEqual(j, self.db.issue.get(nid, "foo"))
223     def testIntervalUnset(self):
224         for commit in (0,1):
225             nid = self.db.issue.create(title="spam", status='1')
226             self.db.issue.set(nid, foo=date.Interval('-1d'))
227             if commit: self.db.commit()
228             self.assertNotEqual(self.db.issue.get(nid, "foo"), None)
229             self.db.issue.set(nid, foo=None)
230             if commit: self.db.commit()
231             self.assertEqual(self.db.issue.get(nid, "foo"), None)
233     def testBooleanChange(self):
234         userid = self.db.user.create(username='foo', assignable=1)
235         self.assertEqual(1, self.db.user.get(userid, 'assignable'))
236         self.db.user.set(userid, assignable=0)
237         self.assertEqual(self.db.user.get(userid, 'assignable'), 0)
239     def testBooleanUnset(self):
240         nid = self.db.user.create(username='foo', assignable=1)
241         self.db.user.set(nid, assignable=None)
242         self.assertEqual(self.db.user.get(nid, "assignable"), None)
244     def testNumberChange(self):
245         nid = self.db.user.create(username='foo', age=1)
246         self.assertEqual(1, self.db.user.get(nid, 'age'))
247         self.db.user.set(nid, age=3)
248         self.assertNotEqual(self.db.user.get(nid, 'age'), 1)
249         self.db.user.set(nid, age=1.0)
250         self.assertEqual(self.db.user.get(nid, 'age'), 1)
251         self.db.user.set(nid, age=0)
252         self.assertEqual(self.db.user.get(nid, 'age'), 0)
254         nid = self.db.user.create(username='bar', age=0)
255         self.assertEqual(self.db.user.get(nid, 'age'), 0)
257     def testNumberUnset(self):
258         nid = self.db.user.create(username='foo', age=1)
259         self.db.user.set(nid, age=None)
260         self.assertEqual(self.db.user.get(nid, "age"), None)
262     def testPasswordChange(self):
263         x = password.Password('x')
264         userid = self.db.user.create(username='foo', password=x)
265         self.assertEqual(x, self.db.user.get(userid, 'password'))
266         self.assertEqual(self.db.user.get(userid, 'password'), 'x')
267         y = password.Password('y')
268         self.db.user.set(userid, password=y)
269         self.assertEqual(self.db.user.get(userid, 'password'), 'y')
270         self.assertRaises(TypeError, self.db.user.create, userid,
271             username='bar', password='x')
272         self.assertRaises(TypeError, self.db.user.set, userid, password='x')
274     def testPasswordUnset(self):
275         x = password.Password('x')
276         nid = self.db.user.create(username='foo', password=x)
277         self.db.user.set(nid, assignable=None)
278         self.assertEqual(self.db.user.get(nid, "assignable"), None)
280     def testKeyValue(self):
281         self.assertRaises(ValueError, self.db.user.create)
283         newid = self.db.user.create(username="spam")
284         self.assertEqual(self.db.user.lookup('spam'), newid)
285         self.db.commit()
286         self.assertEqual(self.db.user.lookup('spam'), newid)
287         self.db.user.retire(newid)
288         self.assertRaises(KeyError, self.db.user.lookup, 'spam')
290         # use the key again now that the old is retired
291         newid2 = self.db.user.create(username="spam")
292         self.assertNotEqual(newid, newid2)
293         # try to restore old node. this shouldn't succeed!
294         self.assertRaises(KeyError, self.db.user.restore, newid)
296         self.assertRaises(TypeError, self.db.issue.lookup, 'fubar')
298     def testLabelProp(self):
299         # key prop
300         self.assertEqual(self.db.status.labelprop(), 'name')
301         self.assertEqual(self.db.user.labelprop(), 'username')
302         # title
303         self.assertEqual(self.db.issue.labelprop(), 'title')
304         # name
305         self.assertEqual(self.db.file.labelprop(), 'name')
306         # id
307         self.assertEqual(self.db.stuff.labelprop(default_to_id=1), 'id')
309     def testRetire(self):
310         self.db.issue.create(title="spam", status='1')
311         b = self.db.status.get('1', 'name')
312         a = self.db.status.list()
313         self.db.status.retire('1')
314         # make sure the list is different 
315         self.assertNotEqual(a, self.db.status.list())
316         # can still access the node if necessary
317         self.assertEqual(self.db.status.get('1', 'name'), b)
318         self.assertRaises(IndexError, self.db.status.set, '1', name='hello')
319         self.db.commit()
320         self.assertEqual(self.db.status.get('1', 'name'), b)
321         self.assertNotEqual(a, self.db.status.list())
322         # try to restore retired node
323         self.db.status.restore('1')
324  
325     def testCacheCreateSet(self):
326         self.db.issue.create(title="spam", status='1')
327         a = self.db.issue.get('1', 'title')
328         self.assertEqual(a, 'spam')
329         self.db.issue.set('1', title='ham')
330         b = self.db.issue.get('1', 'title')
331         self.assertEqual(b, 'ham')
333     def testSerialisation(self):
334         nid = self.db.issue.create(title="spam", status='1',
335             deadline=date.Date(), foo=date.Interval('-1d'))
336         self.db.commit()
337         assert isinstance(self.db.issue.get(nid, 'deadline'), date.Date)
338         assert isinstance(self.db.issue.get(nid, 'foo'), date.Interval)
339         uid = self.db.user.create(username="fozzy",
340             password=password.Password('t. bear'))
341         self.db.commit()
342         assert isinstance(self.db.user.get(uid, 'password'), password.Password)
344     def testTransactions(self):
345         # remember the number of items we started
346         num_issues = len(self.db.issue.list())
347         num_files = self.db.numfiles()
348         self.db.issue.create(title="don't commit me!", status='1')
349         self.assertNotEqual(num_issues, len(self.db.issue.list()))
350         self.db.rollback()
351         self.assertEqual(num_issues, len(self.db.issue.list()))
352         self.db.issue.create(title="please commit me!", status='1')
353         self.assertNotEqual(num_issues, len(self.db.issue.list()))
354         self.db.commit()
355         self.assertNotEqual(num_issues, len(self.db.issue.list()))
356         self.db.rollback()
357         self.assertNotEqual(num_issues, len(self.db.issue.list()))
358         self.db.file.create(name="test", type="text/plain", content="hi")
359         self.db.rollback()
360         self.assertEqual(num_files, self.db.numfiles())
361         for i in range(10):
362             self.db.file.create(name="test", type="text/plain", 
363                     content="hi %d"%(i))
364             self.db.commit()
365         num_files2 = self.db.numfiles()
366         self.assertNotEqual(num_files, num_files2)
367         self.db.file.create(name="test", type="text/plain", content="hi")
368         self.db.rollback()
369         self.assertNotEqual(num_files, self.db.numfiles())
370         self.assertEqual(num_files2, self.db.numfiles())
372         # rollback / cache interaction
373         name1 = self.db.user.get('1', 'username')
374         self.db.user.set('1', username = name1+name1)
375         # get the prop so the info's forced into the cache (if there is one)
376         self.db.user.get('1', 'username')
377         self.db.rollback()
378         name2 = self.db.user.get('1', 'username')
379         self.assertEqual(name1, name2)
381     def testDestroyNoJournalling(self):
382         self.innerTestDestroy(klass=self.db.session)
384     def testDestroyJournalling(self):
385         self.innerTestDestroy(klass=self.db.issue)
387     def innerTestDestroy(self, klass):
388         newid = klass.create(title='Mr Friendly')
389         n = len(klass.list())
390         self.assertEqual(klass.get(newid, 'title'), 'Mr Friendly')
391         count = klass.count()
392         klass.destroy(newid)
393         self.assertNotEqual(count, klass.count())
394         self.assertRaises(IndexError, klass.get, newid, 'title')
395         self.assertNotEqual(len(klass.list()), n)
396         if klass.do_journal:
397             self.assertRaises(IndexError, klass.history, newid)
399         # now with a commit
400         newid = klass.create(title='Mr Friendly')
401         n = len(klass.list())
402         self.assertEqual(klass.get(newid, 'title'), 'Mr Friendly')
403         self.db.commit()
404         count = klass.count()
405         klass.destroy(newid)
406         self.assertNotEqual(count, klass.count())
407         self.assertRaises(IndexError, klass.get, newid, 'title')
408         self.db.commit()
409         self.assertRaises(IndexError, klass.get, newid, 'title')
410         self.assertNotEqual(len(klass.list()), n)
411         if klass.do_journal:
412             self.assertRaises(IndexError, klass.history, newid)
414         # now with a rollback
415         newid = klass.create(title='Mr Friendly')
416         n = len(klass.list())
417         self.assertEqual(klass.get(newid, 'title'), 'Mr Friendly')
418         self.db.commit()
419         count = klass.count()
420         klass.destroy(newid)
421         self.assertNotEqual(len(klass.list()), n)
422         self.assertRaises(IndexError, klass.get, newid, 'title')
423         self.db.rollback()
424         self.assertEqual(count, klass.count())
425         self.assertEqual(klass.get(newid, 'title'), 'Mr Friendly')
426         self.assertEqual(len(klass.list()), n)
427         if klass.do_journal:
428             self.assertNotEqual(klass.history(newid), [])
430     def testExceptions(self):
431         # this tests the exceptions that should be raised
432         ar = self.assertRaises
434         ar(KeyError, self.db.getclass, 'fubar')
436         #
437         # class create
438         #
439         # string property
440         ar(TypeError, self.db.status.create, name=1)
441         # id, creation, creator and activity properties are reserved
442         ar(KeyError, self.db.status.create, id=1)
443         ar(KeyError, self.db.status.create, creation=1)
444         ar(KeyError, self.db.status.create, creator=1)
445         ar(KeyError, self.db.status.create, activity=1)
446         ar(KeyError, self.db.status.create, actor=1)
447         # invalid property name
448         ar(KeyError, self.db.status.create, foo='foo')
449         # key name clash
450         ar(ValueError, self.db.status.create, name='unread')
451         # invalid link index
452         ar(IndexError, self.db.issue.create, title='foo', status='bar')
453         # invalid link value
454         ar(ValueError, self.db.issue.create, title='foo', status=1)
455         # invalid multilink type
456         ar(TypeError, self.db.issue.create, title='foo', status='1',
457             nosy='hello')
458         # invalid multilink index type
459         ar(ValueError, self.db.issue.create, title='foo', status='1',
460             nosy=[1])
461         # invalid multilink index
462         ar(IndexError, self.db.issue.create, title='foo', status='1',
463             nosy=['10'])
465         #
466         # key property
467         # 
468         # key must be a String
469         ar(TypeError, self.db.file.setkey, 'fooz')
470         # key must exist
471         ar(KeyError, self.db.file.setkey, 'fubar')
473         #
474         # class get
475         #
476         # invalid node id
477         ar(IndexError, self.db.issue.get, '99', 'title')
478         # invalid property name
479         ar(KeyError, self.db.status.get, '2', 'foo')
481         #
482         # class set
483         #
484         # invalid node id
485         ar(IndexError, self.db.issue.set, '99', title='foo')
486         # invalid property name
487         ar(KeyError, self.db.status.set, '1', foo='foo')
488         # string property
489         ar(TypeError, self.db.status.set, '1', name=1)
490         # key name clash
491         ar(ValueError, self.db.status.set, '2', name='unread')
492         # set up a valid issue for me to work on
493         id = self.db.issue.create(title="spam", status='1')
494         # invalid link index
495         ar(IndexError, self.db.issue.set, id, title='foo', status='bar')
496         # invalid link value
497         ar(ValueError, self.db.issue.set, id, title='foo', status=1)
498         # invalid multilink type
499         ar(TypeError, self.db.issue.set, id, title='foo', status='1',
500             nosy='hello')
501         # invalid multilink index type
502         ar(ValueError, self.db.issue.set, id, title='foo', status='1',
503             nosy=[1])
504         # invalid multilink index
505         ar(IndexError, self.db.issue.set, id, title='foo', status='1',
506             nosy=['10'])
507         # NOTE: the following increment the username to avoid problems
508         # within metakit's backend (it creates the node, and then sets the
509         # info, so the create (and by a fluke the username set) go through
510         # before the age/assignable/etc. set, which raises the exception)
511         # invalid number value
512         ar(TypeError, self.db.user.create, username='foo', age='a')
513         # invalid boolean value
514         ar(TypeError, self.db.user.create, username='foo2', assignable='true')
515         nid = self.db.user.create(username='foo3')
516         # invalid number value
517         ar(TypeError, self.db.user.set, nid, age='a')
518         # invalid boolean value
519         ar(TypeError, self.db.user.set, nid, assignable='true')
521     def testJournals(self):
522         self.db.user.create(username="mary")
523         self.db.user.create(username="pete")
524         self.db.issue.create(title="spam", status='1')
525         self.db.commit()
527         # journal entry for issue create
528         journal = self.db.getjournal('issue', '1')
529         self.assertEqual(1, len(journal))
530         (nodeid, date_stamp, journaltag, action, params) = journal[0]
531         self.assertEqual(nodeid, '1')
532         self.assertEqual(journaltag, self.db.user.lookup('admin'))
533         self.assertEqual(action, 'create')
534         keys = params.keys()
535         keys.sort()
536         self.assertEqual(keys, [])
538         # journal entry for link
539         journal = self.db.getjournal('user', '1')
540         self.assertEqual(1, len(journal))
541         self.db.issue.set('1', assignedto='1')
542         self.db.commit()
543         journal = self.db.getjournal('user', '1')
544         self.assertEqual(2, len(journal))
545         (nodeid, date_stamp, journaltag, action, params) = journal[1]
546         self.assertEqual('1', nodeid)
547         self.assertEqual('1', journaltag)
548         self.assertEqual('link', action)
549         self.assertEqual(('issue', '1', 'assignedto'), params)
551         # journal entry for unlink
552         self.db.issue.set('1', assignedto='2')
553         self.db.commit()
554         journal = self.db.getjournal('user', '1')
555         self.assertEqual(3, len(journal))
556         (nodeid, date_stamp, journaltag, action, params) = journal[2]
557         self.assertEqual('1', nodeid)
558         self.assertEqual('1', journaltag)
559         self.assertEqual('unlink', action)
560         self.assertEqual(('issue', '1', 'assignedto'), params)
562         # test disabling journalling
563         # ... get the last entry
564         time.sleep(1)
565         entry = self.db.getjournal('issue', '1')[-1]
566         (x, date_stamp, x, x, x) = entry
567         self.db.issue.disableJournalling()
568         self.db.issue.set('1', title='hello world')
569         self.db.commit()
570         entry = self.db.getjournal('issue', '1')[-1]
571         (x, date_stamp2, x, x, x) = entry
572         # see if the change was journalled when it shouldn't have been
573         self.assertEqual(date_stamp, date_stamp2)
574         time.sleep(1)
575         self.db.issue.enableJournalling()
576         self.db.issue.set('1', title='hello world 2')
577         self.db.commit()
578         entry = self.db.getjournal('issue', '1')[-1]
579         (x, date_stamp2, x, x, x) = entry
580         # see if the change was journalled
581         self.assertNotEqual(date_stamp, date_stamp2)
583     def testJournalPreCommit(self):
584         id = self.db.user.create(username="mary")
585         self.assertEqual(len(self.db.getjournal('user', id)), 1)
586         self.db.commit()
588     def testPack(self):
589         id = self.db.issue.create(title="spam", status='1')
590         self.db.commit()
591         self.db.issue.set(id, status='2')
592         self.db.commit()
594         # sleep for at least a second, then get a date to pack at
595         time.sleep(1)
596         pack_before = date.Date('.')
598         # wait another second and add one more entry
599         time.sleep(1)
600         self.db.issue.set(id, status='3')
601         self.db.commit()
602         jlen = len(self.db.getjournal('issue', id))
604         # pack
605         self.db.pack(pack_before)
607         # we should have the create and last set entries now
608         self.assertEqual(jlen-1, len(self.db.getjournal('issue', id)))
610     def testIndexerSearching(self):
611         f1 = self.db.file.create(content='hello', type="text/plain")
612         f2 = self.db.file.create(content='world', type="text/frozz",
613             comment='blah blah')
614         i1 = self.db.issue.create(files=[f1, f2], title="flebble plop")
615         i2 = self.db.issue.create(title="flebble frooz")
616         self.db.commit()
617         self.assertEquals(self.db.indexer.search(['hello'], self.db.issue),
618             {i1: {'files': [f1]}})
619         self.assertEquals(self.db.indexer.search(['world'], self.db.issue), {})
620         self.assertEquals(self.db.indexer.search(['frooz'], self.db.issue),
621             {i2: {}})
622         self.assertEquals(self.db.indexer.search(['flebble'], self.db.issue),
623             {i1: {}, i2: {}})
625     def testReindexing(self):
626         self.db.issue.create(title="frooz")
627         self.db.commit()
628         self.assertEquals(self.db.indexer.search(['frooz'], self.db.issue),
629             {'1': {}})
630         self.db.issue.set('1', title="dooble")
631         self.db.commit()
632         self.assertEquals(self.db.indexer.search(['dooble'], self.db.issue),
633             {'1': {}})
634         self.assertEquals(self.db.indexer.search(['frooz'], self.db.issue), {})
636     def testForcedReindexing(self):
637         self.db.issue.create(title="flebble frooz")
638         self.db.commit()
639         self.assertEquals(self.db.indexer.search(['flebble'], self.db.issue),
640             {'1': {}})
641         self.db.indexer.quiet = 1
642         self.db.indexer.force_reindex()
643         self.db.post_init()
644         self.db.indexer.quiet = 9
645         self.assertEquals(self.db.indexer.search(['flebble'], self.db.issue),
646             {'1': {}})
648     #
649     # searching tests follow
650     #
651     def testFindIncorrectProperty(self):
652         self.assertRaises(TypeError, self.db.issue.find, title='fubar')
654     def _find_test_setup(self):
655         self.db.file.create(content='')
656         self.db.file.create(content='')
657         self.db.user.create(username='')
658         one = self.db.issue.create(status="1", nosy=['1'])
659         two = self.db.issue.create(status="2", nosy=['2'], files=['1'],
660             assignedto='2')
661         three = self.db.issue.create(status="1", nosy=['1','2'])
662         four = self.db.issue.create(status="3", assignedto='1',
663             files=['1','2'])
664         return one, two, three, four
666     def testFindLink(self):
667         one, two, three, four = self._find_test_setup()
668         got = self.db.issue.find(status='1')
669         got.sort()
670         self.assertEqual(got, [one, three])
671         got = self.db.issue.find(status={'1':1})
672         got.sort()
673         self.assertEqual(got, [one, three])
675     def testFindLinkFail(self):
676         self._find_test_setup()
677         self.assertEqual(self.db.issue.find(status='4'), [])
678         self.assertEqual(self.db.issue.find(status={'4':1}), [])
680     def testFindLinkUnset(self):
681         one, two, three, four = self._find_test_setup()
682         got = self.db.issue.find(assignedto=None)
683         got.sort()
684         self.assertEqual(got, [one, three])
685         got = self.db.issue.find(assignedto={None:1})
686         got.sort()
687         self.assertEqual(got, [one, three])
689     def testFindMultilink(self):
690         one, two, three, four = self._find_test_setup()
691         got = self.db.issue.find(nosy='2')
692         got.sort()
693         self.assertEqual(got, [two, three])
694         got = self.db.issue.find(nosy={'2':1})
695         got.sort()
696         self.assertEqual(got, [two, three])
697         got = self.db.issue.find(nosy={'2':1}, files={})
698         got.sort()
699         self.assertEqual(got, [two, three])
701     def testFindMultiMultilink(self):
702         one, two, three, four = self._find_test_setup()
703         got = self.db.issue.find(nosy='2', files='1')
704         got.sort()
705         self.assertEqual(got, [two, three, four])
706         got = self.db.issue.find(nosy={'2':1}, files={'1':1})
707         got.sort()
708         self.assertEqual(got, [two, three, four])
710     def testFindMultilinkFail(self):
711         self._find_test_setup()
712         self.assertEqual(self.db.issue.find(nosy='3'), [])
713         self.assertEqual(self.db.issue.find(nosy={'3':1}), [])
715     def testFindMultilinkUnset(self):
716         self._find_test_setup()
717         self.assertEqual(self.db.issue.find(nosy={}), [])
719     def testFindLinkAndMultilink(self):
720         one, two, three, four = self._find_test_setup()
721         got = self.db.issue.find(status='1', nosy='2')
722         got.sort()
723         self.assertEqual(got, [one, two, three])
724         got = self.db.issue.find(status={'1':1}, nosy={'2':1})
725         got.sort()
726         self.assertEqual(got, [one, two, three])
728     def testFindRetired(self):
729         one, two, three, four = self._find_test_setup()
730         self.assertEqual(len(self.db.issue.find(status='1')), 2)
731         self.db.issue.retire(one)
732         self.assertEqual(len(self.db.issue.find(status='1')), 1)
734     def testStringFind(self):
735         self.assertRaises(TypeError, self.db.issue.stringFind, status='1')
737         ids = []
738         ids.append(self.db.issue.create(title="spam"))
739         self.db.issue.create(title="not spam")
740         ids.append(self.db.issue.create(title="spam"))
741         ids.sort()
742         got = self.db.issue.stringFind(title='spam')
743         got.sort()
744         self.assertEqual(got, ids)
745         self.assertEqual(self.db.issue.stringFind(title='fubar'), [])
747         # test retiring a node
748         self.db.issue.retire(ids[0])
749         self.assertEqual(len(self.db.issue.stringFind(title='spam')), 1)
751     def filteringSetup(self):
752         for user in (
753                 {'username': 'bleep'},
754                 {'username': 'blop'},
755                 {'username': 'blorp'}):
756             self.db.user.create(**user)
757         iss = self.db.issue
758         for issue in (
759                 {'title': 'issue one', 'status': '2', 'assignedto': '1',
760                     'foo': date.Interval('1:10'), 
761                     'deadline': date.Date('2003-01-01.00:00')},
762                     {'title': 'issue two', 'status': '1', 'assignedto': '2',
763                     'foo': date.Interval('1d'), 
764                     'deadline': date.Date('2003-02-16.22:50')},
765                 {'title': 'issue three', 'status': '1',
766                     'nosy': ['1','2'], 'deadline': date.Date('2003-02-18')},
767                 {'title': 'non four', 'status': '3',
768                     'foo': date.Interval('0:10'), 
769                     'nosy': ['1'], 'deadline': date.Date('2004-03-08')}):
770             self.db.issue.create(**issue)
771         self.db.commit()
772         return self.assertEqual, self.db.issue.filter
774     def testFilteringID(self):
775         ae, filt = self.filteringSetup()
776         ae(filt(None, {'id': '1'}, ('+','id'), (None,None)), ['1'])
777         ae(filt(None, {'id': '2'}, ('+','id'), (None,None)), ['2'])
778         ae(filt(None, {'id': '10'}, ('+','id'), (None,None)), [])
780     def testFilteringString(self):
781         ae, filt = self.filteringSetup()
782         ae(filt(None, {'title': ['one']}, ('+','id'), (None,None)), ['1'])
783         ae(filt(None, {'title': ['issue']}, ('+','id'), (None,None)),
784             ['1','2','3'])
785         ae(filt(None, {'title': ['one', 'two']}, ('+','id'), (None,None)),
786             ['1', '2'])
788     def testFilteringLink(self):
789         ae, filt = self.filteringSetup()
790         ae(filt(None, {'status': '1'}, ('+','id'), (None,None)), ['2','3'])
791         ae(filt(None, {'assignedto': '-1'}, ('+','id'), (None,None)), ['3','4'])
793     def testFilteringRetired(self):
794         ae, filt = self.filteringSetup()
795         self.db.issue.retire('2')
796         ae(filt(None, {'status': '1'}, ('+','id'), (None,None)), ['3'])
798     def testFilteringMultilink(self):
799         ae, filt = self.filteringSetup()
800         ae(filt(None, {'nosy': '2'}, ('+','id'), (None,None)), ['3'])
801         ae(filt(None, {'nosy': '-1'}, ('+','id'), (None,None)), ['1', '2'])
803     def testFilteringMany(self):
804         ae, filt = self.filteringSetup()
805         ae(filt(None, {'nosy': '2', 'status': '1'}, ('+','id'), (None,None)),
806             ['3'])
808     def testFilteringRange(self):
809         ae, filt = self.filteringSetup()
810         # Date ranges
811         ae(filt(None, {'deadline': 'from 2003-02-10 to 2003-02-23'}), ['2','3'])
812         ae(filt(None, {'deadline': '2003-02-10; 2003-02-23'}), ['2','3'])
813         ae(filt(None, {'deadline': '; 2003-02-16'}), ['1'])
814         # Lets assume people won't invent a time machine, otherwise this test
815         # may fail :)
816         ae(filt(None, {'deadline': 'from 2003-02-16'}), ['2', '3', '4'])
817         ae(filt(None, {'deadline': '2003-02-16;'}), ['2', '3', '4'])
818         # year and month granularity
819         ae(filt(None, {'deadline': '2002'}), [])
820         ae(filt(None, {'deadline': '2003'}), ['1', '2', '3'])
821         ae(filt(None, {'deadline': '2004'}), ['4'])
822         ae(filt(None, {'deadline': '2003-02'}), ['2', '3'])
823         ae(filt(None, {'deadline': '2003-03'}), [])
824         ae(filt(None, {'deadline': '2003-02-16'}), ['2'])
825         ae(filt(None, {'deadline': '2003-02-17'}), [])
826         # Interval ranges
827         ae(filt(None, {'foo': 'from 0:50 to 2:00'}), ['1'])
828         ae(filt(None, {'foo': 'from 0:50 to 1d 2:00'}), ['1', '2'])
829         ae(filt(None, {'foo': 'from 5:50'}), ['2'])
830         ae(filt(None, {'foo': 'to 0:05'}), [])
832     def testFilteringIntervalSort(self):
833         ae, filt = self.filteringSetup()
834         # ascending should sort None, 1:10, 1d
835         ae(filt(None, {}, ('+','foo'), (None,None)), ['3', '4', '1', '2'])
836         # descending should sort 1d, 1:10, None
837         ae(filt(None, {}, ('-','foo'), (None,None)), ['2', '1', '4', '3'])
839 # XXX add sorting tests for other types
840 # XXX test auditors and reactors
842     def testImportExport(self):
843         # use the filtering setup to create a bunch of items
844         ae, filt = self.filteringSetup()
845         self.db.user.retire('3')
846         self.db.issue.retire('2')
848         # grab snapshot of the current database
849         orig = {}
850         for cn,klass in self.db.classes.items():
851             cl = orig[cn] = {}
852             for id in klass.list():
853                 it = cl[id] = {}
854                 for name in klass.getprops().keys():
855                     it[name] = klass.get(id, name)
857         # grab the export
858         export = {}
859         for cn,klass in self.db.classes.items():
860             names = klass.getprops().keys()
861             cl = export[cn] = [names+['is retired']]
862             for id in klass.getnodeids():
863                 cl.append(klass.export_list(names, id))
865         # shut down this db and nuke it
866         self.db.close()
867         self.nuke_database()
869         # open a new, empty database
870         os.makedirs(config.DATABASE + '/files')
871         self.db = self.module.Database(config, 'admin')
872         setupSchema(self.db, 0, self.module)
874         # import
875         for cn, items in export.items():
876             klass = self.db.classes[cn]
877             names = items[0]
878             maxid = 1
879             for itemprops in items[1:]:
880                 maxid = max(maxid, int(klass.import_list(names, itemprops)))
881             self.db.setid(cn, str(maxid+1))
883         # compare with snapshot of the database
884         for cn, items in orig.items():
885             klass = self.db.classes[cn]
886             # ensure retired items are retired :)
887             l = items.keys(); l.sort()
888             m = klass.list(); m.sort()
889             ae(l, m)
890             for id, props in items.items():
891                 for name, value in props.items():
892                     ae(klass.get(id, name), value)
894         # make sure the retired items are actually imported
895         ae(self.db.user.get('3', 'username'), 'blop')
896         ae(self.db.issue.get('2', 'title'), 'issue two')
898         # make sure id counters are set correctly
899         maxid = max([int(id) for id in self.db.user.list()])
900         newid = self.db.user.create(username='testing')
901         assert newid > maxid
903     def testSafeGet(self):
904         # existent nodeid, existent property
905         self.assertEqual(self.db.user.safeget('1', 'username'), 'admin')
906         # nonexistent nodeid, existent property
907         self.assertEqual(self.db.user.safeget('999', 'username'), None)
908         # different default
909         self.assertEqual(self.db.issue.safeget('999', 'nosy', []), [])
911     def testAddProperty(self):
912         self.db.issue.create(title="spam", status='1')
913         self.db.commit()
915         self.db.issue.addprop(fixer=Link("user"))
916         # force any post-init stuff to happen
917         self.db.post_init()
918         props = self.db.issue.getprops()
919         keys = props.keys()
920         keys.sort()
921         self.assertEqual(keys, ['activity', 'actor', 'assignedto', 'creation',
922             'creator', 'deadline', 'files', 'fixer', 'foo', 'id', 'messages',
923             'nosy', 'status', 'superseder', 'title'])
924         self.assertEqual(self.db.issue.get('1', "fixer"), None)
926     def testRemoveProperty(self):
927         self.db.issue.create(title="spam", status='1')
928         self.db.commit()
930         del self.db.issue.properties['title']
931         self.db.post_init()
932         props = self.db.issue.getprops()
933         keys = props.keys()
934         keys.sort()
935         self.assertEqual(keys, ['activity', 'actor', 'assignedto', 'creation',
936             'creator', 'deadline', 'files', 'foo', 'id', 'messages',
937             'nosy', 'status', 'superseder'])
938         self.assertEqual(self.db.issue.list(), ['1'])
940     def testAddRemoveProperty(self):
941         self.db.issue.create(title="spam", status='1')
942         self.db.commit()
944         self.db.issue.addprop(fixer=Link("user"))
945         del self.db.issue.properties['title']
946         self.db.post_init()
947         props = self.db.issue.getprops()
948         keys = props.keys()
949         keys.sort()
950         self.assertEqual(keys, ['activity', 'actor', 'assignedto', 'creation',
951             'creator', 'deadline', 'files', 'fixer', 'foo', 'id', 'messages',
952             'nosy', 'status', 'superseder'])
953         self.assertEqual(self.db.issue.list(), ['1'])
955 class ROTest(MyTestCase):
956     def setUp(self):
957         # remove previous test, ignore errors
958         if os.path.exists(config.DATABASE):
959             shutil.rmtree(config.DATABASE)
960         os.makedirs(config.DATABASE + '/files')
961         self.db = self.module.Database(config, 'admin')
962         setupSchema(self.db, 1, self.module)
963         self.db.close()
965         self.db = self.module.Database(config)
966         setupSchema(self.db, 0, self.module)
968     def testExceptions(self):
969         # this tests the exceptions that should be raised
970         ar = self.assertRaises
972         # this tests the exceptions that should be raised
973         ar(DatabaseError, self.db.status.create, name="foo")
974         ar(DatabaseError, self.db.status.set, '1', name="foo")
975         ar(DatabaseError, self.db.status.retire, '1')
978 class SchemaTest(MyTestCase):
979     def setUp(self):
980         # remove previous test, ignore errors
981         if os.path.exists(config.DATABASE):
982             shutil.rmtree(config.DATABASE)
983         os.makedirs(config.DATABASE + '/files')
985     def test_reservedProperties(self):
986         self.db = self.module.Database(config, 'admin')
987         self.assertRaises(ValueError, self.module.Class, self.db, "a",
988             creation=String())
989         self.assertRaises(ValueError, self.module.Class, self.db, "a",
990             activity=String())
991         self.assertRaises(ValueError, self.module.Class, self.db, "a",
992             creator=String())
993         self.assertRaises(ValueError, self.module.Class, self.db, "a",
994             actor=String())
996     def init_a(self):
997         self.db = self.module.Database(config, 'admin')
998         a = self.module.Class(self.db, "a", name=String())
999         a.setkey("name")
1000         self.db.post_init()
1002     def init_ab(self):
1003         self.db = self.module.Database(config, 'admin')
1004         a = self.module.Class(self.db, "a", name=String())
1005         a.setkey("name")
1006         b = self.module.Class(self.db, "b", name=String())
1007         b.setkey("name")
1008         self.db.post_init()
1010     def test_addNewClass(self):
1011         self.init_a()
1013         self.assertRaises(ValueError, self.module.Class, self.db, "a",
1014             name=String())
1016         aid = self.db.a.create(name='apple')
1017         self.db.commit(); self.db.close()
1019         # add a new class to the schema and check creation of new items
1020         # (and existence of old ones)
1021         self.init_ab()
1022         bid = self.db.b.create(name='bear')
1023         self.assertEqual(self.db.a.get(aid, 'name'), 'apple')
1024         self.db.commit()
1025         self.db.close()
1027         # now check we can recall the added class' items
1028         self.init_ab()
1029         self.assertEqual(self.db.a.get(aid, 'name'), 'apple')
1030         self.assertEqual(self.db.a.lookup('apple'), aid)
1031         self.assertEqual(self.db.b.get(bid, 'name'), 'bear')
1032         self.assertEqual(self.db.b.lookup('bear'), bid)
1034         # confirm journal's ok
1035         self.db.getjournal('a', aid)
1036         self.db.getjournal('b', bid)
1038     def init_amod(self):
1039         self.db = self.module.Database(config, 'admin')
1040         a = self.module.Class(self.db, "a", name=String(), fooz=String())
1041         a.setkey("name")
1042         b = self.module.Class(self.db, "b", name=String())
1043         b.setkey("name")
1044         self.db.post_init()
1046     def test_modifyClass(self):
1047         self.init_ab()
1049         # add item to user and issue class
1050         aid = self.db.a.create(name='apple')
1051         bid = self.db.b.create(name='bear')
1052         self.db.commit(); self.db.close()
1054         # modify "a" schema
1055         self.init_amod()
1056         self.assertEqual(self.db.a.get(aid, 'name'), 'apple')
1057         self.assertEqual(self.db.a.get(aid, 'fooz'), None)
1058         self.assertEqual(self.db.b.get(aid, 'name'), 'bear')
1059         aid2 = self.db.a.create(name='aardvark', fooz='booz')
1060         self.db.commit(); self.db.close()
1062         # test
1063         self.init_amod()
1064         self.assertEqual(self.db.a.get(aid, 'name'), 'apple')
1065         self.assertEqual(self.db.a.get(aid, 'fooz'), None)
1066         self.assertEqual(self.db.b.get(aid, 'name'), 'bear')
1067         self.assertEqual(self.db.a.get(aid2, 'name'), 'aardvark')
1068         self.assertEqual(self.db.a.get(aid2, 'fooz'), 'booz')
1070         # confirm journal's ok
1071         self.db.getjournal('a', aid)
1072         self.db.getjournal('a', aid2)
1074     def init_amodkey(self):
1075         self.db = self.module.Database(config, 'admin')
1076         a = self.module.Class(self.db, "a", name=String(), fooz=String())
1077         a.setkey("fooz")
1078         b = self.module.Class(self.db, "b", name=String())
1079         b.setkey("name")
1080         self.db.post_init()
1082     def test_changeClassKey(self):
1083         self.init_amod()
1084         aid = self.db.a.create(name='apple')
1085         self.assertEqual(self.db.a.lookup('apple'), aid)
1086         self.db.commit(); self.db.close()
1088         # change the key to fooz on a
1089         self.init_amodkey()
1090         self.assertEqual(self.db.a.get(aid, 'name'), 'apple')
1091         self.assertEqual(self.db.a.get(aid, 'fooz'), None)
1092         self.assertRaises(KeyError, self.db.a.lookup, 'apple')
1093         aid2 = self.db.a.create(name='aardvark', fooz='booz')
1094         self.db.commit(); self.db.close()
1096         # check
1097         self.init_amodkey()
1098         self.assertEqual(self.db.a.lookup('booz'), aid2)
1100         # confirm journal's ok
1101         self.db.getjournal('a', aid)
1103     def init_ml(self):
1104         self.db = self.module.Database(config, 'admin')
1105         a = self.module.Class(self.db, "a", name=String())
1106         a.setkey('name')
1107         b = self.module.Class(self.db, "b", name=String(),
1108             fooz=Multilink('a'))
1109         b.setkey("name")
1110         self.db.post_init()
1112     def test_makeNewMultilink(self):
1113         self.init_a()
1114         aid = self.db.a.create(name='apple')
1115         self.assertEqual(self.db.a.lookup('apple'), aid)
1116         self.db.commit(); self.db.close()
1118         # add a multilink prop
1119         self.init_ml()
1120         bid = self.db.b.create(name='bear', fooz=[aid])
1121         self.assertEqual(self.db.b.find(fooz=aid), [bid])
1122         self.assertEqual(self.db.a.lookup('apple'), aid)
1123         self.db.commit(); self.db.close()
1125         # check
1126         self.init_ml()
1127         self.assertEqual(self.db.b.find(fooz=aid), [bid])
1128         self.assertEqual(self.db.a.lookup('apple'), aid)
1129         self.assertEqual(self.db.b.lookup('bear'), bid)
1131         # confirm journal's ok
1132         self.db.getjournal('a', aid)
1133         self.db.getjournal('b', bid)
1135     def test_removeMultilink(self):
1136         # add a multilink prop
1137         self.init_ml()
1138         aid = self.db.a.create(name='apple')
1139         bid = self.db.b.create(name='bear', fooz=[aid])
1140         self.assertEqual(self.db.b.find(fooz=aid), [bid])
1141         self.assertEqual(self.db.a.lookup('apple'), aid)
1142         self.assertEqual(self.db.b.lookup('bear'), bid)
1143         self.db.commit(); self.db.close()
1145         # remove the multilink
1146         self.init_ab()
1147         self.assertEqual(self.db.a.lookup('apple'), aid)
1148         self.assertEqual(self.db.b.lookup('bear'), bid)
1150         # confirm journal's ok
1151         self.db.getjournal('a', aid)
1152         self.db.getjournal('b', bid)
1154     def test_removeClass(self):
1155         self.init_ml()
1156         aid = self.db.a.create(name='apple')
1157         bid = self.db.b.create(name='bear', fooz=[aid])
1158         self.db.commit(); self.db.close()
1160         # drop the b class
1161         self.init_a()
1162         self.assertEqual(self.db.a.get(aid, 'name'), 'apple')
1163         self.assertEqual(self.db.a.lookup('apple'), aid)
1164         self.db.commit(); self.db.close()
1166         # now check we can recall the added class' items
1167         self.init_a()
1168         self.assertEqual(self.db.a.get(aid, 'name'), 'apple')
1169         self.assertEqual(self.db.a.lookup('apple'), aid)
1171         # confirm journal's ok
1172         self.db.getjournal('a', aid)
1174 class RDBMSTest:
1175     ''' tests specific to RDBMS backends '''
1176     def test_indexTest(self):
1177         self.assertEqual(self.db.sql_index_exists('_issue', '_issue_id_idx'), 1)
1178         self.assertEqual(self.db.sql_index_exists('_issue', '_issue_x_idx'), 0)
1181 class ClassicInitTest(unittest.TestCase):
1182     count = 0
1183     db = None
1184     extra_config = ''
1186     def setUp(self):
1187         ClassicInitTest.count = ClassicInitTest.count + 1
1188         self.dirname = '_test_init_%s'%self.count
1189         try:
1190             shutil.rmtree(self.dirname)
1191         except OSError, error:
1192             if error.errno not in (errno.ENOENT, errno.ESRCH): raise
1194     def testCreation(self):
1195         ae = self.assertEqual
1197         # create the instance
1198         init.install(self.dirname, 'templates/classic')
1199         init.write_select_db(self.dirname, self.backend)
1201         if self.extra_config:
1202             f = open(os.path.join(self.dirname, 'config.py'), 'a')
1203             try:
1204                 f.write(self.extra_config)
1205             finally:
1206                 f.close()
1207         
1208         init.initialise(self.dirname, 'sekrit')
1210         # check we can load the package
1211         instance = imp.load_package(self.dirname, self.dirname)
1213         # and open the database
1214         db = self.db = instance.open()
1216         # check the basics of the schema and initial data set
1217         l = db.priority.list()
1218         ae(l, ['1', '2', '3', '4', '5'])
1219         l = db.status.list()
1220         ae(l, ['1', '2', '3', '4', '5', '6', '7', '8'])
1221         l = db.keyword.list()
1222         ae(l, [])
1223         l = db.user.list()
1224         ae(l, ['1', '2'])
1225         l = db.msg.list()
1226         ae(l, [])
1227         l = db.file.list()
1228         ae(l, [])
1229         l = db.issue.list()
1230         ae(l, [])
1232     def tearDown(self):
1233         if self.db is not None:
1234             self.db.close()
1235         try:
1236             shutil.rmtree(self.dirname)
1237         except OSError, error:
1238             if error.errno not in (errno.ENOENT, errno.ESRCH): raise