diff --git a/test/test_actions.py b/test/test_actions.py
index 0728246b1c2a26d41d7f9133fb0b5945fec12f9d..edebe0c4a5d6d256a35dc930f8a4e84671373c4a 100755 (executable)
--- a/test/test_actions.py
+++ b/test/test_actions.py
-from __future__ import nested_scopes\r
-\r
-import unittest\r
-from cgi import FieldStorage, MiniFieldStorage\r
-\r
-from roundup import hyperdb\r
-from roundup.date import Date, Interval\r
-from roundup.cgi.actions import *\r
-from roundup.cgi.exceptions import Redirect, Unauthorised, SeriousError\r
-\r
-class MockNull:\r
- def __init__(self, **kwargs):\r
- for key, value in kwargs.items():\r
- self.__dict__[key] = value\r
-\r
- def __call__(self, *args, **kwargs): return MockNull()\r
- def __getattr__(self, name):\r
- # This allows assignments which assume all intermediate steps are Null\r
- # objects if they don't exist yet.\r
- #\r
- # For example (with just 'client' defined):\r
- #\r
- # client.db.config.TRACKER_WEB = 'BASE/'\r
- self.__dict__[name] = MockNull()\r
- return getattr(self, name)\r
-\r
- def __getitem__(self, key): return self\r
- def __nonzero__(self): return 0\r
- def __str__(self): return ''\r
- def __repr__(self): return '<MockNull 0x%x>'%id(self)\r
-\r
-def true(*args, **kwargs):\r
- return 1\r
-\r
-class ActionTestCase(unittest.TestCase):\r
- def setUp(self):\r
- self.form = FieldStorage()\r
- self.client = MockNull()\r
- self.client.form = self.form\r
-\r
-class ShowActionTestCase(ActionTestCase):\r
- def assertRaisesMessage(self, exception, callable, message, *args,\r
- **kwargs):\r
- """An extension of assertRaises, which also checks the exception\r
- message. We need this because we rely on exception messages when\r
- redirecting.\r
- """\r
- try:\r
- callable(*args, **kwargs)\r
- except exception, msg:\r
- self.assertEqual(str(msg), message)\r
- else:\r
- if hasattr(excClass,'__name__'):\r
- excName = excClass.__name__\r
- else:\r
- excName = str(excClass)\r
- raise self.failureException, excName\r
-\r
- def testShowAction(self):\r
- self.client.base = 'BASE/'\r
-\r
- action = ShowAction(self.client)\r
- self.assertRaises(ValueError, action.handle)\r
-\r
- self.form.value.append(MiniFieldStorage('@type', 'issue'))\r
- self.assertRaises(SeriousError, action.handle)\r
-\r
- self.form.value.append(MiniFieldStorage('@number', '1'))\r
- self.assertRaisesMessage(Redirect, action.handle, 'BASE/issue1')\r
-\r
- def testShowActionNoType(self):\r
- action = ShowAction(self.client)\r
- self.assertRaises(ValueError, action.handle)\r
- self.form.value.append(MiniFieldStorage('@number', '1'))\r
- self.assertRaisesMessage(ValueError, action.handle,\r
- 'No type specified')\r
-\r
-class RetireActionTestCase(ActionTestCase):\r
- def testRetireAction(self):\r
- self.client.db.security.hasPermission = true\r
- self.client.ok_message = []\r
- RetireAction(self.client).handle()\r
- self.assert_(len(self.client.ok_message) == 1)\r
-\r
- def testNoPermission(self):\r
- self.assertRaises(Unauthorised, RetireAction(self.client).execute)\r
-\r
- def testDontRetireAdminOrAnonymous(self):\r
- self.client.db.security.hasPermission=true\r
- # look up the user class\r
- self.client.classname = 'user'\r
- # but always look up admin, regardless of nodeid\r
- self.client.db.user.get = lambda a,b: 'admin'\r
- self.assertRaises(ValueError, RetireAction(self.client).handle)\r
- # .. or anonymous\r
- self.client.db.user.get = lambda a,b: 'anonymous'\r
- self.assertRaises(ValueError, RetireAction(self.client).handle)\r
-\r
-class SearchActionTestCase(ActionTestCase):\r
- def setUp(self):\r
- ActionTestCase.setUp(self)\r
- self.action = SearchAction(self.client)\r
-\r
-class StandardSearchActionTestCase(SearchActionTestCase):\r
- def testNoPermission(self):\r
- self.assertRaises(Unauthorised, self.action.execute)\r
-\r
- def testQueryName(self):\r
- self.assertEqual(self.action.getQueryName(), '')\r
-\r
- self.form.value.append(MiniFieldStorage('@queryname', 'foo'))\r
- self.assertEqual(self.action.getQueryName(), 'foo')\r
-\r
-class FakeFilterVarsTestCase(SearchActionTestCase):\r
- def setUp(self):\r
- SearchActionTestCase.setUp(self)\r
- self.client.db.classes.getprops = lambda: {'foo': hyperdb.Multilink('foo')}\r
-\r
- def assertFilterEquals(self, expected):\r
- self.action.fakeFilterVars()\r
- self.assertEqual(self.form.getvalue('@filter'), expected)\r
-\r
- def testEmptyMultilink(self):\r
- self.form.value.append(MiniFieldStorage('foo', ''))\r
- self.form.value.append(MiniFieldStorage('foo', ''))\r
-\r
- self.assertFilterEquals(None)\r
-\r
- def testNonEmptyMultilink(self):\r
- self.form.value.append(MiniFieldStorage('foo', ''))\r
- self.form.value.append(MiniFieldStorage('foo', '1'))\r
-\r
- self.assertFilterEquals('foo')\r
-\r
- def testEmptyKey(self):\r
- self.form.value.append(MiniFieldStorage('foo', ''))\r
- self.assertFilterEquals(None)\r
-\r
- def testStandardKey(self):\r
- self.form.value.append(MiniFieldStorage('foo', '1'))\r
- self.assertFilterEquals('foo')\r
-\r
- def testStringKey(self):\r
- self.client.db.classes.getprops = lambda: {'foo': hyperdb.String()}\r
- self.form.value.append(MiniFieldStorage('foo', 'hello'))\r
- self.assertFilterEquals('foo')\r
-\r
- def testTokenizedStringKey(self):\r
- self.client.db.classes.getprops = lambda: {'foo': hyperdb.String()}\r
- self.form.value.append(MiniFieldStorage('foo', 'hello world'))\r
-\r
- self.assertFilterEquals('foo')\r
-\r
- # The single value gets replaced with the tokenized list.\r
- self.assertEqual([x.value for x in self.form['foo']], ['hello', 'world'])\r
-\r
-class CollisionDetectionTestCase(ActionTestCase):\r
- def setUp(self):\r
- ActionTestCase.setUp(self)\r
- self.action = EditItemAction(self.client)\r
- self.now = Date('.')\r
- # round off for testing\r
- self.now.second = int(self.now.second)\r
-\r
- def testLastUserActivity(self):\r
- self.assertEqual(self.action.lastUserActivity(), None)\r
-\r
- self.client.form.value.append(MiniFieldStorage('@lastactivity', str(self.now)))\r
- self.assertEqual(self.action.lastUserActivity(), self.now)\r
-\r
- def testLastNodeActivity(self):\r
- self.action.classname = 'issue'\r
- self.action.nodeid = '1'\r
-\r
- def get(nodeid, propname):\r
- self.assertEqual(nodeid, '1')\r
- self.assertEqual(propname, 'activity')\r
- return self.now\r
- self.client.db.issue.get = get\r
-\r
- self.assertEqual(self.action.lastNodeActivity(), self.now)\r
-\r
- def testCollision(self):\r
- self.failUnless(self.action.detectCollision(self.now, self.now + Interval("1d")))\r
- self.failIf(self.action.detectCollision(self.now, self.now - Interval("1d")))\r
- self.failIf(self.action.detectCollision(None, self.now))\r
-\r
-def test_suite():\r
- suite = unittest.TestSuite()\r
- suite.addTest(unittest.makeSuite(RetireActionTestCase))\r
- suite.addTest(unittest.makeSuite(StandardSearchActionTestCase))\r
- suite.addTest(unittest.makeSuite(FakeFilterVarsTestCase))\r
- suite.addTest(unittest.makeSuite(ShowActionTestCase))\r
- suite.addTest(unittest.makeSuite(CollisionDetectionTestCase))\r
- return suite\r
-\r
-if __name__ == '__main__':\r
- runner = unittest.TextTestRunner()\r
- unittest.main(testRunner=runner)\r
-\r
+from __future__ import nested_scopes
+
+import unittest
+from cgi import FieldStorage, MiniFieldStorage
+
+from roundup import hyperdb
+from roundup.date import Date, Interval
+from roundup.cgi.actions import *
+from roundup.cgi.exceptions import Redirect, Unauthorised, SeriousError
+
+from mocknull import MockNull
+
+def true(*args, **kwargs):
+ return 1
+
+class ActionTestCase(unittest.TestCase):
+ def setUp(self):
+ self.form = FieldStorage()
+ self.client = MockNull()
+ self.client.form = self.form
+ class TemplatingUtils:
+ pass
+ self.client.instance.interfaces.TemplatingUtils = TemplatingUtils
+
+class ShowActionTestCase(ActionTestCase):
+ def assertRaisesMessage(self, exception, callable, message, *args,
+ **kwargs):
+ """An extension of assertRaises, which also checks the exception
+ message. We need this because we rely on exception messages when
+ redirecting.
+ """
+ try:
+ callable(*args, **kwargs)
+ except exception, msg:
+ self.assertEqual(str(msg), message)
+ else:
+ if hasattr(exception, '__name__'):
+ excName = exception.__name__
+ else:
+ excName = str(exception)
+ raise self.failureException, excName
+
+ def testShowAction(self):
+ self.client.base = 'BASE/'
+
+ action = ShowAction(self.client)
+ self.assertRaises(ValueError, action.handle)
+
+ self.form.value.append(MiniFieldStorage('@type', 'issue'))
+ self.assertRaises(SeriousError, action.handle)
+
+ self.form.value.append(MiniFieldStorage('@number', '1'))
+ self.assertRaisesMessage(Redirect, action.handle, 'BASE/issue1')
+
+ def testShowActionNoType(self):
+ action = ShowAction(self.client)
+ self.assertRaises(ValueError, action.handle)
+ self.form.value.append(MiniFieldStorage('@number', '1'))
+ self.assertRaisesMessage(ValueError, action.handle,
+ 'No type specified')
+
+class RetireActionTestCase(ActionTestCase):
+ def testRetireAction(self):
+ self.client.db.security.hasPermission = true
+ self.client.ok_message = []
+ RetireAction(self.client).handle()
+ self.assert_(len(self.client.ok_message) == 1)
+
+ def testNoPermission(self):
+ self.assertRaises(Unauthorised, RetireAction(self.client).execute)
+
+ def testDontRetireAdminOrAnonymous(self):
+ self.client.db.security.hasPermission=true
+ # look up the user class
+ self.client.classname = 'user'
+ # but always look up admin, regardless of nodeid
+ self.client.db.user.get = lambda a,b: 'admin'
+ self.assertRaises(ValueError, RetireAction(self.client).handle)
+ # .. or anonymous
+ self.client.db.user.get = lambda a,b: 'anonymous'
+ self.assertRaises(ValueError, RetireAction(self.client).handle)
+
+class SearchActionTestCase(ActionTestCase):
+ def setUp(self):
+ ActionTestCase.setUp(self)
+ self.action = SearchAction(self.client)
+
+class StandardSearchActionTestCase(SearchActionTestCase):
+ def testNoPermission(self):
+ self.assertRaises(Unauthorised, self.action.execute)
+
+ def testQueryName(self):
+ self.assertEqual(self.action.getQueryName(), '')
+
+ self.form.value.append(MiniFieldStorage('@queryname', 'foo'))
+ self.assertEqual(self.action.getQueryName(), 'foo')
+
+class FakeFilterVarsTestCase(SearchActionTestCase):
+ def setUp(self):
+ SearchActionTestCase.setUp(self)
+ self.client.db.classes.get_transitive_prop = lambda x: \
+ hyperdb.Multilink('foo')
+
+ def assertFilterEquals(self, expected):
+ self.action.fakeFilterVars()
+ self.assertEqual(self.form.getvalue('@filter'), expected)
+
+ def testEmptyMultilink(self):
+ self.form.value.append(MiniFieldStorage('foo', ''))
+ self.form.value.append(MiniFieldStorage('foo', ''))
+
+ self.assertFilterEquals(None)
+
+ def testNonEmptyMultilink(self):
+ self.form.value.append(MiniFieldStorage('foo', ''))
+ self.form.value.append(MiniFieldStorage('foo', '1'))
+
+ self.assertFilterEquals('foo')
+
+ def testEmptyKey(self):
+ self.form.value.append(MiniFieldStorage('foo', ''))
+ self.assertFilterEquals(None)
+
+ def testStandardKey(self):
+ self.form.value.append(MiniFieldStorage('foo', '1'))
+ self.assertFilterEquals('foo')
+
+ def testStringKey(self):
+ self.client.db.classes.getprops = lambda: {'foo': hyperdb.String()}
+ self.form.value.append(MiniFieldStorage('foo', 'hello'))
+ self.assertFilterEquals('foo')
+
+ def testTokenizedStringKey(self):
+ self.client.db.classes.get_transitive_prop = lambda x: hyperdb.String()
+ self.form.value.append(MiniFieldStorage('foo', 'hello world'))
+
+ self.assertFilterEquals('foo')
+
+ # The single value gets replaced with the tokenized list.
+ self.assertEqual([x.value for x in self.form['foo']],
+ ['hello', 'world'])
+
+class CollisionDetectionTestCase(ActionTestCase):
+ def setUp(self):
+ ActionTestCase.setUp(self)
+ self.action = EditItemAction(self.client)
+ self.now = Date('.')
+ # round off for testing
+ self.now.second = int(self.now.second)
+
+ def testLastUserActivity(self):
+ self.assertEqual(self.action.lastUserActivity(), None)
+
+ self.client.form.value.append(
+ MiniFieldStorage('@lastactivity', str(self.now)))
+ self.assertEqual(self.action.lastUserActivity(), self.now)
+
+ def testLastNodeActivity(self):
+ self.action.classname = 'issue'
+ self.action.nodeid = '1'
+
+ def get(nodeid, propname):
+ self.assertEqual(nodeid, '1')
+ self.assertEqual(propname, 'activity')
+ return self.now
+ self.client.db.issue.get = get
+
+ self.assertEqual(self.action.lastNodeActivity(), self.now)
+
+ def testCollision(self):
+ # fake up an actual change
+ self.action.classname = 'test'
+ self.action.nodeid = '1'
+ self.client.parsePropsFromForm = lambda: ({('test','1'):{1:1}}, [])
+ self.failUnless(self.action.detectCollision(self.now,
+ self.now + Interval("1d")))
+ self.failIf(self.action.detectCollision(self.now,
+ self.now - Interval("1d")))
+ self.failIf(self.action.detectCollision(None, self.now))
+
+class LoginTestCase(ActionTestCase):
+ def setUp(self):
+ ActionTestCase.setUp(self)
+ self.client.error_message = []
+
+ # set the db password to 'right'
+ self.client.db.user.get = lambda a,b: 'right'
+
+ # unless explicitly overridden, we should never get here
+ self.client.opendb = lambda a: self.fail(
+ "Logged in, but we shouldn't be.")
+
+ def assertLoginLeavesMessages(self, messages, username=None, password=None):
+ if username is not None:
+ self.form.value.append(MiniFieldStorage('__login_name', username))
+ if password is not None:
+ self.form.value.append(
+ MiniFieldStorage('__login_password', password))
+
+ LoginAction(self.client).handle()
+ self.assertEqual(self.client.error_message, messages)
+
+ def testNoUsername(self):
+ self.assertLoginLeavesMessages(['Username required'])
+
+ def testInvalidUsername(self):
+ def raiseKeyError(a):
+ raise KeyError
+ self.client.db.user.lookup = raiseKeyError
+ self.assertLoginLeavesMessages(['Invalid login'], 'foo')
+
+ def testInvalidPassword(self):
+ self.assertLoginLeavesMessages(['Invalid login'], 'foo', 'wrong')
+
+ def testNoWebAccess(self):
+ self.assertLoginLeavesMessages(['You do not have permission to login'],
+ 'foo', 'right')
+
+ def testCorrectLogin(self):
+ self.client.db.security.hasPermission = lambda *args, **kwargs: True
+
+ def opendb(username):
+ self.assertEqual(username, 'foo')
+ self.client.opendb = opendb
+
+ self.assertLoginLeavesMessages([], 'foo', 'right')
+
+class EditItemActionTestCase(ActionTestCase):
+ def setUp(self):
+ ActionTestCase.setUp(self)
+ self.result = []
+ class AppendResult:
+ def __init__(inner_self, name):
+ inner_self.name = name
+ def __call__(inner_self, *args, **kw):
+ self.result.append((inner_self.name, args, kw))
+ if inner_self.name == 'set':
+ return kw
+ return '17'
+
+ self.client.db.security.hasPermission = true
+ self.client.classname = 'issue'
+ self.client.base = 'http://tracker/'
+ self.client.nodeid = '4711'
+ self.client.template = 'item'
+ self.client.db.classes.create = AppendResult('create')
+ self.client.db.classes.set = AppendResult('set')
+ self.client.db.classes.getprops = lambda: \
+ ({'messages':hyperdb.Multilink('msg')
+ ,'content':hyperdb.String()
+ ,'files':hyperdb.Multilink('file')
+ ,'msg':hyperdb.Link('msg')
+ })
+ self.action = EditItemAction(self.client)
+
+ def testMessageAttach(self):
+ expect = \
+ [ ('create',(),{'content':'t'})
+ , ('set',('4711',), {'messages':['23','42','17']})
+ ]
+ self.client.db.classes.get = lambda a, b:['23','42']
+ self.client.parsePropsFromForm = lambda: \
+ ( {('msg','-1'):{'content':'t'},('issue','4711'):{}}
+ , [('issue','4711','messages',[('msg','-1')])]
+ )
+ try :
+ self.action.handle()
+ except Redirect, msg:
+ pass
+ self.assertEqual(expect, self.result)
+
+ def testFileAttach(self):
+ expect = \
+ [('create',(),{'content':'t','type':'text/plain','name':'t.txt'})
+ ,('set',('4711',),{'files':['23','42','17']})
+ ]
+ self.client.db.classes.get = lambda a, b:['23','42']
+ self.client.parsePropsFromForm = lambda: \
+ ( {('file','-1'):{'content':'t','type':'text/plain','name':'t.txt'}
+ ,('issue','4711'):{}
+ }
+ , [('issue','4711','messages',[('msg','-1')])
+ ,('issue','4711','files',[('file','-1')])
+ ,('msg','-1','files',[('file','-1')])
+ ]
+ )
+ try :
+ self.action.handle()
+ except Redirect, msg:
+ pass
+ self.assertEqual(expect, self.result)
+
+ def testLinkExisting(self):
+ expect = [('set',('4711',),{'messages':['23','42','1']})]
+ self.client.db.classes.get = lambda a, b:['23','42']
+ self.client.parsePropsFromForm = lambda: \
+ ( {('issue','4711'):{},('msg','1'):{}}
+ , [('issue','4711','messages',[('msg','1')])]
+ )
+ try :
+ self.action.handle()
+ except Redirect, msg:
+ pass
+ self.assertEqual(expect, self.result)
+
+ def testLinkNewToExisting(self):
+ expect = [('create',(),{'msg':'1','title':'TEST'})]
+ self.client.db.classes.get = lambda a, b:['23','42']
+ self.client.parsePropsFromForm = lambda: \
+ ( {('issue','-1'):{'title':'TEST'},('msg','1'):{}}
+ , [('issue','-1','msg',[('msg','1')])]
+ )
+ try :
+ self.action.handle()
+ except Redirect, msg:
+ pass
+ self.assertEqual(expect, self.result)
+
+def test_suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(RetireActionTestCase))
+ suite.addTest(unittest.makeSuite(StandardSearchActionTestCase))
+ suite.addTest(unittest.makeSuite(FakeFilterVarsTestCase))
+ suite.addTest(unittest.makeSuite(ShowActionTestCase))
+ suite.addTest(unittest.makeSuite(CollisionDetectionTestCase))
+ suite.addTest(unittest.makeSuite(LoginTestCase))
+ suite.addTest(unittest.makeSuite(EditItemActionTestCase))
+ return suite
+
+if __name__ == '__main__':
+ runner = unittest.TextTestRunner()
+ unittest.main(testRunner=runner)
+
+# vim: set et sts=4 sw=4 :