diff --git a/encryptdef/__main__.py b/encryptdef/__main__.py index 8e1fbcf..6411034 100644 --- a/encryptdef/__main__.py +++ b/encryptdef/__main__.py @@ -1,7 +1,8 @@ """Modulo que é o ponto de entrada do programa""" -from encryptdef.cli import main +from encryptdef.cli import main # pragma: no cover + +if __name__ == "__main__": # pragma: no cover -if __name__ == "__main__": # Passando um contexto vazio main(ctx=None) diff --git a/encryptdef/cli.py b/encryptdef/cli.py index 57b18ce..794a117 100644 --- a/encryptdef/cli.py +++ b/encryptdef/cli.py @@ -41,7 +41,7 @@ def main(ctx: Optional[click.Context] = None) -> None: correta, não será possível desencriptar os dados ou arquivos.** """ if ctx is None: - ctx = click.Context(main) + ctx = click.Context(main) # pragma: no cover if ctx.invoked_subcommand is None: core.interactive_mode() diff --git a/encryptdef/core.py b/encryptdef/core.py index 42d6129..d621069 100644 --- a/encryptdef/core.py +++ b/encryptdef/core.py @@ -29,6 +29,7 @@ TEMPLATE_DECRYPTED, TEMPLATE_DECRYPTED_FILE, TEMPLATE_DECRYPTED_MESSAGE, + TEMPLATE_EMPTY_FILE_ERROR, TEMPLATE_ENCRYPT_FILE, TEMPLATE_ENCRYPT_KEY, TEMPLATE_ENCRYPT_MESSAGE, @@ -56,6 +57,10 @@ class InvalidKey(Exception): """Formato de string criptografada inválido""" +class EmptyFileError(Exception): + """Formato de string criptografada inválido""" + + def encrypt(message: str, password: str) -> str: """ Criptografa uma mensagem usando AES GCM com uma chave derivada por Scrypt. @@ -227,7 +232,7 @@ def process_lines( def process_file_content( - file: str, + file_path: str, key: str, new_file_path: str, process_line_func: Callable[[str, str], Union[str, bool]], @@ -236,7 +241,7 @@ def process_file_content( Processa o conteúdo de um arquivo e salva o resultado em um novo arquivo. Args: - file (str): Caminho do arquivo original. + file_path (str): Caminho do arquivo original. key (str): Chave para criptografar ou descriptografar. new_file_path (str): Caminho do novo arquivo. process_line_func (Callable[[str, str], Union[str, bool]]): Função para @@ -245,13 +250,13 @@ def process_file_content( Returns: bool: True se o processamento for bem-sucedido, False caso contrário. """ - lines = read_file(file) + lines = read_file(file_path) + if not lines: + raise EmptyFileError(TEMPLATE_EMPTY_FILE_ERROR % file_path) + max_workers = print_get_max_workers(lines) processed_lines = process_lines(lines, key, process_line_func, max_workers) - if not processed_lines: - return False - write_file(new_file_path, processed_lines) print_and_record_log( ( @@ -281,24 +286,25 @@ def process_file( Returns: bool: True se o processamento for bem-sucedido, False caso contrário. """ - file, key, new_file = data_list - try: - new_file_path = get_new_file_path(file, new_file, CURRENT_DIR) + file_path, key, new_file = data_list + new_file_path = get_new_file_path(file_path, new_file, CURRENT_DIR) return process_file_content( - file, key, new_file_path, process_line_func + file_path, key, new_file_path, process_line_func ) except FileNotFoundError: - print_and_record_log(TEMPLATE_FILE_NOT_FOUND % file, "error") + print_and_record_log(TEMPLATE_FILE_NOT_FOUND % file_path, "error") console.print(TEMPLATE_INFO_FILE) return False except ( TypeError, IsADirectoryError, + ValueError, InvalidEncryptedFormat, InvalidKey, + EmptyFileError, ) as e: print_and_record_log(str(e), "error") return False @@ -307,7 +313,7 @@ def process_file( def process_keyfile_and_args( keyfile: Optional[str], message: Optional[str], - file: Optional[str], + file_: Optional[str], template_key: str, ) -> str: """ @@ -321,7 +327,7 @@ def process_keyfile_and_args( a chave será solicitada. message (Optional[str]): Dados para criptografar ou descriptografar. Usado se 'file' não for fornecido. - file (Optional[str]): Caminho do arquivo a ser criptografado ou + file_ (Optional[str]): Caminho do arquivo a ser criptografado ou descriptografado. Usado se 'message' não for fornecido. template_key (str): Template para solicitar a chave ao usuário, se necessário. @@ -335,12 +341,12 @@ def process_keyfile_and_args( SystemExit: Se o arquivo de chave não for encontrado, ou se a chave fornecida for inválida. """ - if message and file: + if message and file_: raise click.UsageError( "Você deve fornecer apenas um dos argumentos: --message ou --file," - "não ambos." + " não ambos." ) - if not message and not file: + if not message and not file_: raise click.UsageError( "Você deve fornecer um dos argumentos: --message ou --file." ) @@ -355,13 +361,9 @@ def process_keyfile_and_args( else: while not key or key.isspace(): key = console.input(template_key, password=True).strip() - if key.isspace(): + if not key: print_and_record_log(TEMPLATE_ERROR_EMPTY_FIELD, "error") - if key is None: - print_and_record_log(TEMPLATE_ERROR_EMPTY_FIELD, "error") - sys.exit(1) - return key diff --git a/encryptdef/interactive_interface.py b/encryptdef/interactive_interface.py index a8ef32e..710aadd 100644 --- a/encryptdef/interactive_interface.py +++ b/encryptdef/interactive_interface.py @@ -65,7 +65,6 @@ def validate_and_get_input(prompts: List[str]) -> List[str]: inputs = [ get_user_input(prompt, "🔑" in prompt) for prompt in prompts ] - if any(not input for input in inputs): raise ValueError(TEMPLATE_ERROR_EMPTY_FIELD) return inputs @@ -174,15 +173,17 @@ def print_get_max_workers(lines: List[str]) -> int: max_workers = 1 if len(lines) > 500: - use_more_cores = int( - console.input(TEMPLATE_GET_MAX_WORKERS % max_workers) - ) + user_input = console.input( + TEMPLATE_GET_MAX_WORKERS % max_workers + ).strip() console.print("\n", end="") - if not use_more_cores or use_more_cores > max_workers: + if not user_input.isdigit() or not ( + 0 < int(user_input) <= max_workers + ): raise ValueError(TEMPLATE_ERROR_INVALID_CHOICE) - return use_more_cores + return int(user_input) return 1 diff --git a/encryptdef/log.py b/encryptdef/log.py index 9c665a9..3586483 100644 --- a/encryptdef/log.py +++ b/encryptdef/log.py @@ -19,7 +19,7 @@ def configure_logger( - logfile: Optional[Union[str, os.PathLike[str]]] = None + log_level: str, logfile: Optional[Union[str, os.PathLike[str]]] = None ) -> None: """Configura o logger com um handler de arquivo rotativo.""" if logfile is None: @@ -32,10 +32,10 @@ def configure_logger( fh = handlers.RotatingFileHandler( logfile, maxBytes=10**6, backupCount=10 ) - fh.setLevel(LOG_LEVEL) + fh.setLevel(log_level) fh.setFormatter(fmt) log_instance.addHandler(fh) - log_instance.setLevel(LOG_LEVEL) + log_instance.setLevel(log_level) def get_logger() -> logging.Logger: @@ -44,7 +44,7 @@ def get_logger() -> logging.Logger: # Configure o logger ao importar o módulo -configure_logger() +configure_logger(LOG_LEVEL) log = get_logger() diff --git a/encryptdef/template.py b/encryptdef/template.py index c6de1c8..a8ac983 100644 --- a/encryptdef/template.py +++ b/encryptdef/template.py @@ -124,7 +124,10 @@ ⚠ ERRO - FORMATO DE STRING CRIPTOGRAFADA INVÁLIDO!""" TEMPLATE_TYPE_ERROR = """ - ⚠ ERRO - ESPERADO UMA STRING, OBTIDO '%s'""" + ⚠ ERRO - ESPERADO UMA STRING, OBTIDO '%s'.""" TEMPLATE_IS_DIRECTORY = """ ⚠ ERRO - '%s' É UM DIRETÓRIO, NÃO UM ARQUIVO.""" + +TEMPLATE_EMPTY_FILE_ERROR = """ + ⚠ ERRO - ARQUIVO '%s' ESTÁ VAZIO.""" diff --git a/encryptdef/utils.py b/encryptdef/utils.py index cc35245..6fa99c4 100644 --- a/encryptdef/utils.py +++ b/encryptdef/utils.py @@ -4,13 +4,8 @@ import re from typing import List -from colorama import init - from encryptdef.template import TEMPLATE_IS_DIRECTORY -# Inicializa colorama para garantir o funcionamento em sistemas Windows -init() - def get_new_file_path(file: str, new_file: str, current_dir: str) -> str: """ @@ -93,10 +88,9 @@ def write_file(new_file_path: str, processed_lines: List[str]) -> None: def clear_console(): - """Limpa o console de forma segura.""" + """Limpa o console de forma segura. + Utilizando sequências de escape ANSI""" if os.name == "posix": print("\033[H\033[J", end="") elif os.name == "nt": - # Inicializa o colorama, que é seguro e portátil - init() print("\033c", end="") diff --git a/pyproject.toml b/pyproject.toml index 4e649e7..88cfa42 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,6 +40,7 @@ local_scheme = "no-local-version" # determina a parte local (como dev1+g7aff1b9. minversion = "6.0" addopts = "-ra -q -vv" testpaths = "tests" +filterwarnings = "ignore::DeprecationWarning:rich_click" [tool.flake8] exclude = [".venv", "build", ".vscodelocal", "migrations", "template.py"] diff --git a/requirements.test.txt b/requirements.test.txt index 8f0911e..bbe2d73 100644 --- a/requirements.test.txt +++ b/requirements.test.txt @@ -1,5 +1,6 @@ #teste pytest +pytest-mock # coverage coverage diff --git a/requirements.txt b/requirements.txt index eb72303..d957b74 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,2 @@ pycryptodomex >=3.20.0 rich-click >=1.8.2 -colorama >=0.4.6 diff --git a/tests/conftest.py b/tests/conftest.py index 929e30a..5a31e02 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,14 +2,6 @@ import pytest -MARKER = """\ -unit: Mark unit tests -integration: Mark integration tests -high: High Priority -medium: Medium Priority -low: Low Priority -""" - @pytest.fixture(autouse=True) def go_to_tmpdir(request): # injeção de dependencias diff --git a/tests/test_assigning_a_name_file.py b/tests/test_assigning_a_name_file.py index 6a871ba..d10e4db 100644 --- a/tests/test_assigning_a_name_file.py +++ b/tests/test_assigning_a_name_file.py @@ -4,7 +4,7 @@ def test_assigning_a_name_file_absolute(): - """Test function assigning_a_name_file""" + """Testando a função assigning_a_name_file""" file = "/tmp/test/file-test-123.txt" name = "encrypt-" expected_result = "/tmp/test/encrypt-file-test-123.txt" @@ -14,7 +14,7 @@ def test_assigning_a_name_file_absolute(): def test_assigning_a_name_file_relative(): - """Test function assigning_a_name_file""" + """Testando a função assigning_a_name_file""" file = "-123-file-test.txt" name = "decrypt-" expected_result = "decrypt--123-file-test.txt" diff --git a/tests/test_decrypt.py b/tests/test_decrypt.py new file mode 100644 index 0000000..feed0dc --- /dev/null +++ b/tests/test_decrypt.py @@ -0,0 +1,74 @@ +"""Modulo para testar a função decrypt em core.py""" + +from base64 import b64decode, b64encode + +import pytest + +from encryptdef.core import ( + InvalidEncryptedFormat, + InvalidKey, + decrypt, + encrypt, +) +from encryptdef.template import ( + TEMPLATE_ERROR_INVALID_ENCRYPTED_FORMAT, + TEMPLATE_INVALID_KEY, +) + + +def test_decrypt_correct_message(): + """Testa a função decrypt""" + message1 = "Message one: djas;kl/d$!@&()&&¨|#!@&*%#312312adasd" + password1 = "strongpassword123" + + message2 = "Message two: 1~[]31'2>~[]dADd12