blob: 1489362b35f1dac58c036cb4046d55f43cd0ceba [file] [log] [blame]
showardf828c772010-01-25 21:49:42 +00001#!/usr/bin/python
2
3import common
4import operator, unittest
5import simplejson
6from autotest_lib.frontend import setup_django_environment
7from autotest_lib.frontend import setup_test_environment
8from django.test import client
9from autotest_lib.frontend.afe import control_file, frontend_test_utils, models
jamesrendd855242010-03-02 22:23:44 +000010from autotest_lib.frontend.afe import model_attributes
showardf828c772010-01-25 21:49:42 +000011
12class ResourceTestCase(unittest.TestCase,
13 frontend_test_utils.FrontendTestMixin):
showardf46ad4c2010-02-03 20:28:59 +000014 URI_PREFIX = 'http://testserver/afe/server/resources'
showardf828c772010-01-25 21:49:42 +000015
16 CONTROL_FILE_CONTENTS = 'my control file contents'
17
18 def setUp(self):
19 super(ResourceTestCase, self).setUp()
20 self._frontend_common_setup()
21 self._setup_debug_user()
22 self._add_additional_data()
23 self.client = client.Client()
24
25
26 def tearDown(self):
27 super(ResourceTestCase, self).tearDown()
28 self._frontend_common_teardown()
29
30
31 def _setup_debug_user(self):
32 user = models.User.objects.create(login='debug_user')
33 acl = models.AclGroup.objects.get(name='my_acl')
34 user.aclgroup_set.add(acl)
35
36
37 def _add_additional_data(self):
38 models.Test.objects.create(name='mytest',
jamesrendd855242010-03-02 22:23:44 +000039 test_type=model_attributes.TestTypes.SERVER,
showardf828c772010-01-25 21:49:42 +000040 path='/path/to/mytest')
41
42
43 def _expected_status(self, method):
44 if method == 'post':
45 return 201
46 if method == 'delete':
47 return 204
48 return 200
49
50
51 def request(self, method, uri, **kwargs):
52 expected_status = self._expected_status(method)
53
54 if method == 'put':
55 # the put() implementation in Django's test client is poorly
56 # implemented and only supports url-encoded keyvals for the data.
57 # the post() implementation is correct, though, so use that, with a
58 # trick to override the method.
59 method = 'post'
60 kwargs['REQUEST_METHOD'] = 'PUT'
61 if 'data' in kwargs:
62 kwargs.setdefault('content_type', 'application/json')
63 if kwargs['content_type'] == 'application/json':
64 kwargs['data'] = simplejson.dumps(kwargs['data'])
65
66 client_method = getattr(self.client, method)
jamesren3981f442010-02-16 19:27:59 +000067 if uri.startswith('http://'):
68 full_uri = uri
69 else:
70 full_uri = self.URI_PREFIX + '/' + uri
71 response = client_method(full_uri, **kwargs)
showardf828c772010-01-25 21:49:42 +000072 self.assertEquals(
73 response.status_code, expected_status,
jamesren3981f442010-02-16 19:27:59 +000074 'Requesting %s\nExpected %s, got %s: %s'
75 % (full_uri, expected_status, response.status_code,
76 response.content))
showardf828c772010-01-25 21:49:42 +000077
78 if response['content-type'] != 'application/json':
79 return response.content
80
81 try:
82 return simplejson.loads(response.content)
83 except ValueError:
84 self.fail('Invalid reponse body: %s' % response.content)
85
86
87 def sorted_by(self, collection, attribute):
88 return sorted(collection, key=operator.itemgetter(attribute))
89
90
91 def _read_attribute(self, item, attribute_or_list):
92 if isinstance(attribute_or_list, basestring):
93 attribute_or_list = [attribute_or_list]
94 for attribute in attribute_or_list:
95 item = item[attribute]
96 return item
97
98
99 def check_collection(self, collection, attribute_or_list, expected_list,
100 length=None, check_number=None):
101 """Check the members of a collection of dicts.
102
103 @param collection: an iterable of dicts
104 @param attribute_or_list: an attribute or list of attributes to read.
105 the results will be sorted and compared with expected_list. if
106 a list of attributes is given, the attributes will be read
107 hierarchically, i.e. item[attribute1][attribute2]...
108 @param expected_list: list of expected values
109 @param check_number: if given, only check this number of entries
110 @param length: expected length of list, only necessary if check_number
111 is given
112 """
113 actual_list = sorted(self._read_attribute(item, attribute_or_list)
jamesren3981f442010-02-16 19:27:59 +0000114 for item in collection['members'])
showardf828c772010-01-25 21:49:42 +0000115 if length is None and check_number is None:
116 length = len(expected_list)
117 if length is not None:
118 self.assertEquals(len(actual_list), length,
119 'Expected %s, got %s: %s'
120 % (length, len(actual_list),
121 ', '.join(str(item) for item in actual_list)))
122 if check_number:
123 actual_list = actual_list[:check_number]
124 self.assertEquals(actual_list, expected_list)
125
126
jamesren3981f442010-02-16 19:27:59 +0000127 def check_relationship(self, resource_uri, relationship_name,
128 other_entry_name, field, expected_values,
129 length=None, check_number=None):
130 """Check the members of a relationship collection.
131
132 @param resource_uri: URI of base resource
133 @param relationship_name: name of relationship attribute on base
134 resource
135 @param other_entry_name: name of other entry in relationship
136 @param field: name of field to grab on other entry
137 @param expected values: list of expected values for the given field
138 """
139 response = self.request('get', resource_uri)
140 relationship_uri = response[relationship_name]['href']
141 relationships = self.request('get', relationship_uri)
142 self.check_collection(relationships, [other_entry_name, field],
143 expected_values, length, check_number)
144
145
showardf828c772010-01-25 21:49:42 +0000146class FilteringPagingTest(ResourceTestCase):
147 # we'll arbitarily choose to use hosts for this
148
149 def setUp(self):
150 super(FilteringPagingTest, self).setUp()
151
152 self.labels[0].host_set = [self.hosts[0], self.hosts[1]]
153 for host in self.hosts[:3]:
154 host.locked = True
155 host.save()
156
157 def test_simple_filtering(self):
158 response = self.request('get', 'hosts?locked=true&has_label=label1')
jamesren3981f442010-02-16 19:27:59 +0000159 self.check_collection(response, 'hostname', ['host1', 'host2'])
showardf828c772010-01-25 21:49:42 +0000160
161
162 def test_paging(self):
163 response = self.request('get', 'hosts?start_index=1&items_per_page=2')
jamesren3981f442010-02-16 19:27:59 +0000164 self.check_collection(response, 'hostname', ['host2', 'host3'])
showardf828c772010-01-25 21:49:42 +0000165 self.assertEquals(response['total_results'], 9)
166 self.assertEquals(response['items_per_page'], 2)
167 self.assertEquals(response['start_index'], 1)
168
169
jamesren3981f442010-02-16 19:27:59 +0000170class MiscellaneousTest(ResourceTestCase):
171 def test_trailing_slash(self):
172 response = self.request('get', 'hosts/host1/')
173 self.assertEquals(response['hostname'], 'host1')
174
175
showardf828c772010-01-25 21:49:42 +0000176class AtomicGroupClassTest(ResourceTestCase):
177 def test_collection(self):
178 response = self.request('get', 'atomic_group_classes')
jamesren3981f442010-02-16 19:27:59 +0000179 self.check_collection(response, 'name', ['atomic1', 'atomic2'],
180 length=2)
showardf828c772010-01-25 21:49:42 +0000181
182
183 def test_entry(self):
184 response = self.request('get', 'atomic_group_classes/atomic1')
185 self.assertEquals(response['name'], 'atomic1')
186 self.assertEquals(response['max_number_of_machines'], 2)
187
188
189 def test_labels(self):
jamesren3981f442010-02-16 19:27:59 +0000190 self.check_relationship('atomic_group_classes/atomic1', 'labels',
191 'label', 'name', ['label4', 'label5'])
showardf828c772010-01-25 21:49:42 +0000192
193
194class LabelTest(ResourceTestCase):
195 def test_collection(self):
196 response = self.request('get', 'labels')
jamesren3981f442010-02-16 19:27:59 +0000197 self.check_collection(response, 'name', ['label1', 'label2'], length=9,
198 check_number=2)
showardf828c772010-01-25 21:49:42 +0000199 label1 = self.sorted_by(response['members'], 'name')[0]
200 self.assertEquals(label1['is_platform'], False)
201
202
203 def test_entry(self):
204 response = self.request('get', 'labels/label1')
205 self.assertEquals(response['name'], 'label1')
206 self.assertEquals(response['is_platform'], False)
207 self.assertEquals(response['atomic_group_class'], None)
208
209
210 def test_hosts(self):
jamesren3981f442010-02-16 19:27:59 +0000211 self.check_relationship('labels/label1', 'hosts', 'host', 'hostname',
212 ['host1'])
showardf828c772010-01-25 21:49:42 +0000213
214
215class UserTest(ResourceTestCase):
216 def test_collection(self):
217 response = self.request('get', 'users')
jamesren3981f442010-02-16 19:27:59 +0000218 self.check_collection(response, 'username',
showardf828c772010-01-25 21:49:42 +0000219 ['autotest_system', 'debug_user'])
220
221
222 def test_entry(self):
223 response = self.request('get', 'users/debug_user')
224 self.assertEquals(response['username'], 'debug_user')
225
226 me_response = self.request('get', 'users/@me')
227 self.assertEquals(response, me_response)
228
229
230 def test_acls(self):
jamesren3981f442010-02-16 19:27:59 +0000231 self.check_relationship('users/debug_user', 'acls', 'acl', 'name',
232 ['Everyone', 'my_acl'])
showardf828c772010-01-25 21:49:42 +0000233
234
235 def test_accessible_hosts(self):
236 group = models.AclGroup.objects.create(name='mygroup')
237 models.User.objects.get(login='debug_user').aclgroup_set = [group]
238 self.hosts[0].aclgroup_set = [group]
239
jamesren3981f442010-02-16 19:27:59 +0000240 user = self.request('get', 'users/debug_user')
241 response = self.request('get', user['accessible_hosts']['href'])
242 self.check_collection(response, 'hostname', ['host1'])
showardf828c772010-01-25 21:49:42 +0000243
244
245class AclTest(ResourceTestCase):
246 def test_collection(self):
247 response = self.request('get', 'acls')
jamesren3981f442010-02-16 19:27:59 +0000248 self.check_collection(response, 'name', ['Everyone', 'my_acl'])
showardf828c772010-01-25 21:49:42 +0000249
250
251 def test_entry(self):
252 response = self.request('get', 'acls/my_acl')
253 self.assertEquals(response['name'], 'my_acl')
254
255
256 def test_users(self):
jamesren3981f442010-02-16 19:27:59 +0000257 self.check_relationship('acls/my_acl', 'users', 'user', 'username',
258 ['autotest_system', 'debug_user'])
showardf828c772010-01-25 21:49:42 +0000259
260
261 def test_hosts(self):
jamesren3981f442010-02-16 19:27:59 +0000262 self.check_relationship('acls/my_acl', 'hosts', 'host', 'hostname',
263 ['host1', 'host2'], length=9, check_number=2)
showardf828c772010-01-25 21:49:42 +0000264
265
266class HostTest(ResourceTestCase):
267 def test_collection(self):
268 response = self.request('get', 'hosts')
jamesren3981f442010-02-16 19:27:59 +0000269 self.check_collection(response, 'hostname', ['host1', 'host2'],
270 length=9, check_number=2)
showardf828c772010-01-25 21:49:42 +0000271 host1 = self.sorted_by(response['members'], 'hostname')[0]
272 self.assertEquals(host1['platform']['name'], 'myplatform')
273 self.assertEquals(host1['locked'], False)
274 self.assertEquals(host1['status'], 'Ready')
275
276
277 def test_entry(self):
278 response = self.request('get', 'hosts/host1')
279 self.assertEquals(response['protection_level'], 'No protection')
280
281
282 def test_labels(self):
jamesren3981f442010-02-16 19:27:59 +0000283 self.check_relationship('hosts/host1', 'labels', 'label', 'name',
284 ['label1', 'myplatform'])
showardf828c772010-01-25 21:49:42 +0000285
286
287 def test_acls(self):
jamesren3981f442010-02-16 19:27:59 +0000288 self.check_relationship('hosts/host1', 'acls', 'acl', 'name',
289 ['my_acl'])
showardf828c772010-01-25 21:49:42 +0000290
291
292 def test_queue_entries(self):
293 self._create_job(hosts=[1])
jamesren3981f442010-02-16 19:27:59 +0000294 host = self.request('get', 'hosts/host1')
295 entries = self.request('get', host['queue_entries']['href'])
296 self.check_collection(entries, ['job', 'id'], [1])
showardf828c772010-01-25 21:49:42 +0000297
298
299 def test_health_tasks(self):
300 models.SpecialTask.schedule_special_task(
301 host=self.hosts[0], task=models.SpecialTask.Task.VERIFY)
jamesren3981f442010-02-16 19:27:59 +0000302 host = self.request('get', 'hosts/host1')
303 tasks = self.request('get', host['health_tasks']['href'])
304 self.check_collection(tasks, 'task_type', ['Verify'])
showardf828c772010-01-25 21:49:42 +0000305
306
307 def test_put(self):
308 response = self.request('put', 'hosts/host1', data={'locked': True})
309 self.assertEquals(response['locked'], True)
310 response = self.request('get', 'hosts/host1')
311 self.assertEquals(response['locked'], True)
312 self.assertEquals(response['locked_by']['username'], 'debug_user')
313
314
315 def test_post(self):
316 data = {'hostname': 'newhost',
showardf828c772010-01-25 21:49:42 +0000317 'platform': {'href': self.URI_PREFIX + '/labels/myplatform'},
318 'protection_level': 'Do not verify'}
319 response = self.request('post', 'hosts', data=data)
320 self.assertEquals(response, self.URI_PREFIX + '/hosts/newhost')
321
322 host = models.Host.objects.get(hostname='newhost')
323 self.assertEquals(host.platform().name, 'myplatform')
324 self.assertEquals(host.protection, models.Host.Protection.DO_NOT_VERIFY)
jamesren3981f442010-02-16 19:27:59 +0000325
326
327 def _check_labels(self, host, expected_labels):
328 label_names = sorted(label.name for label in host.labels.all())
329 self.assertEquals(label_names, sorted(expected_labels))
showardf828c772010-01-25 21:49:42 +0000330
331
332 def test_add_label(self):
jamesren3981f442010-02-16 19:27:59 +0000333 labels_href = self.request('get', 'hosts/host1')['labels']['href']
334 data = {'label': self.URI_PREFIX + '/labels/label2'}
335 response = self.request('post', labels_href, data=data)
336 self._check_labels(self.hosts[0], ['label1', 'label2', 'myplatform'])
337
338
339 def test_remove_label(self):
340 labels_href = self.request('get', 'hosts/host1')['labels']['href']
341 labels_href += '&label=label1'
342 labelings = self.request('get', labels_href)['members']
343 self.assertEquals(len(labelings), 1)
344 self.request('delete', labelings[0]['href'])
345 self._check_labels(self.hosts[0], ['myplatform'])
showardf828c772010-01-25 21:49:42 +0000346
347
348 def test_delete(self):
349 self.request('delete', 'hosts/host1')
350 hosts = models.Host.valid_objects.filter(hostname='host1')
351 self.assertEquals(len(hosts), 0)
352
353
354class TestTest(ResourceTestCase): # yes, we're testing the "tests" resource
355 def test_collection(self):
356 response = self.request('get', 'tests')
jamesren3981f442010-02-16 19:27:59 +0000357 self.check_collection(response, 'name', ['mytest'])
showardf828c772010-01-25 21:49:42 +0000358
359
360 def test_entry(self):
361 response = self.request('get', 'tests/mytest')
362 self.assertEquals(response['name'], 'mytest')
363 self.assertEquals(response['control_file_type'], 'Server')
364 self.assertEquals(response['control_file_path'], '/path/to/mytest')
365
366
367 def test_dependencies(self):
368 models.Test.objects.get(name='mytest').dependency_labels = [self.label3]
jamesren3981f442010-02-16 19:27:59 +0000369 self.check_relationship('tests/mytest', 'dependencies', 'label', 'name',
370 ['label3'])
showardf828c772010-01-25 21:49:42 +0000371
372
373class ExecutionInfoTest(ResourceTestCase):
374 def setUp(self):
375 super(ExecutionInfoTest, self).setUp()
376
377 def mock_read_control_file(test):
378 return self.CONTROL_FILE_CONTENTS
379 self.god.stub_with(control_file, 'read_control_file',
380 mock_read_control_file)
381 def test_get(self):
382 response = self.request('get', 'execution_info?tests=mytest')
383 info = response['execution_info']
384 self.assert_(self.CONTROL_FILE_CONTENTS in info['control_file'])
385 self.assertEquals(info['is_server'], True)
386 self.assertEquals(info['machines_per_execution'], 1)
387
388
389class QueueEntriesRequestTest(ResourceTestCase):
390 def test_get(self):
391 response = self.request(
392 'get',
393 'queue_entries_request?hosts=host1,host2&meta_hosts=label1')
394
395 # choose an arbitrary but consistent ordering to ease checking
396 def entry_href(entry):
397 if 'host' in entry:
398 return entry['host']['href']
399 return entry['meta_host']['href']
400 entries = sorted(response['queue_entries'], key=entry_href)
401
402 expected = [
403 {'host': {'href': self.URI_PREFIX + '/hosts/host1'}},
404 {'host': {'href': self.URI_PREFIX + '/hosts/host2'}},
405 {'meta_host': {'href': self.URI_PREFIX + '/labels/label1'}}]
406 self.assertEquals(entries, expected)
407
408
409class JobTest(ResourceTestCase):
410 def setUp(self):
411 super(JobTest, self).setUp()
412
413 for _ in xrange(2):
414 self._create_job(hosts=[1, 2])
415
416 job = models.Job.objects.get(id=1)
417 job.control_file = self.CONTROL_FILE_CONTENTS
418 job.save()
419
420
421 def test_collection(self):
422 response = self.request('get', 'jobs')
jamesren3981f442010-02-16 19:27:59 +0000423 self.check_collection(response, 'id', [1, 2])
showardf828c772010-01-25 21:49:42 +0000424
425
426 def test_entry(self):
427 response = self.request('get', 'jobs/1')
428 self.assertEquals(response['id'], 1)
429 self.assertEquals(response['name'], 'test')
430 info = response['execution_info']
431 self.assertEquals(info['control_file'], self.CONTROL_FILE_CONTENTS)
432 self.assertEquals(info['is_server'], False)
433 self.assertEquals(info['cleanup_before_job'], 'Never')
434 self.assertEquals(info['cleanup_after_job'], 'Always')
435 self.assertEquals(info['machines_per_execution'], 1)
436 self.assertEquals(info['run_verify'], True)
437
438
439 def test_queue_entries(self):
jamesren3981f442010-02-16 19:27:59 +0000440 job = self.request('get', 'jobs/1')
441 entries = self.request('get', job['queue_entries']['href'])
442 self.check_collection(entries, ['host', 'hostname'], ['host1', 'host2'])
showardf828c772010-01-25 21:49:42 +0000443
444
jamesrene38a0a72010-04-19 18:05:31 +0000445 def _test_post_helper(self, owner):
showardf828c772010-01-25 21:49:42 +0000446 data = {'name': 'myjob',
447 'execution_info': {'control_file': self.CONTROL_FILE_CONTENTS,
448 'is_server': True},
jamesrene38a0a72010-04-19 18:05:31 +0000449 'owner': owner,
showardf828c772010-01-25 21:49:42 +0000450 'queue_entries':
451 [{'host': {'href': self.URI_PREFIX + '/hosts/host1'}},
452 {'host': {'href': self.URI_PREFIX + '/hosts/host2'}}]}
453 response = self.request('post', 'jobs', data=data)
454 self.assertEquals(response, self.URI_PREFIX + '/jobs/3')
455 job = models.Job.objects.get(id=3)
456 self.assertEquals(job.name, 'myjob')
457 self.assertEquals(job.control_file, self.CONTROL_FILE_CONTENTS)
458 self.assertEquals(job.control_type, models.Job.ControlType.SERVER)
459 entries = job.hostqueueentry_set.order_by('host__hostname')
460 self.assertEquals(entries[0].host.hostname, 'host1')
461 self.assertEquals(entries[1].host.hostname, 'host2')
462
jamesrene38a0a72010-04-19 18:05:31 +0000463 owner_test = owner
464 if not owner_test:
465 owner_test = models.User.current_user().login
466 self.assertEquals(job.owner, owner_test)
467
468
469 def test_post_no_owner(self):
470 self._test_post_helper(None)
471
472
473 def test_post_with_owner(self):
474 self._test_post_helper('job_owner')
475
showardf828c772010-01-25 21:49:42 +0000476
477class DirectoryTest(ResourceTestCase):
478 def test_get(self):
479 response = self.request('get', '')
480 for key in ('atomic_group_classes', 'labels', 'users', 'acl_groups',
481 'hosts', 'tests', 'jobs', 'execution_info',
482 'queue_entries_request'):
483 self.assert_(key in response)
484
485
486if __name__ == '__main__':
487 unittest.main()