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

OP-2063: fix number test #144

Merged
merged 4 commits into from
Sep 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 28 additions & 19 deletions insuree/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
"renewal_photo_age_child": 12, # age (in months) of a picture due for renewal for children
"insuree_number_validator": None, # Insuree number *function* that validates the insuree number for example
# 'msystems.utils.is_valid_resident_identifier'
"insuree_number_length": 50, # Insuree number length to validate
"insuree_number_max_length": None, # Insuree number length to validate
"insuree_number_min_length": None, # Insuree number length to validate
"insuree_number_modulo_root": None, # modulo base for checksum on last digit, requires length to be set too
"validation_code_taken_insuree_number": 1,
"validation_code_no_insuree_number": 2,
Expand Down Expand Up @@ -70,7 +71,8 @@ class InsureeConfig(AppConfig):
renewal_photo_age_adult = None
renewal_photo_age_child = None
insuree_number_validator = None
insuree_number_length = None
insuree_number_max_length = None
insuree_number_min_length = None
insuree_number_modulo_root = None
insuree_fsp_mandatory = None
insuree_as_worker = None
Expand All @@ -86,27 +88,30 @@ def ready(self):
cfg = ModuleConfiguration.get_or_default(MODULE_NAME, DEFAULT_CFG)
self.__load_config(cfg)
self._configure_photo_root(cfg)
self.reset_validation_settings()


@classmethod
def reset_validation_settings(cls):
cls.insuree_number_validator = cls.__get_from_settings_or_default("INSUREE_NUMBER_VALIDATOR")
cls.insuree_number_length = cls.__get_from_settings_or_default("INSUREE_NUMBER_LENGTH")
cls.insuree_number_modulo_root = cls.__get_from_settings_or_default("INSUREE_NUMBER_MODULE_ROOT")
# Getting these at runtime for easier testing
@classmethod
def get_insuree_number_validator(cls):
return cls.insuree_number_validator or cls.__get_from_settings_or_default("INSUREE_NUMBER_VALIDATOR")

@classmethod
def get_insuree_number_length(cls):
value = cls.insuree_number_length or cls.__get_from_settings_or_default("INSUREE_NUMBER_LENGTH")
return int(value) if value else None
cls.insuree_number_validator = cls.__get_from_settings_or_default(
"INSUREE_NUMBER_VALIDATOR",
DEFAULT_CFG['insuree_number_validator']
)
value = cls.__get_from_settings_or_default(
"INSUREE_NUMBER_LENGTH",
)
if value:
cls.insuree_number_max_length = int(value)
cls.insuree_number_min_length = int(value)
else:
cls.insuree_number_max_length = DEFAULT_CFG['insuree_number_max_length']
cls.insuree_number_min_length = DEFAULT_CFG['insuree_number_min_length']

value = cls.__get_from_settings_or_default(
"INSUREE_NUMBER_MODULE_ROOT"
)
cls.insuree_number_modulo_root = int(value) if value else DEFAULT_CFG['insuree_number_modulo_root']

@classmethod
def get_insuree_number_modulo_root(cls):
value = cls.insuree_number_modulo_root or cls.__get_from_settings_or_default("INSUREE_NUMBER_MODULE_ROOT")
return int(value) if value else None

def set_dataloaders(self, dataloaders):
from .dataloaders import InsureeLoader, FamilyLoader
Expand All @@ -116,7 +121,11 @@ def set_dataloaders(self, dataloaders):

@classmethod
def __get_from_settings_or_default(cls, attribute_name, default=None):
return getattr(settings, attribute_name) if hasattr(settings, attribute_name) else default
if hasattr(settings, attribute_name):
value = getattr(settings, attribute_name) or default
else:
value = default
return value

def _configure_photo_root(self, cfg):
# TODO: To be confirmed. I left loading from config for integrity reasons
Expand Down
10 changes: 5 additions & 5 deletions insuree/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from location.models import Location, LocationManager

from insuree.apps import InsureeConfig
from insuree.services import validate_insuree_number
from .models import FamilyMutation, InsureeMutation
from django.utils.translation import gettext as _
from location.apps import LocationConfig
Expand Down Expand Up @@ -164,12 +165,11 @@ def resolve_insurees(self, info, **kwargs):
filters = []
additional_filter = kwargs.get('additional_filters', None)
chf_id = kwargs.get('chf_id')
chf_id_max_length = getattr(InsureeConfig, 'insuree_number_length')

