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

100 lines
3.3 KiB
Python

"""
©AngelaMos | 2026
test_analyzer.py
Tests for FrequencyAnalyzer covering chi-squared scoring and candidate ranking
Tests:
chi-squared scoring distinguishes English text from gibberish
empty string returns infinity
rank_candidates() orders results by score ascending
end-to-end crack-and-rank against a known plaintext
Connects to:
analyzer.py - the class under test
cipher.py - crack() output used as input to rank_candidates()
"""
from caesar_cipher.analyzer import FrequencyAnalyzer
from caesar_cipher.cipher import CaesarCipher
class TestFrequencyAnalyzer:
def test_calculate_chi_squared_english_text(self) -> None:
"""
Confirms real English text scores below a reasonable chi-squared threshold
"""
analyzer = FrequencyAnalyzer()
english_text = "THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG"
score = analyzer.calculate_chi_squared(english_text)
assert score < 150
def test_calculate_chi_squared_gibberish(self) -> None:
"""
Confirms text made of rare letters scores above the threshold
"""
analyzer = FrequencyAnalyzer()
gibberish = "ZZZZZ QQQQQ XXXXX"
score = analyzer.calculate_chi_squared(gibberish)
assert score > 100
def test_calculate_chi_squared_empty_string(self) -> None:
"""
Confirms an empty string returns infinity since there are no letters to score
"""
analyzer = FrequencyAnalyzer()
assert analyzer.calculate_chi_squared("") == float("inf")
def test_score_text_lowercase(self) -> None:
"""
Confirms score_text returns a non-negative float for lowercase input
"""
analyzer = FrequencyAnalyzer()
score = analyzer.score_text("hello world")
assert isinstance(score, float)
assert score >= 0
def test_rank_candidates_orders_by_score(self) -> None:
"""
Confirms candidates are sorted so the most English-like text ranks first
"""
analyzer = FrequencyAnalyzer()
candidates = [
(0,
"gibberish zzz"),
(1,
"the quick brown fox"),
(2,
"qqq xxx zzz"),
]
ranked = analyzer.rank_candidates(candidates)
assert len(ranked) == 3
assert ranked[0][1] == "the quick brown fox"
assert ranked[0][2] < ranked[1][2]
assert ranked[1][2] < ranked[2][2]
def test_rank_candidates_with_actual_cipher(self) -> None:
"""
Encrypts known text, brute-forces all shifts, and confirms the correct key ranks first
"""
cipher = CaesarCipher(key = 3)
plaintext = "THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG"
ciphertext = cipher.encrypt(plaintext)
candidates = CaesarCipher.crack(ciphertext)
analyzer = FrequencyAnalyzer()
ranked = analyzer.rank_candidates(candidates)
best_shift, best_text, _best_score = ranked[0]
assert best_shift == 3
assert best_text == plaintext
def test_rank_candidates_empty_list(self) -> None:
"""
Confirms an empty candidate list returns an empty ranked list
"""
analyzer = FrequencyAnalyzer()
ranked = analyzer.rank_candidates([])
assert ranked == []