Skip to content

Commit

Permalink
Merge pull request #5 from hudsonbrendon/feature/cnh
Browse files Browse the repository at this point in the history
Feature/cnh
  • Loading branch information
hudsonbrendon authored Jun 8, 2024
2 parents 30516e7 + a9fbfd1 commit da3fc31
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 0 deletions.
1 change: 1 addition & 0 deletions pydantic_br_validator/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@
CEPDigits = str
else:
from .fields.cnpj_field import * # noqa
from .fields.cnh_field import * # noqa
from .fields.cpf_field import * # noqa
from .fields.cep_field import * # noqa
16 changes: 16 additions & 0 deletions pydantic_br_validator/fields/cnh_field.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from ..validators.cnh_validator import CNHValidator
from .base_field import BaseDigits

__all__ = ["CNH"]


class CNH(BaseDigits):
"""
Only Accepts string of CNH with digits.
Attributes:
number (str): CNH number.
"""

format = "cnh"
Validator = CNHValidator
50 changes: 50 additions & 0 deletions pydantic_br_validator/validators/cnh_validator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import re

from .base_validator import FieldValidator

__all__ = ["CNHValidator"]


class CNHValidator(FieldValidator):
def __init__(self, cnh: str) -> None:
self.cnh = cnh

def validate(self) -> bool:
cnh = re.sub("[^0-9]", "", str(self.cnh))

if len(set(cnh)) == 1:
return False

if len(cnh) != 11:
return False

first_digit = self._validate_first_digit(cnh)
second_digit = self._validate_second_digit(cnh)
return cnh[9] == first_digit and cnh[10] == second_digit

def _validate_first_digit(self, cnh: str) -> str:
self.dsc = 0
sum = 0

for i in range(9, 0, -1):
sum += int(cnh[9 - i]) * i

first_digit = sum % 11
if first_digit >= 10:
first_digit, self.dsc = 0, 2
return str(first_digit)

def _validate_second_digit(self, cnh: str) -> str:
sum = 0

for i in range(1, 10):
sum += int(cnh[i - 1]) * i

rest = sum % 11

second_digit = rest - self.dsc
if second_digit < 0:
second_digit += 11
if second_digit >= 10:
second_digit = 0
return str(second_digit)
101 changes: 101 additions & 0 deletions tests/test_cnh.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
from string import ascii_letters

import pytest
from pydantic import BaseModel, ValidationError

from pydantic_br_validator import (
CNH,
FieldDigitError,
FieldInvalidError,
FieldTypeError,
)

cnh_mock = [
"49761142867",
"15706519597",
"18820839790",
"93025633607",
"22255370700",
"74487688509",
"83002264521",
"21671642456",
"36407284795",
"93017746007",
]


@pytest.fixture
def person():
class Person(BaseModel):
cnh: CNH

yield Person


@pytest.mark.parametrize("cnh", cnh_mock)
def test_must_be_string(person, cnh):
p1 = person(cnh=cnh)
assert isinstance(p1.cnh, str)


@pytest.mark.parametrize("cnh", cnh_mock)
def test_must_accept_only_numbers(person, cnh):
p1 = person(cnh=cnh)
assert p1.cnh == cnh


@pytest.mark.parametrize("cnh", cnh_mock)
def test_must_fail_when_use_another_type(person, cnh):
with pytest.raises(ValidationError) as e:
person(cnh=int(cnh))
assert FieldTypeError.msg_template in str(e.value)


@pytest.mark.parametrize("cnh", cnh_mock)
def test_must_fail_when_use_invalid_cnh(person, cnh):
with pytest.raises(ValidationError) as e:
invalid_cnh = cnh[:5] + cnh[6:]
person(cnh=invalid_cnh)
assert FieldInvalidError.msg_template in str(e.value)


@pytest.mark.parametrize("cnh", cnh_mock)
def test_must_fail_when_use_digits_count_above_cnh(person, cnh):
with pytest.raises(ValidationError) as e:
person(cnh=cnh * 2)
assert FieldInvalidError.msg_template in str(e.value)


@pytest.mark.parametrize("cnh", cnh_mock)
def test_must_fail_when_use_digits_count_below_cnh(person, cnh):
with pytest.raises(ValidationError) as e:
person(cnh=cnh[:5])
assert FieldInvalidError.msg_template in str(e.value)


@pytest.mark.parametrize(
"cnh",
[
"00000000000",
"11111111111",
"22222222222",
"33333333333",
"44444444444",
"55555555555",
"66666666666",
"77777777777",
"88888888888",
"99999999999",
],
)
def test_must_fail_when_use_sequecial_digits(person, cnh):
with pytest.raises(ValidationError) as e:
person(cnh=cnh)
assert FieldInvalidError.msg_template in str(e.value)


def test_must_fail_when_not_use_only_digits(person):
with pytest.raises(ValidationError) as e:
letters = ascii_letters[:11]
person(cnh=letters)
assert FieldDigitError.msg_template in str(e.value)

0 comments on commit da3fc31

Please sign in to comment.