tcms.testplans.tests.tests   A
last analyzed

Complexity

Total Complexity 24

Size/Duplication

Total Lines 347
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 24
eloc 236
dl 0
loc 347
rs 10
c 0
b 0
f 0

17 Methods

Rating   Name   Duplication   Size   Complexity  
A PlanTests.test_open_plans_search() 0 4 1
A PlanTests.test_plan_edit() 0 4 1
A TestCloneView.verify_cloned_plan() 0 9 1
A PlanTests.test_plan_details() 0 7 1
A BasePlanTest.setUpTestData() 0 33 3
B TestCloneView.setUpTestData() 0 66 1
A BasePlanTest.setUp() 0 5 1
A PlanTests.test_search_page_is_shown_with_get_parameter_used() 0 8 1
A TestPlanModel.setUpTestData() 0 13 1
B TestCloneView._verify_options() 0 40 5
A TestPlanModel.test_get_full_url() 0 10 1
A TestCloneView.test_clone_a_plan_by_copying_cases() 0 19 1
A PlanTests.test_plan_history() 0 5 1
A TestCloneView.test_clone_a_plan_with_default_options() 0 24 1
A TestPlanModel.test_add_cases_sortkey_autoincrement() 0 20 2
A TestCloneView.test_open_clone_page_to_clone_one_plan() 0 19 1
A TestPlanModel.test_plan_delete_case() 0 5 1
1
# -*- coding: utf-8 -*-
2
# pylint: disable=invalid-name
3
4
from http import HTTPStatus
5
6
from django import test
7
from django.db.models import F
8
from django.urls import reverse
9
from django.utils.translation import gettext_lazy as _
10
11
from tcms.management.models import Product, Version
12
from tcms.testcases.models import TestCasePlan, TestCaseStatus
13
from tcms.testplans.models import TestPlan
14
from tcms.tests import BasePlanCase, user_should_have_perm
15
from tcms.tests.factories import (
16
    ClassificationFactory,
17
    PlanTypeFactory,
18
    ProductFactory,
19
    TestCaseFactory,
20
    TestPlanFactory,
21
    UserFactory,
22
    VersionFactory,
23
)
24
25
26
class BasePlanTest(test.TestCase):
27
    @classmethod
28
    def setUpTestData(cls):
29
        cls.user = UserFactory(username="admin", email="[email protected]")
30
        cls.user.set_password("admin")
31
        cls.user.is_superuser = True
32
        cls.user.is_staff = True
33
        cls.user.save()
34
35
        cls.classification = ClassificationFactory(name="Auto")
36
        cls.product = ProductFactory(name="Kiwi", classification=cls.classification)
37
        cls.product_version = VersionFactory(value="0.1", product=cls.product)
38
        cls.plan_type = PlanTypeFactory()
39
40
        cls.test_plan = TestPlanFactory(
41
            name="another test plan for testing",
42
            product_version=cls.product_version,
43
            author=cls.user,
44
            product=cls.product,
45
            type=cls.plan_type,
46
        )
47
        # add TestCases to plan with status CONFIRMED
48
        for _i in range(5):
49
            TestCaseFactory(
50
                plan=[cls.test_plan],
51
                case_status=TestCaseStatus.objects.get(name="CONFIRMED"),
52
            )
53
54
        # also add a few PROPOSED TestCases
55
        for _i in range(3):
56
            TestCaseFactory(plan=[cls.test_plan])
57
58
        cls.plan_id = cls.test_plan.pk
59
        cls.child_plan = TestPlanFactory(parent=cls.test_plan)
60
61
    def setUp(self):
62
        super().setUp()
63
        self.client.login(  # nosec:B106:hardcoded_password_funcarg
64
            username=self.user.username,
65
            password="admin",
66
        )
67
68
69
class PlanTests(BasePlanTest):
70
    def test_open_plans_search(self):
71
        location = reverse("plans-search")
72
        response = self.client.get(location)
73
        self.assertEqual(response.status_code, HTTPStatus.OK)
74
75
    def test_search_page_is_shown_with_get_parameter_used(self):
76
        response = self.client.get(
77
            reverse("plans-search"), {"product": self.product.pk}
78
        )
79
        self.assertContains(
80
            response,
81
            f'<option value="{self.product.pk}" selected>{self.product.name}</option>',
82
            html=True,
83
        )
84
85
    def test_plan_details(self):
86
        location = reverse("test_plan_url_short", args=[self.plan_id])
87
        response = self.client.get(location)
88
        self.assertEqual(response.status_code, HTTPStatus.MOVED_PERMANENTLY)
89
90
        response = self.client.get(location, follow=True)
91
        self.assertEqual(response.status_code, HTTPStatus.OK)
92
93
    def test_plan_edit(self):
94
        location = reverse("plan-edit", args=[self.plan_id])
95
        response = self.client.get(location)
96
        self.assertEqual(response.status_code, HTTPStatus.OK)
