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, SeriousError\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,\r
42 **kwargs):\r
43 """An extension of assertRaises, which also checks the exception\r
44 message. We need this because we rely on exception messages when\r
45 redirecting.\r
46 """\r
47 try:\r
48 callable(*args, **kwargs)\r
49 except exception, msg:\r
50 self.assertEqual(str(msg), message)\r
51 else:\r
52 if hasattr(excClass,'__name__'):\r
53 excName = excClass.__name__\r
54 else:\r
55 excName = str(excClass)\r
56 raise self.failureException, excName\r
57 \r
58 def testShowAction(self):\r
59 self.client.db.config.TRACKER_WEB = 'BASE/'\r
60 \r
61 action = ShowAction(self.client)\r
62 self.assertRaises(ValueError, action.handle)\r
63 \r
64 self.form.value.append(MiniFieldStorage('@type', 'issue'))\r
65 self.assertRaises(SeriousError, action.handle)\r
66 \r
67 self.form.value.append(MiniFieldStorage('@number', '1'))\r
68 self.assertRaisesMessage(Redirect, action.handle, 'BASE/issue1')\r
69 \r
70 def testShowActionNoType(self):\r
71 action = ShowAction(self.client)\r
72 self.assertRaises(ValueError, action.handle)\r
73 self.form.value.append(MiniFieldStorage('@number', '1'))\r
74 self.assertRaisesMessage(ValueError, action.handle,\r
75 'No type specified')\r
76 \r
77 class RetireActionTestCase(ActionTestCase):\r
78 def testRetireAction(self):\r
79 self.client.db.security.hasPermission = true\r
80 self.client.ok_message = []\r
81 RetireAction(self.client).handle()\r
82 self.assert_(len(self.client.ok_message) == 1)\r
83 \r
84 def testNoPermission(self):\r
85 self.assertRaises(Unauthorised, RetireAction(self.client).execute)\r
86 \r
87 def testDontRetireAdminOrAnonymous(self):\r
88 self.client.db.security.hasPermission=true\r
89 # look up the user class\r
90 self.client.classname = 'user'\r
91 # but always look up admin, regardless of nodeid\r
92 self.client.db.user.get = lambda a,b: 'admin'\r
93 self.assertRaises(ValueError, RetireAction(self.client).handle)\r
94 # .. or anonymous\r
95 self.client.db.user.get = lambda a,b: 'anonymous'\r
96 self.assertRaises(ValueError, RetireAction(self.client).handle)\r
97 \r
98 class SearchActionTestCase(ActionTestCase):\r
99 def setUp(self):\r
100 ActionTestCase.setUp(self)\r
101 self.action = SearchAction(self.client)\r
102 \r
103 class StandardSearchActionTestCase(SearchActionTestCase):\r
104 def testNoPermission(self):\r
105 self.assertRaises(Unauthorised, self.action.execute)\r
106 \r
107 def testQueryName(self):\r
108 self.assertEqual(self.action.getQueryName(), '')\r
109 \r
110 self.form.value.append(MiniFieldStorage('@queryname', 'foo'))\r
111 self.assertEqual(self.action.getQueryName(), 'foo')\r
112 \r
113 class FakeFilterVarsTestCase(SearchActionTestCase):\r
114 def setUp(self):\r
115 SearchActionTestCase.setUp(self)\r
116 self.client.db.classes.getprops = lambda: {'foo': hyperdb.Multilink('foo')}\r
117 \r
118 def assertFilterEquals(self, expected):\r
119 self.action.fakeFilterVars()\r
120 self.assertEqual(self.form.getvalue('@filter'), expected)\r
121 \r
122 def testEmptyMultilink(self):\r
123 self.form.value.append(MiniFieldStorage('foo', ''))\r
124 self.form.value.append(MiniFieldStorage('foo', ''))\r
125 \r
126 self.assertFilterEquals(None)\r
127 \r
128 def testNonEmptyMultilink(self):\r
129 self.form.value.append(MiniFieldStorage('foo', ''))\r
130 self.form.value.append(MiniFieldStorage('foo', '1'))\r
131 \r
132 self.assertFilterEquals('foo')\r
133 \r
134 def testEmptyKey(self):\r
135 self.form.value.append(MiniFieldStorage('foo', ''))\r
136 self.assertFilterEquals(None)\r
137 \r
138 def testStandardKey(self):\r
139 self.form.value.append(MiniFieldStorage('foo', '1'))\r
140 self.assertFilterEquals('foo')\r
141 \r
142 def testStringKey(self):\r
143 self.client.db.classes.getprops = lambda: {'foo': hyperdb.String()}\r
144 self.form.value.append(MiniFieldStorage('foo', 'hello'))\r
145 self.assertFilterEquals('foo')\r
146 \r
147 def testTokenizedStringKey(self):\r
148 self.client.db.classes.getprops = lambda: {'foo': hyperdb.String()}\r
149 self.form.value.append(MiniFieldStorage('foo', 'hello world'))\r
150 \r
151 self.assertFilterEquals('foo')\r
152 \r
153 # The single value gets replaced with the tokenized list.\r
154 self.assertEqual([x.value for x in self.form['foo']], ['hello', 'world'])\r
155 \r
156 class CollisionDetectionTestCase(ActionTestCase):\r
157 def setUp(self):\r
158 ActionTestCase.setUp(self)\r
159 self.action = EditItemAction(self.client)\r
160 self.now = Date('.')\r
161 # round off for testing\r
162 self.now.second = int(self.now.second)\r
163 \r
164 def testLastUserActivity(self):\r
165 self.assertEqual(self.action.lastUserActivity(), None)\r
166 \r
167 self.client.form.value.append(MiniFieldStorage('@lastactivity', str(self.now)))\r
168 self.assertEqual(self.action.lastUserActivity(), self.now)\r
169 \r
170 def testLastNodeActivity(self):\r
171 self.action.classname = 'issue'\r
172 self.action.nodeid = '1'\r
173 \r
174 def get(nodeid, propname):\r
175 self.assertEqual(nodeid, '1')\r
176 self.assertEqual(propname, 'activity')\r
177 return self.now\r
178 self.client.db.issue.get = get\r
179 \r
180 self.assertEqual(self.action.lastNodeActivity(), self.now)\r
181 \r
182 def testCollision(self):\r
183 self.failUnless(self.action.detectCollision(self.now, self.now + Interval("1d")))\r
184 self.failIf(self.action.detectCollision(self.now, self.now - Interval("1d")))\r
185 self.failIf(self.action.detectCollision(None, self.now))\r
186 \r
187 def test_suite():\r
188 suite = unittest.TestSuite()\r
189 suite.addTest(unittest.makeSuite(RetireActionTestCase))\r
190 suite.addTest(unittest.makeSuite(StandardSearchActionTestCase))\r
191 suite.addTest(unittest.makeSuite(FakeFilterVarsTestCase))\r
192 suite.addTest(unittest.makeSuite(ShowActionTestCase))\r
193 suite.addTest(unittest.makeSuite(CollisionDetectionTestCase))\r
194 return suite\r
195 \r
196 if __name__ == '__main__':\r
197 runner = unittest.TextTestRunner()\r
198 unittest.main(testRunner=runner)\r