"""Tests for the readability metric.""" import pytest from veritext.metrics import Readability, ReadabilityResult class TestReadability: """Tests for the Readability metric class.""" @pytest.fixture def readability(self) -> Readability: """Provide a readability metric instance.""" return Readability() def test_name(self, readability: Readability) -> None: """Test that name returns 'readability'.""" assert readability.name == "readability" def test_requires_reference(self, readability: Readability) -> None: """Test that readability does NOT require reference text.""" assert readability.requires_reference is False def test_simple_text(self, readability: Readability) -> None: """Test readability of simple, easy text.""" # Simple children's text - short sentences, simple words text = "The cat sat. The dog ran. I see a bird." result = readability.score(text) # Should have low grade level and high reading ease assert result.flesch_kincaid_grade < 5.0 assert result.flesch_reading_ease > 80.0 def test_complex_text(self, readability: Readability) -> None: """Test readability of complex, academic text.""" # Complex academic text - long sentences, polysyllabic words text = ( "The implementation of sophisticated computational methodologies " "necessitates comprehensive understanding of algorithmic complexity " "and architectural considerations." ) result = readability.score(text) # Should have high grade level and low reading ease assert result.flesch_kincaid_grade > 12.0 assert result.flesch_reading_ease < 30.0 def test_medium_text(self, readability: Readability) -> None: """Test readability of medium-difficulty text.""" text = ( "The weather today is quite pleasant. " "Many people are enjoying the sunshine in the park. " "Children play while parents watch nearby." ) result = readability.score(text) # Should be middle of the road assert 3.0 < result.flesch_kincaid_grade < 10.0 assert 50.0 < result.flesch_reading_ease < 90.0 def test_single_sentence(self, readability: Readability) -> None: """Test readability with a single sentence.""" text = "The cat sat on the mat." result = readability.score(text) # Should compute without error assert result.flesch_kincaid_grade is not None assert result.flesch_reading_ease is not None def test_single_word(self, readability: Readability) -> None: """Test readability with a single word.""" text = "Cat" result = readability.score(text) # Should handle single word (1 word, 1 sentence, 1 syllable) assert result.flesch_kincaid_grade is not None assert result.flesch_reading_ease is not None def test_empty_text(self, readability: Readability) -> None: """Test that empty text returns zero scores.""" result = readability.score("") assert result.flesch_kincaid_grade == 0.0 assert result.flesch_reading_ease == 0.0 def test_whitespace_only(self, readability: Readability) -> None: """Test that whitespace-only text returns zero scores.""" result = readability.score(" \t\n ") assert result.flesch_kincaid_grade == 0.0 assert result.flesch_reading_ease == 0.0 def test_reference_ignored(self, readability: Readability) -> None: """Test that reference parameter is ignored.""" text = "The cat sat on the mat." # Score with no reference result1 = readability.score(text) # Score with reference (should be ignored) result2 = readability.score(text, "Completely different text") # Score with list of references result3 = readability.score(text, ["ref1", "ref2"]) # All should produce identical results assert result1.flesch_kincaid_grade == result2.flesch_kincaid_grade assert result1.flesch_reading_ease == result2.flesch_reading_ease assert result1.flesch_kincaid_grade == result3.flesch_kincaid_grade def test_punctuation_handling(self, readability: Readability) -> None: """Test that punctuation affects sentence counting.""" # Same words, different sentence structure text1 = "The cat sat on the mat" # 1 sentence text2 = "The cat sat. On the mat." # 2 sentences result1 = readability.score(text1) result2 = readability.score(text2) # Different sentence counts should affect scores assert result1.flesch_kincaid_grade != result2.flesch_kincaid_grade def test_question_marks_count_sentences(self, readability: Readability) -> None: """Test that question marks end sentences.""" text = "What is this? It is a test." result = readability.score(text) # Should count as 2 sentences # With 7 words total, words_per_sentence = 3.5 assert result.flesch_kincaid_grade is not None def test_exclamation_marks_count_sentences(self, readability: Readability) -> None: """Test that exclamation marks end sentences.""" text = "Wow! That is amazing!" result = readability.score(text) # Should count as 2 sentences assert result.flesch_kincaid_grade is not None def test_multiple_punctuation(self, readability: Readability) -> None: """Test handling of multiple punctuation marks.""" text = "What?! That's crazy... Well then." result = readability.score(text) # Should handle gracefully assert result.flesch_kincaid_grade is not None def test_result_score_property(self, readability: Readability) -> None: """Test that result.score returns flesch_reading_ease.""" result = readability.score("The cat sat on the mat.") assert result.score == result.flesch_reading_ease def test_contractions(self, readability: Readability) -> None: """Test handling of contractions.""" text = "I'm going to the store. It's not far away." result = readability.score(text) # Should handle contractions as words assert result.flesch_kincaid_grade is not None assert result.flesch_reading_ease is not None class TestReadabilityBatch: """Tests for readability batch scoring.""" @pytest.fixture def readability(self) -> Readability: """Provide a readability metric instance.""" return Readability() def test_batch_score_basic(self, readability: Readability) -> None: """Test basic batch scoring.""" candidates = [ "The cat sat on the mat.", "A dog ran through the park.", ] result = readability.batch_score(candidates) assert result.count == 2 assert len(result.results) == 2 def test_batch_score_statistics(self, readability: Readability) -> None: """Test that batch scoring computes statistics.""" candidates = [ "Cat sat.", # Very simple "The implementation of sophisticated methodologies requires expertise.", ] result = readability.batch_score(candidates) # Check statistics are computed assert "flesch_kincaid_grade" in result.stats assert "flesch_reading_ease" in result.stats # First should be easier than second assert ( result.results[0].flesch_reading_ease > result.results[1].flesch_reading_ease ) def test_batch_score_percentiles(self, readability: Readability) -> None: """Test that batch scoring computes percentiles.""" candidates = ["a", "b", "c", "d", "e"] result = readability.batch_score(candidates) stats = result.stats["flesch_reading_ease"] assert 25 in stats.percentiles assert 50 in stats.percentiles assert 75 in stats.percentiles assert 95 in stats.percentiles def test_batch_score_references_ignored(self, readability: Readability) -> None: """Test that batch scoring ignores references.""" candidates = ["The cat sat.", "A dog ran."] result1 = readability.batch_score(candidates) result2 = readability.batch_score(candidates, ["ref1", "ref2"]) # Results should be identical assert result1.results[0].flesch_kincaid_grade == ( result2.results[0].flesch_kincaid_grade ) def test_batch_score_empty_list_raises(self, readability: Readability) -> None: """Test that empty candidate list raises ValueError.""" with pytest.raises(ValueError, match="empty"): readability.batch_score([]) class TestReadabilityResult: """Tests for ReadabilityResult type.""" def test_frozen(self) -> None: """Test that ReadabilityResult is frozen.""" from pydantic import ValidationError result = ReadabilityResult(flesch_kincaid_grade=5.0, flesch_reading_ease=70.0) with pytest.raises(ValidationError): result.flesch_kincaid_grade = 6.0 # type: ignore[misc] def test_values(self) -> None: """Test that values are stored correctly.""" result = ReadabilityResult(flesch_kincaid_grade=8.5, flesch_reading_ease=65.0) assert result.flesch_kincaid_grade == 8.5 assert result.flesch_reading_ease == 65.0 def test_score_property(self) -> None: """Test that score property returns flesch_reading_ease.""" result = ReadabilityResult(flesch_kincaid_grade=8.5, flesch_reading_ease=65.0) assert result.score == 65.0 class TestSyllableCounting: """Tests for syllable counting heuristics.""" @pytest.fixture def readability(self) -> Readability: """Provide a readability metric instance.""" return Readability() def test_monosyllabic_words(self, readability: Readability) -> None: """Test that monosyllabic words don't inflate scores.""" # All one-syllable words text = "The cat sat on the mat." result = readability.score(text) # Should be very easy to read assert result.flesch_reading_ease > 90.0 def test_polysyllabic_words(self, readability: Readability) -> None: """Test that polysyllabic words affect scores.""" # Words with multiple syllables text = "International communication facilitates understanding." result = readability.score(text) # Should be harder to read assert result.flesch_reading_ease < 50.0