97
98
    def test_plan_history(self):
99
        # note: history URL is generated on the fly and not accessible via name
100
        location = f"/admin/testplans/testplan/{self.plan_id}/history/"
101
        response = self.client.get(location)
102
        self.assertEqual(response.status_code, HTTPStatus.OK)
103
104
105
class TestPlanModel(test.TestCase):
106
    """Test some model operations directly without a view"""
107
108
    @classmethod
109
    def setUpTestData(cls):
110
        cls.plan_tester = UserFactory(username="tester")
111
        cls.plan_tester.set_password("password")
112
        cls.plan_tester.save()
113
        user_should_have_perm(cls.plan_tester, "testplans.view_testplan")
114
115
        cls.plan_1 = TestPlanFactory()
116
        cls.testcase_1 = TestCaseFactory()
117
        cls.testcase_2 = TestCaseFactory()
118
119
        cls.plan_1.add_case(cls.testcase_1)
120
        cls.plan_1.add_case(cls.testcase_2)
121
122
    def test_plan_delete_case(self):
123
        self.plan_1.delete_case(self.testcase_1)
124
        cases_left = TestCasePlan.objects.filter(plan=self.plan_1.pk)
125
        self.assertEqual(1, cases_left.count())
126
        self.assertEqual(self.testcase_2.pk, cases_left[0].case.pk)
127
128
    def test_add_cases_sortkey_autoincrement(self):
129
        """
130
        When you add new cases, each new case should get a sortkey of the
131
        highest sortkey in the database + 10.
132
133
        The first case should get sortkey 0. The offset between the sortkeys is
134
        to leave space to insert cases in between without having to update all
135
        cases.
136
        """
137
138
        plan = TestPlanFactory()
139
140
        for sequence_no in range(3):
141
            case_plan = plan.add_case(TestCaseFactory())
142
            self.assertEqual(sequence_no * 10, case_plan.sortkey)
143
144
        # Check if you can still specify a sortkey manually to insert a case in
145
        # between the other cases.
146
        case_plan = plan.add_case(TestCaseFactory(), sortkey=15)
147
        self.assertEqual(15, case_plan.sortkey)
148
149
    def test_get_full_url(self):
150
        self.client.login(  # nosec:B106:hardcoded_password_funcarg
151
            username=self.plan_tester.username, password="password"
152
        )
153
        test_plan_url = self.plan_1.get_full_url()
154
        response = self.client.get(test_plan_url, follow=True)
155
156
        self.assertIsNotNone(test_plan_url)
157
        self.assertNotEqual(test_plan_url[-1], "/")
158
        self.assertContains(response, self.plan_1.name)
159
160
161
class TestCloneView(BasePlanCase):
162
    """Test case for cloning a plan"""
163
164
    @classmethod
165
    def setUpTestData(cls):
166
        super().setUpTestData()
167
168
        cls.another_plan = TestPlanFactory(
169
            name="Another plan for test",
170
            author=cls.tester,
171
            product=cls.product,
172
            product_version=cls.version,
173
        )
174
        cls.another_case_1 = TestCaseFactory(
175
            author=cls.tester,
176
            default_tester=None,
177
            reviewer=cls.tester,
178
            plan=[cls.another_plan],
179
        )
180
        cls.another_case_2 = TestCaseFactory(
181
            author=cls.tester,
182
            default_tester=None,
183
            reviewer=cls.tester,
184
            plan=[cls.another_plan],
185
        )
186
187
        cls.third_plan = TestPlanFactory(
188
            name="Third plan for test",
189
            author=cls.tester,
190
            product=cls.product,
191
            product_version=cls.version,
192
        )
193
        cls.third_case_1 = TestCaseFactory(
194
            author=cls.tester,
195
            default_tester=None,
196
            reviewer=cls.tester,
197
            plan=[cls.third_plan],
198
        )
199
        cls.third_case_2 = TestCaseFactory(
200
            author=cls.tester,
201
            default_tester=None,
202
            reviewer=cls.tester,
203
            plan=[cls.third_plan],
204
        )
205
206
        cls.totally_new_plan = TestPlanFactory(
207
            name="Test clone plan with copying cases",
208
            author=cls.tester,
209
            product=cls.product,
210
            product_version=cls.version,
211
        )
212
        cls.case_maintain_original_author = TestCaseFactory(
213
            author=cls.tester,
214
            default_tester=None,
215
            reviewer=cls.tester,
216
            plan=[cls.totally_new_plan],
217
        )
218
        cls.case_keep_default_tester = TestCaseFactory(
219
            author=cls.tester,
220
            default_tester=None,
221
            reviewer=cls.tester,
222
            plan=[cls.totally_new_plan],
223
        )
224
225
        cls.plan_tester = UserFactory()
226
        cls.plan_tester.set_password("password")
227
        cls.plan_tester.save()
