From cb01cc9e07f92bcfeb9ef0e05f55486504a8b985 Mon Sep 17 00:00:00 2001 From: Renne Rocha Date: Thu, 2 Jul 2026 08:34:28 -0300 Subject: [PATCH 1/5] =?UTF-8?q?refactor:=20Utilizar=20regex=20para=20valid?= =?UTF-8?q?ar=20o=20conte=C3=BAdo=20da=20string=20do=20CNPJ?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- brutils/cnpj.py | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/brutils/cnpj.py b/brutils/cnpj.py index f0b27cd..0b73eeb 100644 --- a/brutils/cnpj.py +++ b/brutils/cnpj.py @@ -1,7 +1,12 @@ +import random +import re from itertools import chain from random import choices, randint from string import ascii_uppercase, digits + +CNPJ_RE = re.compile(r"^[A-Z0-9]{12}[0-9]{2}") + # FORMATTING ############ @@ -31,7 +36,7 @@ def sieve(dirty: str) -> str: backward compatibility. """ - return "".join(filter(lambda char: char not in "./-", dirty)) + return re.sub(r"[\.\/\-]", "", dirty) def remove_symbols(dirty: str) -> str: @@ -84,13 +89,9 @@ def display(cnpj: str) -> str | None: backward compatibility. """ - if ( - len(cnpj) != 14 - or not _is_alphanumeric(cnpj[:12]) - or not cnpj[12:].isdigit() - or len(set(cnpj)) == 1 - ): + if not re.search(CNPJ_RE, cnpj): return None + return "{}.{}.{}/{}-{}".format( cnpj[:2], cnpj[2:5], cnpj[5:8], cnpj[8:12], cnpj[12:] ) @@ -177,14 +178,9 @@ def validate(cnpj: str) -> bool: This method should not be used in new code and is only provided for backward compatibility. """ - - if ( - len(cnpj) != 14 - or not _is_alphanumeric(cnpj[:12]) - or not cnpj[12:].isdigit() - or len(set(cnpj)) == 1 - ): + if not re.search(CNPJ_RE, cnpj): return False + return all( _hashdigit(cnpj, i + 13) == int(v) for i, v in enumerate(cnpj[12:]) ) From 8819c791f3ac63e6e9951ae1a60962dd69e8772a Mon Sep 17 00:00:00 2001 From: Renne Rocha Date: Thu, 2 Jul 2026 08:37:07 -0300 Subject: [PATCH 2/5] =?UTF-8?q?refactor:=20Simplifica=20o=20processo=20de?= =?UTF-8?q?=20gera=C3=A7=C3=A3o=20de=20CNPJs=20de=20teste?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- brutils/cnpj.py | 46 +++++----------------------------------------- tests/test_cnpj.py | 25 ++++++++++++++++--------- 2 files changed, 21 insertions(+), 50 deletions(-) diff --git a/brutils/cnpj.py b/brutils/cnpj.py index 0b73eeb..ed83ac3 100644 --- a/brutils/cnpj.py +++ b/brutils/cnpj.py @@ -1,10 +1,8 @@ import random import re from itertools import chain -from random import choices, randint from string import ascii_uppercase, digits - CNPJ_RE = re.compile(r"^[A-Z0-9]{12}[0-9]{2}") # FORMATTING @@ -96,6 +94,7 @@ def display(cnpj: str) -> str | None: cnpj[:2], cnpj[2:5], cnpj[5:8], cnpj[8:12], cnpj[12:] ) + return all(char in (digits + ascii_uppercase) for char in cnpj) def format_cnpj(cnpj: str) -> str | None: """ @@ -131,27 +130,6 @@ def format_cnpj(cnpj: str) -> str | None: ############ -def _is_alphanumeric(cnpj: str) -> bool: - """ - Checks whether all characters are digits or uppercase letters. - - Args: - cnpj (str): The CNPJ string to be validated. - - Returns: - bool: True if all characters are either digits or uppercase letters, - False otherwise. - - Example: - >>> _is_alphanumeric("035ABC1400Z142") - True - >>> _is_alphanumeric("0011-22200013!") - False - """ - - return all(char in (digits + ascii_uppercase) for char in cnpj) - - def validate(cnpj: str) -> bool: """ Validates a CNPJ (Brazilian Company Registration Number) by comparing its @@ -235,26 +213,12 @@ def generate(branch: int | str = 1, alphanumeric: bool = False) -> str: >>> generate(branch="AB12", alphanumeric=True) "NX9K79E2AB1200" """ + final_branch = str(branch)[:4].zfill(4) - if alphanumeric: - branch = str(branch) - branch = branch[:4] if len(branch) >= 4 else branch.zfill(4) - branch = ( - "0001" - if branch == "0000" or not _is_alphanumeric(branch) - else branch - ) - base = "".join(choices(digits * 3 + ascii_uppercase, k=8)) + branch - - return base + _checksum(base) - - branch = int(branch) - branch %= 10000 - branch += int(branch == 0) - branch = str(branch).zfill(4) - base = str(randint(0, 99999999)).zfill(8) + branch + character_choices = digits + ascii_uppercase if alphanumeric else digits + cnpj_base = "".join(random.sample(character_choices, 8)) + final_branch - return base + _checksum(base) + return cnpj_base + _checksum(cnpj_base) def _hashdigit(cnpj: str, position: int) -> int: diff --git a/tests/test_cnpj.py b/tests/test_cnpj.py index c0464a0..4e90a61 100644 --- a/tests/test_cnpj.py +++ b/tests/test_cnpj.py @@ -1,10 +1,10 @@ +import re from unittest import TestCase, main from unittest.mock import patch from brutils.cnpj import ( _checksum, _hashdigit, - _is_alphanumeric, display, format_cnpj, generate, @@ -28,18 +28,12 @@ def test_sieve(self): def test_display(self): self.assertEqual(display("00000000000109"), "00.000.000/0001-09") + self.assertIsNone(display("00000000000000", "00.000.000/0000-00")) self.assertEqual(display("12ABC34501DE35"), "12.ABC.345/01DE-35") self.assertIsNone(display("12ABC34501DEAA")) - self.assertIsNone(display("00000000000000")) self.assertIsNone(display("0000000000000")) self.assertIsNone(display("0000000000000a")) - def test__is_alphanumeric(self): - self.assertIs(_is_alphanumeric("12ABC34501DE35"), True) - self.assertIs(_is_alphanumeric("12345678910111"), True) - self.assertIs(_is_alphanumeric("123456a78b10C1"), False) - self.assertIs(_is_alphanumeric("12.ABC.345/01DE-35"), False) - def test_validate(self): self.assertIs(validate("34665388000161"), True) self.assertIs(validate("12ABC34501DE35"), True) @@ -85,10 +79,23 @@ def test_generate(self): self.assertIsNotNone(display(generate())) self.assertIs(validate(generate(branch=1234)), True) + def test_generate_with_branch(self): + test_branches = [ + (1, "0001"), + (2, "0002"), + ("A", "000A"), + ("ABCD", "ABCD"), + (1234, "1234"), + (12345, "1234"), + ] + for branch, final_branch in test_branches: + generated_cnpj = generate(branch=branch) + self.assertTrue(re.search(rf"[0-9]{{8}}{final_branch}[0-9]{{2}}", generated_cnpj), msg=(final_branch, generated_cnpj)) + def test_generate_alphanumeric(self): for _ in range(10_000): generated = generate(alphanumeric=True) - self.assertIs(validate(generated), True) + self.assertIs(validate(generated), True, msg=generated) self.assertIsNotNone(display(generated)) self.assertIs( validate(generate(branch="1234", alphanumeric=True)), True From ba0a8ead8d47009ff0a19b30a09b8f87da957f1d Mon Sep 17 00:00:00 2001 From: Renne Rocha Date: Thu, 2 Jul 2026 14:44:53 -0300 Subject: [PATCH 3/5] =?UTF-8?q?Corre=C3=A7=C3=A3o=20do=20assert=20de=20tes?= =?UTF-8?q?te?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_cnpj.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_cnpj.py b/tests/test_cnpj.py index 4e90a61..d3fc924 100644 --- a/tests/test_cnpj.py +++ b/tests/test_cnpj.py @@ -28,7 +28,7 @@ def test_sieve(self): def test_display(self): self.assertEqual(display("00000000000109"), "00.000.000/0001-09") - self.assertIsNone(display("00000000000000", "00.000.000/0000-00")) + self.assertEqual(display("00000000000000"), "00.000.000/0000-00") self.assertEqual(display("12ABC34501DE35"), "12.ABC.345/01DE-35") self.assertIsNone(display("12ABC34501DEAA")) self.assertIsNone(display("0000000000000")) From 903a5a7a2955dcbcb8ef30dea6da4112a68f2e01 Mon Sep 17 00:00:00 2001 From: Renne Rocha Date: Thu, 2 Jul 2026 14:47:55 -0300 Subject: [PATCH 4/5] Corrigindo linter --- brutils/cnpj.py | 1 + tests/test_cnpj.py | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/brutils/cnpj.py b/brutils/cnpj.py index ed83ac3..a18afdb 100644 --- a/brutils/cnpj.py +++ b/brutils/cnpj.py @@ -96,6 +96,7 @@ def display(cnpj: str) -> str | None: return all(char in (digits + ascii_uppercase) for char in cnpj) + def format_cnpj(cnpj: str) -> str | None: """ Formats a CNPJ (Brazilian Company Registration Number) string for visual diff --git a/tests/test_cnpj.py b/tests/test_cnpj.py index d3fc924..4f858a2 100644 --- a/tests/test_cnpj.py +++ b/tests/test_cnpj.py @@ -90,7 +90,12 @@ def test_generate_with_branch(self): ] for branch, final_branch in test_branches: generated_cnpj = generate(branch=branch) - self.assertTrue(re.search(rf"[0-9]{{8}}{final_branch}[0-9]{{2}}", generated_cnpj), msg=(final_branch, generated_cnpj)) + self.assertTrue( + re.search( + rf"[0-9]{{8}}{final_branch}[0-9]{{2}}", generated_cnpj + ), + msg=(final_branch, generated_cnpj), + ) def test_generate_alphanumeric(self): for _ in range(10_000): From 18e5d4957ba34a7a21730e9868bd4eb6b5c98216 Mon Sep 17 00:00:00 2001 From: Renne Rocha Date: Thu, 2 Jul 2026 14:56:10 -0300 Subject: [PATCH 5/5] =?UTF-8?q?Removendo=20c=C3=B3digo=20morto?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- brutils/cnpj.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/brutils/cnpj.py b/brutils/cnpj.py index a18afdb..5d54398 100644 --- a/brutils/cnpj.py +++ b/brutils/cnpj.py @@ -94,8 +94,6 @@ def display(cnpj: str) -> str | None: cnpj[:2], cnpj[2:5], cnpj[5:8], cnpj[8:12], cnpj[12:] ) - return all(char in (digits + ascii_uppercase) for char in cnpj) - def format_cnpj(cnpj: str) -> str | None: """