Passed
Push — master ( b7ec87...174f4e )
by Alexander
02:09
created

PermissionsTestMixin.verify_post_with_permission()   A

Complexity

Conditions 1

Size

Total Lines 2
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 2
rs 10
c 0
b 0
f 0
cc 1
nop 1
1
# -*- coding: utf-8 -*-
2
# pylint: disable=invalid-name
3
4
from django import test
5
from django.urls import reverse
6
from django.conf import settings
7
from django.contrib.auth.models import Permission
8
9
from tcms.testruns.models import TestExecutionStatus
10
from tcms.testcases.models import TestCaseStatus
11
from tcms.tests.factories import ProductFactory
12
from tcms.tests.factories import TestCaseFactory
13
from tcms.tests.factories import TestExecutionFactory
14
from tcms.tests.factories import TestPlanFactory
15
from tcms.tests.factories import TestRunFactory
16
from tcms.tests.factories import UserFactory
17
from tcms.tests.factories import VersionFactory
18
from tcms.tests.factories import BuildFactory
19
20
21 View Code Duplication
def user_should_have_perm(user, perm):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
22
    if isinstance(perm, str):
23
        try:
24
            app_label, codename = perm.split('.')
25
        except ValueError:
26
            raise ValueError('%s is not valid. Should be in format app_label.perm_codename')
27
        else:
28
            if not app_label or not codename:
29
                raise ValueError('Invalid app_label or codename')
30
            user.user_permissions.add(
31
                Permission.objects.get(content_type__app_label=app_label, codename=codename))
32
    elif isinstance(perm, Permission):
33
        user.user_permissions.add(perm)
34
    else:
35
        raise TypeError('perm should be an instance of either str or Permission')
36
37
38 View Code Duplication
def remove_perm_from_user(user, perm):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
39
    """Remove a permission from an user"""
40
41
    if isinstance(perm, str):
42
        try:
43
            app_label, codename = perm.split('.')
44
        except ValueError:
45
            raise ValueError('%s is not valid. Should be in format app_label.perm_codename')
46
        else:
47
            if not app_label or not codename:
48
                raise ValueError('Invalid app_label or codename')
49
            get_permission = Permission.objects.get
50
            user.user_permissions.remove(
51
                get_permission(content_type__app_label=app_label, codename=codename))
52
    elif isinstance(perm, Permission):
53
        user.user_permissions.remove(perm)
54
    else:
55
        raise TypeError('perm should be an instance of either str or Permission')
56
57
58
def create_request_user(username=None, password=None):
59
    if username:
60
        user = UserFactory(username=username)
61
    else:
62
        user = UserFactory()
63
    if password:
64
        user.set_password(password)
65
    else:
66
        user.set_password('password')
67
    user.save()
68
    return user
69
70
71
class LoggedInTestCase(test.TestCase):
72
    """
73
        Test case class for logged-in users which also provides couple of
74
        helper assertion methods.
75
    """
76
77
    @classmethod
78
    def setUpTestData(cls):
79
        cls.tester = UserFactory()
80
        cls.tester.set_password('password')
81
        cls.tester.save()
82
83
    def setUp(self):
84
        """
85
            Login because by default we have GlobalLoginRequiredMiddleware enabled!
86
        """
87
        super().setUp()
88
        self.client.login(username=self.tester.username,  # nosec:B106:hardcoded_password_funcarg
89
                          password='password')
90
91
    def assertJsonResponse(self, response, expected, status_code=200):
92
        self.assertEqual(status_code, response.status_code)
93
        self.assertJSONEqual(
94
            str(response.content, encoding=settings.DEFAULT_CHARSET),
95
            expected)
96
97
98
class BasePlanCase(LoggedInTestCase):
99
    """Base test case by providing essential Plan and Case objects used in tests"""
100
101
    @classmethod
102
    def setUpTestData(cls):
103
        super().setUpTestData()
104
105
        cls.case_status_confirmed = TestCaseStatus.objects.get(name='CONFIRMED')
106
        cls.case_status_proposed = TestCaseStatus.objects.get(name='PROPOSED')
107
108
        cls.product = ProductFactory(name='Kiwi')
109
        cls.version = VersionFactory(value='0.1', product=cls.product)
110
111
        cls.plan = TestPlanFactory(
112
            author=cls.tester,
113
            product=cls.product,
114
            product_version=cls.version)
