Coverage for CIResults/tests/test_views.py: 100%

278 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-04-23 13:11 +0000

1import json 

2from unittest.mock import patch, MagicMock 

3from rest_framework.test import APIRequestFactory 

4from django.conf import settings 

5from django.contrib.auth import get_user_model 

6from django.contrib.auth.models import Permission 

7from django.contrib.messages import get_messages 

8from django.core.exceptions import ValidationError 

9from django.test import TestCase 

10from django.urls import reverse 

11from django.utils import timezone 

12 

13from CIResults.models import Test, Machine, RunConfigTag, TestSuite, Build 

14from CIResults.models import TextStatus, IssueFilter, Issue 

15from CIResults.models import RunConfig, TestsuiteRun, TestResult, Component 

16from CIResults.views import IssueFilterView, ResultsCompareView 

17 

18from datetime import timedelta 

19 

20 

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

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

23 

24 

25def create_user_and_log_in(client, admin=False, permissions=[]): 

26 user = get_user_model().objects.create_user('user', 'user@provider.com', 'pwd', 

27 first_name='First', last_name='Last', 

28 is_superuser=admin) 

29 perm_objs = [] 

30 for codename in permissions: 

31 perm_objs.append(Permission.objects.get(codename=codename)) 

32 user.user_permissions.add(*perm_objs) 

33 

34 client.login(username='user', password='pwd') 

35 return user 

36 

37 

38class ViewMixin: 

39 view_kwargs = {} 

40 

41 @property 

42 def url(self): 

43 return reverse(self.reverse_name, kwargs=self.view_kwargs) 

44 

45 def test_get__authorized(self): 

46 if hasattr(self, 'permissions_needed'): 

47 create_user_and_log_in(self.client, permissions=self.permissions_needed) 

48 

49 response = self.client.get(self.url) 

50 self.assertEqual(response.status_code, 200) 

51 

52 def test_get__unauthorized_access(self): 

53 if not hasattr(self, 'permissions_needed'): 

54 self.skipTest("No need for authorization for the class") # pragma: no cover 

55 

56 create_user_and_log_in(self.client, permissions=[]) 

57 

58 response = self.client.get(self.url) 

59 self.assertEqual(response.status_code, 403) 

60 

61 

62class UserFiltrableViewMixin(ViewMixin): 

63 def test_invalid_query(self): 

64 response = self.client.get(self.url + "?query=djzkhjkf") 

65 self.assertEqual(response.status_code, 200) 

66 self.assertContains(response, "Filtering error") 

67 

68 def test_valid_query(self): 

69 response = self.client.get(self.url + "?query=" + self.query) 

70 self.assertEqual(response.status_code, 200) 

71 self.assertNotContains(response, "Filtering error") 

72 

73 

74class IndexTests(ViewMixin, TestCase): 

75 reverse_name = "CIResults-index" 

76 

77 

78class IssueListTests(UserFiltrableViewMixin, TestCase): 

79 reverse_name = "CIResults-issues-list" 

80 query = "filter_description = 'desc'" 

81 

82 

83class IssueAddTests(ViewMixin, TestCase): 

84 reverse_name = "CIResults-issue" 

85 permissions_needed = ['add_issue'] 

86 view_kwargs = {'action': 'create'} 

87 

88 

89class IssueEditTests(ViewMixin, TestCase): 

90 reverse_name = "CIResults-issue" 

91 permissions_needed = ['change_issue'] 

92 

93 def setUp(self): 

94 issue = Issue.objects.create() 

95 self.view_kwargs = {'action': 'edit', 'pk': issue.pk} 

96 

97 

98class IssueMiscTests(TestCase): 

99 post_actions = ["archive", "restore", "hide", "show"] 

100 

101 def setUp(self): 

102 self.issue = Issue.objects.create() 

103 

104 def url(self, action): 

105 return reverse("CIResults-issue", kwargs={'action': action, 'pk': self.issue.pk}) 

106 

107 def test_post__unauthorized_access(self): 

108 create_user_and_log_in(self.client, permissions=[]) 

109 for action in self.post_actions: 

110 with self.subTest(action=action): 

111 response = self.client.post(self.url(action), {}) 

112 self.assertEqual(response.status_code, 403) 

113 

114 def test_post__authorized_access(self): 

