Code

82fd6b43cf6bbb54de6cb94396ee0f0d44f306de
[roundup.git] / test / test_actions.py
1 from __future__ import nested_scopes
3 import unittest
4 from cgi import FieldStorage, MiniFieldStorage
6 from roundup import hyperdb
7 from roundup.date import Date, Interval
8 from roundup.cgi.actions import *
9 from roundup.cgi.exceptions import Redirect, Unauthorised, SeriousError
11 from mocknull import MockNull
13 def true(*args, **kwargs):
14     return 1
16 class ActionTestCase(unittest.TestCase):
17     def setUp(self):
18         self.form = FieldStorage()
19         self.client = MockNull()
20         self.client.form = self.form
21         class TemplatingUtils:
22             pass
23         self.client.instance.interfaces.TemplatingUtils = TemplatingUtils
25 class ShowActionTestCase(ActionTestCase):
26     def assertRaisesMessage(self, exception, callable, message, *args,
27                             **kwargs):
28         """An extension of assertRaises, which also checks the exception
29         message. We need this because we rely on exception messages when
30         redirecting.
31         """
32         try:
33             callable(*args, **kwargs)
34         except exception, msg:
35             self.assertEqual(str(msg), message)
36         else:
37             if hasattr(exception, '__name__'):
38                 excName = exception.__name__
39             else:
40                 excName = str(exception)
41             raise self.failureException, excName
43     def testShowAction(self):
44         self.client.base = 'BASE/'
46         action = ShowAction(self.client)
47         self.assertRaises(ValueError, action.handle)
49         self.form.value.append(MiniFieldStorage('@type', 'issue'))
50         self.assertRaises(SeriousError, action.handle)
52         self.form.value.append(MiniFieldStorage('@number', '1'))
53         self.assertRaisesMessage(Redirect, action.handle, 'BASE/issue1')
55     def testShowActionNoType(self):
56         action = ShowAction(self.client)
57         self.assertRaises(ValueError, action.handle)
58         self.form.value.append(MiniFieldStorage('@number', '1'))
59         self.assertRaisesMessage(ValueError, action.handle,
60             'No type specified')
62 class RetireActionTestCase(ActionTestCase):
63     def testRetireAction(self):
64         self.client.db.security.hasPermission = true
65         self.client.ok_message = []
66         RetireAction(self.client).handle()
67         self.assert_(len(self.client.ok_message) == 1)
69     def testNoPermission(self):
70         self.assertRaises(Unauthorised, RetireAction(self.client).execute)
72     def testDontRetireAdminOrAnonymous(self):
73         self.client.db.security.hasPermission=true
74         # look up the user class
75         self.client.classname = 'user'
76         # but always look up admin, regardless of nodeid
77         self.client.db.user.get = lambda a,b: 'admin'
78         self.assertRaises(ValueError, RetireAction(self.client).handle)
79         # .. or anonymous
80         self.client.db.user.get = lambda a,b: 'anonymous'
81         self.assertRaises(ValueError, RetireAction(self.client).handle)
83 class SearchActionTestCase(ActionTestCase):
84     def setUp(self):
85         ActionTestCase.setUp(self)
86         self.action = SearchAction(self.client)
88 class StandardSearchActionTestCase(SearchActionTestCase):
89     def testNoPermission(self):
90         self.assertRaises(Unauthorised, self.action.execute)
92     def testQueryName(self):
93         self.assertEqual(self.action.getQueryName(), '')
95         self.form.value.append(MiniFieldStorage('@queryname', 'foo'))
96         self.assertEqual(self.action.getQueryName(), 'foo')
98 class FakeFilterVarsTestCase(SearchActionTestCase):
99     def setUp(self):
100         SearchActionTestCase.setUp(self)
101         self.client.db.classes.get_transitive_prop = lambda x: \
102             hyperdb.Multilink('foo')
104     def assertFilterEquals(self, expected):
105         self.action.fakeFilterVars()
106         self.assertEqual(self.form.getvalue('@filter'), expected)
108     def testEmptyMultilink(self):
109         self.form.value.append(MiniFieldStorage('foo', ''))
110         self.form.value.append(MiniFieldStorage('foo', ''))
112         self.assertFilterEquals(None)
114     def testNonEmptyMultilink(self):
115         self.form.value.append(MiniFieldStorage('foo', ''))
116         self.form.value.append(MiniFieldStorage('foo', '1'))
118         self.assertFilterEquals('foo')
120     def testEmptyKey(self):
121         self.form.value.append(MiniFieldStorage('foo', ''))
122         self.assertFilterEquals(None)
124     def testStandardKey(self):
125         self.form.value.append(MiniFieldStorage('foo', '1'))
126         self.assertFilterEquals('foo')
128     def testStringKey(self):
129         self.client.db.classes.getprops = lambda: {'foo': hyperdb.String()}
130         self.form.value.append(MiniFieldStorage('foo', 'hello'))
131         self.assertFilterEquals('foo')
133     def testTokenizedStringKey(self):
134         self.client.db.classes.get_transitive_prop = lambda x: hyperdb.String()
135         self.form.value.append(MiniFieldStorage('foo', 'hello world'))
137         self.assertFilterEquals('foo')
139         # The single value gets replaced with the tokenized list.
140         self.assertEqual([x.value for x in self.form['foo']],
141             ['hello', 'world'])
143 class CollisionDetectionTestCase(ActionTestCase):
144     def setUp(self):
145         ActionTestCase.setUp(self)
146         self.action = EditItemAction(self.client)
147         self.now = Date('.')
148         # round off for testing
149         self.now.second = int(self.now.second)
151     def testLastUserActivity(self):
152         self.assertEqual(self.action.lastUserActivity(), None)
154         self.client.form.value.append(
155             MiniFieldStorage('@lastactivity', str(self.now)))
156         self.assertEqual(self.action.lastUserActivity(), self.now)
158     def testLastNodeActivity(self):
159         self.action.classname = 'issue'
160         self.action.nodeid = '1'
162         def get(nodeid, propname):
163             self.assertEqual(nodeid, '1')
164             self.assertEqual(propname, 'activity')
165             return self.now
166         self.client.db.issue.get = get
168         self.assertEqual(self.action.lastNodeActivity(), self.now)
170     def testCollision(self):
171         # fake up an actual change
172         self.action.classname = 'test'
173         self.action.nodeid = '1'
174         self.client.parsePropsFromForm = lambda: ({('test','1'):{1:1}}, [])
175         self.failUnless(self.action.detectCollision(self.now,
176             self.now + Interval("1d")))
177         self.failIf(self.action.detectCollision(self.now,
178             self.now - Interval("1d")))
179         self.failIf(self.action.detectCollision(None, self.now))
181 class LoginTestCase(ActionTestCase):
182     def setUp(self):
183         ActionTestCase.setUp(self)
184         self.client.error_message = []
186         # set the db password to 'right'
187         self.client.db.user.get = lambda a,b: 'right'
189         # unless explicitly overridden, we should never get here
190         self.client.opendb = lambda a: self.fail(
191             "Logged in, but we shouldn't be.")
193     def assertLoginLeavesMessages(self, messages, username=None, password=None):
194         if username is not None:
195             self.form.value.append(MiniFieldStorage('__login_name', username))
196         if password is not None:
197             self.form.value.append(
198                 MiniFieldStorage('__login_password', password))
200         LoginAction(self.client).handle()
201         self.assertEqual(self.client.error_message, messages)
203     def testNoUsername(self):
204         self.assertLoginLeavesMessages(['Username required'])
206     def testInvalidUsername(self):
207         def raiseKeyError(a):
208             raise KeyError
209         self.client.db.user.lookup = raiseKeyError
210         self.assertLoginLeavesMessages(['Invalid login'], 'foo')
212     def testInvalidPassword(self):
213         self.assertLoginLeavesMessages(['Invalid login'], 'foo', 'wrong')
215     def testNoWebAccess(self):
216         self.assertLoginLeavesMessages(['You do not have permission to login'],
217                                         'foo', 'right')
219     def testCorrectLogin(self):
220         self.client.db.security.hasPermission = lambda *args, **kwargs: True
222         def opendb(username):
223             self.assertEqual(username, 'foo')
224         self.client.opendb = opendb
226         self.assertLoginLeavesMessages([], 'foo', 'right')
228 class EditItemActionTestCase(ActionTestCase):
229     def setUp(self):
230         ActionTestCase.setUp(self)
231         self.result = []
232         class AppendResult:
233             def __init__(inner_self, name):
234                 inner_self.name = name
235             def __call__(inner_self, *args, **kw):
236                 self.result.append((inner_self.name, args, kw))
237                 if inner_self.name == 'set':
238                     return kw
239                 return '17'
241         self.client.db.security.hasPermission = true
242         self.client.classname = 'issue'
243         self.client.base = 'http://tracker/'
244         self.client.nodeid = '4711'
245         self.client.template = 'item'
246         self.client.db.classes.create = AppendResult('create')
247         self.client.db.classes.set = AppendResult('set')
248         self.client.db.classes.getprops = lambda: \
249             ({'messages':hyperdb.Multilink('msg')
250              ,'content':hyperdb.String()
251              ,'files':hyperdb.Multilink('file')
252              })
253         self.action = EditItemAction(self.client)
255     def testMessageAttach(self):
256         expect = \
257             [ ('create',(),{'content':'t'})
258             , ('set',('4711',), {'messages':['23','42','17']})
259             ]
260         self.client.db.classes.get = lambda a, b:['23','42']
261         self.client.parsePropsFromForm = lambda: \
262             ( {('msg','-1'):{'content':'t'},('issue','4711'):{}}
263             , [('issue','4711','messages',[('msg','-1')])]
264             )
265         try :
266             self.action.handle()
267         except Redirect, msg:
268             pass
269         self.assertEqual(expect, self.result)
271     def testFileAttach(self):
272         expect = \
273             [('create',(),{'content':'t','type':'text/plain','name':'t.txt'})
274             ,('set',('4711',),{'files':['23','42','17']})
275             ]
276         self.client.db.classes.get = lambda a, b:['23','42']
277         self.client.parsePropsFromForm = lambda: \
278             ( {('file','-1'):{'content':'t','type':'text/plain','name':'t.txt'}
279               ,('issue','4711'):{}
280               }
281             , [('issue','4711','messages',[('msg','-1')])
282               ,('issue','4711','files',[('file','-1')])
283               ,('msg','-1','files',[('file','-1')])
284               ]
285             )
286         try :
287             self.action.handle()
288         except Redirect, msg:
289             pass
290         self.assertEqual(expect, self.result)
292     def testLinkExisting(self):
293         expect = [('set',('4711',),{'messages':['23','42','1']})]
294         self.client.db.classes.get = lambda a, b:['23','42']
295         self.client.parsePropsFromForm = lambda: \
296             ( {('issue','4711'):{},('msg','1'):{}}
297             , [('issue','4711','messages',[('msg','1')])]
298             )
299         try :
300             self.action.handle()
301         except Redirect, msg:
302             pass
303         self.assertEqual(expect, self.result)
305 def test_suite():
306     suite = unittest.TestSuite()
307     suite.addTest(unittest.makeSuite(RetireActionTestCase))
308     suite.addTest(unittest.makeSuite(StandardSearchActionTestCase))
309     suite.addTest(unittest.makeSuite(FakeFilterVarsTestCase))
310     suite.addTest(unittest.makeSuite(ShowActionTestCase))
311     suite.addTest(unittest.makeSuite(CollisionDetectionTestCase))
312     suite.addTest(unittest.makeSuite(LoginTestCase))
313     suite.addTest(unittest.makeSuite(EditItemActionTestCase))
314     return suite
316 if __name__ == '__main__':
317     runner = unittest.TextTestRunner()
318     unittest.main(testRunner=runner)
320 # vim: set et sts=4 sw=4 :