id-ocr-engine / tests /test_id_parser.py
Kevin-KES
Deploy current main to HF Space
50c6ee2
Raw
History Blame Contribute Delete
7.85 kB
import pytest
from engine.id_parser import (
validate_luhn,
parse_id_number,
extract_id_number,
cross_validate,
validate_id_for_country,
_normalize_date,
_check_date_transposition,
)
class TestValidateLuhn:
def test_valid_id(self):
# Known valid SA ID number
assert validate_luhn("8801015800082") is True
def test_invalid_id_bad_checksum(self):
assert validate_luhn("8801015800083") is False
def test_invalid_too_short(self):
assert validate_luhn("880101580008") is False
def test_invalid_too_long(self):
assert validate_luhn("88010158000820") is False
def test_invalid_non_digits(self):
assert validate_luhn("880101580008A") is False
def test_empty_string(self):
assert validate_luhn("") is False
def test_none(self):
assert validate_luhn(None) is False
def test_valid_female_citizen(self):
assert validate_luhn("9202204720083") is True
def test_valid_male_resident(self):
assert validate_luhn("7801015800182") is True
class TestParseIdNumber:
def test_male_citizen(self):
result = parse_id_number("8801015800082")
assert result["date_of_birth"] == "1988-01-01"
assert result["sex"] == "Male"
assert result["citizenship"] == "SA Citizen"
assert result["is_valid"] is True
def test_female(self):
result = parse_id_number("9202204720083")
assert result["sex"] == "Female"
assert result["is_valid"] is True
def test_permanent_resident(self):
result = parse_id_number("7801015800182")
assert result["citizenship"] == "Permanent Resident"
def test_invalid_id(self):
result = parse_id_number("1234567890123")
assert result["is_valid"] is False
def test_empty(self):
result = parse_id_number("")
assert result["is_valid"] is False
assert result["date_of_birth"] is None
class TestExtractIdNumber:
def test_find_in_clean_text(self):
blocks = [
([[0, 0], [100, 0], [100, 20], [0, 20]], "IDENTITY NUMBER", 0.95),
([[0, 25], [200, 25], [200, 45], [0, 45]], "8801015800082", 0.92),
]
id_num, conf = extract_id_number(blocks)
assert id_num == "8801015800082"
assert conf > 0
def test_find_with_spaces(self):
blocks = [
([[0, 0], [200, 0], [200, 20], [0, 20]], "8801 0158 0008 2", 0.85),
]
id_num, conf = extract_id_number(blocks)
assert id_num == "8801015800082"
def test_find_with_dashes(self):
blocks = [
([[0, 0], [200, 0], [200, 20], [0, 20]], "880101-5800-082", 0.85),
]
id_num, conf = extract_id_number(blocks)
assert id_num == "8801015800082"
def test_no_valid_id(self):
blocks = [
([[0, 0], [100, 0], [100, 20], [0, 20]], "REPUBLIC OF SOUTH AFRICA", 0.95),
([[0, 25], [100, 25], [100, 45], [0, 45]], "IDENTITY CARD", 0.90),
]
id_num, conf = extract_id_number(blocks)
assert id_num is None
class TestCrossValidate:
def test_all_match(self):
fields = {
"date_of_birth": "01/01/1988",
"sex": "Male",
"citizenship_status": "SA Citizen",
}
parsed = {
"date_of_birth": "1988-01-01",
"sex": "Male",
"citizenship": "SA Citizen",
"is_valid": True,
}
result = cross_validate(fields, parsed)
assert result["luhn_valid"] is True
assert result["dob_cross_check"] is True
assert result["gender_cross_check"] is True
assert result["citizenship_cross_check"] is True
def test_gender_mismatch(self):
fields = {"sex": "Female"}
parsed = {"sex": "Male", "is_valid": True}
result = cross_validate(fields, parsed)
assert result["gender_cross_check"] is False
class TestNormalizeDate:
def test_iso_format(self):
assert _normalize_date("1988-01-01") == "1988-01-01"
def test_slash_format(self):
assert _normalize_date("01/01/1988") == "1988-01-01"
def test_dash_dmy(self):
assert _normalize_date("01-01-1988") == "1988-01-01"
def test_invalid(self):
assert _normalize_date("not a date") is None
class TestDateTransposition:
def test_exact_match(self):
result = _check_date_transposition("01/01/1988", "1988-01-01")
assert result["exact_match"] is True
assert result["transposed_match"] is False
def test_transposed_day_month(self):
# OCR reads "1988-05-03" but ID actually encodes 1988-03-05 (day/month swapped)
result = _check_date_transposition("1988-05-03", "1988-03-05")
assert result["transposed_match"] is True
assert result["corrected_date"] == "1988-03-05"
def test_no_match(self):
result = _check_date_transposition("15/06/1990", "1988-01-01")
assert result["exact_match"] is False
assert result["transposed_match"] is False
def test_none_input(self):
result = _check_date_transposition(None, "1988-01-01")
assert result["exact_match"] is False
def test_unambiguous_date_not_transposed(self):
# Day=25 can't be a month, so transposition shouldn't match
result = _check_date_transposition("25/06/1990", "1990-06-25")
assert result["exact_match"] is True
class TestValidateIdForCountry:
def test_sa_valid(self):
result = validate_id_for_country("8801015800082", "ZAF")
assert result["valid"] is True
assert result["method"] == "luhn"
def test_sa_invalid_luhn(self):
result = validate_id_for_country("8801015800083", "ZAF")
assert result["valid"] is False
def test_nigeria_valid(self):
result = validate_id_for_country("12345678901", "NGA")
assert result["valid"] is True
assert result["method"] == "regex"
def test_nigeria_invalid_too_short(self):
result = validate_id_for_country("1234567890", "NGA")
assert result["valid"] is False
def test_kenya_valid(self):
result = validate_id_for_country("12345678", "KEN")
assert result["valid"] is True
def test_zimbabwe_valid(self):
result = validate_id_for_country("12345678A12", "ZWE")
assert result["valid"] is True
def test_zimbabwe_invalid(self):
result = validate_id_for_country("12345678112", "ZWE")
assert result["valid"] is False
def test_uganda_valid(self):
result = validate_id_for_country("AB123456789012", "UGA")
assert result["valid"] is True
def test_zambia_valid(self):
result = validate_id_for_country("1234567890", "ZMB")
assert result["valid"] is True
def test_unknown_country_passes(self):
result = validate_id_for_country("ANY123", "XXX")
assert result["valid"] is True
assert result["method"] == "no_rules"
def test_empty_id_fails(self):
result = validate_id_for_country("", "ZAF")
assert result["valid"] is False
class TestCrossValidateTransposition:
def test_transposed_dob_auto_corrected(self):
# OCR extracted date has day/month swapped compared to ID-encoded date
fields = {
"date_of_birth": "1988-05-03",
"sex": "Male",
}
parsed = {
"date_of_birth": "1988-03-05",
"sex": "Male",
"citizenship": "SA Citizen",
"is_valid": True,
}
result = cross_validate(fields, parsed)
assert result["dob_cross_check"] is True
assert result["dob_transposed"] is True
# The field should be corrected in-place to match the ID
assert fields["date_of_birth"] == "1988-03-05"