115 for action in self.post_actions: 

116 with self.subTest(action=action): 

117 user = create_user_and_log_in(self.client, permissions=['{}_issue'.format(action)]) 

118 

119 response = self.client.post(self.url(action), {}) 

120 self.assertEqual(response.status_code, 302) 

121 self.assertEqual(response.url, reverse('CIResults-index')) 

122 

123 user.delete() 

124 

125 def test_get_on_post_action(self): 

126 for action in self.post_actions: 

127 with self.subTest(action=action): 

128 self.assertRaises(ValidationError, self.client.get, self.url(action)) 

129 

130 

131class TestTests(ViewMixin, TestCase): 

132 reverse_name = "CIResults-tests" 

133 

134 

135class TestMassRenameTests(ViewMixin, TestCase): 

136 reverse_name = "CIResults-tests-massrename" 

137 permissions_needed = ['change_test'] 

138 

139 

140class TestRenameTests(ViewMixin, TestCase): 

141 reverse_name = "CIResults-test-rename" 

142 permissions_needed = ['change_test'] 

143 

144 def setUp(self): 

145 testsuite = TestSuite.objects.create(name='ts1', public=True) 

146 t = Test.objects.create(name='test', testsuite=testsuite, public=True) 

147 self.view_kwargs = {'pk': t.pk} 

148 

149 

150class MachineTests(ViewMixin, TestCase): 

151 reverse_name = "CIResults-machines" 

152 

153 

154class TestResultListViewTests(UserFiltrableViewMixin, TestCase): 

155 reverse_name = "CIResults-results" 

156 query = "test_name = 'test'" 

157 

158 

159class KnownFailureListViewTests(UserFiltrableViewMixin, TestCase): 

160 reverse_name = "CIResults-knownfailures" 

161 query = "test_name = 'test'" 

162 

163 

164class ResultsCompareTests(ViewMixin, TestCase): 

165 reverse_name = "CIResults-compare" 

166 

167 def test_urlify(self): 

168 view = ResultsCompareView() 

169 string = "Hello http://gitlab.freedesktop.org, https://x.org" 

170 self.assertEqual(view.urlify(string), 

171 "Hello <http://gitlab.freedesktop.org>, <https://x.org>") 

172 

173 def test_invalid_runconfig(self): 

174 response = self.client.get(self.url + "?from=RUNCONFIG1&to=RUNCONFIG2") 

175 self.assertEqual(response.status_code, 200) 

176 

177 @patch('CIResults.models.RunConfig.objects.filter') 

178 def test_valid_runconfig(self, filter_mocked): 

179 filter_mocked.return_value.first.return_value.compare.return_value.text = "Test" 

180 response = self.client.get(self.url + "?from=RUNCONFIG1&to=RUNCONFIG2") 

181 self.assertEqual(response.status_code, 200) 

182 

183 

184class MassVettingMixin: 

185 def test_get_request_should_fail(self): 

186 create_user_and_log_in(self.client, permissions=[self.needed_permission]) 

187 response = self.client.get(self.url) 

188 self.assertEqual(response.status_code, 405) 

189 

190 def test_normal_query__without_privileges(self): 

191 create_user_and_log_in(self.client) 

192 response = self.client.post(self.url, {}) 

193 self.assertEqual(response.status_code, 403) 

194 

195 def test_normal_query__with_privileges(self): 

196 create_user_and_log_in(self.client, permissions=[self.needed_permission]) 

197 

198 with patch('CIResults.models.{}.objects.filter'.format(self.klass), 

199 return_value=[MagicMock(name='object 1'), 

200 MagicMock(name='object 2'), 

201 MagicMock(name='object 3')]) as filter_mocked: 

202 response = self.client.post(self.url, {'1': 'on', '2': 'on', '3': 'on', 'gfdgdfg': 'invalid'}) 

203 self.assertEqual(response.status_code, 302) 

204 

205 # Check that all the machines were requested from the DB 

206 filter_mocked.assert_called_once_with(pk__in=set([1, 2, 3])) 

207 

208 # Check that all machine returned were vetted 

209 for obj in filter_mocked.return_value: 

210 obj.vet.assert_called_once_with() 

211 

212 

213class MachineMassVettingTests(MassVettingMixin, TestCase): 

214 def setUp(self): 

