Repository: brazilian-utils/brutils-python (2023-06-27 ~ 2023-07-08)

problem_statement: Criar mais testes para o utilitário is_valid_cnpj
Criar mais casos de teste para o utilitário is_valid_cnpj.

Alguns exemplos:
* testar quando o input não é uma string
* testar quando o input não tem o tamanho correto
* testar quando o input não contém apenas dígitos

Você pode utilizar o exemplo do CPF como base: https://github.com/brazilian-utils/brutils-python/blob/1b12ebfcbdef3715a1969139549d1038074f42aa/tests/test_cpf.py#L64

O algoritmo considerado para validação é este: https://www.geradorcnpj.com/algoritmo_do_cnpj.htm

Lembre-se também que você pode consultar como foi feita a implementação na biblioteca JS: https://github.com/brazilian-utils/brazilian-utils/tree/main/src/utilities/cnpj
date: 2023-06-27
patch: diff --git a/brutils/cnpj.py b/brutils/cnpj.py
index efca157..913a41e 100644
--- a/brutils/cnpj.py
+++ b/brutils/cnpj.py
@@ -93,7 +93,7 @@ def is_valid(cnpj):  # type: (str) -> bool
     Using this method name to match with the js library  api.
     Using the same method to ensure backwards compatibility.
     """
-    return validate(cnpj)
+    return isinstance(cnpj, str) and validate(cnpj)
 
 
 def generate(branch=1):  # type: (int) -> str

test_patch: diff --git a/tests/test_cnpj.py b/tests/test_cnpj.py
index 08b5cb8..9b26234 100644
--- a/tests/test_cnpj.py
+++ b/tests/test_cnpj.py
@@ -50,31 +50,55 @@ class CNPJ(TestCase):
         assert format_cnpj("0000000000000a") is None
         assert format_cnpj("0000000000000") is None
 
-    def test_hashdigit(self):
-        assert hashdigit("00000000000000", 13) == 0
-        assert hashdigit("00000000000000", 14) == 0
-        assert hashdigit("52513127000292", 13) == 9
-        assert hashdigit("52513127000292", 14) == 9
-
-    def test_checksum(self):
-        assert checksum("00000000000000") == "00"
-        assert checksum("52513127000299") == "99"
-
     def test_validate(self):
         assert validate("34665388000161")
         assert not validate("52599927000100")
         assert not validate("00000000000")
 
     def test_is_valid(self):
+        # When CNPJ is not string, returns False
+        assert not is_valid(1)
+
+        # When CNPJ's len is different of 14, returns False
+        assert not is_valid("1")
+
+        # When CNPJ does not contain only digits, returns False
+        assert not is_valid("1112223334445-")
+
+        # When CNPJ has only the same digit, returns false
+        assert not is_valid("11111111111111")
+
+        # When rest_1 is lt 2 and the 13th digit is not 0, returns False
+        assert not is_valid("1111111111315")
+
+        # When rest_1 is gte 2 and the 13th digit is not (11 - rest), returns False
+        assert not is_valid("1111111111115")
+
+        # When rest_2 is lt 2 and the 14th digit is not 0, returns False
+        assert not is_valid("11111111121205")
+
+        # When rest_2 is gte 2 and the 14th digit is not (11 - rest), returns False
+        assert not is_valid("11111111113105")
+
+        # When CNPJ is valid
         assert is_valid("34665388000161")
-        assert not is_valid("52599927000100")
-        assert not is_valid("00000000000")
+        assert is_valid("01838723000127")
 
     def test_generate(self):
         for i in range(1000):
             assert validate(generate())
             assert display(generate()) is not None
 
+    def test_hashdigit(self):
+        assert hashdigit("00000000000000", 13) == 0
+        assert hashdigit("00000000000000", 14) == 0
+        assert hashdigit("52513127000292", 13) == 9
+        assert hashdigit("52513127000292", 14) == 9
+
+    def test_checksum(self):
+        assert checksum("00000000000000") == "00"
+        assert checksum("52513127000299") == "99"
+
 
 if __name__ == "__main__":
     main()


----

problem_statement: Melhorar format_cnpj e o seus testes
1. Chamar a função `is_valid` e formatar o cnpj, sem utilizar o método display.
Código referência:
https://github.com/brazilian-utils/brutils-python/blob/756f1267b78ed597a7b65fd344a8ae2c6bf6a0b9/brutils/cpf.py#L38-L48

2. Melhorar os testes da função format_cnpj:

Código referência:
https://github.com/brazilian-utils/brutils-python/blob/756f1267b78ed597a7b65fd344a8ae2c6bf6a0b9/tests/test_cpf.py#L47-L57
date: 2023-07-01
patch: diff --git a/brutils/cnpj.py b/brutils/cnpj.py
index 913a41e..0b7fd73 100644
--- a/brutils/cnpj.py
+++ b/brutils/cnpj.py
@@ -38,7 +38,13 @@ def format_cnpj(cnpj):  # type: (str) -> str
     Will format an adequately formatted numbers-only CNPJ string,
     adding in standard formatting visual aid symbols for display.
     """
