Coverage for CIResults/tests/test_rest_views.py: 100%
532 statements
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-19 09:20 +0000
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-19 09:20 +0000
1import json
2import datetime
4from unittest.mock import patch, MagicMock
5from rest_framework.test import APIRequestFactory, APITestCase
6from django.conf import settings
7from django.db import transaction
8from django.test import TestCase
9from django.http.response import Http404
10from django.urls import reverse
11from model_bakery import baker
13from CIResults.models import (
14 Bug,
15 BugTracker,
16 Build,
17 Component,
18 Issue,
19 IssueFilter,
20 Machine,
21 MachineTag,
22 RunConfig,
23 RunConfigTag,
24 Test,
25 TestResult,
26 TestSuite,
27 TextStatus,
28 UnknownFailure,
29)
30from CIResults.rest_views import CustomPagination, IssueFilterViewSet, RunConfigViewSet, get_obj_by_id_or_name
31from CIResults.rest_views import BuildViewSet
32from CIResults.tests.test_views import create_user_and_log_in
34from shortener.models import Shortener
36# HACK: Massively speed up the login primitive. We don't care about security in tests
37settings.PASSWORD_HASHERS = ('django.contrib.auth.hashers.MD5PasswordHasher', )
40class UtilsTests(TestCase):
41 def test_get_obj_by_id_or_name__id(self):
42 rc = RunConfig.objects.create(name='valid', temporary=True)
43 self.assertEqual(get_obj_by_id_or_name(RunConfig, 1), rc)
45 def test_get_obj_by_id_or_name__name(self):
46 rc = RunConfig.objects.create(name='valid', temporary=True)
47 self.assertEqual(get_obj_by_id_or_name(RunConfig, "valid"), rc)
49 def test_get_obj_by_id_or_name__not_exist(self):
50 self.assertRaises(RunConfig.DoesNotExist, get_obj_by_id_or_name, RunConfig, 1)
53class CustomPaginationTests(TestCase):
54 def get_page_size(self, query_params, page_size_query_param='page_size', max_page_size=None):
55 request = MagicMock(query_params=query_params)
57 pagination = CustomPagination()
58 pagination.page_size_query_param = page_size_query_param
59 pagination.max_page_size = max_page_size
61 return pagination.get_page_size(request)
63 def test_default_page_size(self):
64 self.assertEqual(self.get_page_size({}), CustomPagination.page_size)
66 def test_default_page_size_without_page_size_field(self):
67 self.assertEqual(self.get_page_size({}, page_size_query_param=None), CustomPagination.page_size)
69 def test_invalid_page_size(self):
70 self.assertEqual(self.get_page_size({'page_size': 'toto'}), CustomPagination.page_size)
72 def test_negative_page_size(self):
73 self.assertEqual(self.get_page_size({CustomPagination.page_size_query_param: -0}), None)
75 def test_page_size_too_big(self):
76 self.assertEqual(self.get_page_size({'page_size': '1000'}, max_page_size=10), 10)
78 def test_page_size_big_but_no_limits(self):
79 self.assertEqual(self.get_page_size({'page_size': '1000'}, max_page_size=None), 1000)
81 def test_acceptable_page_size(self):
82 self.assertEqual(self.get_page_size({'page_size2': '42'}, page_size_query_param='page_size2'), 42)
85class IssueFilterTests(APITestCase):
86 maxDiff = None
88 def setUp(self):
89 self.view = IssueFilterViewSet()
90 self.user = None
92 def __post__(self, body_dict, logged_in=True, permissions=['add_issuefilter']):
93 if logged_in:
94 self.user = create_user_and_log_in(self.client, permissions=permissions)
96 return self.client.post(reverse('api:issuefilter-list'), body_dict, format='json')
98 def test__get_or_None__empty_field(self):
99 errors = []
100 self.assertIsNone(self.view.__get_or_None__(Machine, 'field', {'field': ''}, errors))
101 self.assertEqual(errors, [])
103 def test__get_or_None__invalid_id(self):
104 errors = []
105 self.assertIsNone(self.view.__get_or_None__(Machine, 'field', {'field': '12'}, errors))
106 self.assertEqual(errors, ["The object referenced by 'field' does not exist"])
108 def test_get_filter_by_description(self):
109 IssueFilter.objects.create(description="filter one")
110 IssueFilter.objects.create(description="filter two")
111 response = self.client.get(reverse('api:issuefilter-list'), {'description': 'one'})
112 self.assertEqual(response.status_code, 200)
113 self.assertEqual(response.data["count"], 1)
114 self.assertEqual(response.data["results"][0]["description"], "filter one")
116 def test_create_empty(self):
117 response = self.__post__({})
118 self.assertEqual(response.status_code, 400)
119 self.assertEqual(response.data, ["The field 'description' cannot be empty"])
121 def test_invalid_regexps(self):
122 response = self.__post__({"description": "Minimal IssueFilter",
123 "stdout_regex": '(', "stderr_regex": '(',
124 "dmesg_regex": '('})
125 self.assertEqual(response.status_code, 400)
126 self.assertEqual(response.data, ["The field 'stdout_regex' does not contain a valid regular expression",
127 "The field 'stderr_regex' does not contain a valid regular expression",
128 "The field 'dmesg_regex' does not contain a valid regular expression"])
130 def test_create_minimal__unauthenticated(self):
131 response = self.__post__({"description": "Minimal IssueFilter"}, logged_in=False)
132 self.assertEqual(response.status_code, 401)
134 # FIXME: This is not currently working
135 # def test_create_minimal__invalid_permissions(self):
136 # response = self.__post__({"description": "Minimal IssueFilter"}, logged_in=True, permissions=[])
137 # self.assertEqual(response.status_code, 403)
139 def test_create_minimal(self):
140 response = self.__post__({"description": "Minimal IssueFilter"})
141 self.assertEqual(response.status_code, 201)
142 self.assertEqual(response.data,
143 {'id': response.data['id'], 'description': 'Minimal IssueFilter',
144 'tags': [], 'machine_tags': [], 'machines': [], 'tests': [],
145 'statuses': [], 'stdout_regex': '',
146 'stderr_regex': '', 'dmesg_regex': '',
147 'user_query': '', '__str__': 'Minimal IssueFilter'})
149 def test_create_invalid(self):
150 response = self.__post__({
151 "description": "Invalid tags, machines, tests, and statuses",
152 'tags': [1], 'machines': [2], 'tests': [3], 'statuses': [4]
153 })
154 self.assertEqual(response.status_code, 400, response.data)
155 self.assertEqual(response.data, ['At least one tag does not exist',
156 'At least one machine does not exist',
157 'At least one test does not exist',
158 'At least one status does not exist'])
160 def test_create_complete(self):
161 # Create some objects before referencing them
162 ts = TestSuite.objects.create(name="ts", description="", url="", public=True)
163 for i in range(1, 6):
164 RunConfigTag.objects.create(id=i, name="tag{}".format(i), description="", url="", public=True)
165 Machine.objects.create(id=i, name="machine{}".format(i), public=True)
166 Test.objects.create(id=i, name="test{}".format(i), testsuite=ts, public=True)
167 TextStatus.objects.create(id=i, testsuite=ts, name="status{}".format(i))
168 MachineTag.objects.create(id=i, name="TAG{}".format(i), public=True)
170 # Make the request and check that we get the expected output
171 with transaction.atomic():
172 response = self.__post__({
173 "description": "Minimal IssueFilter",
174 'tags': [1, 2], 'machine_tags': [1, 5], 'machines': [2, 3], 'tests': [3, 4], 'statuses': [4, 5],
175 'stdout_regex': 'stdout', 'stderr_regex': 'stderr', 'dmesg_regex': 'dmesg'
176 })
178 self.assertEqual(response.status_code, 201, response.data)
179 self.assertEqual(dict(response.data),
180 {'id': response.data['id'], 'description': 'Minimal IssueFilter',
181 'tags': [
182 {'id': 1, 'description': '', 'url': '', 'public': True, '__str__': 'tag1'},
183 {'id': 2, 'description': '', 'url': '', 'public': True, '__str__': 'tag2'}
184 ],
185 'machine_tags': [
186 {'id': 1, 'name': 'TAG1', 'public': True},
187 {'id': 5, 'name': 'TAG5', 'public': True},
188 ],
189 'machines': [
190 {'id': 2, 'vetted_on': None, 'public': True, '__str__': 'machine2'},
191 {'id': 3, 'vetted_on': None, 'public': True, '__str__': 'machine3'},
192 ],
193 'tests': [
194 {'id': 3, 'testsuite': {'id': ts.id, '__str__': 'ts'}, 'public': True,
195 'vetted_on': None, 'first_runconfig': None, 'name': 'test3', '__str__': 'ts: test3'},
196 {'id': 4, 'testsuite': {'id': ts.id, '__str__': 'ts'}, 'public': True,
197 'vetted_on': None, 'first_runconfig': None, 'name': 'test4', '__str__': 'ts: test4'},
198 ],
199 'statuses': [
200 {'id': 4, 'testsuite': {'id': ts.id, '__str__': 'ts'}, 'name': 'status4',
201 '__str__': 'ts: status4'},
202 {'id': 5, 'testsuite': {'id': ts.id, '__str__': 'ts'}, 'name': 'status5',
203 '__str__': 'ts: status5'},
204 ],
205 'stdout_regex': 'stdout', 'stderr_regex': 'stderr',
206 'dmesg_regex': 'dmesg', '__str__': 'Minimal IssueFilter',
207 'user_query': ''})
209 def test_edit_invalid(self):
210 response = self.__post__({"description": "new filter",
211 "edit_filter": "a", "edit_issue": "b"})
212 self.assertEqual(response.status_code, 400, response.data)
213 self.assertEqual(response.data, ["The field 'edit_filter' needs to be an integer",
214 "The field 'edit_issue' needs to be an integer"])
216 @patch('CIResults.models.IssueFilter.replace')
217 @patch('CIResults.models.Issue.replace_filter')
218 def test_edit_all_issues(self, mock_replace_filter, mock_filter_replace):
219 filter = IssueFilter.objects.create(description="old filter")
221 with transaction.atomic():
222 response = self.__post__({"description": "new filter", "edit_filter": filter.id})
223 self.assertEqual(response.status_code, 201, response.data)
224 self.assertEqual(response.data,
225 {'id': response.data['id'], 'description': 'new filter',
226 'tags': [], 'machine_tags': [], 'machines': [], 'tests': [],
227 'statuses': [], 'stdout_regex': '',
228 'stderr_regex': '', 'dmesg_regex': '',
229 'user_query': '', '__str__': 'new filter'})
231 new_filter = IssueFilter.objects.get(pk=response.data['id'])
232 mock_replace_filter.assert_not_called()
233 mock_filter_replace.assert_called_once_with(new_filter, self.user)
234 self.assertEqual(mock_filter_replace.call_args_list[0][0][0].id, response.data['id'])
236 @patch('CIResults.models.IssueFilter.replace')
237 @patch('CIResults.models.Issue.replace_filter')
238 def test_edit_one_issue(self, mock_replace_filter, mock_filter_replace):
239 filter = IssueFilter.objects.create(description="desc")
240 issue = Issue.objects.create(description="issue")
242 with transaction.atomic():
243 response = self.__post__({"description": "new filter",
244 "edit_filter": "{}".format(filter.id),
245 "edit_issue": issue.id})
247 self.assertEqual(response.status_code, 201, response.data)
248 self.assertEqual(response.data,
249 {'id': response.data['id'], 'description': 'new filter',
250 'tags': [], 'machine_tags': [], 'machines': [], 'tests': [],
251 'statuses': [], 'stdout_regex': '',
252 'stderr_regex': '', 'dmesg_regex': '',
253 'user_query': '', '__str__': 'new filter'})
255 new_filter = IssueFilter.objects.get(pk=response.data['id'])
256 mock_filter_replace.assert_not_called()
257 mock_replace_filter.assert_called_once_with(filter, new_filter, self.user)
258 self.assertEqual(mock_replace_filter.call_args_list[0][0][0].id, filter.id)
259 self.assertEqual(mock_replace_filter.call_args_list[0][0][1].id, response.data['id'])
262class RunConfigTests(TestCase):
263 def setUp(self):
264 self.view = RunConfigViewSet()
265 self.arf = APIRequestFactory()
267 @patch('CIResults.models.RunConfig.objects.get')
268 def test_get_runcfg__by_id(self, runcfg_get_mocked):
269 self.assertEqual(self.view._get_runcfg('1'), runcfg_get_mocked.return_value)
270 runcfg_get_mocked.assert_called_once_with(pk=1)
272 @patch('CIResults.models.RunConfig.objects.get')
273 def test_get_runcfg__by_invalid_name(self, runcfg_get_mocked):
274 self.assertRaises(Http404, self.view._get_runcfg, 'missing')
275 runcfg_get_mocked.assert_not_called()
277 @patch('CIResults.models.RunConfig.objects.get')
278 def test_get_runcfg__by_valid_name(self, runcfg_get_mocked):
279 rc = RunConfig.objects.create(name='valid', temporary=True)
280 self.assertEqual(self.view._get_runcfg('valid').pk, rc.id)
281 runcfg_get_mocked.assert_not_called()
283 def test_retrieve__by_id(self):
284 rc = RunConfig.objects.create(name='valid', temporary=True)
285 data = self.view.retrieve(self.arf.get("/", {}, format="json"), pk="valid").data
286 self.assertEqual(data["id"], rc.id)
287 self.assertEqual(data["name"], "valid")
288 self.assertEqual(data["tags"], [])
289 self.assertIsNone(data["url"])
290 self.assertIsNotNone(data["added_on"])
292 def test_known_failures(self):
293 # TODO: Revisit the test whenever we start generating fixtures
294 RunConfig.objects.create(name='valid', temporary=True)
295 response = self.view.known_failures(self.arf.get('/', {}, format='json'),
296 pk='valid')
297 self.assertEqual(response.data, [])
299 @patch('CIResults.serializers.RunConfigDiffSerializer.data')
300 @patch('CIResults.models.RunConfig.compare')
301 def test_compare(self, compare_mocked, serializer_mocked):
302 RunConfig.objects.create(name='runcfg1', temporary=True)
303 runcfg2 = RunConfig.objects.create(name='runcfg2', temporary=True)
304 response = self.view.compare(self.arf.get('/', {'to': 'runcfg2'}, format='json'),
305 pk='runcfg1')
306 compare_mocked.assert_called_once_with(runcfg2, no_compress=False)
307 self.assertEqual(response.data, serializer_mocked)
309 @patch('CIResults.models.RunConfig.compare')
310 def test_compare__no_compress(self, compare_mocked):
311 RunConfig.objects.create(name='runcfg1', temporary=True)
312 runcfg2 = RunConfig.objects.create(name='runcfg2', temporary=True)
313 self.view.compare(self.arf.get('/', {'to': 'runcfg2', 'no_compress': '1'}, format='json'),
314 pk='runcfg1')
315 compare_mocked.assert_called_once_with(runcfg2, no_compress=True)
317 def test_compare__only_summary(self):
318 RunConfig.objects.create(name='runcfg1', temporary=True)
319 RunConfig.objects.create(name='runcfg2', temporary=True)
320 response = self.view.compare(self.arf.get('/', {'to': 'runcfg2', 'summary': ''}, format='json'),
321 pk='runcfg1')
322 self.assertIn('CI Bug Log', response.content.decode())
324 def test_create__no_permissions(self):
325 response = self.client.post(reverse('api:runconfig-list'),
326 data={"name": "runcfg", "tags": ["tag1"], "temporary": False},
327 content_type="application/json")
328 self.assertEqual(response.status_code, 401)
330 def test_create(self):
331 RunConfigTag.objects.create(id=1, name="tag1", public=True)
332 create_user_and_log_in(self.client, permissions=['add_runconfig'])
333 response = self.client.post(reverse('api:runconfig-list'),
334 data={"name": "runcfg", "tags": ["tag1"], "builds": [], "temporary": False},
335 content_type="application/json")
336 self.assertEqual(response.status_code, 201)
337 self.assertEqual(response.data["name"], "runcfg")
338 self.assertEqual(response.data["tags"], ["tag1"])
340 def test_create__invalid_data(self):
341 create_user_and_log_in(self.client, permissions=['add_runconfig'])
342 response = self.client.post(reverse('api:runconfig-list'), data={"name": "runcfg"},
343 content_type="application/json")
344 self.assertEqual(response.status_code, 400)
346 def test_create__invalid_data_missing_tag(self):
347 create_user_and_log_in(self.client, permissions=['add_runconfig'])
348 response = self.client.post(reverse('api:runconfig-list'),
349 data={"name": "runcfg", "tags": ["tag1"], "temporary": False},
350 content_type="application/json")
351 self.assertEqual(response.status_code, 400)
353 def test_import_testsuite_run(self):
354 RunConfig.objects.create(name='runcfg1', temporary=False)
355 component = TestSuite.objects.create(id=1, name='testsuite', public=True)
356 Build.objects.create(name='testsuite-1', component=component)
357 create_user_and_log_in(self.client, admin=True)
358 response = self.client.post("/api/runconfig/runcfg1/testsuiterun/", content_type="application/json", data={
359 "test_suite": "testsuite-1",
360 "test_results": {
361 "machine-1": {
362 "001": {
363 "test1": {
364 "status": "pass",
365 "stdout": "output"
366 }
367 },
368 "002": {
369 "test2": {
370 "status": "pass",
371 "stdout": "output"
372 }
373 }
374 }
375 }
376 })
377 self.assertEqual(response.status_code, 200)
378 self.assertIsNotNone(Machine.objects.get(name="machine-1"))
379 self.assertIsNotNone(Test.objects.get(name="test1"))
380 self.assertIsNotNone(TextStatus.objects.get(name="pass"))
381 self.assertEqual(TestResult.objects.count(), 2)
382 for test_name in ["test1", "test2"]:
383 result = TestResult.objects.filter(test__name=test_name).first()
384 self.assertEqual(result.status.name, "pass")
385 self.assertEqual(result.stdout, "output")
387 def test_import_testsuite_run__no_permissions(self):
388 response = self.client.post("/api/runconfig/runcfg1/testsuiterun/", content_type="application/json", data={})
389 self.assertEqual(response.status_code, 401)
391 def test_import_testsuite_run__invalid_data(self):
392 RunConfig.objects.create(name='runcfg1', temporary=False)
393 component = TestSuite.objects.create(id=1, name='testsuite', public=True)
394 Build.objects.create(name='testsuite-1', component=component)
395 create_user_and_log_in(self.client, admin=True)
396 response = self.client.post("/api/runconfig/runcfg1/testsuiterun/", content_type="application/json", data={
397 "test_suite": "testsuite-1",
398 "test_results": "invalid_data"
399 })
400 self.assertEqual(response.status_code, 400)
401 self.assertEqual(response.json()["message"]["test_results"],
402 ['Expected a dictionary of items but got type "str".'])
404 def test_import_testsuite_run__invalid_data_no_testsuite(self):
405 RunConfig.objects.create(name='runcfg1', temporary=False)
406 create_user_and_log_in(self.client, admin=True)
407 response = self.client.post("/api/runconfig/runcfg1/testsuiterun/", content_type="application/json", data={
408 "test_suite": "testsuite-1",
409 "test_results": {
410 "machine-1": {
411 "001": {
412 "test1": {
413 "status": "pass",
414 "stdout": "output"
415 }
416 }
417 }
418 }
419 })
420 self.assertEqual(response.status_code, 400)
421 self.assertEqual(response.json()["message"], 'Testsuite build testsuite-1 does not exist')
424class BuildViewSetTests(APITestCase):
425 def setUp(self) -> None:
426 self.component = Component.objects.create(name="component", public=False)
427 self.view = BuildViewSet()
428 self.arf = APIRequestFactory()
430 def test_retrieve(self):
431 rc = Build.objects.create(name='valid', component=self.component)
432 data = self.view.retrieve(self.arf.get("/", {}, format="json"), pk="valid").data
433 self.assertEqual(data["id"], rc.id)
434 self.assertEqual(data["name"], "valid")
435 self.assertIsNotNone(data["added_on"])
437 def test_create_build_without_permissions(self):
438 response = self.client.post(reverse('api:build-list'),
439 data={"name": "test-build", "component": "component", "version": "1"},
440 format="json")
441 self.assertEqual(response.status_code, 401)
443 def test_create_build(self):
444 create_user_and_log_in(self.client, permissions=['add_build'])
445 response = self.client.post(reverse('api:build-list'),
446 data={"name": "build1", "component": "component", "version": "1", "parents": []},
447 format="json")
448 self.assertEqual(response.status_code, 201)
449 self.assertEqual(response.data["name"], "build1")
451 def test_create_build__invalid_data_schema(self):
452 create_user_and_log_in(self.client, permissions=['add_build'])
453 response = self.client.post(reverse('api:build-list'),
454 data={"name": "test-build", "component": "component"},
455 format="json")
456 self.assertEqual(response.status_code, 400)
457 self.assertEqual(json.loads(response.content)["version"], ["This field is required."])
459 def test_create_build__invalid_data(self):
460 create_user_and_log_in(self.client, permissions=['add_build'])
461 response = self.client.post(reverse('api:build-list'),
462 data={"name": "test-build", "component": "no-component", "version": "1"},
463 format="json")
464 self.assertEqual(response.status_code, 400)
467class BugViewSetTests(TestCase):
468 def setUp(self):
469 self.arf = APIRequestFactory()
471 @classmethod
472 def setUpTestData(cls):
473 cls.bt1 = BugTracker.objects.create(name='Test Suite 1', short_name='ts1', public=True)
474 cls.bt2 = BugTracker.objects.create(name='Test Suite 2', short_name='ts2', public=True)
476 cls.bugs = dict()
477 for bt in (cls.bt1, cls.bt2):
478 cls.bugs[bt] = []
479 for i in range(2):
480 cls.bugs[bt].append(Bug.objects.create(tracker=bt, bug_id='b_'+str(i)))
482 def test_retrieving_by_tracker_id(self):
483 tracker = self.bt2
484 bug = self.bugs[self.bt2][1]
485 response = self.client.get(reverse('api-bugtracker-get-bug',
486 kwargs={'tracker': tracker.id, 'bug_id': bug.bug_id}), format='json')
487 self.assertEqual(response.status_code, 200)
488 self.assertEqual(response.data['bug_id'], bug.bug_id)
489 self.assertEqual(response.data['tracker']['short_name'], tracker.short_name)
491 def test_retrieving_by_tracker_name(self):
492 tracker = self.bt1
493 bug = self.bugs[self.bt2][0]
494 response = self.client.get(reverse('api-bugtracker-get-bug',
495 kwargs={'tracker': tracker.name, 'bug_id': bug.bug_id}), format='json')
496 self.assertEqual(response.status_code, 200)
497 self.assertEqual(response.data['bug_id'], bug.bug_id)
498 self.assertEqual(response.data['tracker']['short_name'], tracker.short_name)
500 def test_retrieving_by_tracker_short_name(self):
501 tracker = self.bt2
502 bug = self.bugs[self.bt2][0]
503 response = self.client.get(reverse('api-bugtracker-get-bug',
504 kwargs={'tracker': tracker.short_name, 'bug_id': bug.bug_id}), format='json')
505 self.assertEqual(response.status_code, 200)
506 self.assertEqual(response.data['bug_id'], bug.bug_id)
507 self.assertEqual(response.data['tracker']['short_name'], tracker.short_name)
510class ShortenerViewSetTests(TestCase):
511 def setUp(self):
512 self.arf = APIRequestFactory()
514 @classmethod
515 def setUpTestData(cls):
516 cls.short1 = Shortener.get_or_create("Some sort of long query!")
518 def test_create_invalid_request(self):
519 with self.assertRaisesMessage(ValueError, "Only JSON POST requests are supported"):
520 self.client.post(reverse('api:shortener-list'), data={})
522 def test_create_empty(self):
523 with self.assertRaisesMessage(ValueError,
524 "Missing the field 'full' which should contain the full text to be shortened"):
525 self.client.post(reverse('api:shortener-list'), data={}, content_type="application/json")
527 def test_create_single(self):
528 full = 'Some sort of long query, but different!'
530 response = self.client.post(reverse('api:shortener-list'), data={'full': full}, content_type="application/json")
531 self.assertEqual(response.status_code, 200)
533 data = response.json()
534 self.assertNotEqual(data['id'], self.short1.id)
535 self.assertEqual(data['full'], full)
537 def test_create_multiple(self):
538 fulls = ["query1", "query2"]
540 response = self.client.post(reverse('api:shortener-list'), data={'full': fulls},
541 content_type="application/json")
542 self.assertEqual(response.status_code, 200)
544 data = response.json()
545 self.assertEqual(data[0]['full'], fulls[0])
546 self.assertEqual(data[1]['full'], fulls[1])
548 def test_retrieving_existing(self):
549 response = self.client.post(reverse('api:shortener-list'), data={'full': self.short1.full},
550 content_type="application/json")
551 self.assertEqual(response.status_code, 200)
553 data = response.json()
554 self.assertEqual(data['id'], self.short1.id)
557class MachineViewSetTests(TestCase):
558 def setUp(self):
559 self.arf = APIRequestFactory()
561 def test_list_machines(self):
562 Machine.objects.create(name="machine_1", public=True)
563 response = self.client.get(reverse('api:machine-list'), content_type="application/json")
564 self.assertEqual(response.status_code, 200)
565 self.assertEqual(json.loads(response.content)["results"],
566 [{'id': 1,
567 'name': 'machine_1',
568 'description': None,
569 'public': True,
570 'vetted_on': None,
571 'aliases': None,
572 'tags': []}])
574 def test_create_machine_without_permission(self):
575 response = self.client.post(reverse('api:machine-list'), data={'name': 'machine_1', 'tags': ['tag1']},
576 content_type="application/json")
577 self.assertEqual(response.status_code, 401)
579 def test_create_machine(self):
580 self.user = create_user_and_log_in(self.client, permissions=['add_machine'])
581 response = self.client.post(reverse('api:machine-list'), data={'name': 'machine_1', 'tags': ['tag1']},
582 content_type="application/json")
583 self.assertEqual(response.status_code, 201)
584 self.assertEqual(json.loads(response.content), {
585 "data": {
586 "id": 1,
587 "name": "machine_1",
588 "description": None,
589 "public": False,
590 "vetted_on": None,
591 "aliases": None,
592 "tags": ["tag1"]
594 },
595 "status": "success"
596 })
598 def test_create_machine_invalid_data(self):
599 self.user = create_user_and_log_in(self.client, permissions=['add_machine'])
600 response = self.client.post(reverse('api:machine-list'), data={'name': True},
601 content_type="application/json")
602 self.assertEqual(response.status_code, 400)
603 self.assertEqual(json.loads(response.content), {
604 "message": {"name": ["Not a valid string."]},
605 "status": "error"
606 })
608 def test_create_machine_import_error(self):
609 self.user = create_user_and_log_in(self.client, permissions=['add_machine'])
610 response = self.client.post(reverse('api:machine-list'), data={'name': 'machine_1', 'alias': 'machine_2'},
611 content_type="application/json")
612 self.assertEqual(response.status_code, 400)
613 self.assertEqual(json.loads(response.content), {
614 "message": "The machine this machine is supposed to alias does not exist. "
615 "Create it first...",
616 "status": "error"
617 })
619 def test_vet_machine_without_permission(self):
620 machine = baker.make(Machine)
621 self.user = create_user_and_log_in(self.client, admin=False)
622 response = self.client.post(reverse('api:machine-vet', kwargs={"pk": machine.pk}))
623 self.assertEqual(response.status_code, 403)
625 @patch("django.utils.timezone.now")
626 def test_vet_machine(self, now_mocked):
627 date = datetime.datetime(2021, 1, 1, tzinfo=datetime.timezone.utc)
628 now_mocked.return_value = date
629 machine = baker.make(Machine)
630 self.user = create_user_and_log_in(self.client, admin=True)
631 response = self.client.post(reverse("api:machine-vet", kwargs={"pk": machine.pk}))
632 machine.refresh_from_db()
633 self.assertEqual(machine.vetted_on, date)
634 self.assertEqual(response.status_code, 200)
636 def test_vet_already_vetted(self):
637 date = datetime.datetime(2021, 1, 1, tzinfo=datetime.timezone.utc)
638 machine = baker.make(Machine, vetted_on=date)
639 self.user = create_user_and_log_in(self.client, admin=True)
640 response = self.client.post(reverse('api:machine-vet', kwargs={"pk": machine.pk}))
641 machine.refresh_from_db()
642 self.assertEqual(response.status_code, 200)
643 self.assertEqual(machine.vetted_on, date)
645 def test_suppress_machine_without_permission(self):
646 machine = baker.make(Machine)
647 self.user = create_user_and_log_in(self.client, admin=False)
648 response = self.client.post(reverse('api:machine-suppress', kwargs={"pk": machine.pk}))
649 self.assertEqual(response.status_code, 403)
651 def test_suppress_machine(self):
652 machine = baker.make(Machine, vetted_on="2021-01-01")
653 self.user = create_user_and_log_in(self.client, admin=True)
654 response = self.client.post(reverse('api:machine-suppress', kwargs={"pk": machine.pk}))
655 self.assertEqual(response.status_code, 200)
658class IssueViewSetTests(TestCase):
659 def setUp(self):
660 self.arf = APIRequestFactory()
662 def test_archive_issue_without_permission(self):
663 response = self.client.get(reverse('api:issue-archive', kwargs={"pk": 1}))
664 self.assertEqual(response.status_code, 401)
665 self.assertEqual(response.json(), {"message": "User AnonymousUser doesn't have sufficient permissions"})
667 def test_archive_issue(self):
668 Issue.objects.create()
669 self.user = create_user_and_log_in(self.client, admin=True)
670 response = self.client.get(reverse('api:issue-archive', kwargs={"pk": 1}))
671 self.assertEqual(response.status_code, 200)
673 def test_archive_archived_issue(self):
674 self.user = create_user_and_log_in(self.client, admin=True)
675 Issue.objects.create().archive(self.user)
676 response = self.client.get(reverse('api:issue-archive', kwargs={"pk": 1}))
677 self.assertEqual(response.status_code, 400)
678 self.assertEqual(response.json(), {"message": "The issue is already archived"})
680 def test_restore_issue_without_permission(self):
681 response = self.client.get(reverse('api:issue-restore', kwargs={"pk": 1}))
682 self.assertEqual(response.status_code, 401)
683 self.assertEqual(response.json(), {"message": "User AnonymousUser doesn't have sufficient permissions"})
685 def test_restore_issue(self):
686 self.user = create_user_and_log_in(self.client, admin=True)
687 Issue.objects.create().archive(self.user)
688 response = self.client.get(reverse('api:issue-restore', kwargs={"pk": 1}))
689 self.assertEqual(response.status_code, 200)
691 def test_restore_not_archived_issue(self):
692 Issue.objects.create()
693 self.user = create_user_and_log_in(self.client, admin=True)
694 response = self.client.get(reverse('api:issue-restore', kwargs={"pk": 1}))
695 self.assertEqual(response.status_code, 400)
696 self.assertEqual(response.json(), {"message": "The issue is not currently archived"})
698 def test_update_to_expected(self):
699 Issue.objects.create(expected=False)
700 self.user = create_user_and_log_in(self.client, admin=True)
701 response = self.client.patch(reverse('api:issue-detail', kwargs={"pk": 1}), data={"id": 1, "expected": True},
702 content_type="application/json")
703 self.assertEqual(response.status_code, 200)
704 self.assertEqual(response.json()["expected"], True)
705 self.assertEqual(Issue.objects.get(pk=1).expected, True)
707 def test_update_to_expected_wrong_value(self):
708 Issue.objects.create(expected=False)
709 self.user = create_user_and_log_in(self.client, admin=True)
710 response = self.client.patch(reverse('api:issue-detail', kwargs={"pk": 1}), data={"id": 1, "expected": "Str"},
711 content_type="application/json")
712 self.assertEqual(response.status_code, 400)
713 self.assertEqual(response.json()["expected"], ["Must be a valid boolean."])
715 def test_try_to_update_read_only_field(self):
716 Issue.objects.create(runconfigs_covered_count=100)
717 self.user = create_user_and_log_in(self.client, admin=True)
718 response = self.client.patch(reverse('api:issue-detail', kwargs={"pk": 1}),
719 data={"id": 1, "runconfigs_covered_count": 99}, content_type="application/json")
720 self.assertEqual(response.status_code, 200)
721 self.assertEqual(response.json()["runconfigs_covered_count"], 100)
724class UnknownFailureViewSetTests(TestCase):
725 def test_retrieve_by_id(self):
726 unknown_failure = baker.make(UnknownFailure)
727 response = self.client.get(
728 reverse("api:unknownfailure-detail", kwargs={"pk": unknown_failure.id}), content_type="application/json"
729 )
730 self.assertEqual(response.status_code, 200)
731 data = response.json()
732 self.assertEqual(data["id"], unknown_failure.id)
733 self.assertEqual(data["test"], unknown_failure.result.test.name)
734 self.assertEqual(data["status"], unknown_failure.result.status.name)
735 self.assertRaises(KeyError, lambda: data["dmesg"])
736 self.assertRaises(KeyError, lambda: data["stdout"])
737 self.assertRaises(KeyError, lambda: data["stderr"])
738 self.assertEqual(data["testsuite"], unknown_failure.result.ts_run.testsuite.name)
739 self.assertEqual(data["runconfig"], {
740 "name": unknown_failure.result.ts_run.runconfig.name,
741 "tags": list(unknown_failure.result.ts_run.runconfig.tags.values_list('name', flat=True))
742 })
743 self.assertEqual(data["machine"], {
744 "name": unknown_failure.result.ts_run.machine.name,
745 "tags": list(unknown_failure.result.ts_run.machine.tags.values_list('name', flat=True))
746 })
748 def test_retrieve_by_id__extra_fields(self):
749 unknown_failure = baker.make(UnknownFailure)
750 response = self.client.get(
751 reverse("api:unknownfailure-detail", kwargs={"pk": unknown_failure.id}),
752 content_type="application/json",
753 data={"extra_fields": "stdout"},
754 )
755 self.assertEqual(response.status_code, 200)
756 data = response.json()
757 self.assertRaises(KeyError, lambda: data["dmesg"])
758 self.assertRaises(KeyError, lambda: data["stderr"])
759 self.assertEqual(data["stdout"], unknown_failure.result.stdout)
761 def test_list(self):
762 unknown_failures = baker.make(UnknownFailure, _quantity=3)
763 response = self.client.get(
764 reverse("api:unknownfailure-list"),
765 content_type="application/json",
766 )
767 data = response.json()
768 self.assertEqual(response.status_code, 200)
769 self.assertEqual(data["count"], 3)
770 for unknown_failure in unknown_failures:
771 self.assertIn(
772 {
773 "id": unknown_failure.id,
774 "test": unknown_failure.result.test.name,
775 "status": unknown_failure.result.status.name,
776 "testsuite": unknown_failure.result.ts_run.testsuite.name,
777 "runconfig": {
778 "name": unknown_failure.result.ts_run.runconfig.name,
779 "tags": list(unknown_failure.result.ts_run.runconfig.tags.values_list('name', flat=True))
780 },
781 "machine": {
782 "name": unknown_failure.result.ts_run.machine.name,
783 "tags": list(unknown_failure.result.ts_run.machine.tags.values_list('name', flat=True))
784 },
785 },
786 data["results"],
787 )
790class TestSetTests(TestCase):
791 def setUp(self):
792 self.arf = APIRequestFactory()
793 ts = TestSuite.objects.create(name="ts", description="", url="", public=True)
794 Test.objects.create(id=1, name="test1", testsuite=ts, public=True)
796 def test_vet_test_without_permission(self):
797 self.user = create_user_and_log_in(self.client, admin=False)
798 response = self.client.post(reverse('api:test-vet', kwargs={"pk": 1}))
799 self.assertEqual(response.status_code, 403)
801 @patch("django.utils.timezone.now")
802 def test_vet_test(self, now_mocked):
803 date = datetime.datetime(2021, 1, 1, tzinfo=datetime.timezone.utc)
804 now_mocked.return_value = date
805 test = baker.make(Test, vetted_on=None)
806 self.user = create_user_and_log_in(self.client, admin=True)
807 response = self.client.post(reverse('api:test-vet', kwargs={"pk": test.pk}))
808 test.refresh_from_db()
809 self.assertEqual(response.status_code, 200)
810 self.assertEqual(test.vetted_on, date)
812 def test_vet_already_vetted(self):
813 date = datetime.datetime(2021, 1, 1, tzinfo=datetime.timezone.utc)
814 test = baker.make(Test, vetted_on=date)
815 self.user = create_user_and_log_in(self.client, admin=True)
816 response = self.client.post(reverse('api:test-vet', kwargs={"pk": test.pk}))
817 test.refresh_from_db()
818 self.assertEqual(response.status_code, 200)
819 self.assertEqual(test.vetted_on, date)
821 def test_suppress_test_without_permission(self):
822 self.user = create_user_and_log_in(self.client, admin=False)
823 response = self.client.post(reverse('api:test-suppress', kwargs={"pk": 1}))
824 self.assertEqual(response.status_code, 403)
826 def test_suppress_test(self):
827 test = baker.make(Test, vetted_on=datetime.datetime(2021, 1, 1))
828 self.user = create_user_and_log_in(self.client, admin=True)
829 response = self.client.post(reverse('api:test-suppress', kwargs={"pk": test.pk}))
830 test.refresh_from_db()
831 self.assertEqual(response.status_code, 200)
832 self.assertIsNone(test.vetted_on)
835class TextStatusViewSetTests(TestCase):
836 def setUp(self):
837 self.arf = APIRequestFactory()
838 self.user = create_user_and_log_in(self.client, admin=True)
840 @patch("django.utils.timezone.now")
841 def test_vet(self, now_mocked):
842 date = datetime.datetime(2021, 1, 1, tzinfo=datetime.timezone.utc)
843 now_mocked.return_value = date
844 ts = baker.make(TextStatus, vetted_on=None)
845 response = self.client.get(reverse('api:textstatus-vet', kwargs={"pk": ts.pk}))
846 ts.refresh_from_db()
847 self.assertEqual(response.status_code, 200)
848 self.assertEqual(ts.vetted_on, date)
850 def test_suppress(self):
851 ts = baker.make(TextStatus, vetted_on="2021-01-01")
852 response = self.client.get(reverse('api:textstatus-suppress', kwargs={"pk": ts.pk}))
853 ts.refresh_from_db()
854 self.assertEqual(response.status_code, 200)
855 self.assertIsNone(ts.vetted_on)
858class metrics_passrate_trend_viewTests(TestCase):
859 def setUp(self):
860 self.arf = APIRequestFactory()
862 def test_basic(self):
863 # TODO: Add a fixture that would return useful data here
864 response = self.client.get(reverse('api-metrics-passrate-per-runconfig'),
865 {'query': ""}, format='json')
866 self.assertEqual(response.status_code, 200)
869class metrics_passrate_viewTests(TestCase):
870 def setUp(self):
871 self.arf = APIRequestFactory()
873 def test_basic(self):
874 # TODO: Add a fixture that would return useful data here
875 response = self.client.get(reverse('api-metrics-passrate-per-test'),
876 {'query': ""}, format='json')
877 self.assertEqual(response.status_code, 200)