Skip to content

Commit

Permalink
(api) Make testcase filtering aware of the duration fields
Browse files Browse the repository at this point in the history
The API method `TestCase.filter` is extended to work for queries that
contain lookups on the fields `setup_duration`, `testing_duration` and
`expected_duration`.
It also returns the values of all three fields in its result.

Refs kiwitcms#1923
  • Loading branch information
mfonism committed Sep 16, 2021
1 parent af3d189 commit 57a50bc
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 2 deletions.
48 changes: 46 additions & 2 deletions tcms/rpc/api/testcase.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
# -*- coding: utf-8 -*-

from datetime import timedelta

from django.db.models import Case, F, Q, When
from django.forms import EmailField, ValidationError
from django.forms.models import model_to_dict
from django.utils.dateparse import parse_duration
from modernrpc.core import REQUEST_KEY, rpc_method

from tcms.core import helpers
from tcms.core.utils import form_errors_to_list
from tcms.management.models import Component, Tag
from tcms.rpc import utils
from tcms.rpc.api.forms.testcase import NewForm, UpdateForm
from tcms.rpc.api.utils import stringify_values
from tcms.rpc.decorators import permissions_required
from tcms.testcases.models import TestCase, TestCasePlan

Expand Down Expand Up @@ -279,8 +284,37 @@ def filter(query=None): # pylint: disable=redefined-builtin
if query is None:
query = {}

return list(
TestCase.objects.filter(**query)
for key, val in query.items():
if not key.startswith(
("setup_duration", "testing_duration", "expected_duration")
):
continue

try:
duration = parse_duration(val)
except TypeError:
# val isn't a string or byte-like object
# item is probably something like 'setup_duration__isnull=True'
continue

if duration is None:
continue

query[key] = duration

qs = (
TestCase.objects.annotate(
expected_duration=Case(
When(
Q(setup_duration__isnull=True) & Q(testing_duration__isnull=True),
then=timedelta(0),
),
When(Q(setup_duration__isnull=True), then="testing_duration"),
When(Q(testing_duration__isnull=True), then="setup_duration"),
default=F("setup_duration") + F("testing_duration"),
)
)
.filter(**query)
.values(
"id",
"create_date",
Expand All @@ -304,9 +338,19 @@ def filter(query=None): # pylint: disable=redefined-builtin
"default_tester__username",
"reviewer",
"reviewer__username",
"setup_duration",
"testing_duration",
"expected_duration",
)
.distinct()
)
return [
stringify_values(
testcase_dict,
keys=["setup_duration", "testing_duration", "expected_duration"],
)
for testcase_dict in qs.iterator()
]


@permissions_required("testcases.view_testcase")
Expand Down
8 changes: 8 additions & 0 deletions tcms/rpc/api/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,11 @@ def tracker_from_url(url, request):
return import_string(bug_system.tracker_type)(bug_system, request)

return None


def stringify_values(d, keys=None):
keys = keys or []
return {
key: (str(val) if (key in keys and val is not None) else val)
for (key, val) in d.items()
}
24 changes: 24 additions & 0 deletions tcms/rpc/tests/test_testcase.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,12 +173,36 @@ def test_filter_query_none(self):
self.assertIn("author", result[0])
self.assertIn("default_tester", result[0])
self.assertIn("reviewer", result[0])
self.assertIn("setup_duration", result[0])
self.assertIn("testing_duration", result[0])
self.assertIn("expected_duration", result[0])

def test_filter_by_product_id(self):
cases = self.rpc_client.TestCase.filter({"category__product": self.product.pk})
self.assertIsNotNone(cases)
self.assertEqual(len(cases), self.cases_count)

def test_filter_by_setup_duration(self):
TestCaseFactory(setup_duration=timedelta(seconds=30))
cases = self.rpc_client.TestCase.filter({"setup_duration": "00:00:30"})
self.assertIsNotNone(cases)
self.assertEqual(len(cases), 1)

def test_filter_by_testing_duration(self):
TestCaseFactory(testing_duration=timedelta(minutes=5, seconds=1))
cases = self.rpc_client.TestCase.filter({"testing_duration__gt": "00:05:00"})
self.assertIsNotNone(cases)
self.assertEqual(len(cases), 1)

def test_filter_by_expected_duration(self):
TestCaseFactory(
setup_duration=timedelta(seconds=30),
testing_duration=timedelta(minutes=5, seconds=1),
)
cases = self.rpc_client.TestCase.filter({"expected_duration__lt": "00:5:31"})
self.assertIsNotNone(cases)
self.assertEqual(len(cases), self.cases_count)


class TestUpdate(APITestCase):
non_existing_username = "FakeUsername"
Expand Down

0 comments on commit 57a50bc

Please sign in to comment.