diff --git a/README.md b/README.md index 858628b..6065357 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,40 @@ cliente = Cliente( pprint(cliente.dict()) ``` +# CEP + +```python +from pprint import pprint + +from pydantic import BaseModel + +from pydantic_br_validator import CEP, CEPDigits, CEPMask + + +class Endereco(BaseModel): + rua: str + numero: str + bairro: str + cidade: str + cep: CEP # aceita CEP válidos com ou sem máscara + cep_mask: CEPMask # aceita CEP válido apenas com máscara + cep_digits: CEPDigits # aceita CEP válido apnas com dígitos + + +endereco = Endereco( + rua="Avenida Paulista", + numero="100", + bairro="Aclimação", + cidade="São Paulo", + cep="01310100", + cep_mask="01310-100", + cep_digits="01310100", +) + + +pprint(endereco.dict()) +``` + # Licença Este projeto está licenciado sob os termos da licença do [MIT licença](https://en.wikipedia.org/wiki/MIT_License) diff --git a/pydantic_br_validator/__init__.py b/pydantic_br_validator/__init__.py index 60a1bc8..b5c9c6b 100644 --- a/pydantic_br_validator/__init__.py +++ b/pydantic_br_validator/__init__.py @@ -20,6 +20,10 @@ Certidao = str CertidaoMask = str CertidaoDigits = str + CEP = str + CEPMask = str + CEPDigits = str else: from .fields.cnpj_field import * # noqa from .fields.cpf_field import * # noqa + from .fields.cep_field import * # noqa diff --git a/pydantic_br_validator/fields/cep_field.py b/pydantic_br_validator/fields/cep_field.py new file mode 100644 index 0000000..e5e0083 --- /dev/null +++ b/pydantic_br_validator/fields/cep_field.py @@ -0,0 +1,45 @@ +from ..validators.cep_validator import CEPValidator +from .base_field import Base, BaseDigits, BaseMask + +__all__ = [ + "CEP", + "CEPMask", + "CEPDigits", +] + + +class CEP(Base): + """ + Only Accepts string of CEP. + + Attributes: + number (str): CEP number. + + """ + + format = "cep" + Validator = CEPValidator + + +class CEPMask(BaseMask): + """ + Only Accepts string of CEP with mask. + + Attributes: + number (str): CEP number. + """ + + format = "cep" + Validator = CEPValidator + + +class CEPDigits(BaseDigits): + """ + Only Accepts string of CEP without mask. + + Attributes: + number (str): CEP number. + """ + + format = "cep" + Validator = CEPValidator diff --git a/pydantic_br_validator/validators/cep_validator.py b/pydantic_br_validator/validators/cep_validator.py new file mode 100644 index 0000000..bae6da9 --- /dev/null +++ b/pydantic_br_validator/validators/cep_validator.py @@ -0,0 +1,19 @@ +from .base_validator import FieldMaskValidator + +__all__ = ["CEPValidator"] + + +class CEPValidator(FieldMaskValidator): + def __init__(self, cep) -> None: + self.cep = cep + + def validate_mask(self) -> bool: + if len(self.cep) == 9: + return self.cep[5] == "-" + + def validate(self) -> bool: + cep = self.cep.replace("-", "") + + if len(cep) != 8: + return False + return True diff --git a/tests/test_cep.py b/tests/test_cep.py new file mode 100644 index 0000000..21aaf11 --- /dev/null +++ b/tests/test_cep.py @@ -0,0 +1,112 @@ +import pytest +from pydantic import BaseModel, ValidationError + +from pydantic_br_validator import ( + CEP, + CEPDigits, + CEPMask, +) +from pydantic_br_validator.field_erros import FieldInvalidError, FieldTypeError + +cep_mock = [ + ("59151650", "59151-650", "59151650"), + ("57602660", "57602-660", "57602660"), + ("78735819", "78735-819", "78735819"), + ("69315235", "69315-235", "69315235"), + ("15056133", "15056-133", "15056133"), + ("60761971", "60761-971", "60761971"), + ("72760022", "72760-022", "72760022"), + ("79020160", "79020-160", "79020160"), + ("64040670", "64040-670", "64040670"), + ("76965520", "76965-520", "76965520"), +] + + +@pytest.fixture +def address(): + class Address(BaseModel): + cep: CEP + cep_mask: CEPMask + cep_digits: CEPDigits + + yield Address + + +@pytest.mark.parametrize("cep, cep_mask, cep_digits", cep_mock) +def test_must_cep_string(address, cep, cep_mask, cep_digits): + address_1 = address(cep=cep, cep_mask=cep_mask, cep_digits=cep_digits) + assert isinstance(address_1.cep, str) + assert isinstance(address_1.cep_mask, str) + assert isinstance(address_1.cep_digits, str) + + +@pytest.mark.parametrize("cep, cep_mask, cep_digits", cep_mock) +def test_must_accept_only_numbers(address, cep, cep_mask, cep_digits): + address_1 = address(cep=cep, cep_mask=cep_mask, cep_digits=cep_digits) + assert address_1.cep == cep + assert address_1.cep_mask == cep_mask + assert address_1.cep_digits == cep_digits + + +@pytest.mark.parametrize("cep, cep_mask, cep_digits", cep_mock) +def test_must_fail_when_use_another_type(address, cep, cep_mask, cep_digits): + with pytest.raises(ValidationError) as e: + address( + cep=int(cep), + cep_mask=str(cep_mask), + cep_digits=int(cep_digits), + ) + assert FieldTypeError.msg_template in str(e.value) + + +@pytest.mark.parametrize("cep, cep_mask, cep_digits", cep_mock) +def test_must_fail_when_use_invalid_cep(address, cep, cep_mask, cep_digits): + with pytest.raises(ValidationError) as e: + invalid_cep = cep[:5] + cep[6:] + invalid_cep_mask = cep_mask[:5] + cep_mask[6:] + invalid_cep_digits = cep_digits[:5] + cep_digits[6:] + address( + cep=invalid_cep, + cep_mask=invalid_cep_mask, + cep_digits=invalid_cep_digits, + ) + assert FieldInvalidError.msg_template in str(e.value) + + +@pytest.mark.parametrize("cep, cep_mask, cep_digits", cep_mock) +def test_must_fail_when_use_digits_count_above_cep(address, cep, cep_mask, cep_digits): + with pytest.raises(ValidationError) as e: + address(cep=cep * 2, cep_mask=cep_mask * 2, cep_digits=cep_digits * 2) + assert FieldInvalidError.msg_template in str(e.value) + + +@pytest.mark.parametrize("cep, cep_mask, cep_digits", cep_mock) +def test_must_fail_when_use_digits_count_below_cep(address, cep, cep_mask, cep_digits): + with pytest.raises(ValidationError) as e: + address(cep=cep[:5], cep_mask=cep_mask[:5], cep_digits=cep_digits[:5]) + assert FieldInvalidError.msg_template in str(e.value) + + +@pytest.mark.parametrize( + "cep, cep_mask, cep_digits", + [ + ("000000000000", "00000-000", "00000000"), + ("111111111111", "11111-111", "11111111"), + ("222222222222", "22222-222", "22222222"), + ("333333333333", "33333-333", "33333333"), + ("444444444444", "44444-444", "44444444"), + ("555555555555", "55555-555", "55555555"), + ("666666666666", "66666-666", "66666666"), + ("777777777777", "77777-777", "77777777"), + ("888888888888", "88888-888", "88888888"), + ("999999999999", "99999-999", "99999999"), + ], +) +def test_must_fail_when_use_sequecial_digits(address, cep, cep_mask, cep_digits): + with pytest.raises(ValidationError) as e: + address( + cep=cep, + cep_mask=cep_mask, + cep_digits=cep_digits, + ) + assert FieldInvalidError.msg_template in str(e.value)