if chf_id is not None:
if len(chf_id) > chf_id_max_length:
raise ValidationError(_("Insuree no. cannot be longer than 12 characters"))
if not re.match("^[a-zA-Z0-9]*$", chf_id):
raise ValidationError(_("Insuree no. can only contain letters and numbers"))
errors = validate_insuree_number(chf_id)
if errors:
return ValidationMessageGQLType(False, errors[0]['errorCode'], errors[0]['message'])
filters.append(Q(chf_id=chf_id))
if additional_filter:
filters_from_signal = _insuree_insuree_additional_filters(
Expand Down
50 changes: 34 additions & 16 deletions insuree/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def create_insuree_renewal_detail(policy_renewal):


def custom_insuree_number_validation(insuree_number):
function_string = InsureeConfig.get_insuree_number_validator()
function_string = InsureeConfig.insuree_number_validator
try:
mod, name = function_string.rsplit('.', 1)
module = import_module(mod)
Expand All @@ -65,34 +65,42 @@ def custom_insuree_number_validation(insuree_number):

def validate_insuree_number(insuree_number, insuree_uuid=None):
insuree_number = str(insuree_number)
query = Insuree.objects.filter(
chf_id=insuree_number, validity_to__isnull=True)
insuree = query.first()
if insuree_uuid and insuree and uuid.UUID(insuree.uuid) != uuid.UUID(insuree_uuid):
return [{"errorCode": InsureeConfig.validation_code_taken_insuree_number,
"message": "Insuree number has to be unique, %s exists in system" % insuree_number}]

if InsureeConfig.get_insuree_number_validator():
if InsureeConfig.insuree_number_validator:
return custom_insuree_number_validation(insuree_number)
if InsureeConfig.get_insuree_number_length():
if InsureeConfig.insuree_number_max_length:
if not insuree_number:
return [
{
"errorCode": InsureeConfig.validation_code_no_insuree_number,
"message": "Invalid insuree number (empty), should be %s" %
(InsureeConfig.get_insuree_number_length(),)
"message": _("Invalid insuree number (empty), should be %s") %
(InsureeConfig.insuree_number_max_length,)
}
]
if len(insuree_number) != InsureeConfig.get_insuree_number_length():
if len(insuree_number) > InsureeConfig.insuree_number_max_length:
return [
{
"errorCode": InsureeConfig.validation_code_invalid_insuree_number_len,
"message": "Invalid insuree number length %s, should be %s" %
(len(insuree_number),
InsureeConfig.get_insuree_number_length())
"message": _("Invalid insuree number length %s, should be maximun %s") %
(
len(insuree_number),
InsureeConfig.insuree_number_max_length
)
}
]
config_modulo = InsureeConfig.get_insuree_number_modulo_root()
if InsureeConfig.insuree_number_min_length and len(insuree_number) < InsureeConfig.insuree_number_min_length:
return [
{
"errorCode": InsureeConfig.validation_code_invalid_insuree_number_len,
"message": _("Invalid insuree number length %s, should be minimum %s") %
(
len(insuree_number),
InsureeConfig.insuree_number_min_length
)
}
]

config_modulo = InsureeConfig.insuree_number_modulo_root
if config_modulo:
try:
if config_modulo == 10:
Expand All @@ -107,6 +115,16 @@ def validate_insuree_number(insuree_number, insuree_uuid=None):
logger.exception("Failed insuree number validation", exc)
return [{"errorCode": InsureeConfig.validation_code_invalid_insuree_number_exception,
"message": "Insuree number validation failed"}]
query = Insuree.objects.filter(
chf_id=insuree_number, validity_to__isnull=True)
insuree = query.first()
if insuree_uuid and insuree and uuid.UUID(insuree.uuid) != uuid.UUID(insuree_uuid):
return [{
"errorCode": InsureeConfig.validation_code_taken_insuree_number,
"message": "Insuree number has to be unique, %s exists in system" % insuree_number
}]


return []


Expand Down
2 changes: 1 addition & 1 deletion insuree/test_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from core import filter_validity

def generate_random_insuree_number():
start_number = pow(10, (InsureeConfig.get_insuree_number_length() or 8) - 1)
start_number = pow(10, (InsureeConfig.insuree_number_max_length or 8) - 1)
end_number = start_number * 10 - 1
return random.randrange(start_number, end_number)

Expand Down
66 changes: 49 additions & 17 deletions insuree/tests/test_graphql.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from location.test_helpers import create_test_location, create_test_health_facility, create_test_village
from insuree.models import Family


from insuree.apps import InsureeConfig
# from openIMIS import schema


Expand Down Expand Up @@ -417,21 +417,53 @@ def test_inquire(self):
self.assertResponseNoErrors(response)


def test_validate_number_validditiy_with_variables(self):
response = self.query(
'''
query ($insuranceNumber: String!) {
insureeNumberValidity(insureeNumber: $insuranceNumber) {
isValid
errorCode
errorMessage
}
}
''',
headers={"HTTP_AUTHORIZATION": f"Bearer {self.admin_token}"},
variables={"insuranceNumber": "070707070"} )
def test_validate_number_unvalidity_with_variables(self):
with self.settings(
INSUREE_NUMBER_VALIDATOR=None,
INSUREE_NUMBER_LENGTH=9,
INSUREE_NUMBER_MODULE_ROOT=None):
InsureeConfig.reset_validation_settings()
response = self.query(
'''
query ($insuranceNumber: String!) {
insureeNumberValidity(insureeNumber: $insuranceNumber) {
isValid
errorCode
errorMessage
}
}
''',
headers={"HTTP_AUTHORIZATION": f"Bearer {self.admin_token}"},
variables={"insuranceNumber": "07070"} )

content = json.loads(response.content)

# This validates the status code and if you get errors
self.assertResponseNoErrors(response)
self.assertFalse(content['data']['insureeNumberValidity']['isValid'])


def test_validate_number_validity_with_variables(self):
with self.settings(
INSUREE_NUMBER_VALIDATOR=None,
INSUREE_NUMBER_LENGTH=9,
INSUREE_NUMBER_MODULE_ROOT=None):
InsureeConfig.reset_validation_settings()
response = self.query(
'''
query ($insuranceNumber: String!) {
insureeNumberValidity(insureeNumber: $insuranceNumber) {
isValid
errorCode
errorMessage
}
}
''',
headers={"HTTP_AUTHORIZATION": f"Bearer {self.admin_token}"},
variables={"insuranceNumber": "070707070"})
Copy link
Member

Choose a reason for hiding this comment

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

Same insurance number as in invalid test. Is invalid test correct? (can't add comment on that)

Copy link
Member Author

Choose a reason for hiding this comment

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

not the second time as I have the with length=9


content = json.loads(response.content)
content = json.loads(response.content)

# This validates the status code and if you get errors
self.assertResponseNoErrors(response)
# This validates the status code and if you get errors
self.assertResponseNoErrors(response)
self.assertTrue(content['data']['insureeNumberValidity']['isValid'])
Loading