163 lines
5.3 KiB
Python
163 lines
5.3 KiB
Python
"""
|
|
©AngelaMos | 2026
|
|
test_cipher.py
|
|
|
|
Tests for CaesarCipher covering encryption, decryption, and brute-force cracking
|
|
|
|
Tests:
|
|
encrypt/decrypt correctness for upper, lower, and mixed case
|
|
non-letter preservation (spaces, punctuation, numbers)
|
|
encrypt/decrypt roundtrip fidelity
|
|
key validation and edge cases (zero key, negative key, alphabet wrapping)
|
|
crack() generating all 26 shifts and locating the correct one
|
|
|
|
Connects to:
|
|
cipher.py - the class under test
|
|
"""
|
|
|
|
import pytest
|
|
|
|
from caesar_cipher.cipher import CaesarCipher
|
|
|
|
|
|
class TestCaesarCipher:
|
|
def test_encrypt_basic(self) -> None:
|
|
"""
|
|
Encrypts uppercase text with key 3 and checks the shifted output
|
|
"""
|
|
cipher = CaesarCipher(key = 3)
|
|
assert cipher.encrypt("HELLO") == "KHOOR"
|
|
|
|
def test_encrypt_lowercase(self) -> None:
|
|
"""
|
|
Encrypts lowercase text and confirms lowercase output is preserved
|
|
"""
|
|
cipher = CaesarCipher(key = 3)
|
|
assert cipher.encrypt("hello") == "khoor"
|
|
|
|
def test_encrypt_mixed_case(self) -> None:
|
|
"""
|
|
Encrypts mixed-case text and confirms upper and lower shift independently
|
|
"""
|
|
cipher = CaesarCipher(key = 3)
|
|
assert cipher.encrypt("Hello World") == "Khoor Zruog"
|
|
|
|
def test_encrypt_preserves_spaces(self) -> None:
|
|
"""
|
|
Confirms spaces pass through encryption without being shifted
|
|
"""
|
|
cipher = CaesarCipher(key = 5)
|
|
assert cipher.encrypt("ABC XYZ") == "FGH CDE"
|
|
|
|
def test_encrypt_preserves_punctuation(self) -> None:
|
|
"""
|
|
Confirms commas and exclamation marks pass through encryption unchanged
|
|
"""
|
|
cipher = CaesarCipher(key = 3)
|
|
assert cipher.encrypt("Hello, World!") == "Khoor, Zruog!"
|
|
|
|
def test_encrypt_preserves_numbers(self) -> None:
|
|
"""
|
|
Confirms digits pass through encryption unchanged
|
|
"""
|
|
cipher = CaesarCipher(key = 3)
|
|
assert cipher.encrypt("Test123") == "Whvw123"
|
|
|
|
def test_decrypt_basic(self) -> None:
|
|
"""
|
|
Decrypts uppercase ciphertext back to plaintext using the matching key
|
|
"""
|
|
cipher = CaesarCipher(key = 3)
|
|
assert cipher.decrypt("KHOOR") == "HELLO"
|
|
|
|
def test_decrypt_lowercase(self) -> None:
|
|
"""
|
|
Decrypts lowercase ciphertext and confirms case is preserved in output
|
|
"""
|
|
cipher = CaesarCipher(key = 3)
|
|
assert cipher.decrypt("khoor") == "hello"
|
|
|
|
def test_encrypt_decrypt_roundtrip(self) -> None:
|
|
"""
|
|
Encrypts then decrypts a full sentence and asserts the result matches the original
|
|
"""
|
|
cipher = CaesarCipher(key = 13)
|
|
original = "The Quick Brown Fox Jumps Over The Lazy Dog!"
|
|
encrypted = cipher.encrypt(original)
|
|
decrypted = cipher.decrypt(encrypted)
|
|
assert decrypted == original
|
|
|
|
def test_key_wrapping(self) -> None:
|
|
"""
|
|
Confirms key 26 is equivalent to key 0 and produces no shift
|
|
"""
|
|
cipher = CaesarCipher(key = 26)
|
|
assert cipher.encrypt("ABC") == "ABC"
|
|
|
|
def test_negative_key(self) -> None:
|
|
"""
|
|
Confirms a negative key shifts letters backward through the alphabet
|
|
"""
|
|
cipher = CaesarCipher(key = -3)
|
|
assert cipher.encrypt("HELLO") == "EBIIL"
|
|
|
|
def test_zero_key(self) -> None:
|
|
"""
|
|
Confirms key 0 leaves the plaintext completely unchanged
|
|
"""
|
|
cipher = CaesarCipher(key = 0)
|
|
assert cipher.encrypt("HELLO") == "HELLO"
|
|
|
|
def test_key_validation_too_large(self) -> None:
|
|
"""
|
|
Confirms keys above 26 raise ValueError with the expected message
|
|
"""
|
|
with pytest.raises(ValueError, match = "Key must be between -25 and 26"):
|
|
CaesarCipher(key = 30)
|
|
|
|
def test_key_validation_too_small(self) -> None:
|
|
"""
|
|
Confirms keys below -25 raise ValueError with the expected message
|
|
"""
|
|
with pytest.raises(ValueError, match = "Key must be between -25 and 26"):
|
|
CaesarCipher(key = -30)
|
|
|
|
def test_crack_returns_all_shifts(self) -> None:
|
|
"""
|
|
Confirms crack() produces exactly 26 candidate decryptions
|
|
"""
|
|
results = CaesarCipher.crack("KHOOR")
|
|
assert len(results) == 26
|
|
|
|
def test_crack_finds_correct_shift(self) -> None:
|
|
"""
|
|
Confirms the known shift key appears in crack() output with the correct plaintext
|
|
"""
|
|
cipher = CaesarCipher(key = 3)
|
|
encrypted = cipher.encrypt("HELLO")
|
|
results = CaesarCipher.crack(encrypted)
|
|
shifts_dict = dict(results)
|
|
assert shifts_dict[3] == "HELLO"
|
|
|
|
def test_empty_string(self) -> None:
|
|
"""
|
|
Confirms empty string input returns empty string for both encrypt and decrypt
|
|
"""
|
|
cipher = CaesarCipher(key = 3)
|
|
assert cipher.encrypt("") == ""
|
|
assert cipher.decrypt("") == ""
|
|
|
|
def test_alphabet_wraparound_uppercase(self) -> None:
|
|
"""
|
|
Confirms XYZ wraps around to ABC when shifted forward by 3
|
|
"""
|
|
cipher = CaesarCipher(key = 3)
|
|
assert cipher.encrypt("XYZ") == "ABC"
|
|
|
|
def test_alphabet_wraparound_lowercase(self) -> None:
|
|
"""
|
|
Confirms xyz wraps around to abc when shifted forward by 3
|
|
"""
|
|
cipher = CaesarCipher(key = 3)
|
|
assert cipher.encrypt("xyz") == "abc"
|