-    return display(cnpj)
+
+    if not is_valid(cnpj):
+        return None
+
+    return "{}.{}.{}/{}-{}".format(
+        cnpj[:2], cnpj[2:5], cnpj[5:8], cnpj[8:12], cnpj[12:14]
+    )
 
 
 # CALCULATORS
diff --git a/brutils/cpf.py b/brutils/cpf.py
index ea93e75..e030ed7 100644
--- a/brutils/cpf.py
+++ b/brutils/cpf.py
@@ -45,7 +45,7 @@ def format_cpf(cpf):  # type: (str) -> str
     if not is_valid(cpf):
         return None
 
-    return "{}.{}.{}-{}".format(cpf[:3], cpf[3:6], cpf[6:9], cpf[9:])
+    return "{}.{}.{}-{}".format(cpf[:3], cpf[3:6], cpf[6:9], cpf[9:11])
 
 
 # OPERATIONS

test_patch: diff --git a/tests/test_cnpj.py b/tests/test_cnpj.py
index 9b26234..25e7375 100644
--- a/tests/test_cnpj.py
+++ b/tests/test_cnpj.py
@@ -45,10 +45,18 @@ class CNPJ(TestCase):
         assert display("0000000000000") is None
 
     def test_format_cnpj(self):
-        assert format_cnpj("00000000000109") == "00.000.000/0001-09"
-        assert format_cnpj("00000000000000") is None
-        assert format_cnpj("0000000000000a") is None
-        assert format_cnpj("0000000000000") is None
+        with patch("brutils.cnpj.is_valid", return_value=True) as mock_is_valid:
+            # When cnpj is_valid, returns formatted cnpj
+            assert format_cnpj("01838723000127") == "01.838.723/0001-27"
+
+        # Checks if function is_valid_cnpj is called
+        mock_is_valid.assert_called_once_with("01838723000127")
+
+        with patch(
+            "brutils.cnpj.is_valid", return_value=False
+        ) as mock_is_valid:
+            # When cnpj isn't valid, returns None
+            assert format_cnpj("01838723000127") is None
 
     def test_validate(self):
         assert validate("34665388000161")


----