228
        user_should_have_perm(cls.plan_tester, "testplans.add_testplan")
229
        user_should_have_perm(cls.plan_tester, "testplans.view_testplan")
230
231
    def test_open_clone_page_to_clone_one_plan(self):
232
        self.client.login(  # nosec:B106:hardcoded_password_funcarg
233
            username=self.plan_tester.username, password="password"
234
        )
235
236
        response = self.client.get(reverse("plans-clone", args=[self.plan.pk]))
237
238
        _name = _("Name")
239
        self.assertContains(
240
            response,
241
            f'<label class="col-md-1 col-lg-1" for="id_name">{_name}</label>',
242
            html=True,
243
        )
244
245
        self.assertContains(
246
            response,
247
            f'<input type="text" id="id_name" name="name" value="{self.plan.make_cloned_name()}"'
248
            ' class="form-control" required>',
249
            html=True,
250
        )
251
252
    def verify_cloned_plan(self, original_plan, cloned_plan, copy_cases=None):
253
        self.assertEqual(f"Copy of {original_plan.name}", cloned_plan.name)
254
        self.assertEqual(cloned_plan.text, original_plan.text)
255
        self.assertEqual(Product.objects.get(pk=self.product.pk), cloned_plan.product)
256
        self.assertEqual(
257
            Version.objects.get(pk=self.version.pk), cloned_plan.product_version
258
        )
259
260
        self._verify_options(original_plan, cloned_plan, copy_cases)
261
262
    def _verify_options(self, original_plan, cloned_plan, copy_cases):
263
        # number of TCs should always be the same
264
        self.assertEqual(cloned_plan.cases.count(), original_plan.cases.count())
265
266
        # Verify option set_parent
267
        self.assertEqual(TestPlan.objects.get(pk=original_plan.pk), cloned_plan.parent)
268
269
        # Verify option copy_testcases
270
        for case in cloned_plan.cases.all():
271
            is_case_linked = TestCasePlan.objects.filter(
272
                plan=original_plan, case=case
273
            ).exists()
274
275
            if copy_cases:
276
                # Ensure cases of original plan are not linked to cloned plan
277
                self.assertFalse(is_case_linked)
278
279
                # verify author was updated
280
                self.assertEqual(self.plan_tester, case.author)
281
            else:
282
                self.assertTrue(is_case_linked)
283
284
            cases_from_original_plan = original_plan.cases.all().annotate(
285
                sortkey=F("testcaseplan__sortkey")
286
            )
287
            cases_from_cloned_plan = cloned_plan.cases.all().annotate(
288
                sortkey=F("testcaseplan__sortkey")
289
            )
290
            for original_case, copied_case in zip(
291
                cases_from_original_plan, cases_from_cloned_plan
292
            ):
293
                # default tester is always kept
294
                self.assertEqual(
295
                    original_case.default_tester, copied_case.default_tester
296
                )
297
                self.assertEqual(original_case.sortkey, copied_case.sortkey)
298
299
                if not copy_cases:
300
                    # when linking TCs author doesn't change
301
                    self.assertEqual(original_case.author, copied_case.author)
302
303
    def test_clone_a_plan_with_default_options(self):
304
        post_data = {
305
            "name": self.third_plan.make_cloned_name(),
306
            "product": self.product.pk,
307
            "version": self.version.pk,
308
            "set_parent": "on",
309
            "submit": "Clone",
310
        }
311
        self.client.login(  # nosec:B106:hardcoded_password_funcarg
312
            username=self.plan_tester.username, password="password"
313
        )
314
        response = self.client.post(
315
            reverse("plans-clone", args=[self.third_plan.pk]), post_data
316
        )
317
318
        cloned_plan = TestPlan.objects.get(name=self.third_plan.make_cloned_name())
319
320
        self.assertRedirects(
321
            response,
322
            reverse("test_plan_url_short", args=[cloned_plan.pk]),
323
            target_status_code=HTTPStatus.MOVED_PERMANENTLY,
324
        )
325
326
        self.verify_cloned_plan(self.third_plan, cloned_plan)
327
328
    def test_clone_a_plan_by_copying_cases(self):
329
        post_data = {
330
            "name": self.totally_new_plan.make_cloned_name(),
331
            "product": self.product.pk,
332
            "version": self.version.pk,
333
            "set_parent": "on",
334
            "submit": "Clone",
335
            "copy_testcases": "on",
336
        }
337
        self.client.login(  # nosec:B106:hardcoded_password_funcarg
338
            username=self.plan_tester.username, password="password"
339
        )
340
        self.client.post(
341
            reverse("plans-clone", args=[self.totally_new_plan.pk]), post_data
342
        )
343
        cloned_plan = TestPlan.objects.get(
344
            name=self.totally_new_plan.make_cloned_name()
345
        )
346
        self.verify_cloned_plan(self.totally_new_plan, cloned_plan, copy_cases=True)
347