115
116
        cls.case = TestCaseFactory(
117
            author=cls.tester,
118
            default_tester=None,
119
            reviewer=cls.tester,
120
            case_status=cls.case_status_confirmed,
121
            plan=[cls.plan])
122
        cls.case.save()  # will generate history object
123
124
        cls.case_1 = TestCaseFactory(
125
            author=cls.tester,
126
            default_tester=None,
127
            reviewer=cls.tester,
128
            case_status=cls.case_status_confirmed,
129
            plan=[cls.plan])
130
        cls.case_1.save()  # will generate history object
131
132
        cls.case_2 = TestCaseFactory(
133
            author=cls.tester,
134
            default_tester=None,
135
            reviewer=cls.tester,
136
            case_status=cls.case_status_confirmed,
137
            plan=[cls.plan])
138
        cls.case_2.save()  # will generate history object
139
140
        cls.case_3 = TestCaseFactory(
141
            author=cls.tester,
142
            default_tester=None,
143
            reviewer=cls.tester,
144
            case_status=cls.case_status_confirmed,
145
            plan=[cls.plan])
146
        cls.case_3.save()  # will generate history object
147
148
        cls.case_4 = TestCaseFactory(
149
            author=cls.tester,
150
            default_tester=None,
151
            reviewer=cls.tester,
152
            case_status=cls.case_status_confirmed,
153
            plan=[cls.plan])
154
        cls.case_4.save()  # will generate history object
155
156
        cls.case_5 = TestCaseFactory(
157
            author=cls.tester,
158
            default_tester=None,
159
            reviewer=cls.tester,
160
            case_status=cls.case_status_confirmed,
161
            plan=[cls.plan])
162
        cls.case_5.save()  # will generate history object
163
164
        cls.case_6 = TestCaseFactory(
165
            author=cls.tester,
166
            default_tester=None,
167
            reviewer=cls.tester,
168
            case_status=cls.case_status_confirmed,
169
            plan=[cls.plan])
170
        cls.case_6.save()  # will generate history object
171
172
173
class BaseCaseRun(BasePlanCase):
174
    """Base test case containing test run and case runs"""
175
176
    @classmethod
177
    def setUpTestData(cls):
178
        super(BaseCaseRun, cls).setUpTestData()
179
180
        cls.status_idle = TestExecutionStatus.objects.filter(weight=0).first()
181
182
        cls.build = BuildFactory(product=cls.product)
183
184
        cls.test_run = TestRunFactory(product_version=cls.version,
185
                                      plan=cls.plan,
186
                                      build=cls.build,
187
                                      manager=cls.tester,
188
                                      default_tester=cls.tester)
189
190
        executions = []
191
        for i, case in enumerate((cls.case_1, cls.case_2, cls.case_3), 1):
192
            executions.append(TestExecutionFactory(assignee=cls.tester,
193
                                                   run=cls.test_run,
194
                                                   build=cls.build,
195
                                                   status=cls.status_idle,
196
                                                   case=case, sortkey=i * 10))
197
198
        # used in other tests as well
199
        cls.execution_1 = executions[0]
200
        cls.execution_2 = executions[1]
201
        cls.execution_3 = executions[2]
202
203
        cls.test_run_1 = TestRunFactory(product_version=cls.version,
204
                                        plan=cls.plan,
205
                                        build=cls.build,
206
                                        manager=cls.tester,
207
                                        default_tester=cls.tester)
208
209
        # create a few more TestExecution objects
210
        for i, case in enumerate((cls.case_4, cls.case_5, cls.case_6), 1):
211
            executions.append(TestExecutionFactory(assignee=cls.tester,
212
                                                   tested_by=cls.tester,
213
                                                   run=cls.test_run_1,
214
                                                   build=cls.build,
215
                                                   status=cls.status_idle,
216
                                                   case=case, sortkey=i * 10))
217
        # used in other tests as well
218
        cls.execution_4 = executions[3]
219
        cls.execution_5 = executions[4]
220
        cls.execution_6 = executions[5]
221
222
223
class PermissionsTestMixin:
224
    base_classes = ['PermissionsTestCase', 'APIPermissionsTestCase']
225
    http_method_names = []  # api, get or post
226
    permission_label = None
227
228
    # skip running if class called directly by test runner
229
    @classmethod
230
    def setUpClass(cls):
231
        if cls.__name__ in cls.base_classes:
232
            return
233
        super().setUpClass()
234
235
    @classmethod
236
    def tearDownClass(cls):
237
        if cls.__name__ in cls.base_classes:
238
            return
239
        super().tearDownClass()
