-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #5 from hudsonbrendon/feature/cnh
Feature/cnh
- Loading branch information
Showing
4 changed files
with
168 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |