first version
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
"""Definition of integration test fixtures."""
|
||||
|
||||
import pytest
|
||||
import json
|
||||
from pathlib import Path
|
||||
import tempfile
|
||||
from collections.abc import Iterator
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def password() -> str:
|
||||
"""Fixture that provides a test password.
|
||||
|
||||
Returns:
|
||||
str: A test password.
|
||||
"""
|
||||
return "sZoOOTSY32x-4nzb_6XXtmfY2tA3R6KFcHLGS1hGA30=" # Base64-encoded 32-byte key
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def wrong_password() -> str:
|
||||
"""Fixture that provides a wrong test password.
|
||||
|
||||
Returns:
|
||||
str: A wrong test password.
|
||||
"""
|
||||
return "5mtlZEFgdvVyrcpF-__xoBNFuDaUjpqLOb-Y05AOdKY=" # Base64-encoded 32-byte key
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def aad() -> str:
|
||||
"""Fixture that provides additional authenticated data (AAD) for tests.
|
||||
|
||||
Returns:
|
||||
str: AAD JSON string.
|
||||
"""
|
||||
return json.dumps(
|
||||
{
|
||||
"TEST_KEY": "TEST_VALUE",
|
||||
"MODULE_VERSION": "1.0.0",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def wrong_aad() -> str:
|
||||
"""Fixture that provides wrong additional authenticated data (AAD) for tests.
|
||||
|
||||
Returns:
|
||||
str: Wrong AAD JSON string.
|
||||
"""
|
||||
return json.dumps(
|
||||
{
|
||||
"TEST_KEY": "TEST_VALUE",
|
||||
"MODULE_VERSION": "1.1.0",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def tmp_dir() -> Iterator[Path]:
|
||||
"""Fixture that provides a temporary directory for tests.
|
||||
|
||||
Yields:
|
||||
Path: A temporary directory path.
|
||||
"""
|
||||
with tempfile.TemporaryDirectory() as tmpdirname:
|
||||
yield Path(tmpdirname)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def simple_python_module() -> Path:
|
||||
"""Path to a simple hello world Python module."""
|
||||
return Path(__file__).parent.parent / "test_data" / "simple_python_module"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def nested_python_module() -> Path:
|
||||
"""Path to a complex Python module with internal dependencies."""
|
||||
return Path(__file__).parent.parent / "test_data" / "nested_python_module"
|
||||
@@ -0,0 +1,177 @@
|
||||
"""Definition of integration tests for the CLI functionality."""
|
||||
|
||||
import subprocess
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def test_generate_password_command() -> None:
|
||||
"""Test the generate-password CLI command."""
|
||||
generate_password_args = [
|
||||
sys.executable,
|
||||
"-m",
|
||||
"python_encrypt_code",
|
||||
"generate-password",
|
||||
]
|
||||
output = subprocess.run(
|
||||
generate_password_args, check=True, capture_output=True, text=True
|
||||
)
|
||||
password = output.stdout.strip()
|
||||
assert len(password) > 0, "Generated password should not be empty"
|
||||
|
||||
|
||||
def test_encrypt_command(
|
||||
password: str,
|
||||
aad: str,
|
||||
simple_python_module: Path,
|
||||
tmp_dir: Path,
|
||||
) -> None:
|
||||
"""Test the encrypt CLI command."""
|
||||
|
||||
encrypted_file = tmp_dir / "encrypted.pec"
|
||||
|
||||
# Encrypt the simple python module
|
||||
encrypt_args = [
|
||||
sys.executable,
|
||||
"-m",
|
||||
"python_encrypt_code",
|
||||
"encrypt",
|
||||
str(simple_python_module),
|
||||
"-o",
|
||||
str(encrypted_file),
|
||||
"-p",
|
||||
password,
|
||||
"-aad",
|
||||
aad,
|
||||
]
|
||||
subprocess.run(encrypt_args, check=True)
|
||||
|
||||
assert encrypted_file.exists(), "Encrypted file should be created"
|
||||
|
||||
# Encrypt without optional AAD
|
||||
encrypted_file_no_aad = tmp_dir / "encrypted_no_aad.pec"
|
||||
encrypt_args_no_aad = [
|
||||
sys.executable,
|
||||
"-m",
|
||||
"python_encrypt_code",
|
||||
"encrypt",
|
||||
str(simple_python_module),
|
||||
"-o",
|
||||
str(encrypted_file_no_aad),
|
||||
"-p",
|
||||
password,
|
||||
]
|
||||
subprocess.run(encrypt_args_no_aad, check=True)
|
||||
|
||||
assert encrypted_file_no_aad.exists(), "Encrypted file should be created"
|
||||
|
||||
|
||||
def test_decrypt_command(
|
||||
password: str,
|
||||
aad: str,
|
||||
simple_python_module: Path,
|
||||
tmp_dir: Path,
|
||||
) -> None:
|
||||
"""Test the decrypt CLI command."""
|
||||
|
||||
encrypted_file = tmp_dir / "encrypted.pec"
|
||||
decrypted_output_dir = tmp_dir / "decrypted_output"
|
||||
|
||||
# Prepare an encrypted file
|
||||
encrypt_args = [
|
||||
sys.executable,
|
||||
"-m",
|
||||
"python_encrypt_code",
|
||||
"encrypt",
|
||||
str(simple_python_module),
|
||||
"-o",
|
||||
str(encrypted_file),
|
||||
"-p",
|
||||
password,
|
||||
"-aad",
|
||||
aad,
|
||||
]
|
||||
subprocess.run(encrypt_args, check=True)
|
||||
|
||||
# The PasswordProviderExample reads from environment variables
|
||||
# to mock getting data from external secret management systems.
|
||||
env = {**os.environ}
|
||||
env["PEC_PASSWORD"] = password
|
||||
env["PEC_AAD"] = aad
|
||||
|
||||
# Decrypt the file
|
||||
decrypt_args = [
|
||||
sys.executable,
|
||||
"-m",
|
||||
"python_encrypt_code",
|
||||
"decrypt",
|
||||
str(encrypted_file),
|
||||
"-o",
|
||||
str(decrypted_output_dir),
|
||||
]
|
||||
subprocess.run(decrypt_args, check=True, env=env)
|
||||
|
||||
assert decrypted_output_dir.exists(), "Decrypted output directory should be created"
|
||||
assert any(decrypted_output_dir.iterdir()), (
|
||||
"Decrypted output directory should not be empty"
|
||||
)
|
||||
|
||||
|
||||
def test_run_insecure_command(
|
||||
password: str,
|
||||
aad: str,
|
||||
simple_python_module: Path,
|
||||
tmp_dir: Path,
|
||||
) -> None:
|
||||
"""Test the run-insecure CLI command."""
|
||||
|
||||
encrypted_file = tmp_dir / "encrypted_run.pec"
|
||||
|
||||
# Encrypt the simple python module
|
||||
encrypt_args = [
|
||||
sys.executable,
|
||||
"-m",
|
||||
"python_encrypt_code",
|
||||
"encrypt",
|
||||
str(simple_python_module),
|
||||
"-o",
|
||||
str(encrypted_file),
|
||||
"-p",
|
||||
password,
|
||||
"-aad",
|
||||
aad,
|
||||
]
|
||||
subprocess.run(
|
||||
args=encrypt_args,
|
||||
check=True,
|
||||
)
|
||||
|
||||
# Verify that the encrypted file was created
|
||||
assert encrypted_file.exists()
|
||||
|
||||
# Store password and AAD in environment variables for the subprocesses
|
||||
env = dict(**os.environ)
|
||||
env["PEC_PASSWORD"] = password
|
||||
env["PEC_AAD"] = aad
|
||||
|
||||
# Step 2: Run the encrypted module
|
||||
run_args = [
|
||||
sys.executable,
|
||||
"-m",
|
||||
"python_encrypt_code",
|
||||
"run-insecure",
|
||||
str(encrypted_file),
|
||||
"--script",
|
||||
"hello_world.py",
|
||||
]
|
||||
result = subprocess.run(
|
||||
args=run_args,
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
env=env,
|
||||
)
|
||||
|
||||
# Verify output
|
||||
assert "Hello, World!" in result.stdout
|
||||
@@ -0,0 +1,72 @@
|
||||
"""Test the encryption of complex python modules with multiple files and imports."""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def test_encrypt_and_run_nested_module(
|
||||
nested_python_module: Path,
|
||||
tmp_dir: Path,
|
||||
password: str,
|
||||
aad: str,
|
||||
) -> None:
|
||||
"""Test encrypting and running a module with nested imports.
|
||||
|
||||
Args:
|
||||
nested_python_module (Path): Path to the nested module directory.
|
||||
tmp_dir (Path): Temporary directory for test files.
|
||||
password (str): The test password.
|
||||
aad (str): Additional authenticated data.
|
||||
"""
|
||||
|
||||
encrypted_file = tmp_dir / "encrypted_nested.pec"
|
||||
|
||||
# Encrypt the folder containing the complex module
|
||||
encrypt_args = [
|
||||
sys.executable,
|
||||
"-m",
|
||||
"python_encrypt_code",
|
||||
"encrypt",
|
||||
str(nested_python_module),
|
||||
"-o",
|
||||
str(encrypted_file),
|
||||
"-p",
|
||||
password,
|
||||
"-aad",
|
||||
aad,
|
||||
]
|
||||
subprocess.run(
|
||||
args=encrypt_args,
|
||||
check=True,
|
||||
)
|
||||
|
||||
# Verify that the encrypted file was created
|
||||
assert encrypted_file.exists()
|
||||
|
||||
# Prepare environment for the PasswordProviderExample
|
||||
env = {**os.environ}
|
||||
env["PEC_PASSWORD"] = password
|
||||
env["PEC_AAD"] = aad
|
||||
|
||||
# Run the encrypted module with environment set
|
||||
run_args = [
|
||||
sys.executable,
|
||||
"-m",
|
||||
"python_encrypt_code",
|
||||
"run-insecure",
|
||||
str(encrypted_file),
|
||||
"--script",
|
||||
"main.py",
|
||||
]
|
||||
result = subprocess.run(
|
||||
args=run_args,
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
env=env,
|
||||
)
|
||||
|
||||
# Verify nested module functionality works
|
||||
assert "Nested Python Module executed successfully." in result.stderr
|
||||
@@ -0,0 +1,200 @@
|
||||
"""Test the encryption of a simple python module and its execution."""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def test_encrypt_and_run_simple_module(
|
||||
simple_python_module: Path,
|
||||
tmp_dir: Path,
|
||||
password: str,
|
||||
aad: str,
|
||||
) -> None:
|
||||
"""Test encrypting a simple hello world module.
|
||||
|
||||
Args:
|
||||
simple_python_module (Path): Path to the simple python module.
|
||||
tmp_dir (Path): Temporary directory for test files.
|
||||
password (str): The test password.
|
||||
aad (str): Additional authenticated data.
|
||||
"""
|
||||
|
||||
encrypted_file = tmp_dir / "encrypted.pec"
|
||||
|
||||
# Encrypt the simple python module
|
||||
encrypt_args = [
|
||||
sys.executable,
|
||||
"-m",
|
||||
"python_encrypt_code",
|
||||
"encrypt",
|
||||
str(simple_python_module),
|
||||
"-o",
|
||||
str(encrypted_file),
|
||||
"-p",
|
||||
password,
|
||||
"-aad",
|
||||
aad,
|
||||
]
|
||||
subprocess.run(
|
||||
args=encrypt_args,
|
||||
check=True,
|
||||
)
|
||||
|
||||
# Verify that the encrypted file was created
|
||||
assert encrypted_file.exists()
|
||||
|
||||
# Store password and AAD in environment variables
|
||||
# for the PasswordProviderExample used by the subprocesses
|
||||
env = {**os.environ}
|
||||
env["PEC_PASSWORD"] = password
|
||||
env["PEC_AAD"] = aad
|
||||
|
||||
# Run the encrypted module with environment set
|
||||
run_args = [
|
||||
sys.executable,
|
||||
"-m",
|
||||
"python_encrypt_code",
|
||||
"run-insecure",
|
||||
str(encrypted_file),
|
||||
"--script",
|
||||
"hello_world.py",
|
||||
]
|
||||
result = subprocess.run(
|
||||
args=run_args,
|
||||
check=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
env=env,
|
||||
)
|
||||
|
||||
# Verify output
|
||||
assert "Hello, World!" in result.stdout
|
||||
|
||||
|
||||
def test_run_with_wrong_password(
|
||||
simple_python_module: Path,
|
||||
tmp_dir: Path,
|
||||
password: str,
|
||||
aad: str,
|
||||
wrong_password: str,
|
||||
) -> None:
|
||||
"""Test encrypting a simple hello world module and attempting to run it with a wrong password.
|
||||
|
||||
Args:
|
||||
simple_python_module (Path): Path to the simple python module.
|
||||
tmp_dir (Path): Temporary directory for test files.
|
||||
"""
|
||||
|
||||
encrypted_file = tmp_dir / "encrypted_wrong_password.pec"
|
||||
assert password != wrong_password, "Test setup error: passwords should differ."
|
||||
|
||||
# Encrypt the folder containing the hello world module
|
||||
encrypt_args = [
|
||||
sys.executable,
|
||||
"-m",
|
||||
"python_encrypt_code",
|
||||
"encrypt",
|
||||
str(simple_python_module),
|
||||
"-o",
|
||||
str(encrypted_file),
|
||||
"-p",
|
||||
password,
|
||||
"-aad",
|
||||
aad,
|
||||
]
|
||||
subprocess.run(encrypt_args, check=True)
|
||||
|
||||
# Verify that the encrypted file was created
|
||||
assert encrypted_file.exists()
|
||||
|
||||
# Store wrong password and AAD in environment variables for the subprocesses
|
||||
env = {**os.environ}
|
||||
env["PEC_PASSWORD"] = wrong_password
|
||||
env["PEC_AAD"] = aad
|
||||
|
||||
# Run the encrypted module with the wrong password
|
||||
run_args = [
|
||||
sys.executable,
|
||||
"-m",
|
||||
"python_encrypt_code",
|
||||
"run-insecure",
|
||||
str(encrypted_file),
|
||||
"--script",
|
||||
"hello_world.py",
|
||||
]
|
||||
result = subprocess.run(
|
||||
args=run_args,
|
||||
check=False,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
|
||||
# Verify that an error message is in the output
|
||||
assert "Error" in result.stderr
|
||||
|
||||
|
||||
def test_run_with_wrong_aad(
|
||||
simple_python_module: Path,
|
||||
tmp_dir: Path,
|
||||
password: str,
|
||||
aad: str,
|
||||
wrong_aad: str,
|
||||
) -> None:
|
||||
"""Test encrypting a simple hello world module and attempting to run it with wrong AAD.
|
||||
|
||||
Args:
|
||||
simple_python_module (Path): Path to the simple python module.
|
||||
tmp_dir (Path): Temporary directory for test files.
|
||||
"""
|
||||
|
||||
encrypted_file = tmp_dir / "encrypted_wrong_aad.pec"
|
||||
assert aad != wrong_aad, "Test setup error: AADs should differ."
|
||||
|
||||
# Encrypt the folder containing the hello world module
|
||||
encrypt_args = [
|
||||
sys.executable,
|
||||
"-m",
|
||||
"python_encrypt_code",
|
||||
"encrypt",
|
||||
str(simple_python_module),
|
||||
"-o",
|
||||
str(encrypted_file),
|
||||
"-p",
|
||||
password,
|
||||
"-aad",
|
||||
aad,
|
||||
]
|
||||
subprocess.run(
|
||||
args=encrypt_args,
|
||||
check=True,
|
||||
)
|
||||
|
||||
# Verify that the encrypted file was created
|
||||
assert encrypted_file.exists()
|
||||
|
||||
# Store wrong password and AAD in environment variables for the subprocesses
|
||||
env = {**os.environ}
|
||||
env["PEC_PASSWORD"] = password
|
||||
env["PEC_AAD"] = wrong_aad
|
||||
|
||||
# Run the encrypted module with the wrong AAD
|
||||
run_args = [
|
||||
sys.executable,
|
||||
"-m",
|
||||
"python_encrypt_code",
|
||||
"run-insecure",
|
||||
str(encrypted_file),
|
||||
"--script",
|
||||
"hello_world.py",
|
||||
]
|
||||
result = subprocess.run(
|
||||
args=run_args,
|
||||
check=False,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
|
||||
# Verify that an error message is in the output
|
||||
assert "Error" in result.stderr
|
||||
@@ -0,0 +1,17 @@
|
||||
"""Module entry point for nested_python_module testing fixture."""
|
||||
|
||||
import logging
|
||||
|
||||
from src import func_one # type: ignore[attr-defined]
|
||||
from src import func_two # type: ignore[attr-defined]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.info("Nested Python Module is running.")
|
||||
result_one = func_one()
|
||||
logger.info("func_one result: %s", {result_one})
|
||||
result_two = func_two()
|
||||
logger.info("func_two result: %s", {result_two})
|
||||
logger.info("Nested Python Module executed successfully.")
|
||||
@@ -0,0 +1,4 @@
|
||||
from .func_one import func_one
|
||||
from .func_two import func_two
|
||||
|
||||
__all__ = ["func_one", "func_two"]
|
||||
@@ -0,0 +1,10 @@
|
||||
"""Definition of func_one function."""
|
||||
|
||||
|
||||
def func_one() -> str:
|
||||
"""A simple function that returns a string.
|
||||
|
||||
Returns:
|
||||
str: A greeting string.
|
||||
"""
|
||||
return "Function One says Hello!"
|
||||
@@ -0,0 +1,10 @@
|
||||
"""Definition of func_two function."""
|
||||
|
||||
|
||||
def func_two() -> str:
|
||||
"""A simple function that returns a string.
|
||||
|
||||
Returns:
|
||||
str: A greeting string.
|
||||
"""
|
||||
return "Function Two says Hello!"
|
||||
@@ -0,0 +1,4 @@
|
||||
"""hello_world module."""
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Hello, World!")
|
||||
@@ -0,0 +1,73 @@
|
||||
"""Definition of unit test fixtures."""
|
||||
|
||||
from pathlib import Path
|
||||
import tempfile
|
||||
from collections.abc import Iterator
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def tmp_path() -> Iterator[Path]:
|
||||
"""Fixture that provides a temporary directory for tests.
|
||||
|
||||
Yields:
|
||||
Path: The path to the temporary directory.
|
||||
"""
|
||||
with tempfile.TemporaryDirectory() as tmpdirname:
|
||||
yield Path(tmpdirname)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_file() -> Iterator[Path]:
|
||||
"""Fixture that creates a sample file for testing.
|
||||
|
||||
Yields:
|
||||
Path: The path to the sample file.
|
||||
"""
|
||||
with tempfile.TemporaryDirectory() as tmpdirname:
|
||||
sample_file = Path(tmpdirname) / "sample.txt"
|
||||
sample_file.write_text("This is a sample file for testing.")
|
||||
yield sample_file
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def sample_folder() -> Iterator[Path]:
|
||||
"""Fixture that creates a sample folder with files for testing.
|
||||
|
||||
Yields:
|
||||
Path: The path to the sample folder.
|
||||
"""
|
||||
with tempfile.TemporaryDirectory() as tmpdirname:
|
||||
sample_dir = Path(tmpdirname)
|
||||
(sample_dir / "file1.txt").write_text("This is file 1.")
|
||||
(sample_dir / "file2.txt").write_text("This is file 2.")
|
||||
sub_dir = sample_dir / "subdir"
|
||||
sub_dir.mkdir()
|
||||
(sub_dir / "file3.txt").write_text("This is file 3 in subdir.")
|
||||
yield sample_dir
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def nested_python_module() -> Path:
|
||||
"""Path to a complex Python module with internal dependencies."""
|
||||
return Path(__file__).parent.parent / "test_data" / "nested_python_module"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def password() -> str:
|
||||
"""Fixture that provides a sample password for encryption tests.
|
||||
|
||||
Returns:
|
||||
str: A sample password.
|
||||
"""
|
||||
return "sZoOOTSY32x-4nzb_6XXtmfY2tA3R6KFcHLGS1hGA30=" # Base64-encoded 32-byte key
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def wrong_password() -> str:
|
||||
"""Fixture that provides a wrong test password.
|
||||
|
||||
Returns:
|
||||
str: A wrong test password.
|
||||
"""
|
||||
return "5mtlZEFgdvVyrcpF-__xoBNFuDaUjpqLOb-Y05AOdKY=" # Base64-encoded 32-byte key
|
||||
@@ -0,0 +1,221 @@
|
||||
"""Unit tests for data_zipper module."""
|
||||
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
from io import BytesIO
|
||||
|
||||
from python_encrypt_code.data_zipper.data_zipper import DataZipper
|
||||
|
||||
|
||||
def test_data_zipper_zip_data_checks_inputs(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""Test that DataZipper raises errors for invalid inputs."""
|
||||
|
||||
# Test that TypeError is raised for non-Path input_path
|
||||
try:
|
||||
DataZipper.zip_data(
|
||||
input_path="not_a_path", # type: ignore
|
||||
output_path=tmp_path / "output.zip",
|
||||
)
|
||||
except TypeError as e:
|
||||
assert str(e) == "input_path must be a Path object"
|
||||
|
||||
# Test that FileNotFoundError is raised for non-existent input_path
|
||||
try:
|
||||
DataZipper.zip_data(
|
||||
input_path=tmp_path / "non_existent_directory",
|
||||
output_path=tmp_path / "output.zip",
|
||||
)
|
||||
except FileNotFoundError as e:
|
||||
assert (
|
||||
str(e) == f"input_path {tmp_path / 'non_existent_directory'} does not exist"
|
||||
)
|
||||
|
||||
# Test that ValueError is raised for non-directory input_path
|
||||
tmp_not_dir = tmp_path / "not_a_directory"
|
||||
tmp_not_dir.write_text("This is a file, not a directory.")
|
||||
try:
|
||||
DataZipper.zip_data(
|
||||
input_path=tmp_not_dir,
|
||||
output_path=tmp_path / "output.zip",
|
||||
)
|
||||
except ValueError as e:
|
||||
assert str(e) == "input_path must be a directory"
|
||||
|
||||
# Test that TypeError is raised for non-Path output_path
|
||||
try:
|
||||
DataZipper.zip_data(
|
||||
input_path=tmp_path,
|
||||
output_path="not_a_path", # type: ignore
|
||||
)
|
||||
except TypeError as e:
|
||||
assert str(e) == "output_path must be a Path object"
|
||||
|
||||
|
||||
def test_data_zipper_zips_folder(
|
||||
tmp_path: Path,
|
||||
sample_folder: Path,
|
||||
) -> None:
|
||||
"""Test that DataZipper can zip a folder correctly."""
|
||||
|
||||
# Define output zip file path
|
||||
output_zip = tmp_path / "output.zip"
|
||||
|
||||
# Zip the sample folder
|
||||
DataZipper.zip_data(
|
||||
input_path=sample_folder,
|
||||
output_path=output_zip,
|
||||
)
|
||||
|
||||
# Verify that the zip file was created
|
||||
assert output_zip.exists(), "Zip file should be created."
|
||||
|
||||
# Verify the contents of the zip file
|
||||
with zipfile.ZipFile(output_zip, "r") as zipf:
|
||||
zip_contents = zipf.namelist()
|
||||
expected_files = [
|
||||
"file1.txt",
|
||||
"file2.txt",
|
||||
"subdir/file3.txt",
|
||||
]
|
||||
for expected_file in expected_files:
|
||||
assert expected_file in zip_contents, (
|
||||
f"{expected_file} not found in zip file."
|
||||
)
|
||||
|
||||
|
||||
def test_data_zipper_unzip_data_checks_inputs() -> None:
|
||||
"""Test that DataZipper raises errors for invalid inputs during unzip."""
|
||||
|
||||
# Test that TypeError is raised for non-BytesIO input_data
|
||||
try:
|
||||
DataZipper.unzip_data(
|
||||
input_data="not_a_path", # type: ignore
|
||||
)
|
||||
except TypeError as e:
|
||||
assert str(e) == "input_data must be a BytesIO object"
|
||||
|
||||
# Test that ValueError is raised for empty BytesIO input_data
|
||||
empty_bytesio = BytesIO()
|
||||
try:
|
||||
DataZipper.unzip_data(
|
||||
input_data=empty_bytesio,
|
||||
)
|
||||
except ValueError as e:
|
||||
assert str(e) == "input_data must not be empty"
|
||||
|
||||
|
||||
def test_data_zipper_unzips_file(
|
||||
tmp_path: Path,
|
||||
sample_folder: Path,
|
||||
) -> None:
|
||||
"""Test that DataZipper can unzip a file correctly."""
|
||||
|
||||
# First, create a zip file from the sample folder
|
||||
output_zip = tmp_path / "output.zip"
|
||||
DataZipper.zip_data(
|
||||
input_path=sample_folder,
|
||||
output_path=output_zip,
|
||||
)
|
||||
|
||||
# Read the zip file into a BytesIO object
|
||||
with open(output_zip, "rb") as f:
|
||||
zip_data = BytesIO(f.read())
|
||||
|
||||
# Now, unzip the created zip file
|
||||
extracted_files = DataZipper.unzip_data(
|
||||
input_data=zip_data,
|
||||
)
|
||||
|
||||
# Verify that the correct files were extracted
|
||||
expected_files = [
|
||||
"file1.txt",
|
||||
"file2.txt",
|
||||
"subdir/file3.txt",
|
||||
]
|
||||
for expected_file in expected_files:
|
||||
assert expected_file in extracted_files, (
|
||||
f"{expected_file} not found in extracted files."
|
||||
)
|
||||
# Optionally, verify the content of the extracted files
|
||||
if expected_file == "file1.txt":
|
||||
assert extracted_files[expected_file].getvalue() == b"This is file 1."
|
||||
elif expected_file == "file2.txt":
|
||||
assert extracted_files[expected_file].getvalue() == b"This is file 2."
|
||||
elif expected_file == "subdir/file3.txt":
|
||||
assert (
|
||||
extracted_files[expected_file].getvalue()
|
||||
== b"This is file 3 in subdir."
|
||||
)
|
||||
|
||||
|
||||
def test_data_zipper_unzips_file_to_disk(
|
||||
tmp_path: Path,
|
||||
sample_folder: Path,
|
||||
) -> None:
|
||||
"""Test that DataZipper can unzip a file to disk correctly."""
|
||||
|
||||
# First, create a zip file from the sample folder
|
||||
output_zip = tmp_path / "output.zip"
|
||||
DataZipper.zip_data(
|
||||
input_path=sample_folder,
|
||||
output_path=output_zip,
|
||||
)
|
||||
|
||||
# Read the zip file into a BytesIO object
|
||||
with open(output_zip, "rb") as f:
|
||||
zip_data = BytesIO(f.read())
|
||||
|
||||
# Define output directory for unzipping
|
||||
unzip_output_dir = tmp_path / "unzipped"
|
||||
unzip_output_dir.mkdir()
|
||||
|
||||
# Unzip the created zip file to disk
|
||||
DataZipper.unzip_data_to_disk(
|
||||
input_data=zip_data,
|
||||
output_path=unzip_output_dir,
|
||||
)
|
||||
|
||||
# Verify that the correct files were extracted to disk
|
||||
expected_files = [
|
||||
unzip_output_dir / "file1.txt",
|
||||
unzip_output_dir / "file2.txt",
|
||||
unzip_output_dir / "subdir" / "file3.txt",
|
||||
]
|
||||
for expected_file in expected_files:
|
||||
assert expected_file.exists(), f"{expected_file} should exist on disk."
|
||||
|
||||
|
||||
def test_data_zipper_unzip_to_disk_checks_inputs(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""Test that DataZipper raises errors for invalid inputs during unzip to disk."""
|
||||
|
||||
# Test that TypeError is raised for non-BytesIO input_data
|
||||
try:
|
||||
DataZipper.unzip_data_to_disk(
|
||||
input_data="not_a_path", # type: ignore
|
||||
output_path=tmp_path / "output_dir",
|
||||
)
|
||||
except TypeError as e:
|
||||
assert str(e) == "input_data must be a BytesIO object"
|
||||
|
||||
# Test that TypeError is raised for non-Path output_path
|
||||
try:
|
||||
DataZipper.unzip_data_to_disk(
|
||||
input_data=BytesIO(b"some data"),
|
||||
output_path="not_a_path", # type: ignore
|
||||
)
|
||||
except TypeError as e:
|
||||
assert str(e) == "output_path must be a Path object"
|
||||
|
||||
# Test that ValueError is raised for empty BytesIO input_data
|
||||
empty_bytesio = BytesIO()
|
||||
try:
|
||||
DataZipper.unzip_data_to_disk(
|
||||
input_data=empty_bytesio,
|
||||
output_path=tmp_path / "output_dir",
|
||||
)
|
||||
except ValueError as e:
|
||||
assert str(e) == "input_data must not be empty"
|
||||
@@ -0,0 +1,319 @@
|
||||
"""Unit tests for file_encrypter module."""
|
||||
|
||||
from pathlib import Path
|
||||
from io import BytesIO
|
||||
import pytest
|
||||
|
||||
from python_encrypt_code.file_encrypter import FileEncrypter
|
||||
|
||||
|
||||
def test_encrypt_file_checks_inputs(
|
||||
sample_file: Path,
|
||||
password: str,
|
||||
) -> None:
|
||||
"""Test that FileEncrypter raises errors for invalid inputs."""
|
||||
|
||||
temp_dir = sample_file.parent
|
||||
output_file = temp_dir / "encrypted.bin"
|
||||
|
||||
# Test that TypeError is raised for non-Path input_file_path
|
||||
with pytest.raises(TypeError):
|
||||
FileEncrypter.encrypt_file(
|
||||
input_file_path="not_a_path", # type: ignore
|
||||
output_file_path=output_file,
|
||||
password=password,
|
||||
)
|
||||
|
||||
# Test that FileNotFoundError is raised for non-existent input_file_path
|
||||
with pytest.raises(FileNotFoundError):
|
||||
FileEncrypter.encrypt_file(
|
||||
input_file_path=temp_dir / "non_existent_file.txt",
|
||||
output_file_path=output_file,
|
||||
password=password,
|
||||
)
|
||||
|
||||
# Test that ValueError is raised for non-file input_file_path (directory)
|
||||
with pytest.raises(ValueError):
|
||||
FileEncrypter.encrypt_file(
|
||||
input_file_path=temp_dir,
|
||||
output_file_path=output_file,
|
||||
password=password,
|
||||
)
|
||||
|
||||
# Test that TypeError is raised for non-Path output_file_path
|
||||
with pytest.raises(TypeError):
|
||||
FileEncrypter.encrypt_file(
|
||||
input_file_path=sample_file,
|
||||
output_file_path="not_a_path", # type: ignore
|
||||
password=password,
|
||||
)
|
||||
|
||||
# Test that TypeError is raised for non-string password
|
||||
with pytest.raises(TypeError):
|
||||
FileEncrypter.encrypt_file(
|
||||
input_file_path=sample_file,
|
||||
output_file_path=output_file,
|
||||
password=123, # type: ignore
|
||||
)
|
||||
|
||||
# Test that ValueError is raised for empty password
|
||||
with pytest.raises(ValueError):
|
||||
FileEncrypter.encrypt_file(
|
||||
input_file_path=sample_file,
|
||||
output_file_path=output_file,
|
||||
password="",
|
||||
)
|
||||
|
||||
# Test that ValueError is raised for password that does not decode to 32 bytes
|
||||
with pytest.raises(ValueError):
|
||||
FileEncrypter.encrypt_file(
|
||||
input_file_path=sample_file,
|
||||
output_file_path=output_file,
|
||||
password="short_password",
|
||||
)
|
||||
|
||||
# Test that TypeError is raised for non-dict additional_data
|
||||
with pytest.raises(TypeError):
|
||||
FileEncrypter.encrypt_file(
|
||||
input_file_path=sample_file,
|
||||
output_file_path=output_file,
|
||||
password=password,
|
||||
additional_data="not_a_dict", # type: ignore
|
||||
)
|
||||
|
||||
|
||||
def test_encrypt_file_creates_encrypted_file(
|
||||
tmp_path: Path,
|
||||
password: str,
|
||||
) -> None:
|
||||
"""Test that FileEncrypter can encrypt a file correctly."""
|
||||
|
||||
# Create a sample file for testing
|
||||
sample_file = tmp_path / "test_file.txt"
|
||||
original_content = "This is a test file with some content to encrypt."
|
||||
sample_file.write_text(original_content)
|
||||
|
||||
# Define output encrypted file path
|
||||
encrypted_file = tmp_path / "encrypted.bin"
|
||||
|
||||
# Encrypt the file
|
||||
FileEncrypter.encrypt_file(
|
||||
input_file_path=sample_file,
|
||||
output_file_path=encrypted_file,
|
||||
password=password,
|
||||
additional_data=None,
|
||||
)
|
||||
|
||||
# Verify that the encrypted file was created
|
||||
assert encrypted_file.exists(), "Encrypted file should be created."
|
||||
|
||||
# Verify that the encrypted file is different from the original
|
||||
encrypted_content = encrypted_file.read_bytes()
|
||||
original_content_bytes = original_content.encode("utf-8")
|
||||
|
||||
assert encrypted_content != original_content_bytes, (
|
||||
"Encrypted content should be different from original."
|
||||
)
|
||||
assert len(encrypted_content) > len(original_content_bytes), (
|
||||
"Encrypted file should be larger (includes salt and encryption overhead)."
|
||||
)
|
||||
|
||||
# Verify that the file starts with a 16-byte salt
|
||||
assert len(encrypted_content) >= 16, (
|
||||
"Encrypted file should contain at least 16 bytes for salt."
|
||||
)
|
||||
|
||||
|
||||
def test_encrypt_file_with_different_passwords_produces_different_results(
|
||||
tmp_path: Path,
|
||||
password: str,
|
||||
sample_file: Path,
|
||||
wrong_password: str,
|
||||
) -> None:
|
||||
"""Test that encrypting the same file with different passwords produces different results."""
|
||||
|
||||
encrypted_file_1 = tmp_path / "encrypted1.pec"
|
||||
encrypted_file_2 = tmp_path / "encrypted2.pec"
|
||||
|
||||
# Encrypt with first password
|
||||
FileEncrypter.encrypt_file(
|
||||
input_file_path=sample_file,
|
||||
output_file_path=encrypted_file_1,
|
||||
password=password,
|
||||
)
|
||||
|
||||
# Encrypt with second password
|
||||
encrypted_file_2 = tmp_path / "encrypted2.bin"
|
||||
FileEncrypter.encrypt_file(
|
||||
input_file_path=sample_file,
|
||||
output_file_path=encrypted_file_2,
|
||||
password=wrong_password,
|
||||
)
|
||||
|
||||
# Verify that the encrypted files are different
|
||||
encrypted_content_1 = encrypted_file_1.read_bytes()
|
||||
encrypted_content_2 = encrypted_file_2.read_bytes()
|
||||
|
||||
assert encrypted_content_1 != encrypted_content_2, (
|
||||
"Different passwords should produce different encrypted content."
|
||||
)
|
||||
|
||||
|
||||
def test_encrypt_same_file_twice_produces_different_results(
|
||||
tmp_path: Path,
|
||||
sample_file: Path,
|
||||
password: str,
|
||||
) -> None:
|
||||
"""Test that encrypting the same file twice with the same password produces different results (due to nonce)."""
|
||||
|
||||
encrypted_file_1 = tmp_path / "encrypted1.pec"
|
||||
encrypted_file_2 = tmp_path / "encrypted2.pec"
|
||||
|
||||
# Encrypt the file first time
|
||||
FileEncrypter.encrypt_file(
|
||||
input_file_path=sample_file,
|
||||
output_file_path=encrypted_file_1,
|
||||
password=password,
|
||||
)
|
||||
|
||||
# Encrypt the file second time with same password
|
||||
FileEncrypter.encrypt_file(
|
||||
input_file_path=sample_file,
|
||||
output_file_path=encrypted_file_2,
|
||||
password=password,
|
||||
)
|
||||
|
||||
# Verify that the encrypted files are different (due to different salts)
|
||||
encrypted_content_1 = encrypted_file_1.read_bytes()
|
||||
encrypted_content_2 = encrypted_file_2.read_bytes()
|
||||
|
||||
assert encrypted_content_1 != encrypted_content_2, (
|
||||
"Same file encrypted twice should produce different results due to salt randomization."
|
||||
)
|
||||
|
||||
# But the nonces (first 12 bytes) should be different
|
||||
nonce_1 = encrypted_content_1[:12]
|
||||
nonce_2 = encrypted_content_2[:12]
|
||||
|
||||
assert nonce_1 != nonce_2, "Different encryptions should use different nonces."
|
||||
|
||||
|
||||
def test_decrypt_file_checks_inputs(
|
||||
sample_file: Path,
|
||||
password: str,
|
||||
) -> None:
|
||||
"""Test that FileEncrypter raises errors for invalid inputs during decryption."""
|
||||
|
||||
temp_dir = sample_file.parent
|
||||
|
||||
# Test that TypeError is raised for non-Path input_file_path
|
||||
with pytest.raises(TypeError):
|
||||
FileEncrypter.decrypt_file(
|
||||
input_file_path="not_a_path", # type: ignore
|
||||
password=password,
|
||||
)
|
||||
|
||||
# Test that FileNotFoundError is raised for non-existent input_file_path
|
||||
with pytest.raises(FileNotFoundError):
|
||||
FileEncrypter.decrypt_file(
|
||||
input_file_path=temp_dir / "non_existent_file.txt",
|
||||
password=password,
|
||||
)
|
||||
|
||||
# Test that ValueError is raised for non-file input_file_path (directory)
|
||||
with pytest.raises(ValueError):
|
||||
FileEncrypter.decrypt_file(
|
||||
input_file_path=temp_dir,
|
||||
password=password,
|
||||
)
|
||||
|
||||
# Test that TypeError is raised for non-string password
|
||||
with pytest.raises(TypeError):
|
||||
FileEncrypter.decrypt_file(
|
||||
input_file_path=sample_file,
|
||||
password=123, # type: ignore
|
||||
)
|
||||
|
||||
# Test that ValueError is raised for empty password
|
||||
with pytest.raises(ValueError):
|
||||
FileEncrypter.decrypt_file(
|
||||
input_file_path=sample_file,
|
||||
password="",
|
||||
)
|
||||
|
||||
# Test that ValueError is raised for password that does not decode to 32 bytes
|
||||
with pytest.raises(ValueError):
|
||||
FileEncrypter.decrypt_file(
|
||||
input_file_path=sample_file,
|
||||
password="short_password",
|
||||
)
|
||||
|
||||
# Test that TypeError is raised for non-dict additional_data
|
||||
with pytest.raises(TypeError):
|
||||
FileEncrypter.decrypt_file(
|
||||
input_file_path=sample_file,
|
||||
password=password,
|
||||
additional_data="not_a_dict", # type: ignore
|
||||
)
|
||||
|
||||
|
||||
def test_decrypt_file_returns_decrypted_content(
|
||||
sample_file: Path,
|
||||
password: str,
|
||||
) -> None:
|
||||
"""Test that FileEncrypter can decrypt an encrypted file correctly."""
|
||||
|
||||
encrypted_file = sample_file.parent / "encrypted.pec"
|
||||
|
||||
# Encrypt the file
|
||||
FileEncrypter.encrypt_file(
|
||||
input_file_path=sample_file,
|
||||
output_file_path=encrypted_file,
|
||||
password=password,
|
||||
)
|
||||
|
||||
# Decrypt the file
|
||||
decrypted_content: BytesIO = FileEncrypter.decrypt_file(
|
||||
input_file_path=encrypted_file,
|
||||
password=password,
|
||||
)
|
||||
|
||||
# Read original content
|
||||
original_content = sample_file.read_text()
|
||||
|
||||
# Verify that the decrypted content matches the original content
|
||||
assert decrypted_content.getvalue().decode("utf-8") == original_content, (
|
||||
"Decrypted content should match the original content."
|
||||
)
|
||||
|
||||
|
||||
def test_decrypt_to_disk_writes_decrypted_file(
|
||||
sample_file: Path,
|
||||
password: str,
|
||||
) -> None:
|
||||
"""Test that FileEncrypter can decrypt an encrypted file and write to disk."""
|
||||
|
||||
encrypted_file = sample_file.parent / "encrypted.pec"
|
||||
decrypted_file = sample_file.parent / "decrypted.txt"
|
||||
|
||||
# Encrypt the file
|
||||
FileEncrypter.encrypt_file(
|
||||
input_file_path=sample_file,
|
||||
output_file_path=encrypted_file,
|
||||
password=password,
|
||||
)
|
||||
|
||||
# Decrypt the file to disk
|
||||
FileEncrypter.decrypt_to_disk(
|
||||
input_file_path=encrypted_file,
|
||||
output_file_path=decrypted_file,
|
||||
password=password,
|
||||
)
|
||||
|
||||
# Read original content
|
||||
original_content = sample_file.read_text()
|
||||
|
||||
# Verify that the decrypted file matches the original content
|
||||
assert decrypted_file.read_text() == original_content, (
|
||||
"Decrypted file content should match the original content."
|
||||
)
|
||||
@@ -0,0 +1,155 @@
|
||||
"""Unit tests for module importer functionality."""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
import pytest
|
||||
|
||||
from python_encrypt_code.module_importer import ModuleImporter
|
||||
|
||||
|
||||
def test_module_importer_comprehensive_functionality(
|
||||
nested_python_module: Path,
|
||||
) -> None:
|
||||
"""Test comprehensive module importer functionality including packages, modules, imports, and main execution."""
|
||||
|
||||
# Store original modules state
|
||||
original_modules = sys.modules.copy()
|
||||
|
||||
try:
|
||||
# Test regular loading first
|
||||
ModuleImporter.import_module_from_disk(nested_python_module)
|
||||
|
||||
# Verify packages were created
|
||||
assert "src" in sys.modules # src package from __init__.py
|
||||
assert hasattr(sys.modules["src"], "__path__"), (
|
||||
"src should be a package with __path__"
|
||||
)
|
||||
|
||||
# Verify individual modules were created
|
||||
assert "src.func_one" in sys.modules
|
||||
assert "src.func_two" in sys.modules
|
||||
assert "main" in sys.modules
|
||||
|
||||
# Verify module attributes are set correctly
|
||||
func_one_module = sys.modules["src.func_one"]
|
||||
assert func_one_module.__file__ is not None
|
||||
assert func_one_module.__file__.endswith("func_one.py")
|
||||
assert func_one_module.__package__ == "src"
|
||||
|
||||
main_module = sys.modules["main"]
|
||||
assert main_module.__file__ is not None
|
||||
assert main_module.__file__.endswith("main.py")
|
||||
assert main_module.__package__ is None
|
||||
|
||||
# Test that functions are accessible (assuming func_one has a function)
|
||||
if hasattr(func_one_module, "func_one"):
|
||||
assert callable(func_one_module.func_one)
|
||||
|
||||
# Clean up for next test
|
||||
modules_to_remove = [
|
||||
name for name in sys.modules.keys() if name not in original_modules
|
||||
]
|
||||
for module_name in modules_to_remove:
|
||||
del sys.modules[module_name]
|
||||
|
||||
# Test main module functionality
|
||||
ModuleImporter.import_module_from_disk(
|
||||
nested_python_module, main_module="main.py"
|
||||
)
|
||||
|
||||
# Verify main module has correct __name__
|
||||
assert "__main__" in sys.modules
|
||||
assert sys.modules["__main__"].__name__ == "__main__"
|
||||
|
||||
# Verify all expected modules are still loaded
|
||||
assert "src" in sys.modules
|
||||
assert "src.func_one" in sys.modules
|
||||
assert "src.func_two" in sys.modules
|
||||
assert "main" in sys.modules
|
||||
|
||||
# Verify src is still a package
|
||||
src_module = sys.modules["src"]
|
||||
assert hasattr(src_module, "__path__")
|
||||
|
||||
finally:
|
||||
# Clean up
|
||||
modules_to_remove = [
|
||||
name for name in sys.modules.keys() if name not in original_modules
|
||||
]
|
||||
for module_name in modules_to_remove:
|
||||
del sys.modules[module_name]
|
||||
|
||||
|
||||
def test_module_importer_handles_edge_cases(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""Test module importer handling of edge cases like empty files."""
|
||||
|
||||
# Create actual files on disk
|
||||
module_dir = tmp_path / "test_module"
|
||||
module_dir.mkdir()
|
||||
|
||||
pkg_dir = module_dir / "pkg"
|
||||
pkg_dir.mkdir()
|
||||
|
||||
# Create empty files
|
||||
(module_dir / "empty_module.py").write_text("")
|
||||
(pkg_dir / "__init__.py").write_text("# Package init")
|
||||
|
||||
# Also test cross-module imports
|
||||
src_dir = module_dir / "src"
|
||||
src_dir.mkdir()
|
||||
(src_dir / "math_utils.py").write_text("def square(x): return x * x")
|
||||
(src_dir / "geometry.py").write_text(
|
||||
"from src.math_utils import square\ndef area_square(side): return square(side)"
|
||||
)
|
||||
|
||||
# Store original modules state
|
||||
original_modules = sys.modules.copy()
|
||||
|
||||
try:
|
||||
ModuleImporter.import_module_from_disk(module_dir)
|
||||
|
||||
# Verify empty modules were created successfully
|
||||
assert "empty_module" in sys.modules
|
||||
assert "pkg.__init__" in sys.modules
|
||||
|
||||
# Verify cross-module functionality works
|
||||
geometry_module = sys.modules["src.geometry"]
|
||||
assert hasattr(geometry_module, "area_square")
|
||||
assert geometry_module.area_square(5) == 25
|
||||
|
||||
finally:
|
||||
# Clean up
|
||||
modules_to_remove = [
|
||||
name for name in sys.modules.keys() if name not in original_modules
|
||||
]
|
||||
for module_name in modules_to_remove:
|
||||
del sys.modules[module_name]
|
||||
|
||||
|
||||
def test_module_importer_input_validation(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
"""Test input validation for import_module_from_disk."""
|
||||
|
||||
# Test invalid module_path type
|
||||
with pytest.raises(TypeError, match="module_path must be a Path object"):
|
||||
ModuleImporter.import_module_from_disk("not_a_path") # type: ignore
|
||||
|
||||
# Test non-existent path
|
||||
non_existent = tmp_path / "does_not_exist"
|
||||
with pytest.raises(FileNotFoundError, match="does not exist"):
|
||||
ModuleImporter.import_module_from_disk(non_existent)
|
||||
|
||||
# Test path that is not a directory
|
||||
file_path = tmp_path / "file.txt"
|
||||
file_path.write_text("not a directory")
|
||||
with pytest.raises(ValueError, match="module_path must be a directory"):
|
||||
ModuleImporter.import_module_from_disk(file_path)
|
||||
|
||||
# Test directory with no Python files
|
||||
empty_dir = tmp_path / "empty"
|
||||
empty_dir.mkdir()
|
||||
with pytest.raises(ValueError, match="No Python files found"):
|
||||
ModuleImporter.import_module_from_disk(empty_dir)
|
||||
@@ -0,0 +1,54 @@
|
||||
"""Definition of unit tests for PasswordProvider class."""
|
||||
|
||||
import os
|
||||
import base64
|
||||
import json
|
||||
|
||||
from python_encrypt_code.password_provider import PasswordProvider
|
||||
|
||||
|
||||
def test_password_provider_generates_password() -> None:
|
||||
"""Test that PasswordProvider generates a password correctly."""
|
||||
|
||||
# Generate password
|
||||
password = PasswordProvider.generate_password()
|
||||
|
||||
# Verify password is a non-empty string
|
||||
assert isinstance(password, str), "Generated password should be a string."
|
||||
assert len(password) > 0, "Generated password should not be empty."
|
||||
|
||||
# Verify password is URL-safe base64
|
||||
try:
|
||||
decoded_bytes = base64.urlsafe_b64decode(
|
||||
password.encode(PasswordProvider._encoding)
|
||||
)
|
||||
print(decoded_bytes)
|
||||
assert len(decoded_bytes) == 32, "Decoded password should be 32 bytes long."
|
||||
except Exception as e:
|
||||
assert False, f"Generated password is not valid URL-safe base64: {e}"
|
||||
|
||||
|
||||
def test_password_provider_get_decryption_keys() -> None:
|
||||
"""Test that PasswordProvider.get_decryption_keys retrieves the keys correctly."""
|
||||
|
||||
# Set environment variable for get_decryption_keys test
|
||||
expected_password = PasswordProvider.generate_password()
|
||||
expected_aad = PasswordProvider._collect_metadata()
|
||||
os.environ["PEC_PASSWORD"] = expected_password
|
||||
os.environ["PEC_AAD"] = json.dumps(expected_aad)
|
||||
|
||||
# Retrieve keys
|
||||
password, aad = PasswordProvider.get_decryption_keys()
|
||||
|
||||
# Verify retrieved password
|
||||
assert isinstance(password, str), "Password should be a string."
|
||||
assert len(password) > 0, "Password should not be empty."
|
||||
assert password == expected_password, (
|
||||
"get_decryption_keys should return the password from environment variable."
|
||||
)
|
||||
|
||||
# Verify retrieved AAD
|
||||
assert isinstance(aad, dict), "AAD should be a dictionary."
|
||||
assert aad == expected_aad, (
|
||||
"get_decryption_keys should return the AAD from environment variable."
|
||||
)
|
||||
Reference in New Issue
Block a user