Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

improving is_valid cpf function #79

Merged
merged 2 commits into from
May 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ $ git clone git@github.com:brazilian-utils/brutils-python.git
$ cd brutils-python
```

Create a [virtualenv][virtualenv] for ScanAPI and activate it:
Create a [virtualenv][virtualenv] for brutils and activate it:

```shell
$ make shell
Expand Down
50 changes: 39 additions & 11 deletions brutils/cpf.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,20 +90,48 @@ def validate(cpf): # type: (str) -> bool
return all(hashdigit(cpf, i + 10) == int(v) for i, v in enumerate(cpf[9:]))


def is_valid(cpf): # type: (str) -> bool
"""
Returns whether or not the verifying checksum digits of the
given `cpf` match it's base number. Input should be a digit
string of proper length.
Using this method name to match with the js library api.
Using the same method to ensure backwards compatibility.
"""
return validate(cpf)


def generate(): # type: () -> str
"""Generates a random valid CPF digit string."""
base = str(randint(1, 999999998)).zfill(9)
while len(set(base)) == 1:
base = str(randint(1, 999999998)).zfill(9)
return base + checksum(base)


def is_valid(cpf): # type: (str) -> bool
"""
Returns whether or not a cpf is_valid.
Source: https://www.geradorcpf.com/algoritmo_do_cpf.htm
"""
is_syntax_valid = isinstance(cpf, str) and len(cpf) == 11 and cpf.isdigit()

return is_syntax_valid and _is_semantic_valid(cpf)


def _is_semantic_valid(cpf):
cpf = [int(digit) for digit in cpf]

constants_tenth_digit = [10, 9, 8, 7, 6, 5, 4, 3, 2]
is_tenth_digit_valid = _is_digit_valid(cpf, constants_tenth_digit, 9)

constants_eleventh_digit = [11, 10, 9, 8, 7, 6, 5, 4, 3, 2]
is_eleventh_digit_valid = _is_digit_valid(cpf, constants_eleventh_digit, 10)

return is_tenth_digit_valid and is_eleventh_digit_valid


def _is_digit_valid(cpf, constants, digit_index):
sum = _multiply_and_sum_lists(cpf, constants, digit_index)
rest = sum % 11
digit = cpf[digit_index]

return (rest <= 2 and digit == 0) or (rest > 2 and digit == (11 - rest))


def _multiply_and_sum_lists(list_1, list_2, max_index):
sum = 0

for index in range(0, max_index):
sum += list_1[index] * list_2[index]

return sum
27 changes: 24 additions & 3 deletions tests/test_cpf.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,30 @@ def test_validate(self):
assert not validate("00000000000")

def test_is_valid(self):
assert is_valid("52513127765")
assert is_valid("52599927765")
assert not is_valid("00000000000")
# When cpf is not string, returns False
assert not is_valid(1)

# When cpf's len is different of 11, returns False
assert not is_valid("1")

# When cpf does not contain only digits, returns False
assert not is_valid("1112223334-")

# When rest_1 is lt 2 and the 10th digit is not 0, returns False
assert not is_valid("11111111215")

# When rest_1 is gte 2 and the 10th digit is not (11 - rest), returns False
assert not is_valid("11144477705")

# When rest_2 is lt 2 and the 11th digit is not 0, returns False
assert not is_valid("11111111204")

# When rest_2 is gte 2 and the 11th digit is not (11 - rest), returns False
assert not is_valid("11144477732")

# When cpf is valid
assert is_valid("11144477735")
assert is_valid("11111111200")

def test_generate(self):
for i in range(1000):
Expand Down