240
241
    def __call__(self, result=None):
242
        if self.__class__.__name__ in self.base_classes:
243
            return None
244
245
        return super().__call__(result)
246
    # end skip running
247
248
    @classmethod
249
    def setUpTestData(cls):
250
        super().setUpTestData()
251
        cls.check_mandatory_attributes()
252
253
    @classmethod
254
    def check_mandatory_attributes(cls):
255
        """
256
            Make sure important class attributes are defined.
257
        """
258
        if not cls.permission_label:
259
            raise RuntimeError("Configure `permission_label` attribute for this test class")
260
261
        if not cls.http_method_names:
262
            raise RuntimeError("Configure `http_method_names` attribute for this test class")
263
264
    def verify_api_with_permission(self):
265
        self.fail('Not implemented')
266
267
    def verify_get_with_permission(self):
268
        self.fail('Not implemented')
269
270
    def verify_post_with_permission(self):
271
        self.fail('Not implemented')
272
273
    def verify_api_without_permission(self):
274
        self.fail('Not implemented')
275
276
    def verify_get_without_permission(self):
277
        self.fail('Not implemented')
278
279
    def verify_post_without_permission(self):
280
        self.fail('Not implemented')
281
282
    def test_with_permission(self):
283
        """
284
            Actual test method for positive scenario. Will validate
285
            all of the accepted methods by calling the
286
            verify_X_with_permission() method(s).
287
        """
288
        self.no_permissions_but(self.permission_label)
289
        self.client.login(  # nosec:B106:hardcoded_password_funcarg
290
            username=self.tester.username,
291
            password='password')
292
293
        for method in self.http_method_names:
294
            function = getattr(self, 'verify_%s_with_permission' % method)
295
            function()
296
297
    def no_permissions_but(self, tested_permission):
298
        """
299
            Make sure self.tester has no other permissions but
300
            the one required!
301
        """
302
        self.tester.user_permissions.remove()
303
        user_should_have_perm(self.tester, tested_permission)
304
305
    def test_without_permission(self):
306
        """
307
            Actual test method for negative scenario. Will validate
308
            all of the accepted methods by calling the
309
            verify_X_without_permission() method(s).
310
        """
311
        self.all_permissions_except(self.permission_label)
312
        self.client.login(  # nosec:B106:hardcoded_password_funcarg
313
            username=self.tester.username,
314
            password='password')
315
316
        for method in self.http_method_names:
317
            function = getattr(self, 'verify_%s_without_permission' % method)
318
            function()
319
320
    def all_permissions_except(self, tested_permission):
321
        """
322
            Make sure self.tester has all other permissions except
323
            the one required!
324
        """
325
        for perm in Permission.objects.all():
326
            user_should_have_perm(self.tester, perm)
327
328
        remove_perm_from_user(self.tester, tested_permission)
329
330
331
class PermissionsTestCase(PermissionsTestMixin, LoggedInTestCase):
332
    """Base class for all tests around view permissions"""
333
334
    url = None
335
    post_data = {}
336
337
    @classmethod
338
    def check_mandatory_attributes(cls):
339
        """
340
            Make sure important class attributes are defined.
341
        """
342
        super().check_mandatory_attributes()
343
344
        if not cls.url:
345
            raise RuntimeError("Configure `url` attribute for this test class")
346
347
        if not cls.post_data:
348
            raise RuntimeError("Configure `post_data` attribute for this test class")
349
350
    def verify_get_without_permission(self):
351
        """
352
            Implement all validation steps for GET self.url
353
            when self.tester does not have the appropriate permission.
354
355
            Default implementation asserts that user is redirected back
356
            to the login page!
357
        """
358
        response = self.client.get(self.url)
359
        self.assertRedirects(response, reverse('tcms-login') + '?next=' + self.url)
360
361
    def verify_post_without_permission(self):
362
        """
363
            Implement all validation steps for POST self.url
364
            when self.tester does not have the appropriate permission.
365
366
            Default implementation asserts that user is redirected back
367
            to the login page!
368
        """
369
        response = self.client.post(self.url, self.post_data)
370
        self.assertRedirects(response, reverse('tcms-login') + '?next=' + self.url)
371
372
    def all_permissions_except(self, tested_permission):
373
        """
374
            Make sure self.tester has all other permissions except
375
            the one required!
376
        """
377
        for perm in Permission.objects.all():
378
            user_should_have_perm(self.tester, perm)
379
380
        remove_perm_from_user(self.tester, tested_permission)
381