Cybersecurity-Projects/PROJECTS/beginner/caesar-cipher/tests/test_cli.py

163 lines
4.9 KiB
Python

"""
©AngelaMos | 2026
test_cli.py
Integration tests for the encrypt, decrypt, and crack CLI commands
Tests each command's exit codes, output content, key validation, and
file-based I/O using Typer's CliRunner without spawning a subprocess.
Tests:
encrypt command: basic output, spaces, invalid key rejection
decrypt command: basic output, spaces, invalid key rejection
crack command: output format, --top option, --all option
file I/O: reading from and writing to temp files
Connects to:
main.py - invokes the Typer app under test
"""
from pathlib import Path
from typer.testing import CliRunner
from caesar_cipher.main import app
runner = CliRunner()
class TestEncryptCommand:
def test_encrypt_basic(self) -> None:
"""
Runs the encrypt command with key 3 and checks KHOOR appears in output
"""
result = runner.invoke(app, ["encrypt", "HELLO", "--key", "3"])
assert result.exit_code == 0
assert "KHOOR" in result.stdout
def test_encrypt_with_spaces(self) -> None:
"""
Runs encrypt on a two-word phrase and checks both words shift correctly
"""
result = runner.invoke(app, ["encrypt", "HELLO WORLD", "--key", "3"])
assert result.exit_code == 0
assert "KHOOR ZRUOG" in result.stdout
def test_encrypt_invalid_key(self) -> None:
"""
Runs encrypt with key 30 and confirms exit code 1 and an error message
"""
result = runner.invoke(app, ["encrypt", "HELLO", "--key", "30"])
assert result.exit_code == 1
assert "Error" in result.stdout
class TestDecryptCommand:
def test_decrypt_basic(self) -> None:
"""
Runs the decrypt command with key 3 and checks HELLO appears in output
"""
result = runner.invoke(app, ["decrypt", "KHOOR", "--key", "3"])
assert result.exit_code == 0
assert "HELLO" in result.stdout
def test_decrypt_with_spaces(self) -> None:
"""
Runs decrypt on a two-word ciphertext and checks the full plaintext is recovered
"""
result = runner.invoke(app, ["decrypt", "KHOOR ZRUOG", "--key", "3"])
assert result.exit_code == 0
assert "HELLO WORLD" in result.stdout
def test_decrypt_invalid_key(self) -> None:
"""
Runs decrypt with an out-of-range key and confirms exit code 1 and an error message
"""
result = runner.invoke(app, ["decrypt", "KHOOR", "--key", "30"])
assert result.exit_code == 1
assert "Error" in result.stdout
class TestCrackCommand:
def test_crack_basic(self) -> None:
"""
Runs the crack command and confirms HELLO is recovered and best match is shown
"""
result = runner.invoke(app, ["crack", "KHOOR"])
assert result.exit_code == 0
assert "HELLO" in result.stdout
assert "Best match" in result.stdout
def test_crack_with_top_option(self) -> None:
"""
Runs crack with --top 3 and confirms the command completes successfully
"""
result = runner.invoke(app, ["crack", "KHOOR", "--top", "3"])
assert result.exit_code == 0
def test_crack_show_all(self) -> None:
"""
Runs crack with --all and confirms all 26 shifts are displayed without error
"""
result = runner.invoke(app, ["crack", "KHOOR", "--all"])
assert result.exit_code == 0
class TestFileIO:
def test_encrypt_from_file(self, tmp_path: Path) -> None:
"""
Encrypts text read from a temp input file and checks the ciphertext in output
"""
input_file = tmp_path / "input.txt"
input_file.write_text("HELLO WORLD")
result = runner.invoke(
app,
["encrypt",
"--input-file",
str(input_file),
"--key",
"3"]
)
assert result.exit_code == 0
assert "KHOOR ZRUOG" in result.stdout
def test_encrypt_to_file(self, tmp_path: Path) -> None:
"""
Encrypts text and writes to a temp file, then reads it back to verify contents
"""
output_file = tmp_path / "output.txt"
result = runner.invoke(
app,
[
"encrypt",
"HELLO",
"--key",
"3",
"--output-file",
str(output_file),
],
)
assert result.exit_code == 0
assert output_file.read_text() == "KHOOR"
def test_decrypt_from_file(self, tmp_path: Path) -> None:
"""
Decrypts text read from a temp input file and checks the plaintext in output
"""
input_file = tmp_path / "input.txt"
input_file.write_text("KHOOR")
result = runner.invoke(
app,
["decrypt",
"--input-file",
str(input_file),
"--key",
"3"]
)
assert result.exit_code == 0
assert "HELLO" in result.stdout