diff --git a/.coveragerc b/.coveragerc index 2dc3d8b..22f9ed1 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,8 +1,11 @@ [run] source = validate_docbr disable_warnings = no-data-collected +omit = validate_docbr/exceptions.py [report] -fail_under = 97.50 +fail_under = 98.00 precision = 2 show_missing = True +exclude_lines = + raise FunctionNotImplementedError diff --git a/.githooks/pre-push b/.githooks/pre-push index 29f859c..21096ca 100755 --- a/.githooks/pre-push +++ b/.githooks/pre-push @@ -1,23 +1,14 @@ -#!/bin/sh +#!/bin/bash -warning() { - # shellcheck disable=2028 - echo "\\e[1;33m$1" +handle_failure() { + echo "❌ Error: $1 failed!" + exit 1 } -alertMessages() { - warning "One or more $1 are failing." - warning "Please fix those $1 before pushing your branch" -} +echo "🔍 Running lint checks..." +make lint || handle_failure "lint" -if make lint; then - if make test-coverage; then - exit 0 - else - alertMessages "unit tests" - exit 1 - fi -else - alertMessages "linter checks" - exit 1 -fi +echo "🧪 Running test coverage..." +make test-coverage || handle_failure "test-coverage" + +echo "✅ All checks passed successfully!" diff --git a/Makefile b/Makefile index b526348..ec0b250 100644 --- a/Makefile +++ b/Makefile @@ -33,6 +33,11 @@ lint: && lint-shell-script \ && lint-python" +.PHONY: lint-fix +lint-fix: + @docker pull ${DOCKER_IMAGE_LINTER} + @docker run --rm -v ${ROOT}:/app ${DOCKER_IMAGE_LINTER} "lint-python-fix" + .PHONY: test test: @docker compose run --rm -v ${ROOT}:/app \ @@ -41,7 +46,7 @@ test: .PHONY: test-coverage test-coverage: - @docker compose run --rm -v ${ROOT}:/app \ + @docker compose run --rm -T -v ${ROOT}:/app \ --name ${TEST_CONTAINER_NAME} ${APP_NAME} \ /bin/bash -c "pytest --cov=validate_docbr/" diff --git a/tests/test_CNH.py b/tests/test_CNH.py index c9d38a1..eb6e02e 100644 --- a/tests/test_CNH.py +++ b/tests/test_CNH.py @@ -4,34 +4,41 @@ class TestCnh(unittest.TestCase): - """Testar a classe CNH.""" + """Testa a classe CNH.""" def setUp(self): - """Inicia novo objeto em todo os testes.""" self.cnh = docbr.CNH() - def test_generate_validate(self): - """Verifica os métodos de geração e validação de documento.""" - # generate_list + def test_generate_list_with_validate_list(self): + # Given + number_of_documents = 10 + number_of_documents_expected = number_of_documents * 2 + + # When cnhs = ( - self.cnh.generate_list(1) - + self.cnh.generate_list(1, mask=True) - + self.cnh.generate_list(1, mask=True, repeat=True) + self.cnh.generate_list(number_of_documents) + + self.cnh.generate_list(number_of_documents, mask=True) ) - self.assertIsInstance(cnhs, list) - self.assertTrue(len(cnhs) == 3) + validated_cnhs = self.cnh.validate_list(cnhs) - # validate_list - cnhs_validates = self.cnh.validate_list(cnhs) - self.assertTrue(sum(cnhs_validates) == 3) + # Then + self.assertIsInstance(cnhs, list) + self.assertTrue(len(cnhs) == number_of_documents_expected) + self.assertTrue(sum(validated_cnhs) == number_of_documents_expected) def test_mask(self): - """Verifica se o método mask funciona corretamente.""" - masked_cpf = self.cnh.mask('11122233344') - self.assertEqual(masked_cpf, '111 222 333 44') + # Given + doc = '11122233344' + doc_expected = '111 222 333 44' + + # When + masked_cpf = self.cnh.mask(doc) + + # Then + self.assertEqual(masked_cpf, doc_expected) def test_special_case(self): - """ Verifica os casos especiais de CNH """ + # Given cases = [ ('00000000000', False), ('AAAAAAAAAAA', False), @@ -42,5 +49,10 @@ def test_special_case(self): ('10764125809', True), ('77625261946', True) ] + + # When for cnh, is_valid in cases: - self.assertEqual(self.cnh.validate(cnh), is_valid) + doc_validated = self.cnh.validate(cnh) + + # Then + self.assertEqual(doc_validated, is_valid) diff --git a/tests/test_CNPJ.py b/tests/test_CNPJ.py index f254f7a..2805b51 100644 --- a/tests/test_CNPJ.py +++ b/tests/test_CNPJ.py @@ -4,71 +4,80 @@ class TestCnpj(unittest.TestCase): - """Testar a classe CNPJ.""" + """Testa a classe CNPJ.""" def setUp(self): - """Inicia novo objeto em todo os testes.""" self.cnpj = docbr.CNPJ() - def test_generate_validate(self): - """Verifica os métodos de geração e validação de documento.""" - # generate_list - cnpjs = self.cnpj.generate_list(5000) \ - + self.cnpj.generate_list(5000, mask=True) \ - + self.cnpj.generate_list(5000, mask=True, repeat=True) + def test_generate_list_with_validate_list(self): + # Given + number_of_documents = 10 + number_of_documents_expected = number_of_documents * 2 + + # When + cnpjs = self.cnpj.generate_list(number_of_documents) \ + + self.cnpj.generate_list(number_of_documents, mask=True) + validated_cnpjs = self.cnpj.validate_list(cnpjs) + + # Then self.assertIsInstance(cnpjs, list) - self.assertTrue(len(cnpjs) == 15000) - - # validate_list - cnpjs_validates = self.cnpj.validate_list(cnpjs) - self.assertTrue(sum(cnpjs_validates) == 15000) + self.assertTrue(len(cnpjs) == number_of_documents_expected) + self.assertTrue(sum(validated_cnpjs) == number_of_documents_expected) def test_mask(self): - """Verifica se o método mask funciona corretamente.""" - masked_cnpj = self.cnpj.mask('11222333444455') - self.assertEqual(masked_cnpj, '11.222.333/4444-55') + # Given + doc = '11222333444455' + doc_expected = '11.222.333/4444-55' + + # When + masked_cnpj = self.cnpj.mask(doc) + + # Then + self.assertEqual(masked_cnpj, doc_expected) def test_special_case(self): - """Verifica os casos especiais de CNPJ.""" + # Given cases = [ ('00000-000/0000', False), ('AAAA0AAAAAAA2AAAAAA', False), ('74600269000145', True), ] + + # When for cnpj, is_valid in cases: - self.assertEqual(self.cnpj.validate(cnpj), is_valid) + doc_validated = self.cnpj.validate(cnpj) + + # Then + self.assertEqual(doc_validated, is_valid) def test_validate_success(self): - """Testar o método validate do CNPJ.""" - # GIVEN + # Given doc = '74600269000145' - # WHEN - validate_return = self.cnpj.validate(doc) + # When + doc_validated = self.cnpj.validate(doc) - # THEN - self.assertTrue(validate_return) + # Then + self.assertTrue(doc_validated) def test_validate_wrong_input(self): - """Testar o método validate do CNPJ em caso de input errado.""" - # GIVEN + # Given doc = '74600269000145_' - # WHEN + # When validate_return = self.cnpj.validate(doc) - # THEN + # Then self.assertFalse(validate_return) def test_validate_wrong_length(self): - """Testar o método validate do CNPJ em caso de tamanho inválido.""" - # GIVEN + # Given doc = '746002690001450' - # WHEN + # When validate_return = self.cnpj.validate(doc) - # THEN + # Then self.assertFalse(validate_return) def test_alphanumeric(self): diff --git a/tests/test_CNS.py b/tests/test_CNS.py index c55921b..e1f91e2 100644 --- a/tests/test_CNS.py +++ b/tests/test_CNS.py @@ -4,35 +4,47 @@ class TestCns(unittest.TestCase): - """Testar a classe CNS.""" + """Testa a classe CNS.""" def setUp(self): - """Inicia novo objeto em todo os testes.""" self.cns = docbr.CNS() - def test_generate_validate(self): - """Verifica os métodos de geração e validação de documento.""" - # generate_list - cnss = self.cns.generate_list(5000) \ - + self.cns.generate_list(5000, mask=True) \ - + self.cns.generate_list(5000, mask=True, repeat=True) - self.assertIsInstance(cnss, list) - self.assertTrue(len(cnss) == 15000) + def test_generate_list_with_validate_list(self): + # Given + number_of_documents = 10 + number_of_documents_expected = number_of_documents * 2 + + # When + cnss = self.cns.generate_list(number_of_documents) \ + + self.cns.generate_list(number_of_documents, mask=True) + validated_cnss = self.cns.validate_list(cnss) - # validate_list - cnss_validates = self.cns.validate_list(cnss) - self.assertTrue(sum(cnss_validates) == 15000) + # Then + self.assertIsInstance(cnss, list) + self.assertTrue(len(cnss) == number_of_documents_expected) + self.assertTrue(sum(validated_cnss) == number_of_documents_expected) def test_mask(self): - """Verifica se o método mask funciona corretamente.""" - masked_cns = self.cns.mask('111222233334444') - self.assertEqual(masked_cns, '111 2222 3333 4444') + # Given + doc = '111222233334444' + doc_expected = '111 2222 3333 4444' + + # When + masked_cns = self.cns.mask(doc) + + # Then + self.assertEqual(masked_cns, doc_expected) def test_special_case(self): - """ Verifica os casos especiais de CNS """ + # Given cases = [ ('AAAAAAAAAAA', False), ('', False), ] + + # When for cns, is_valid in cases: - self.assertEqual(self.cns.validate(cns), is_valid) + doc_validated = self.cns.validate(cns) + + # Then + self.assertEqual(doc_validated, is_valid) diff --git a/tests/test_CPF.py b/tests/test_CPF.py index 4707d4d..8c512db 100644 --- a/tests/test_CPF.py +++ b/tests/test_CPF.py @@ -4,32 +4,39 @@ class TestCpf(unittest.TestCase): - """Testar a classe CPF.""" + """Testa a classe CPF.""" def setUp(self): - """Inicia novo objeto em todo os testes.""" self.cpf = docbr.CPF() - def test_generate_validate(self): - """Verifica os métodos de geração e validação de documento.""" - # generate_list - cpfs = self.cpf.generate_list(5000) \ - + self.cpf.generate_list(5000, mask=True) \ - + self.cpf.generate_list(5000, mask=True, repeat=True) - self.assertIsInstance(cpfs, list) - self.assertTrue(len(cpfs) == 15000) + def test_generate_list_with_validate_list(self): + # Given + number_of_documents = 10 + number_of_documents_expected = number_of_documents * 2 + + # When + cpfs = self.cpf.generate_list(number_of_documents) \ + + self.cpf.generate_list(number_of_documents, mask=True) + validated_cpfs = self.cpf.validate_list(cpfs) - # validate_list - cpfs_validates = self.cpf.validate_list(cpfs) - self.assertTrue(sum(cpfs_validates) == 15000) + # Then + self.assertIsInstance(cpfs, list) + self.assertTrue(len(cpfs) == number_of_documents_expected) + self.assertTrue(sum(validated_cpfs) == number_of_documents_expected) def test_mask(self): - """Verifica se o método mask funciona corretamente.""" - masked_cpf = self.cpf.mask('11122233344') - self.assertEqual(masked_cpf, '111.222.333-44') + # Given + doc = '11122233344' + doc_expected = '111.222.333-44' + + # When + masked_cpf = self.cpf.mask(doc) + + # Then + self.assertEqual(masked_cpf, doc_expected) def test_special_case(self): - """ Verifica os casos especiais de CPF """ + # Given cpfs_repeated_digits = [ '000.000.000-00', '111.111.111-11', @@ -42,28 +49,45 @@ def test_special_case(self): '888.888.888-88', '999.999.999-99', ] - # Entrada consideradas invalidas - for cpf in cpfs_repeated_digits: - self.assertFalse(self.cpf.validate(cpf)) - # Entrada consideradas validas - self.cpf.repeated_digits = True - for cpf in cpfs_repeated_digits: - self.assertTrue(self.cpf.validate(cpf)) - cases = [ ('AAA.AAA.AAA+AA', False), ('04255791000144', False), ] + + # When + for cpf in cpfs_repeated_digits: + doc_validated = self.cpf.validate(cpf) + + # Then + self.assertFalse(doc_validated) + + # When + self.cpf.repeated_digits = True + for cpf in cpfs_repeated_digits: + doc_validated = self.cpf.validate(cpf) + + # Then + self.assertTrue(doc_validated) + + # When for cpf, is_valid in cases: - self.assertEqual(self.cpf.validate(cpf), is_valid) + doc_validated = self.cpf.validate(cpf) + + # Then + self.assertEqual(doc_validated, is_valid) def test_add_leading_zeros(self): - """Verifica se o método de adicionar zeros à esquerda funciona corretamente.""" + # Given cases = [ - ('123456789', False), # 9 digitos - ('12345678901', False), # 11 digitos - ('1234567', False), # 7 digitos - ('9380826044', True) # cpf valido + ('123456789', False), + ('12345678901', False), + ('1234567', False), + ('9380826044', True) ] - for cpf_input, is_valid in cases: - self.assertEqual(self.cpf.validate(cpf_input), is_valid) + + # When + for cpf, is_valid in cases: + doc_validated = self.cpf.validate(cpf) + + # Then + self.assertEqual(doc_validated, is_valid) diff --git a/tests/test_Certidao.py b/tests/test_Certidao.py index 48458bd..ffe73a5 100644 --- a/tests/test_Certidao.py +++ b/tests/test_Certidao.py @@ -4,34 +4,39 @@ class TestCertidao(unittest.TestCase): - """Testar a classe Certidao.""" + """Testa a classe Certidao.""" def setUp(self): - """Inicia novo objeto em todo os testes.""" self.certidao = docbr.Certidao() - def test_generate_validate(self): - """Verifica os métodos de geração e validação de documento.""" - # generate_list - certidoes = self.certidao.generate_list(5000) \ - + self.certidao.generate_list(5000, mask=True) - self.assertIsInstance(certidoes, list) - self.assertTrue(len(certidoes) == 10000) + def test_generate_list_with_validate_list(self): + # Given + number_of_documents = 10 + number_of_documents_expected = number_of_documents * 2 + + # When + certidoes = self.certidao.generate_list(number_of_documents) \ + + self.certidao.generate_list(number_of_documents, mask=True) + validated_certidoes = self.certidao.validate_list(certidoes) - # validate_list - certidoes_validates = self.certidao.validate_list(certidoes) - self.assertTrue(sum(certidoes_validates) == 10000) + # Then + self.assertIsInstance(certidoes, list) + self.assertTrue(len(certidoes) == number_of_documents_expected) + self.assertTrue(sum(validated_certidoes) == number_of_documents_expected) def test_mask(self): - """Verifica se o método mask funciona corretamente.""" + # Given + doc = '10453901552013100012021000012321' + doc_expected = '104539.01.55.2013.1.00012.021.0000123-21' - masked_certidao = self.certidao.mask( - '10453901552013100012021000012321') - self.assertEqual( - masked_certidao, '104539.01.55.2013.1.00012.021.0000123-21') + # When + masked_certidao = self.certidao.mask(doc) + + # Then + self.assertEqual(masked_certidao, doc_expected) def test_special_case(self): - """ Verifica os casos especiais de Certidão """ + # Given cases = [ ('3467875434578764345789654', False), ('AAAAAAAAAAA', False), @@ -39,5 +44,10 @@ def test_special_case(self): ('27610201552018226521370659786633', True), ('27610201552018226521370659786630', False), ] + + # When for certidao, is_valid in cases: - self.assertEqual(self.certidao.validate(certidao), is_valid) + doc_validated = self.certidao.validate(certidao) + + # Then + self.assertEqual(doc_validated, is_valid) diff --git a/tests/test_DocumentBase.py b/tests/test_DocumentBase.py new file mode 100644 index 0000000..84869e8 --- /dev/null +++ b/tests/test_DocumentBase.py @@ -0,0 +1,45 @@ +import unittest + +import validate_docbr as docbr + + +class DocumentBaseFaker(docbr.DocumentBase): + def validate(self, doc: str = '') -> bool: + pass + + def generate(self, mask: bool = False) -> str: + return "test" + + def mask(self, doc: str = '') -> str: + pass + + +class TestDocumentBase(unittest.TestCase): + """Testa a classe DocumentBase.""" + + def setUp(self): + self.document_base_faker = DocumentBaseFaker() + + def test_generate_list_empty(self): + # Given + number_of_documents = 0 + number_of_documents_expected = 0 + + # When + docs = self.document_base_faker.generate_list(number_of_documents) + + # Then + self.assertIsInstance(docs, list) + self.assertTrue(len(docs) == number_of_documents_expected) + + def test_validate_input(self): + # Given + input_value = "1234567890" + validated_input_value_expected = True + + # When + validated_input_value = self.document_base_faker._validate_input(input_value) + + # Then + self.assertIsInstance(validated_input_value, bool) + self.assertTrue(validated_input_value == validated_input_value_expected) diff --git a/tests/test_PIS.py b/tests/test_PIS.py index b4904d5..7fdecfb 100644 --- a/tests/test_PIS.py +++ b/tests/test_PIS.py @@ -4,42 +4,48 @@ class TestPis(unittest.TestCase): - """Testar a classe PIS/NIS/PASEP/NIT.""" + """Testa a classe PIS/NIS/PASEP/NIT.""" def setUp(self): - """Inicia novo objeto em todo os testes.""" self.pis = docbr.PIS() - def test_generate_validate(self): - """Verifica os métodos de geração e validação de documento.""" - # generate_list - piss = self.pis.generate_list(5000) \ - + self.pis.generate_list(5000, mask=True) \ - + self.pis.generate_list(5000, mask=True, repeat=True) - self.assertIsInstance(piss, list) - self.assertTrue(len(piss) == 15000) + def test_generate_list_with_validate_list(self): + # Given + number_of_documents = 10 + number_of_documents_expected = number_of_documents * 2 + + # When + piss = self.pis.generate_list(number_of_documents) \ + + self.pis.generate_list(number_of_documents, mask=True) + validated_piss = self.pis.validate_list(piss) - # validate_list - piss_validates = self.pis.validate_list(piss) - self.assertTrue(sum(piss_validates) == 15000) + # Then + self.assertIsInstance(piss, list) + self.assertTrue(len(piss) == number_of_documents_expected) + self.assertTrue(sum(validated_piss) == number_of_documents_expected) def test_mask(self): - """Verifica se o método mask funciona corretamente.""" - masked_pis = self.pis.mask('23992734770') - self.assertEqual(masked_pis, '239.92734.77-0') + # Given + doc = '23992734770' + doc_expected = '239.92734.77-0' - masked_pis = self.pis.mask('93999998770') - self.assertEqual(masked_pis, '939.99998.77-0') + # When + masked_pis = self.pis.mask(doc) - masked_pis = self.pis.mask('03953333770') - self.assertEqual(masked_pis, '039.53333.77-0') + # Then + self.assertEqual(masked_pis, doc_expected) def test_special_case(self): - """ Verifica os casos especiais de PIS """ + # Given cases = [ ('3467875434578764345789654', False), ('AAAAAAAAAAA', False), ('', False), ] + + # When for pis, is_valid in cases: - self.assertEqual(self.pis.validate(pis), is_valid) + doc_validated = self.pis.validate(pis) + + # Then + self.assertEqual(doc_validated, is_valid) diff --git a/tests/test_RENAVAM.py b/tests/test_RENAVAM.py index 5d76187..0f7c401 100644 --- a/tests/test_RENAVAM.py +++ b/tests/test_RENAVAM.py @@ -4,34 +4,41 @@ class TestRenavam(unittest.TestCase): - """Testar a classe RENAVAM.""" + """Testa a classe RENAVAM.""" def setUp(self): - """Inicia novo objeto em todo os testes.""" self.renavam = docbr.RENAVAM() - def test_generate_validate(self): - """Verifica os métodos de geração e validação de documento.""" - # generate_list + def test_generate_list_with_validate_list(self): + # Given + number_of_documents = 10 + number_of_documents_expected = number_of_documents * 2 + + # When renavams = ( - self.renavam.generate_list(1) - + self.renavam.generate_list(1, mask=True) - + self.renavam.generate_list(1, mask=True, repeat=True) + self.renavam.generate_list(number_of_documents) + + self.renavam.generate_list(number_of_documents, mask=True) ) - self.assertIsInstance(renavams, list) - self.assertTrue(len(renavams) == 3) + validated_renavams = self.renavam.validate_list(renavams) - # validate_list - renavams_validates = self.renavam.validate_list(renavams) - self.assertTrue(sum(renavams_validates) == 3) + # Then + self.assertIsInstance(renavams, list) + self.assertTrue(len(renavams) == number_of_documents_expected) + self.assertTrue(sum(validated_renavams) == number_of_documents_expected) def test_mask(self): - """Verifica se o método mask funciona corretamente.""" - masked_renavam = self.renavam.mask('13824652268') - self.assertEqual(masked_renavam, '1382465226-8') + # Given + doc = '13824652268' + doc_expected = '1382465226-8' + + # When + masked_renavam = self.renavam.mask(doc) + + # Then + self.assertEqual(masked_renavam, doc_expected) def test_special_case(self): - """ Verifica os casos especiais de RENAVAM """ + # Given cases = [ ('3467875434578764345789654', False), ('', False), @@ -47,5 +54,10 @@ def test_special_case(self): ('04598137389', True), ('05204907510', True), ] + + # When for renavam, is_valid in cases: - self.assertEqual(self.renavam.validate(renavam), is_valid) + doc_validated = self.renavam.validate(renavam) + + # Then + self.assertEqual(doc_validated, is_valid) diff --git a/tests/test_TituloEleitoral.py b/tests/test_TituloEleitoral.py index b7f1c83..24f72e8 100644 --- a/tests/test_TituloEleitoral.py +++ b/tests/test_TituloEleitoral.py @@ -5,43 +5,52 @@ class TestTituloEleitoral(unittest.TestCase): def setUp(self): - """ Inicia novo objeto em todo os testes """ self.titulo_eleitoral = docbr.TituloEleitoral() - def test_generate(self): - """ Verifica se o método generate """ - # generate, generate(mask=True) - titulos = [self.titulo_eleitoral.generate() for i in range(10000)] \ - + [self.titulo_eleitoral.generate(mask=True) for i in range(10000)] - self.assertIsInstance(titulos, list) - self.assertTrue(len(titulos) == 20000) - - def test_generate_list(self): - """ Verifica se o método generate_list """ - # generate_list - titulo_eleitoral = self.titulo_eleitoral.generate_list(10000) \ - + self.titulo_eleitoral.generate_list(10000, True) \ - + self.titulo_eleitoral.generate_list(10000, True, True) - self.assertIsInstance(titulo_eleitoral, list) - self.assertTrue(len(titulo_eleitoral) == 30000) - - def test_validate(self): - """ Verifica se o método validate """ - # validate - for titulo in self.titulo_eleitoral.generate_list(10000): - self.assertTrue(self.titulo_eleitoral.validate(titulo)) - - def test_mask_returns_correctly_formatted_string(self): - masked_titulo = self.titulo_eleitoral.mask('123123123123') - - self.assertEqual(masked_titulo, '1231 2312 3123') + def test_generate_list_with_validate_list(self): + # Given + number_of_documents = 10 + number_of_documents_expected = number_of_documents * 2 + + # When + titulos_eleitorais = self.titulo_eleitoral.generate_list(number_of_documents) \ + + self.titulo_eleitoral.generate_list( + number_of_documents, + True + ) + validated_titulos_eleitorais = self.titulo_eleitoral.validate_list( + titulos_eleitorais + ) + + # Then + self.assertIsInstance(titulos_eleitorais, list) + self.assertTrue(len(titulos_eleitorais) == number_of_documents_expected) + self.assertTrue( + sum(validated_titulos_eleitorais) == number_of_documents_expected + ) + + def test_mask(self): + # Given + doc = '123123123123' + doc_expected = '1231 2312 3123' + + # When + masked_titulo = self.titulo_eleitoral.mask(doc) + + # Then + self.assertEqual(masked_titulo, doc_expected) def test_special_case(self): - """ Verifica os casos especiais de Titulo de Eleitor """ + # Given cases = [ ('3467875434578764345789654', False), ('AAAAAAAAAAA', False), ('', False), ] + + # When for titulo_eleitoral, is_valid in cases: - self.assertEqual(self.titulo_eleitoral.validate(titulo_eleitoral), is_valid) + doc_validated = self.titulo_eleitoral.validate(titulo_eleitoral) + + # Then + self.assertEqual(doc_validated, is_valid) diff --git a/tests/test_generic.py b/tests/test_generic.py index 2cbb0dd..f3459d5 100644 --- a/tests/test_generic.py +++ b/tests/test_generic.py @@ -6,15 +6,15 @@ def get_random_number_str(length): numbers = '0123456789' - return ''.join(random.choice(numbers) for i in range(length)) + return ''.join(random.choice(numbers) for _ in range(length)) class TestValidateDocs(unittest.TestCase): """Testa a função validate_docs""" - def test_correct_argument(self): - """Testa a função quando os argumentos estão corretos""" - DocClasses = [ + def test_validate_docs(self): + # Given + doc_classes = [ docbr.CPF, docbr.CNH, docbr.CNPJ, @@ -22,26 +22,32 @@ def test_correct_argument(self): docbr.PIS, docbr.TituloEleitoral, ] - documents = [] right_answers = [] - - for DocClass in DocClasses: - # Documentos válidos - tuples = [(DocClass, doc) for doc in DocClass().generate_list(200)] - documents += tuples - right_answers += [True] * len(tuples) - - # Documentos aleatórios - len_doc = len(DocClass().generate()) - for _ in range(200): + number_of_documents = 100 + + # When + for doc_class in doc_classes: + valid_documents = [ + (doc_class, doc) + for doc in doc_class().generate_list(number_of_documents) + ] + documents += valid_documents + right_answers += [True] * len(valid_documents) + + len_doc = len(doc_class().generate()) + for _ in range(number_of_documents): random_doc = get_random_number_str(len_doc) - documents += [(DocClass, random_doc)] - right_answers += [DocClass().validate(random_doc)] - + documents += [(doc_class, random_doc)] + right_answers += [doc_class().validate(random_doc)] + + # Then self.assertEqual(docbr.validate_docs(documents), right_answers) - def test_incorrect_argument(self): - """Test a função quando os argumentos estão incorretos""" + def test_validate_docs_type_error(self): + # Given + cpf = [('cpf', docbr.CPF().generate())] + + # When-Then with self.assertRaises(TypeError): - docbr.validate_docs([('cpf', docbr.CPF().generate())]) + docbr.validate_docs(cpf) diff --git a/validate_docbr/CNH.py b/validate_docbr/CNH.py index bb2a603..fdb4958 100644 --- a/validate_docbr/CNH.py +++ b/validate_docbr/CNH.py @@ -1,10 +1,9 @@ from random import sample -from typing import Union -from .BaseDoc import BaseDoc +from validate_docbr.DocumentBase import DocumentBase -class CNH(BaseDoc): +class CNH(DocumentBase): """Classe referente ao Carteira Nacional de Habilitação (CNH).""" def __init__(self): @@ -38,7 +37,7 @@ def mask(self, doc: str = '') -> str: """Coloca a máscara de CNH na variável doc.""" return f"{doc[:3]} {doc[3:6]} {doc[6:9]} {doc[9:]}" - def _generate_first_digit(self, doc: Union[str, list]) -> str: + def _generate_first_digit(self, doc: str | list[str]) -> str: """Gerar o primeiro dígito verificador da CNH.""" self.dsc = 0 sum = 0 @@ -51,7 +50,7 @@ def _generate_first_digit(self, doc: Union[str, list]) -> str: first_value, self.dsc = 0, 2 return str(first_value) - def _generate_second_digit(self, doc: Union[str, list]) -> str: + def _generate_second_digit(self, doc: str | list[str]) -> str: """Gerar o segundo dígito verificador da CNH.""" sum = 0 diff --git a/validate_docbr/CNPJ.py b/validate_docbr/CNPJ.py index 1b81172..2e1f0c1 100644 --- a/validate_docbr/CNPJ.py +++ b/validate_docbr/CNPJ.py @@ -1,11 +1,10 @@ import string from random import sample -from typing import Union -from .BaseDoc import BaseDoc +from validate_docbr.DocumentBase import DocumentBase -class CNPJ(BaseDoc): +class CNPJ(DocumentBase): """Classe referente ao Cadastro Nacional da Pessoa Jurídica (CNPJ).""" def __init__(self): @@ -48,7 +47,7 @@ def mask(self, doc: str = '') -> str: """Coloca a máscara de CNPJ na variável doc.""" return f"{doc[:2]}.{doc[2:5]}.{doc[5:8]}/{doc[8:12]}-{doc[-2:]}" - def _generate_first_digit(self, doc: Union[str, list]) -> str: + def _generate_first_digit(self, doc: str | list[str]) -> str: """Gerar o primeiro dígito verificador do CNPJ.""" sum = 0 @@ -60,7 +59,7 @@ def _generate_first_digit(self, doc: Union[str, list]) -> str: return str(sum) - def _generate_second_digit(self, doc: Union[str, list]) -> str: + def _generate_second_digit(self, doc: str | list[str]) -> str: """Gerar o segundo dígito verificador do CNPJ.""" sum = 0 diff --git a/validate_docbr/CNS.py b/validate_docbr/CNS.py index 242833e..175c6bb 100644 --- a/validate_docbr/CNS.py +++ b/validate_docbr/CNS.py @@ -1,9 +1,9 @@ from random import sample -from .BaseDoc import BaseDoc +from validate_docbr.DocumentBase import DocumentBase -class CNS(BaseDoc): +class CNS(DocumentBase): """Classe referente ao Cartão Nacional de Saúde (CNS).""" def __init__(self): diff --git a/validate_docbr/CPF.py b/validate_docbr/CPF.py index 458458f..73ee1de 100644 --- a/validate_docbr/CPF.py +++ b/validate_docbr/CPF.py @@ -1,10 +1,9 @@ from random import sample -from typing import List -from .BaseDoc import BaseDoc +from validate_docbr.DocumentBase import DocumentBase -class CPF(BaseDoc): +class CPF(DocumentBase): """Classe referente ao Cadastro de Pessoas Físicas (CPF).""" def __init__(self, repeated_digits: bool = False): @@ -72,7 +71,7 @@ def _generate_second_digit(self, doc: list) -> str: return str(sum) - def _check_repeated_digits(self, doc: List[str]) -> bool: + def _check_repeated_digits(self, doc: list[str]) -> bool: """Verifica se é um CPF com números repetidos. Exemplo: 111.111.111-11""" return len(set(doc)) == 1 diff --git a/validate_docbr/Certidao.py b/validate_docbr/Certidao.py index 296f21d..cfa9bcd 100644 --- a/validate_docbr/Certidao.py +++ b/validate_docbr/Certidao.py @@ -1,9 +1,9 @@ from random import sample -from .BaseDoc import BaseDoc +from validate_docbr.DocumentBase import DocumentBase -class Certidao(BaseDoc): +class Certidao(DocumentBase): """Classe referente a Certidão de Nascimento/Casamento/Óbito.""" def __init__(self): diff --git a/validate_docbr/BaseDoc.py b/validate_docbr/DocumentBase.py similarity index 74% rename from validate_docbr/BaseDoc.py rename to validate_docbr/DocumentBase.py index 1f1f98d..7b6fefe 100644 --- a/validate_docbr/BaseDoc.py +++ b/validate_docbr/DocumentBase.py @@ -1,54 +1,58 @@ from abc import ABC, abstractmethod -from typing import List +from validate_docbr.exceptions import FunctionNotImplementedError -class BaseDoc(ABC): + +class DocumentBase(ABC): """Classe base para todas as classes referentes a documentos.""" @abstractmethod def validate(self, doc: str = "") -> bool: """Método para validar o documento desejado.""" - pass - - def validate_list(self, docs: List[str]) -> List[bool]: - """Método para validar uma lista de documentos desejado.""" - return [self.validate(doc) for doc in docs] + raise FunctionNotImplementedError("validate") @abstractmethod def generate(self, mask: bool = False) -> str: """Método para gerar um documento válido.""" - pass + raise FunctionNotImplementedError("generate") + + @abstractmethod + def mask(self, doc: str = '') -> str: + """Mascara o documento enviado""" + raise FunctionNotImplementedError("mask") + + def validate_list(self, docs: list[str]) -> list[bool]: + """Método para validar uma lista de documentos desejado.""" + return [self.validate(doc) for doc in docs] def generate_list( - self, n: int = 1, mask: bool = False, repeat: bool = False + self, + number_of_documents: int = 1, + mask: bool = False, + repeat: bool = False ) -> list: """Gerar uma lista do mesmo documento.""" doc_list = [] - if n <= 0: + if number_of_documents <= 0: return doc_list - for _ in range(n): + for _ in range(number_of_documents): doc_list.append(self.generate(mask)) while not repeat: doc_set = set(doc_list) unique_values = len(doc_set) - if unique_values < n: + if unique_values < number_of_documents: doc_list = list(doc_set) + self.generate_list( - (n - unique_values), mask, repeat + (number_of_documents - unique_values), mask, repeat ) else: repeat = True return doc_list - @abstractmethod - def mask(self, doc: str = "") -> str: - """Mascara o documento enviado""" - pass - def _only_digits(self, doc: str = "") -> str: """Remove os outros caracteres que não sejam dígitos.""" return "".join([x for x in doc if x.isdigit()]) @@ -58,7 +62,7 @@ def _only_digits_and_letters(self, doc: str = "") -> str: return "".join([x for x in doc if x.isdigit() or x.isalpha()]) def _validate_input( - self, input: str, valid_characters: List = None, allow_letters: bool = False + self, input: str, valid_characters: list = None, allow_letters: bool = False ) -> bool: """Validar input. Caso ele possua apenas dígitos (e, opcionalmente, letras) e caracteres diff --git a/validate_docbr/PIS.py b/validate_docbr/PIS.py index 3c17ea4..c5135d0 100644 --- a/validate_docbr/PIS.py +++ b/validate_docbr/PIS.py @@ -1,10 +1,9 @@ from random import sample -from typing import Union -from .BaseDoc import BaseDoc +from validate_docbr.DocumentBase import DocumentBase -class PIS(BaseDoc): +class PIS(DocumentBase): """Classe referente ao PIS/NIS/PASEP/NIT.""" def __init__(self): @@ -36,7 +35,7 @@ def mask(self, doc: str = '') -> str: """Coloca a máscara de PIS/NIS/PASEP/NIT na variável doc.""" return f"{doc[:3]}.{doc[3:8]}.{doc[8:10]}-{doc[10:]}" - def _generate_digit(self, doc: Union[str, list]) -> str: + def _generate_digit(self, doc: str | list[str]) -> str: """Gerar o dígito verificador do PIS/NIS/PASEP/NIT.""" multipliers = [3, 2, 9, 8, 7, 6, 5, 4, 3, 2] summation = 0 diff --git a/validate_docbr/RENAVAM.py b/validate_docbr/RENAVAM.py index 805cb32..ba765d2 100644 --- a/validate_docbr/RENAVAM.py +++ b/validate_docbr/RENAVAM.py @@ -1,10 +1,9 @@ from random import sample -from typing import Union -from .BaseDoc import BaseDoc +from validate_docbr.DocumentBase import DocumentBase -class RENAVAM(BaseDoc): +class RENAVAM(DocumentBase): """Classe referente ao Registro Nacional de Veículos Automotores (RENAVAM).""" def __init__(self): @@ -36,7 +35,7 @@ def mask(self, doc: str = '') -> str: """Coloca a máscara de Renavam na variável doc.""" return f"{doc[:10]}-{doc[10]}" - def _generate_last_digit(self, doc: Union[str, list]) -> str: + def _generate_last_digit(self, doc: str | list[str]) -> str: """Gerar o dígito verificador do Renavam.""" sequence = '3298765432' sum = 0 diff --git a/validate_docbr/TituloEleitoral.py b/validate_docbr/TituloEleitoral.py index 78f187a..fdea4bb 100644 --- a/validate_docbr/TituloEleitoral.py +++ b/validate_docbr/TituloEleitoral.py @@ -1,10 +1,9 @@ from random import sample -from typing import List -from .BaseDoc import BaseDoc +from validate_docbr.DocumentBase import DocumentBase -class TituloEleitoral(BaseDoc): +class TituloEleitoral(DocumentBase): """Classe referente ao Título eleitoral""" def __init__(self): @@ -58,7 +57,7 @@ def mask(self, doc: str = '') -> str: """Mascara o documento enviado""" return f'{doc[0:4]} {doc[4:8]} {doc[8:]}' - def _compute_first_check_digit(self, doc_digits: List[int]) -> int: + def _compute_first_check_digit(self, doc_digits: list[int]) -> int: """Método que calcula o primeiro dígito verificador.""" doc_digits_to_consider = doc_digits[self.first_check_digit_doc_slice] terms = [ @@ -78,7 +77,7 @@ def _compute_first_check_digit(self, doc_digits: List[int]) -> int: def _compute_second_check_digit( self, - doc_digits: List[int], + doc_digits: list[int], first_check_digit: int ) -> int: """Método que calcula o segundo dígito verificador.""" diff --git a/validate_docbr/__init__.py b/validate_docbr/__init__.py index ac18fed..e33090c 100644 --- a/validate_docbr/__init__.py +++ b/validate_docbr/__init__.py @@ -1,16 +1,16 @@ -from .BaseDoc import BaseDoc from .Certidao import Certidao from .CNH import CNH from .CNPJ import CNPJ from .CNS import CNS from .CPF import CPF +from .DocumentBase import DocumentBase from .generic import validate_docs from .PIS import PIS from .RENAVAM import RENAVAM from .TituloEleitoral import TituloEleitoral __all__ = [ - 'BaseDoc', + 'DocumentBase', 'CPF', 'CNPJ', 'CNH', diff --git a/validate_docbr/exceptions.py b/validate_docbr/exceptions.py new file mode 100644 index 0000000..d9f642b --- /dev/null +++ b/validate_docbr/exceptions.py @@ -0,0 +1,3 @@ +class FunctionNotImplementedError(NotImplementedError): + def __init__(self, function_name): + super().__init__(f"The `{function_name}` function must be implemented.") diff --git a/validate_docbr/generic.py b/validate_docbr/generic.py index 9dd71a7..b30da56 100644 --- a/validate_docbr/generic.py +++ b/validate_docbr/generic.py @@ -1,15 +1,14 @@ import inspect -from typing import List, Tuple, Type -from .BaseDoc import BaseDoc +from validate_docbr.DocumentBase import DocumentBase -def validate_docs(documents: List[Tuple[Type[BaseDoc], str]] = list): +def validate_docs(documents: list[tuple[type[DocumentBase], str]] = list): """Recebe uma lista de tuplas (classe, valor) e a valida""" validations = [] for doc in documents: - if not inspect.isclass(doc[0]) or not issubclass(doc[0], BaseDoc): + if not inspect.isclass(doc[0]) or not issubclass(doc[0], DocumentBase): raise TypeError( "O primeiro índice da tupla deve ser uma classe de documento!" )