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