Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add tests and validation for filterset #2588

40 changes: 27 additions & 13 deletions care/facility/api/viewsets/patient.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from django.conf import settings
from django.contrib.postgres.search import TrigramSimilarity
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
from django.db.models import (
Case,
Expand Down Expand Up @@ -78,7 +79,7 @@
NewDischargeReasonEnum,
)
from care.facility.models.patient_consultation import PatientConsultation
from care.users.models import User
from care.users.models import GENDER_CHOICES, User
from care.utils.cache.cache_allowed_facilities import get_accessible_facilities
from care.utils.filters.choicefilter import CareChoiceFilter
from care.utils.filters.multiselect import MultiSelectFilter
Expand Down Expand Up @@ -108,14 +109,22 @@ class PatientFilterSet(filters.FilterSet):
phone_number = filters.CharFilter(field_name="phone_number")
emergency_phone_number = filters.CharFilter(field_name="emergency_phone_number")
allow_transfer = filters.BooleanFilter(field_name="allow_transfer")
name = filters.CharFilter(field_name="name", lookup_expr="icontains")
name = filters.CharFilter(
field_name="name", lookup_expr="icontains", max_length=200
)
patient_no = filters.CharFilter(
field_name=f"{last_consultation_field}__patient_no", lookup_expr="iexact"
field_name=f"{last_consultation_field}__patient_no",
lookup_expr="iexact",
max_length=100,
)
gender = filters.ChoiceFilter(field_name="gender", choices=GENDER_CHOICES)
age = filters.NumberFilter(field_name="age", validators=[MinValueValidator(0)])
age_min = filters.NumberFilter(
field_name="age", lookup_expr="gte", validators=[MinValueValidator(0)]
)
age_max = filters.NumberFilter(
field_name="age", lookup_expr="lte", validators=[MinValueValidator(0)]
)
gender = filters.NumberFilter(field_name="gender")
age = filters.NumberFilter(field_name="age")
age_min = filters.NumberFilter(field_name="age", lookup_expr="gte")
age_max = filters.NumberFilter(field_name="age", lookup_expr="lte")
deprecated_covid_category = filters.ChoiceFilter(
field_name=f"{last_consultation_field}__deprecated_covid_category",
choices=COVID_CATEGORY_CHOICES,
Expand All @@ -142,7 +151,7 @@ def filter_by_category(self, queryset, name, value):

created_date = filters.DateFromToRangeFilter(field_name="created_date")
modified_date = filters.DateFromToRangeFilter(field_name="modified_date")
srf_id = filters.CharFilter(field_name="srf_id")
srf_id = filters.CharFilter(field_name="srf_id", max_length=200)
is_declared_positive = filters.BooleanFilter(field_name="is_declared_positive")
date_declared_positive = filters.DateFromToRangeFilter(
field_name="date_declared_positive"
Expand All @@ -160,14 +169,16 @@ def filter_by_category(self, queryset, name, value):
# Location Based Filtering
district = filters.NumberFilter(field_name="district__id")
district_name = filters.CharFilter(
field_name="district__name", lookup_expr="icontains"
field_name="district__name", lookup_expr="icontains", max_length=255
)
local_body = filters.NumberFilter(field_name="local_body__id")
local_body_name = filters.CharFilter(
field_name="local_body__name", lookup_expr="icontains"
field_name="local_body__name", lookup_expr="icontains", max_length=255
)
state = filters.NumberFilter(field_name="state__id")
state_name = filters.CharFilter(field_name="state__name", lookup_expr="icontains")
state_name = filters.CharFilter(
field_name="state__name", lookup_expr="icontains", max_length=255
)
# Consultation Fields
is_kasp = filters.BooleanFilter(field_name=f"{last_consultation_field}__is_kasp")
last_consultation_kasp_enabled_date = filters.DateFromToRangeFilter(
Expand Down Expand Up @@ -226,9 +237,12 @@ def filter_by_bed_type(self, queryset, name, value):
)

# Vaccination Filters
covin_id = filters.CharFilter(field_name="covin_id")
covin_id = filters.CharFilter(field_name="covin_id", max_length=15)
is_vaccinated = filters.BooleanFilter(field_name="is_vaccinated")
number_of_doses = filters.NumberFilter(field_name="number_of_doses")
number_of_doses = filters.NumberFilter(
field_name="number_of_doses",
validators=[MinValueValidator(0), MaxValueValidator(3)],
)
# Permission Filters
assigned_to = filters.NumberFilter(field_name="assigned_to")
# Other Filters
Expand Down
129 changes: 129 additions & 0 deletions care/facility/tests/test_patient_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -728,6 +728,135 @@ def test_filter_by_has_consents(self):
self.assertEqual(res.status_code, status.HTTP_200_OK)
self.assertEqual(res.json()["count"], 3)

def test_filter_by_invalid_params(self):
self.client.force_authenticate(user=self.user)

# name length > 200 words
invalid_name_param = "a" * 201
res = self.client.get(self.get_base_url() + f"?name={invalid_name_param}")
self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn(
"Ensure this value has at most 200 characters (it has 201).",
res.json()["name"],
)

# invalid gender choice
invalid_gender = 4
res = self.client.get(self.get_base_url() + f"?gender={invalid_gender}")
self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn(
"Select a valid choice. 4 is not one of the available choices.",
res.json()["gender"],
)

# invalid value for age, age max , age min filter (i.e <0)
invalid_age = -2
res = self.client.get(self.get_base_url() + f"?age={invalid_age}")
self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn(
"Ensure this value is greater than or equal to 0.", res.json()["age"]
)

invalid_min_age = -2
res = self.client.get(self.get_base_url() + f"?age_min={invalid_min_age}")
self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn(
"Ensure this value is greater than or equal to 0.", res.json()["age_min"]
)

invalid_max_age = -2
res = self.client.get(self.get_base_url() + f"?age_max={invalid_max_age}")
self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn(
"Ensure this value is greater than or equal to 0.", res.json()["age_max"]
)

# invalid number_of_doses param >3 or <0
invalid_number_of_doses = -2
res = self.client.get(
self.get_base_url() + f"?number_of_doses={invalid_number_of_doses}"
)
self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn(
"Ensure this value is greater than or equal to 0.",
res.json()["number_of_doses"],
)

invalid_number_of_doses = 4
res = self.client.get(
self.get_base_url() + f"?number_of_doses={invalid_number_of_doses}"
)
self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn(
"Ensure this value is less than or equal to 3.",
res.json()["number_of_doses"],
)

# invalid srf id length > 200 words
invalid_srf_param = "a" * 201
res = self.client.get(self.get_base_url() + f"?srf_id={invalid_srf_param}")
self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn(
"Ensure this value has at most 200 characters (it has 201).",
res.json()["srf_id"],
)

# invalid district_name length > 255 words
invalid_district_name_param = "a" * 256
res = self.client.get(
self.get_base_url() + f"?district_name={invalid_district_name_param}"
)
self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn(
"Ensure this value has at most 255 characters (it has 256).",
res.json()["district_name"],
)

# invalid local_body_name length > 255 words
invalid_local_body_name_param = "a" * 256
res = self.client.get(
self.get_base_url() + f"?local_body_name={invalid_local_body_name_param}"
)
self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn(
"Ensure this value has at most 255 characters (it has 256).",
res.json()["local_body_name"],
)

# invalid state_name length > 255 words
invalid_state_name_param = "a" * 256
res = self.client.get(
self.get_base_url() + f"?state_name={invalid_state_name_param}"
)
self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn(
"Ensure this value has at most 255 characters (it has 256).",
res.json()["state_name"],
)

# invalid patient no value > 100
invalid_patient_no_param = "A" * 101
res = self.client.get(
self.get_base_url() + f"?patient_no={invalid_patient_no_param}"
)
self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn(
"Ensure this value has at most 100 characters (it has 101).",
res.json()["patient_no"],
)

Comment on lines +731 to +847
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add validation test for covin_id field.

The PR objectives mention validating the covin_id field with a maximum length of 15 characters, but this test is missing.

Add the following test case:

# Test invalid covin_id length > 15 characters
invalid_covin_id = "A" * 16
res = self.client.get(self.get_base_url() + f"?covin_id={invalid_covin_id}")
self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn(
    "Ensure this value has at most 15 characters (it has 16).",
    res.json()["covin_id"],
)

def test_invalid_covin_id_param(self):
self.client.force_authenticate(user=self.user)

# Test invalid covin_id length > 15 characters
invalid_covin_id = "A" * 16
res = self.client.get(self.get_base_url() + f"?covin_id={invalid_covin_id}")
self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn(
"Ensure this value has at most 15 characters (it has 16).",
res.json()["covin_id"],
)


class DischargePatientFilterTestCase(TestUtils, APITestCase):
@classmethod
Expand Down