215 self.url = reverse('CIResults-machine-mass-vetting') 

216 self.klass = "Machine" 

217 self.needed_permission = 'vet_machine' 

218 

219 

220class TestMassVettingTests(MassVettingMixin, TestCase): 

221 def setUp(self): 

222 self.url = reverse('CIResults-test-mass-vetting') 

223 self.klass = "Test" 

224 self.needed_permission = 'vet_test' 

225 

226 

227class TextStatustMassVettingTests(MassVettingMixin, TestCase): 

228 def setUp(self): 

229 self.url = reverse('CIResults-textstatus-mass-vetting') 

230 self.klass = "TextStatus" 

231 self.needed_permission = 'vet_textstatus' 

232 

233 

234class IssueDetailTests(ViewMixin, TestCase): 

235 reverse_name = "CIResults-issue-detail" 

236 

237 def setUp(self): 

238 issue = Issue.objects.create(filer='me@me.org', expected=True) 

239 self.view_kwargs = {'pk': issue.pk} 

240 

241 

242class IFADetailTests(ViewMixin, TestCase): 

243 reverse_name = "CIResults-ifa-detail" 

244 

245 def setUp(self): 

246 issue = Issue.objects.create(filer='me@me.org', expected=True) 

247 f = IssueFilter.objects.create(description='filter') 

248 

249 user = get_user_model().objects.create_user('user') 

250 issue.set_filters([f], user) 

251 pk = f.issuefilterassociated_set.first().pk 

252 self.view_kwargs = {'pk': pk} 

253 

254 

255class IssueFilterViewTests(TestCase): 

256 def setUp(self): 

257 self.issueFilterView = IssueFilterView() 

258 

259 def test_parse_filter_from_params(self): 

260 filter = self.issueFilterView.__parse_filter_from_params__( 

261 {"description": "Description", "stdout_regex": "stdout regex"} 

262 ) 

263 self.assertEqual(filter.description, "Description") 

264 self.assertEqual(filter.stdout_regex, "stdout regex") 

265 

266 def test_convert_to_user_query(self): 

267 arf = APIRequestFactory() 

268 request = arf.post("/", {"stderr_regex": "stderr regex"}, format="json") 

269 user_query = self.issueFilterView.__convert_to_user_query__(request) 

270 self.assertEqual(json.loads(user_query.content)["userQuery"], "stderr ~= 'stderr regex'") 

271 

272 

273class MachineDetailTests(ViewMixin, TestCase): 

274 reverse_name = "CIResults-machine-detail" 

275 

276 def setUp(self): 

277 machine = Machine.objects.create(name='Machine1', public=True) 

278 self.view_kwargs = {'pk': machine.pk} 

279 

280 

281class TestSuiteDetailTests(ViewMixin, TestCase): 

282 reverse_name = "CIResults-testsuite-detail" 

283 

284 def setUp(self): 

285 ts = TestSuite.objects.create(name='TestSuite', public=True) 

286 self.view_kwargs = {'pk': ts.pk} 

287 

288 

289class TestDetailTests(ViewMixin, TestCase): 

290 reverse_name = "CIResults-test-detail" 

291 

292 def setUp(self): 

293 ts = TestSuite.objects.create(name='TestSuite', public=True) 

294 test = Test.objects.create(name='test1', testsuite=ts, public=True) 

295 self.view_kwargs = {'pk': test.pk} 

296 

297 

298class TextStatusDetailTests(ViewMixin, TestCase): 

299 reverse_name = "CIResults-textstatus-detail" 

300 

301 def setUp(self): 

302 ts = TestSuite.objects.create(name='TestSuite', public=True) 

303 txt_stat = TextStatus.objects.create(name='status1', testsuite=ts) 

304 self.view_kwargs = {'pk': txt_stat.pk} 

305 

306 

307class TestResultDetailTests(ViewMixin, TestCase): 

308 reverse_name = "CIResults-testresult-detail" 

309 

310 def setUp(self): 

311 ts = TestSuite.objects.create(name='TestSuite', public=True) 

312 machine = Machine.objects.create(name='Machine1', public=True) 

313 runconfig = RunConfig.objects.create(name='runconfig', temporary=True) 

314 ts_run = TestsuiteRun.objects.create(testsuite=ts, machine=machine, runconfig=runconfig, 

315 run_id=0, start=timezone.now(), duration=timedelta(hours=3)) 

