Code

Forward-porting of fixes from the maintenance branch.
[roundup.git] / test / test_actions.py
1 from __future__ import nested_scopes\r
2 \r
3 import unittest\r
4 from cgi import FieldStorage, MiniFieldStorage\r
5 \r
6 from roundup import hyperdb\r
7 from roundup.date import Date, Interval\r
8 from roundup.cgi.actions import *\r
9 from roundup.cgi.exceptions import Redirect, Unauthorised\r
10 \r
11 class MockNull:\r
12     def __init__(self, **kwargs):\r
13         for key, value in kwargs.items():\r
14             setattr(self, key, value)\r
15 \r
16     def __call__(self, *args, **kwargs): return MockNull()\r
17     def __getattr__(self, name):\r
18         # This allows assignments which assume all intermediate steps are Null\r
19         # objects if they don't exist yet.\r
20         #\r
21         # For example (with just 'client' defined):\r
22         #\r
23         # client.db.config.TRACKER_WEB = 'BASE/'\r
24         setattr(self, name, MockNull())\r
25         return getattr(self, name)\r
26 \r
27     def __getitem__(self, key): return self\r
28     def __nonzero__(self): return 0\r
29     def __str__(self): return ''\r
30 \r
31 def true(*args, **kwargs):\r
32     return 1\r
33 \r
34 class ActionTestCase(unittest.TestCase):\r
35     def setUp(self):\r
36         self.form = FieldStorage()\r
37         self.client = MockNull()\r
38         self.client.form = self.form\r
39 \r
40 class ShowActionTestCase(ActionTestCase):\r
41     def assertRaisesMessage(self, exception, callable, message, *args, **kwargs):\r
42         try:\r
43             callable(*args, **kwargs)\r
44         except exception, msg:\r
45             self.assertEqual(str(msg), message)\r
46         else:\r
47             if hasattr(excClass,'__name__'): excName = excClass.__name__\r
48             else: excName = str(excClass)\r
49             raise self.failureException, excName\r
50 \r
51     def testShowAction(self):\r
52         self.client.db.config.TRACKER_WEB = 'BASE/'\r
53 \r
54         action = ShowAction(self.client)\r
55         self.assertRaises(ValueError, action.handle)\r
56 \r
57         self.form.value.append(MiniFieldStorage('@type', 'issue'))\r
58         self.assertRaisesMessage(Redirect, action.handle, 'BASE/issue')\r
59 \r
60         self.form.value.append(MiniFieldStorage('@number', '1'))\r
61         self.assertRaisesMessage(Redirect, action.handle, 'BASE/issue1')\r
62 \r
63 class RetireActionTestCase(ActionTestCase):\r
64     def testRetireAction(self):\r
65         self.client.db.security.hasPermission = true\r
66         self.client.ok_message = []\r
67         RetireAction(self.client).handle()\r
68         self.assert_(len(self.client.ok_message) == 1)\r
69 \r
70     def testNoPermission(self):\r
71         self.assertRaises(Unauthorised, RetireAction(self.client).execute)\r
72 \r
73     def testDontRetireAdminOrAnonymous(self):\r
74         self.client.db.security.hasPermission=true\r
75         # look up the user class\r
76         self.client.classname = 'user'\r
77         # but always look up admin, regardless of nodeid\r
78         self.client.db.user.get = lambda a,b: 'admin'\r
79         self.assertRaises(ValueError, RetireAction(self.client).handle)\r
80         # .. or anonymous\r
81         self.client.db.user.get = lambda a,b: 'anonymous'\r
82         self.assertRaises(ValueError, RetireAction(self.client).handle)\r
83 \r
84 class SearchActionTestCase(ActionTestCase):\r
85     def setUp(self):\r
86         ActionTestCase.setUp(self)\r
87         self.action = SearchAction(self.client)\r
88 \r
89 class StandardSearchActionTestCase(SearchActionTestCase):\r
90     def testNoPermission(self):\r
91         self.assertRaises(Unauthorised, self.action.execute)\r
92 \r
93     def testQueryName(self):\r
94         self.assertEqual(self.action.getQueryName(), '')\r
95 \r
96         self.form.value.append(MiniFieldStorage('@queryname', 'foo'))\r
97         self.assertEqual(self.action.getQueryName(), 'foo')\r
98 \r
99 class FakeFilterVarsTestCase(SearchActionTestCase):\r
100     def setUp(self):\r
101         SearchActionTestCase.setUp(self)\r
102         self.client.db.classes.getprops = lambda: {'foo': hyperdb.Multilink('foo')}\r
103 \r
104     def assertFilterEquals(self, expected):\r
105         self.action.fakeFilterVars()\r
106         self.assertEqual(self.form.getvalue('@filter'), expected)\r
107 \r
108     def testEmptyMultilink(self):\r
109         self.form.value.append(MiniFieldStorage('foo', ''))\r
110         self.form.value.append(MiniFieldStorage('foo', ''))\r
111 \r
112         self.assertFilterEquals(None)\r
113 \r
114     def testNonEmptyMultilink(self):\r
115         self.form.value.append(MiniFieldStorage('foo', ''))\r
116         self.form.value.append(MiniFieldStorage('foo', '1'))\r
117 \r
118         self.assertFilterEquals('foo')\r
119 \r
120     def testEmptyKey(self):\r
121         self.form.value.append(MiniFieldStorage('foo', ''))\r
122         self.assertFilterEquals(None)\r
123 \r
124     def testStandardKey(self):\r
125         self.form.value.append(MiniFieldStorage('foo', '1'))\r
126         self.assertFilterEquals('foo')\r
127 \r
128     def testStringKey(self):\r
129         self.client.db.classes.getprops = lambda: {'foo': hyperdb.String()}\r
130         self.form.value.append(MiniFieldStorage('foo', 'hello'))\r
131         self.assertFilterEquals('foo')\r
132 \r
133     def testTokenizedStringKey(self):\r
134         self.client.db.classes.getprops = lambda: {'foo': hyperdb.String()}\r
135         self.form.value.append(MiniFieldStorage('foo', 'hello world'))\r
136 \r
137         self.assertFilterEquals('foo')\r
138 \r
139         # The single value gets replaced with the tokenized list.\r
140         self.assertEqual([x.value for x in self.form['foo']], ['hello', 'world'])\r
141 \r
142 class CollisionDetectionTestCase(ActionTestCase):\r
143     def setUp(self):\r
144         ActionTestCase.setUp(self)\r
145         self.action = EditItemAction(self.client)\r
146         self.now = Date('.')\r
147 \r
148     def testLastUserActivity(self):\r
149         self.assertEqual(self.action.lastUserActivity(), None)\r
150 \r
151         self.client.form.value.append(MiniFieldStorage('@lastactivity', str(self.now)))\r
152         self.assertEqual(self.action.lastUserActivity(), self.now)\r
153 \r
154     def testLastNodeActivity(self):\r
155         self.action.classname = 'issue'\r
156         self.action.nodeid = '1'\r
157 \r
158         def get(nodeid, propname):\r
159             self.assertEqual(nodeid, '1')\r
160             self.assertEqual(propname, 'activity')\r
161             return self.now\r
162         self.client.db.issue.get = get\r
163 \r
164         self.assertEqual(self.action.lastNodeActivity(), self.now)\r
165 \r
166     def testCollision(self):\r
167         self.failUnless(self.action.detectCollision(self.now, self.now + Interval("1d")))\r
168         self.failIf(self.action.detectCollision(self.now, self.now - Interval("1d")))\r
169         self.failIf(self.action.detectCollision(None, self.now))\r
170 \r
171 def test_suite():\r
172     suite = unittest.TestSuite()\r
173     suite.addTest(unittest.makeSuite(RetireActionTestCase))\r
174     suite.addTest(unittest.makeSuite(StandardSearchActionTestCase))\r
175     suite.addTest(unittest.makeSuite(FakeFilterVarsTestCase))\r
176     suite.addTest(unittest.makeSuite(ShowActionTestCase))\r
177     suite.addTest(unittest.makeSuite(CollisionDetectionTestCase))\r
178     return suite\r
179 \r
180 if __name__ == '__main__':\r
181     runner = unittest.TextTestRunner()\r
182     unittest.main(testRunner=runner)\r