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
« 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
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
18from datetime import timedelta
21# HACK: Massively speed up the login primitive. We don't care about security in tests
22settings.PASSWORD_HASHERS = ('django.contrib.auth.hashers.MD5PasswordHasher', )
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)
34 client.login(username='user', password='pwd')
35 return user
38class ViewMixin:
39 view_kwargs = {}
41 @property
42 def url(self):
43 return reverse(self.reverse_name, kwargs=self.view_kwargs)
45 def test_get__authorized(self):
46 if hasattr(self, 'permissions_needed'):
47 create_user_and_log_in(self.client, permissions=self.permissions_needed)
49 response = self.client.get(self.url)
50 self.assertEqual(response.status_code, 200)
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
56 create_user_and_log_in(self.client, permissions=[])
58 response = self.client.get(self.url)
59 self.assertEqual(response.status_code, 403)
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")
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")
74class IndexTests(ViewMixin, TestCase):
75 reverse_name = "CIResults-index"
78class IssueListTests(UserFiltrableViewMixin, TestCase):
79 reverse_name = "CIResults-issues-list"
80 query = "filter_description = 'desc'"
83class IssueAddTests(ViewMixin, TestCase):
84 reverse_name = "CIResults-issue"
85 permissions_needed = ['add_issue']
86 view_kwargs = {'action': 'create'}
89class IssueEditTests(ViewMixin, TestCase):
90 reverse_name = "CIResults-issue"
91 permissions_needed = ['change_issue']
93 def setUp(self):
94 issue = Issue.objects.create()
95 self.view_kwargs = {'action': 'edit', 'pk': issue.pk}
98class IssueMiscTests(TestCase):
99 post_actions = ["archive", "restore", "hide", "show"]
101 def setUp(self):
102 self.issue = Issue.objects.create()
104 def url(self, action):
105 return reverse("CIResults-issue", kwargs={'action': action, 'pk': self.issue.pk})
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)
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)])
119 response = self.client.post(self.url(action), {})
120 self.assertEqual(response.status_code, 302)
121 self.assertEqual(response.url, reverse('CIResults-index'))
123 user.delete()
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))
131class TestTests(ViewMixin, TestCase):
132 reverse_name = "CIResults-tests"
135class TestMassRenameTests(ViewMixin, TestCase):
136 reverse_name = "CIResults-tests-massrename"
137 permissions_needed = ['change_test']
140class TestRenameTests(ViewMixin, TestCase):
141 reverse_name = "CIResults-test-rename"
142 permissions_needed = ['change_test']
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}
150class MachineTests(ViewMixin, TestCase):
151 reverse_name = "CIResults-machines"
154class TestResultListViewTests(UserFiltrableViewMixin, TestCase):
155 reverse_name = "CIResults-results"
156 query = "test_name = 'test'"
159class KnownFailureListViewTests(UserFiltrableViewMixin, TestCase):
160 reverse_name = "CIResults-knownfailures"
161 query = "test_name = 'test'"
164class ResultsCompareTests(ViewMixin, TestCase):
165 reverse_name = "CIResults-compare"
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>")
173 def test_invalid_runconfig(self):
174 response = self.client.get(self.url + "?from=RUNCONFIG1&to=RUNCONFIG2")
175 self.assertEqual(response.status_code, 200)
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)
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)
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)
195 def test_normal_query__with_privileges(self):
196 create_user_and_log_in(self.client, permissions=[self.needed_permission])
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)
205 # Check that all the machines were requested from the DB
206 filter_mocked.assert_called_once_with(pk__in=set([1, 2, 3]))
208 # Check that all machine returned were vetted
209 for obj in filter_mocked.return_value:
210 obj.vet.assert_called_once_with()
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'
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'
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'
234class IssueDetailTests(ViewMixin, TestCase):
235 reverse_name = "CIResults-issue-detail"
237 def setUp(self):
238 issue = Issue.objects.create(filer='me@me.org', expected=True)
239 self.view_kwargs = {'pk': issue.pk}
242class IFADetailTests(ViewMixin, TestCase):
243 reverse_name = "CIResults-ifa-detail"
245 def setUp(self):
246 issue = Issue.objects.create(filer='me@me.org', expected=True)
247 f = IssueFilter.objects.create(description='filter')
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}
255class IssueFilterViewTests(TestCase):
256 def setUp(self):
257 self.issueFilterView = IssueFilterView()
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")
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'")
273class MachineDetailTests(ViewMixin, TestCase):
274 reverse_name = "CIResults-machine-detail"
276 def setUp(self):
277 machine = Machine.objects.create(name='Machine1', public=True)
278 self.view_kwargs = {'pk': machine.pk}
281class TestSuiteDetailTests(ViewMixin, TestCase):
282 reverse_name = "CIResults-testsuite-detail"
284 def setUp(self):
285 ts = TestSuite.objects.create(name='TestSuite', public=True)
286 self.view_kwargs = {'pk': ts.pk}
289class TestDetailTests(ViewMixin, TestCase):
290 reverse_name = "CIResults-test-detail"
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}
298class TextStatusDetailTests(ViewMixin, TestCase):
299 reverse_name = "CIResults-textstatus-detail"
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}
307class TestResultDetailTests(ViewMixin, TestCase):
308 reverse_name = "CIResults-testresult-detail"
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))
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}
324class RunConfigDetailTests(ViewMixin, TestCase):
325 reverse_name = "CIResults-runcfg-detail"
327 def setUp(self):
328 cfg = RunConfig.objects.create(name='runconfig', temporary=True)
329 self.view_kwargs = {'pk': cfg.pk}
332class RunConfigTagDetailTests(ViewMixin, TestCase):
333 reverse_name = "CIResults-runcfgtag-detail"
335 def setUp(self):
336 cfg = RunConfigTag.objects.create(name='tag', public=True)
337 self.view_kwargs = {'pk': cfg.pk}
340class BuildDetailTests(ViewMixin, TestCase):
341 reverse_name = "CIResults-build-detail"
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}
349class ComponentDetailTests(ViewMixin, TestCase):
350 reverse_name = "CIResults-component-detail"
352 def setUp(self):
353 comp = Component.objects.create(name='runconfig', public=True)
354 self.view_kwargs = {'pk': comp.pk}
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})
363 def setUp(self):
364 self.HTTP_REFERER = '/my/address'
365 self.machine = Machine.objects.create(name='machine1', public=True)
367 def check_action(self, action):
368 create_user_and_log_in(self.client, permissions=['{}_machine'.format(action)])
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)
375 return [m.message for m in get_messages(response.wsgi_request)]
377 def test_object_missing(self):
378 create_user_and_log_in(self.client, permissions=['vet_machine', 'suppress_machine'])
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)
385 def test_unauthorized(self):
386 create_user_and_log_in(self.client)
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)
393 def test_object_vet__already_vetted(self):
394 self.machine.vet()
396 messages = self.check_action('vet')
397 self.assertEqual(len(messages), 1)
398 self.assertEqual(messages[0], 'The object machine1 is already vetted')
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')
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')
410 def test_object_suppress__success(self):
411 self.machine.vet()
413 messages = self.check_action('suppress')
414 self.assertEqual(len(messages), 1)
415 self.assertEqual(messages[0], 'The object machine1 has been successfully suppressed')