problem_statement: Formatação de CEPs
Implementar um utilitário que recebe um CEP apenas com dígitos e retorna o CEP com símbolos (ponto e traço)
date: 2023-07-08
patch: diff --git a/README.md b/README.md
index 39db492..686d27d 100644
--- a/README.md
+++ b/README.md
@@ -44,12 +44,16 @@ False
   - [format_cnpj](#format_cnpj)
   - [remove_symbols_cnpj](#remove_symbols_cnpj)
   - [generate_cnpj](#generate_cnpj)
+- [CEP](#cep)
+  - [is_valid_cep](#is_valid_cep)
+  - [format_cep](#format_cep)
+  - [generate_cep](#generate_cep)  
 
 ## CPF
 
 ### is_valid_cpf
 
-Verifica se o CPF é valido.
+Verifica se o CPF é valido. Apenas números, formatados como string. Não verifica se o CPF existe.
 
 ```python
 >>> from brutils import is_valid_cpf
@@ -90,7 +94,7 @@ Gera um CPF válido aleatório.
 
 ### is_valid_cnpj
 
-Verifica se o CNPJ é valido. Apenas números, formatados como string.
+Verifica se o CNPJ é valido. Apenas números, formatados como string. Não verifica se o CNPJ existe.
 
 ```python
 >>> from brutils import is_valid_cnpj
@@ -128,6 +132,39 @@ Gera um CNPJ válido aleatório.
 '34665388000161'
 ```
 
+## CEP
+
+### is_valid_cep
+
+Verifica se o CEP é valido. Apenas números, formatados como string. Não verifica se o CEP existe.
+
+```python
+>>> from brutils import is_valid_cep
+>>> is_valid_cep('01310200')
+True
+```
+
+### format_cep
+
+Formata o CEP. Retorna None se o CEP for inválido.
+
+```python
+>>> from brutils import format_cep
+>>> format_cep('01310200')
+'01310-200'
+```
+
+### generate_cep
+
+Gera um CEP válido aleatório.
+
+```python
+>>> from brutils import generate_cep
+>>> generate_cep()
+'77520503'
+```
+
+
 ## Contributing
 
 Sua colaboração é sempre bem-vinda! Preparamos o [arquivo contributing][contributing] pra te ajudar nos primeiros passos. Toda ajuda conta! Sinta-se livre para criar novas [GitHub issues][github-issues] e interagir aqui.
diff --git a/README_EN.md b/README_EN.md
index 59d7119..9d9422f 100644
--- a/README_EN.md
+++ b/README_EN.md
@@ -44,12 +44,16 @@ False
   - [format_cnpj](#format_cnpj)
   - [remove_symbols_cnpj](#remove_symbols_cnpj)
   - [generate_cnpj](#generate_cnpj)
+- [CEP](#cep)
+  - [is_valid_cep](#is_valid_cep)
+  - [format_cep](#format_cep)   
+  - [generate_cep](#generate_cep)
 
 ## CPF
 
 ### is_valid_cpf
 
-Check if CPF is valid.
+Check if CPF is valid. Numbers only, formatted as strings. Does not check if CPF exists.
 
 ```python
 >>> from brutils import is_valid_cpf
@@ -93,7 +97,7 @@ Generate a valid random CPF.
 
 ### is_valid_cnpj
 
-Check if CNPJ is valid. Numbers only, formatted as strings.
+Check if CNPJ is valid. Numbers only, formatted as strings. Does not check if CNPJ exists.
 
 ```python
 >>> from brutils import is_valid_cnpj
@@ -133,6 +137,38 @@ Generate a valid random CNPJ.
 '34665388000161'
 ```
 
+## CEP
+
+### is_valid_cep
+
+Check if CEP is valid. Numbers only, formatted as strings. Does not check if CEP exists.
+
+```python
+>>> from brutils import is_valid_cep
+>>> is_valid_cep('01310200')
+True
+```
+
+### format_cep
+
+Format CEP. Returns None if CEP is invalid.
+
+```python
+>>> from brutils import format_cep
+>>> format_cpf('01310200')
+'01310-200'
+```
+
+### generate_cep
+
+Generate a valid random CEP.
+
+```python
+>>> from brutils import generate_cep
+>>> generate_cep()
+'77520503'
+```
+
 ## Contributing
 
 Collaboration is super welcome! We prepared the [contributing file][contributing] to help you in the first steps. Every little bit of help counts! Feel free to create new [GitHub issues][github-issues] and interact here.
diff --git a/brutils/__init__.py b/brutils/__init__.py
index 9f5de8d..e51068c 100644
--- a/brutils/__init__.py
+++ b/brutils/__init__.py
@@ -10,3 +10,8 @@ from brutils.cnpj import (
     remove_symbols as remove_symbols_cnpj,
     generate as generate_cnpj,
 )
+from brutils.cep import (
+    is_valid as is_valid_cep,
+    format_cep,
+    generate as generate_cep,
+)
diff --git a/brutils/cep.py b/brutils/cep.py
new file mode 100644
index 0000000..cd86408
--- /dev/null
+++ b/brutils/cep.py
@@ -0,0 +1,47 @@
+from random import randint
+
+
+# FORMATTING
+############
+
+
+def format_cep(cep):  # type: (str) -> str
+    """
+    Will format an adequately formatted numbers-only CEP string,
+    adding in standard formatting visual aid symbols for display.
+    """
+
+    if not is_valid(cep):
+        return None
+
+    return "{}-{}".format(cep[:5], cep[5:8])
+
+
+# OPERATIONS
+############
+
+
+def is_valid(cep):  # type: (str) -> bool
+    """
+    Returns when CEP is valid, input should be a digit
+    string of proper length. Doesn't validate if it's real,
+    'cause only the "Correios" Base does.
+
+    Source: https://en.wikipedia.org/wiki/C%C3%B3digo_de_Endere%C3%A7amento_Postal
+
+    """
+
+    return isinstance(cep, str) and len(cep) == 8 and cep.isdigit()
+
+
+def generate():  # type: () -> str
+    """
+    Generates a random valid CEP digit string. An optional branch
+    number parameter can be given, it defaults to 1.
+    """
+    generated_number = ""
+
+    for _ in range(0, 8):
+        generated_number = generated_number + str(randint(0, 9))
+
+    return generated_number

test_patch: diff --git a/tests/test_cep.py b/tests/test_cep.py
new file mode 100644
index 0000000..a2a2eca
--- /dev/null
+++ b/tests/test_cep.py
@@ -0,0 +1,50 @@
+from os import pardir
+from os.path import abspath, join, dirname
+from sys import path, version_info, dont_write_bytecode
+from inspect import getsourcefile
+from unittest.mock import patch
+
+dont_write_bytecode = True
+range = range if version_info.major >= 3 else xrange
+path.insert(
+    1, abspath(join(dirname(abspath(getsourcefile(lambda: 0))), pardir))
+)
+from brutils.cep import (
+    is_valid,
+    format_cep,
+    generate,
+)
+from unittest import TestCase, main
+
+
+class CEP(TestCase):
+    def test_format_cep(self):
+        with patch("brutils.cep.is_valid", return_value=True) as mock_is_valid:
+            # When cep is_valid, returns formatted cep
+            assert format_cep("01310200") == "01310-200"
+
+        # Checks if function is_valid_cnpj is called
+        mock_is_valid.assert_called_once_with("01310200")
+
+        with patch("brutils.cep.is_valid", return_value=False) as mock_is_valid:
+            # When cep isn't valid, returns None
+            assert format_cep("013102009") is None
+
+    def test_is_valid(self):
+        # When CEP is not string, returns False
+        assert not is_valid(1)
+
+        # When CEP's len is different of 8, returns False
+        assert not is_valid("1")
+
+        # When CEP does not contain only digits, returns False
+        assert not is_valid("1234567-")
+
+        # When CEP is valid
+        assert is_valid("99999999")
+        assert is_valid("88390000")
+
+    def test_generate(self):
+        for _ in range(10_000):
+            assert is_valid(generate())
+        # assert format(generate()) is not None