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

1import json 

2import datetime 

3 

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 

12 

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 

33 

34from shortener.models import Shortener 

35 

36# HACK: Massively speed up the login primitive. We don't care about security in tests 

37settings.PASSWORD_HASHERS = ('django.contrib.auth.hashers.MD5PasswordHasher', ) 

38 

39 

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) 

44 

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) 

48 

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) 

51 

52 

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) 

56 

57 pagination = CustomPagination() 

58 pagination.page_size_query_param = page_size_query_param 

59 pagination.max_page_size = max_page_size 

60 

61 return pagination.get_page_size(request) 

62 

63 def test_default_page_size(self): 

64 self.assertEqual(self.get_page_size({}), CustomPagination.page_size) 

65 

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) 

68 

69 def test_invalid_page_size(self): 

70 self.assertEqual(self.get_page_size({'page_size': 'toto'}), CustomPagination.page_size) 

71 

72 def test_negative_page_size(self): 

73 self.assertEqual(self.get_page_size({CustomPagination.page_size_query_param: -0}), None) 

74 

75 def test_page_size_too_big(self): 

76 self.assertEqual(self.get_page_size({'page_size': '1000'}, max_page_size=10), 10) 

77 

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) 

80 

81 def test_acceptable_page_size(self): 

82 self.assertEqual(self.get_page_size({'page_size2': '42'}, page_size_query_param='page_size2'), 42) 

83 

84 

85class IssueFilterTests(APITestCase): 

86 maxDiff = None 

87 

88 def setUp(self): 

89 self.view = IssueFilterViewSet() 

90 self.user = None 

91 

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) 

95 

96 return self.client.post(reverse('api:issuefilter-list'), body_dict, format='json') 

97 

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, []) 

102 

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"]) 

107 

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") 

115 

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"]) 

120 

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"]) 

129 

130 def test_create_minimal__unauthenticated(self): 

131 response = self.__post__({"description": "Minimal IssueFilter"}, logged_in=False) 

132 self.assertEqual(response.status_code, 401) 

133 

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) 

138 

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'}) 

148 

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']) 

159 

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) 

169 

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 }) 

177 

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': ''}) 

208 

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"]) 

215 

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") 

220 

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'}) 

230 

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']) 

235 

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") 

241 

242 with transaction.atomic(): 

243 response = self.__post__({"description": "new filter", 

244 "edit_filter": "{}".format(filter.id), 

245 "edit_issue": issue.id}) 

246 

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'}) 

254 

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']) 

260 

261 

262class RunConfigTests(TestCase): 

263 def setUp(self): 

264 self.view = RunConfigViewSet() 

265 self.arf = APIRequestFactory() 

266 

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) 

271 

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() 

276 

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() 

282 

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"]) 

291 

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, []) 

298 

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) 

308 

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) 

316 

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()) 

323 

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) 

329 

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"]) 

339 

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) 

345 

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) 

352 

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") 

386 

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) 

390 

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".']) 

403 

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') 

422 

423 

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() 

429 

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"]) 

436 

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) 

442 

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") 

450 

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."]) 

458 

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) 

465 

466 

467class BugViewSetTests(TestCase): 

468 def setUp(self): 

469 self.arf = APIRequestFactory() 

470 

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) 

475 

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))) 

481 

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) 

490 

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) 

499 

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) 

508 

509 

510class ShortenerViewSetTests(TestCase): 

511 def setUp(self): 

512 self.arf = APIRequestFactory() 

513 

514 @classmethod 

515 def setUpTestData(cls): 

516 cls.short1 = Shortener.get_or_create("Some sort of long query!") 

517 

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={}) 

521 

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") 

526 

527 def test_create_single(self): 

528 full = 'Some sort of long query, but different!' 

529 

530 response = self.client.post(reverse('api:shortener-list'), data={'full': full}, content_type="application/json") 

531 self.assertEqual(response.status_code, 200) 

532 

533 data = response.json() 

534 self.assertNotEqual(data['id'], self.short1.id) 

535 self.assertEqual(data['full'], full) 

536 

537 def test_create_multiple(self): 

538 fulls = ["query1", "query2"] 

539 

540 response = self.client.post(reverse('api:shortener-list'), data={'full': fulls}, 

541 content_type="application/json") 

542 self.assertEqual(response.status_code, 200) 

543 

544 data = response.json() 

545 self.assertEqual(data[0]['full'], fulls[0]) 

546 self.assertEqual(data[1]['full'], fulls[1]) 

547 

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) 

552 

553 data = response.json() 

554 self.assertEqual(data['id'], self.short1.id) 

555 

556 

557class MachineViewSetTests(TestCase): 

558 def setUp(self): 

559 self.arf = APIRequestFactory() 

560 

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': []}]) 

573 

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) 

578 

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"] 

593 

594 }, 

595 "status": "success" 

596 }) 

597 

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 }) 

607 

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 }) 

618 

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) 

624 

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) 

635 

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) 

644 

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) 

650 

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) 

656 

657 

658class IssueViewSetTests(TestCase): 

659 def setUp(self): 

660 self.arf = APIRequestFactory() 

661 

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"}) 

666 

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) 

672 

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"}) 

679 

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"}) 

684 

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) 

690 

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"}) 

697 

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) 

706 

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."]) 

714 

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) 

722 

723 

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 }) 

747 

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) 

760 

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 ) 

788 

789 

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) 

795 

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) 

800 

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) 

811 

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) 

820 

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) 

825 

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) 

833 

834 

835class TextStatusViewSetTests(TestCase): 

836 def setUp(self): 

837 self.arf = APIRequestFactory() 

838 self.user = create_user_and_log_in(self.client, admin=True) 

839 

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) 

849 

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) 

856 

857 

858class metrics_passrate_trend_viewTests(TestCase): 

859 def setUp(self): 

860 self.arf = APIRequestFactory() 

861 

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) 

867 

868 

869class metrics_passrate_viewTests(TestCase): 

870 def setUp(self): 

871 self.arf = APIRequestFactory() 

872 

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)