Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(619)

Side by Side Diff: appengine/swarming/handlers_test.py

Issue 2500503002: Redirecting old ui to new ui (Closed)
Patch Set: Remove post handlers Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « appengine/swarming/handlers_frontend.py ('k') | appengine/swarming/templates/bot_view.html » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # coding: utf-8 2 # coding: utf-8
3 # Copyright 2013 The LUCI Authors. All rights reserved. 3 # Copyright 2013 The LUCI Authors. All rights reserved.
4 # Use of this source code is governed under the Apache License, Version 2.0 4 # Use of this source code is governed under the Apache License, Version 2.0
5 # that can be found in the LICENSE file. 5 # that can be found in the LICENSE file.
6 6
7 import datetime 7 import datetime
8 import itertools 8 import itertools
9 import json 9 import json
10 import logging 10 import logging
(...skipping 30 matching lines...) Expand all
41 }) 41 })
42 42
43 def tearDown(self): 43 def tearDown(self):
44 try: 44 try:
45 template.reset() 45 template.reset()
46 finally: 46 finally:
47 super(AppTestBase, self).tearDown() 47 super(AppTestBase, self).tearDown()
48 48
49 49
50 class FrontendTest(AppTestBase): 50 class FrontendTest(AppTestBase):
51 def test_bots(self):
52 self.set_as_admin()
53
54 # Add bots to display.
55 state = {
56 'dict': {'random': 'values'},
57 'float': 0.,
58 'list': ['of', 'things'],
59 'str': u'uni',
60 }
61 bot_management.bot_event(
62 event_type='bot_connected', bot_id='id1',
63 external_ip='8.8.4.4', authenticated_as='bot:whitelisted-ip',
64 dimensions={'id': ['id1']}, state=state, version='123456789',
65 quarantined=False, task_id=None, task_name=None)
66 bot_management.bot_event(
67 event_type='bot_connected', bot_id='id2',
68 external_ip='8.8.8.8', authenticated_as='bot:whitelisted-ip',
69 dimensions={'id': ['id2']}, state={'ram': 65}, version='123456789',
70 quarantined=False, task_id=None, task_name=None)
71
72 response = self.app.get('/restricted/bots', status=200)
73 self.assertGreater(len(response.body), 1000)
74
75 def test_delete_bot(self):
76 self.set_as_admin()
77
78 bot_management.bot_event(
79 event_type='bot_connected', bot_id='id1',
80 external_ip='8.8.4.4', authenticated_as='bot:whitelisted-ip',
81 dimensions={'id': ['id1']}, state={'foo': 'bar'}, version='123456789',
82 quarantined=False, task_id=None, task_name=None)
83 response = self.app.get('/restricted/bots', status=200)
84 self.assertTrue('id1' in response.body)
85
86 response = self.app.post(
87 '/restricted/bot/id1/delete',
88 params={},
89 headers={'X-XSRF-Token': self.get_xsrf_token()})
90 self.assertFalse('id1' in response.body)
91
92 response = self.app.get('/restricted/bots', status=200)
93 self.assertFalse('id1' in response.body)
94 51
95 def test_root(self): 52 def test_root(self):
96 response = self.app.get('/', status=200) 53 response = self.app.get('/', status=200)
97 self.assertGreater(len(response.body), 600) 54 self.assertGreater(len(response.body), 600)
98 55
99 def testAllSwarmingHandlersAreSecured(self): 56 def testAllSwarmingHandlersAreSecured(self):
100 # Test that all handlers are accessible only to authenticated user or 57 # Test that all handlers are accessible only to authenticated user or
101 # bots. Assumes all routes are defined with plain paths (i.e. 58 # bots. Assumes all routes are defined with plain paths (i.e.
102 # '/some/handler/path' and not regexps). 59 # '/some/handler/path' and not regexps).
103 60
(...skipping 11 matching lines...) Expand all
115 '/api/config/v1/validate', 72 '/api/config/v1/validate',
116 '/auth', 73 '/auth',
117 '/ereporter2/api/v1/on_error', 74 '/ereporter2/api/v1/on_error',
118 '/stats', 75 '/stats',
119 '/api/swarming/v1/server/permissions', 76 '/api/swarming/v1/server/permissions',
120 '/swarming/api/v1/client/list', 77 '/swarming/api/v1/client/list',
121 '/swarming/api/v1/bot/server_ping', 78 '/swarming/api/v1/bot/server_ping',
122 '/swarming/api/v1/stats/summary/<resolution:[a-z]+>', 79 '/swarming/api/v1/stats/summary/<resolution:[a-z]+>',
123 '/swarming/api/v1/stats/dimensions/<dimensions:.+>/<resolution:[a-z]+>', 80 '/swarming/api/v1/stats/dimensions/<dimensions:.+>/<resolution:[a-z]+>',
124 '/swarming/api/v1/stats/user/<user:.+>/<resolution:[a-z]+>', 81 '/swarming/api/v1/stats/user/<user:.+>/<resolution:[a-z]+>',
82 '/user/tasks',
83 '/restricted/bots',
125 ]) 84 ])
126 85
127 # Grab the set of all routes. 86 # Grab the set of all routes.
128 app = self.app.app 87 app = self.app.app
129 routes = set(app.router.match_routes) 88 routes = set(app.router.match_routes)
130 routes.update(app.router.build_routes.itervalues()) 89 routes.update(app.router.build_routes.itervalues())
131 90
132 # Get all routes that are not protected by GAE auth mechanism. 91 # Get all routes that are not protected by GAE auth mechanism.
133 routes_to_check = [ 92 routes_to_check = [
134 route for route in routes 93 route for route in routes
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
183 '/swarming/api/v1/stats/dimensions/%s/days' % quoted, 142 '/swarming/api/v1/stats/dimensions/%s/days' % quoted,
184 '/swarming/api/v1/stats/dimensions/%s/hours' % quoted, 143 '/swarming/api/v1/stats/dimensions/%s/hours' % quoted,
185 '/swarming/api/v1/stats/dimensions/%s/minutes' % quoted, 144 '/swarming/api/v1/stats/dimensions/%s/minutes' % quoted,
186 ) 145 )
187 for url in urls: 146 for url in urls:
188 self.app.get(url, status=403) 147 self.app.get(url, status=403)
189 self.set_as_user() 148 self.set_as_user()
190 for url in urls: 149 for url in urls:
191 self.app.get(url, status=200) 150 self.app.get(url, status=200)
192 151
193 def test_task_list_empty(self): 152 def test_task_redirect(self):
194 # Just assert it doesn't throw.
195 self.set_as_privileged_user()
196 self.app.get('/user/tasks', status=200)
197 self.app.get('/user/task/12345', status=404)
198
199 def test_add_task_and_list_user(self):
200 # Add a task via the API as a user, then assert it can be viewed.
201 self.set_as_user()
202 _, task_id = self.client_create_task_raw()
203
204 self.set_as_privileged_user()
205 self.app.get('/user/tasks', status=200)
206 self.app.get('/user/task/%s' % task_id, status=200)
207
208 self.set_as_bot()
209 self.do_handshake()
210 reaped = self.bot_poll()
211 self.bot_complete_task(task_id=reaped['manifest']['task_id'])
212 # Add unicode chars.
213
214 # This can only work once a bot reaped the task.
215 self.set_as_privileged_user()
216 self.app.get('/user/task/%s' % reaped['manifest']['task_id'], status=200)
217
218 def test_task_deduped(self):
219 self.set_as_user()
220 _, task_id_1 = self.client_create_task_raw(properties=dict(idempotent=True))
221
222 self.set_as_bot()
223 task_id_bot = self.bot_run_task()
224 self.assertEqual(task_id_1, task_id_bot[:-1] + '0')
225 self.assertEqual('1', task_id_bot[-1:])
226
227 # Create a second task. Results will be returned immediately without the bot
228 # running anything.
229 self.set_as_user()
230 _, task_id_2 = self.client_create_task_raw(
231 name='ho', properties=dict(idempotent=True))
232
233 self.set_as_bot()
234 resp = self.bot_poll()
235 self.assertEqual('sleep', resp['cmd'])
236
237 self.set_as_privileged_user()
238 # Look at the results. It's the same as the previous run, even if task_id_2
239 # was never executed.
240 response = self.app.get('/user/task/%s' % task_id_2, status=200)
241 self.assertTrue(
242 u'rÉsult string'.encode('utf-8') in response.body, response.body)
243 self.assertTrue('Was deduped from' in response.body, response.body)
244
245 def test_task_denied(self):
246 # Add a task via the API as a user, then assert it can't be viewed by
247 # anonymous user.
248 self.set_as_user()
249 _, task_id = self.client_create_task_raw()
250
251 # Redirect to login page.
252 self.set_as_anonymous() 153 self.set_as_anonymous()
253 self.app.get('/user/tasks', status=302) 154 self.app.get('/user/tasks', status=302)
254 self.app.get('/user/task/%s' % task_id, status=302) 155 self.app.get('/user/task/123', status=302)
255 156
256 @staticmethod 157 def test_bot_redirect(self):
257 def _sort_state_product(): 158 self.set_as_anonymous()
258 sort_choices = [i[0] for i in handlers_frontend.TasksHandler.SORT_CHOICES] 159 self.app.get('/restricted/bots', status=302)
259 state_choices = sum( 160 self.app.get('/restricted/bot/bot321', status=302)
260 ([i[0] for i in j]
261 for j in handlers_frontend.TasksHandler.STATE_CHOICES),
262 [])
263 return itertools.product(sort_choices, state_choices)
264
265 def test_task_list_query(self):
266 # Try all the combinations of task queries to ensure the index exist.
267 self.set_as_privileged_user()
268 self.client_create_task_raw()
269 for sort, state in self._sort_state_product():
270 url = '/user/tasks?sort=%s&state=%s' % (sort, state)
271 # See require_index in ../components/support/test_case.py in case of
272 # NeedIndexError. Do not use status=200 so the output is printed in case
273 # of failure.
274 resp = self.app.get(url, expect_errors=True)
275 self.assertEqual(200, resp.status_code, (resp.body, sort, state))
276
277 self.app.get('/user/tasks?sort=foo', status=400)
278 self.app.get('/user/tasks?state=foo', status=400)
279
280 def test_task_search_task_tag(self):
281 # Try all the combinations of task queries to ensure the index exist.
282 self.set_as_privileged_user()
283 self.client_create_task_raw()
284 self.set_as_bot()
285 reaped = self.bot_poll()
286 self.bot_complete_task(task_id=reaped['manifest']['task_id'])
287 self.set_as_privileged_user()
288 self.app.get('/user/tasks?task_tag=yo:dawg', status=200)
289 for sort, state in self._sort_state_product():
290 url = '/user/tasks?sort=%s&state=%s' % (sort, state)
291 self.app.get(url + '&task_tag=yo:dawg', status=200)
292
293 def test_task_cancel(self):
294 self.set_as_privileged_user()
295 _, task_id = self.client_create_task_raw()
296
297 self.set_as_admin()
298 # Just ensure it doesn't crash when it shows the 'Cancel' button.
299 self.app.get('/user/tasks')
300
301 xsrf_token = self.get_xsrf_token()
302 self.app.post(
303 '/user/task/%s/cancel' % task_id, {'xsrf_token': xsrf_token})
304
305 # Ensure there's no task available anymore by polling.
306 self.set_as_bot()
307 reaped = self.bot_poll('bot1')
308 self.assertEqual('sleep', reaped['cmd'])
309
310 def test_task_retry(self):
311 self.set_as_privileged_user()
312 _, task_id = self.client_create_task_raw()
313 xsrf_token = self.get_xsrf_token()
314 resp = self.app.post(
315 '/user/task/%s/retry' % task_id, {'xsrf_token': xsrf_token})
316 self.assertEqual(302, resp.status_code)
317 prefix = 'http://localhost/user/task/'
318 self.assertTrue(resp.location.startswith(prefix))
319 new_task_id = resp.location[len(prefix):]
320 self.assertNotEqual(new_task_id, task_id)
321
322 # Both tasks are scheduled.
323 self.set_as_bot()
324 reaped = self.bot_poll('bot1')
325 self.assertEqual('run', reaped['cmd'])
326 self.assertEqual(task_id[:-1] + '1', reaped['manifest']['task_id'])
327 reaped = self.bot_poll('bot2')
328 self.assertEqual('run', reaped['cmd'])
329 self.assertEqual(new_task_id[:-1] + '1', reaped['manifest']['task_id'])
330
331 def test_bot_list_empty(self):
332 # Just assert it doesn't throw.
333 self.set_as_admin()
334 self.app.get('/restricted/bots', status=200)
335 self.app.get('/restricted/bot/unknown_bot', status=200)
336
337 def test_bot_listing(self):
338 # Create a task, create 2 bots, one with a task assigned, the other without.
339 self.set_as_admin()
340 self.client_create_task_raw()
341
342 self.set_as_bot()
343 self.bot_poll('bot1')
344 self.bot_poll('bot2')
345 params = self.do_handshake('bot2')
346 params['event'] = 'bot_log'
347 params['message'] = 'for the best'
348 self.assertEqual({}, self.post_json('/swarming/api/v1/bot/event', params))
349
350 self.set_as_admin()
351 response = self.app.get('/restricted/bots', status=200)
352 next_page_re = re.compile(r'<a\s+href="(.+?)">Next page</a>')
353 self.assertFalse(next_page_re.search(response.body))
354 self.app.get('/restricted/bot/bot1', status=200)
355 response = self.app.get('/restricted/bot/bot2', status=200)
356 self.assertIn('for the best', response.body)
357
358 response = self.app.get('/restricted/bots?limit=1', status=200)
359 url = next_page_re.search(response.body).group(1)
360 self.assertTrue(
361 url.startswith('/restricted/bots?limit=1&sort_by=__key__&cursor='), url)
362 response = self.app.get(url, status=200)
363 self.assertFalse(next_page_re.search(response.body))
364
365 # Test more complex indexes.
366 for sort_by in handlers_frontend.BotsListHandler.ACCEPTABLE_BOTS_SORTS:
367 response = self.app.get(
368 '/restricted/bots?limit=1&sort_by=%s' % sort_by, status=200)
369 self.assertTrue(next_page_re.search(response.body), sort_by)
370 response = self.app.get(
371 '/restricted/bots?limit=1&sort_by=%s&dimensions=os:Amiga%%0Aid:bot1' %
372 sort_by,
373 status=200)
374 # The bot1 should be in the response.
375 self.assertTrue('pool' in response.body, sort_by)
376 self.assertTrue('default' in response.body, sort_by)
377 161
378 162
379 class FrontendAdminTest(AppTestBase): 163 class FrontendAdminTest(AppTestBase):
380 # Admin-specific management pages. 164 # Admin-specific management pages.
381 def test_bootstrap_default(self): 165 def test_bootstrap_default(self):
382 self.set_as_bot() 166 self.set_as_bot()
383 self.mock(bot_code, 'generate_bootstrap_token', lambda: 'bootstrap-token') 167 self.mock(bot_code, 'generate_bootstrap_token', lambda: 'bootstrap-token')
384 actual = self.app.get('/bootstrap').body 168 actual = self.app.get('/bootstrap').body
385 path = os.path.join(self.APP_DIR, 'swarming_bot', 'config', 'bootstrap.py') 169 path = os.path.join(self.APP_DIR, 'swarming_bot', 'config', 'bootstrap.py')
386 with open(path, 'rb') as f: 170 with open(path, 'rb') as f:
(...skipping 181 matching lines...) Expand 10 before | Expand all | Expand 10 after
568 url+arg, headers={'X-AppEngine-QueueName': 'bogus name'}, status=403) 352 url+arg, headers={'X-AppEngine-QueueName': 'bogus name'}, status=403)
569 353
570 354
571 if __name__ == '__main__': 355 if __name__ == '__main__':
572 if '-v' in sys.argv: 356 if '-v' in sys.argv:
573 unittest.TestCase.maxDiff = None 357 unittest.TestCase.maxDiff = None
574 logging.basicConfig( 358 logging.basicConfig(
575 level=logging.DEBUG if '-v' in sys.argv else logging.CRITICAL, 359 level=logging.DEBUG if '-v' in sys.argv else logging.CRITICAL,
576 format='%(levelname)-7s %(filename)s:%(lineno)3d %(message)s') 360 format='%(levelname)-7s %(filename)s:%(lineno)3d %(message)s')
577 unittest.main() 361 unittest.main()
OLDNEW
« no previous file with comments | « appengine/swarming/handlers_frontend.py ('k') | appengine/swarming/templates/bot_view.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698