316 

317 test = Test.objects.create(name='test1', testsuite=ts, public=True) 

318 status = TextStatus.objects.create(name='status1', testsuite=ts) 

319 tr = TestResult.objects.create(test=test, status=status, ts_run=ts_run, 

320 start=timezone.now(), duration=timedelta(seconds=5)) 

321 self.view_kwargs = {'pk': tr.pk} 

322 

323 

324class RunConfigDetailTests(ViewMixin, TestCase): 

325 reverse_name = "CIResults-runcfg-detail" 

326 

327 def setUp(self): 

328 cfg = RunConfig.objects.create(name='runconfig', temporary=True) 

329 self.view_kwargs = {'pk': cfg.pk} 

330 

331 

332class RunConfigTagDetailTests(ViewMixin, TestCase): 

333 reverse_name = "CIResults-runcfgtag-detail" 

334 

335 def setUp(self): 

336 cfg = RunConfigTag.objects.create(name='tag', public=True) 

337 self.view_kwargs = {'pk': cfg.pk} 

338 

339 

340class BuildDetailTests(ViewMixin, TestCase): 

341 reverse_name = "CIResults-build-detail" 

342 

343 def setUp(self): 

344 component = Component.objects.create(name='component', public=True) 

345 build = Build.objects.create(name='build1', component=component) 

346 self.view_kwargs = {'pk': build.pk} 

347 

348 

349class ComponentDetailTests(ViewMixin, TestCase): 

350 reverse_name = "CIResults-component-detail" 

351 

352 def setUp(self): 

353 comp = Component.objects.create(name='runconfig', public=True) 

354 self.view_kwargs = {'pk': comp.pk} 

355 

356 

357class SuppressVetObjectTests(TestCase): 

358 def _get_url(self, action, pk=None): 

359 if pk is None: 

360 pk = self.machine.pk 

361 return reverse('CIResults-machine-{}'.format(action), kwargs={"pk": pk}) 

362 

363 def setUp(self): 

364 self.HTTP_REFERER = '/my/address' 

365 self.machine = Machine.objects.create(name='machine1', public=True) 

366 

367 def check_action(self, action): 

368 create_user_and_log_in(self.client, permissions=['{}_machine'.format(action)]) 

369 

370 response = self.client.get(self._get_url(action), 

371 HTTP_REFERER=self.HTTP_REFERER) 

372 self.assertEqual(response.status_code, 302) 

373 self.assertEqual(response.url, self.HTTP_REFERER) 

374 

375 return [m.message for m in get_messages(response.wsgi_request)] 

376 

377 def test_object_missing(self): 

378 create_user_and_log_in(self.client, permissions=['vet_machine', 'suppress_machine']) 

379 

380 for action in ['vet', 'suppress']: 

381 with self.subTest(action=action): 

382 response = self.client.get(self._get_url(action, pk=42)) 

383 self.assertEqual(response.status_code, 404) 

384 

385 def test_unauthorized(self): 

386 create_user_and_log_in(self.client) 

387 

388 for action in ['vet', 'suppress']: 

389 with self.subTest(action=action): 

390 response = self.client.get(self._get_url(action, pk=1)) 

391 self.assertEqual(response.status_code, 403) 

392 

393 def test_object_vet__already_vetted(self): 

394 self.machine.vet() 

395 

396 messages = self.check_action('vet') 

397 self.assertEqual(len(messages), 1) 

398 self.assertEqual(messages[0], 'The object machine1 is already vetted') 

399 

400 def test_object_vet__success(self): 

401 messages = self.check_action('vet') 

402 self.assertEqual(len(messages), 1) 

403 self.assertEqual(messages[0], 'The object machine1 has been successfully vetted') 

404 

405 def test_object_suppress__already_suppressed(self): 

406 messages = self.check_action('suppress') 

407 self.assertEqual(len(messages), 1) 

408 self.assertEqual(messages[0], 'The object machine1 is already suppressed') 

409 

410 def test_object_suppress__success(self): 

411 self.machine.vet() 

412 

413 messages = self.check_action('suppress') 

414 self.assertEqual(len(messages), 1) 

415 self.assertEqual(messages[0], 'The object machine1 has been successfully suppressed')