Files
veritext/tests/test_pytest_plugin/test_assertions.py
Kai Chappell 012b306749 test(pytest-plugin): add plugin tests
Cover validate_text assertions, fixture factories, marker registration,
and pytest integration using pytester for subprocess testing.
2026-02-03 17:40:46 +00:00

212 lines
8.9 KiB
Python

"""Tests for the validate_text assertion function."""
import pytest
from veritext.pytest_plugin import validate_text
class TestValidateTextBasicValidation:
"""Test basic validation scenarios."""
def test_passes_with_valid_length(self) -> None:
"""Test validation passes when length constraints are met."""
text = "The quick brown fox jumps over the lazy dog."
validate_text(text, min_length=10, max_length=100)
def test_fails_when_too_short(self) -> None:
"""Test validation fails when text is below minimum length."""
text = "Short."
with pytest.raises(AssertionError) as exc_info:
validate_text(text, min_length=50)
assert "length" in str(exc_info.value).lower()
def test_fails_when_too_long(self) -> None:
"""Test validation fails when text exceeds maximum length."""
text = "A" * 100
with pytest.raises(AssertionError) as exc_info:
validate_text(text, max_length=50)
assert "length" in str(exc_info.value).lower()
class TestValidateTextReadability:
"""Test readability validation."""
def test_passes_with_simple_text(self) -> None:
"""Test validation passes for simple, readable text."""
text = "The cat sat on the mat. It was a nice day."
validate_text(text, max_reading_grade=10.0)
def test_fails_with_complex_text(self) -> None:
"""Test validation fails for overly complex text."""
text = (
"The implementation of sophisticated metacognitive strategies "
"necessitates the comprehensive understanding of epistemological "
"frameworks and their corresponding methodological implications."
)
with pytest.raises(AssertionError) as exc_info:
validate_text(text, max_reading_grade=3.0)
assert "readability" in str(exc_info.value).lower()
class TestValidateTextPatterns:
"""Test pattern matching validation."""
def test_passes_when_contains_pattern(self) -> None:
"""Test validation passes when required pattern is present."""
text = "Please contact support@example.com for assistance."
validate_text(text, must_contain=["support@example.com"])
def test_fails_when_missing_required_pattern(self) -> None:
"""Test validation fails when required pattern is missing."""
text = "Please contact us for assistance."
with pytest.raises(AssertionError) as exc_info:
validate_text(text, must_contain=["@example.com"])
assert "contains" in str(exc_info.value).lower()
def test_passes_when_excludes_pattern(self) -> None:
"""Test validation passes when forbidden pattern is absent."""
text = "The report is complete and reviewed."
validate_text(text, must_exclude=["TODO", "FIXME"])
def test_fails_when_contains_forbidden_pattern(self) -> None:
"""Test validation fails when forbidden pattern is present."""
text = "The report is almost done. TODO: add conclusion."
with pytest.raises(AssertionError) as exc_info:
validate_text(text, must_exclude=["TODO"])
assert "excludes" in str(exc_info.value).lower()
class TestValidateTextComparisonMetrics:
"""Test comparison-based validation (BLEU, ROUGE)."""
def test_passes_with_high_bleu_score(self) -> None:
"""Test validation passes when BLEU score meets threshold."""
reference = "The quick brown fox jumps over the lazy dog."
text = "The quick brown fox jumps over the lazy dog."
validate_text(text, reference=reference, min_bleu=0.9)
def test_fails_with_low_bleu_score(self) -> None:
"""Test validation fails when BLEU score is below threshold."""
reference = "The quick brown fox jumps over the lazy dog."
text = "A slow red cat sleeps under the active mouse."
with pytest.raises(AssertionError) as exc_info:
validate_text(text, reference=reference, min_bleu=0.5)
assert "bleu" in str(exc_info.value).lower()
def test_passes_with_high_rouge_score(self) -> None:
"""Test validation passes when ROUGE score meets threshold."""
reference = "Machine learning models require extensive training data."
text = "Machine learning models need extensive training data."
validate_text(text, reference=reference, min_rouge=0.5)
def test_fails_with_low_rouge_score(self) -> None:
"""Test validation fails when ROUGE score is below threshold."""
reference = "The algorithm processes input data efficiently."
text = "Cats enjoy sleeping in sunny spots."
with pytest.raises(AssertionError) as exc_info:
validate_text(text, reference=reference, min_rouge=0.5)
assert "rouge" in str(exc_info.value).lower()
class TestValidateTextErrorHandling:
"""Test error handling and edge cases."""
def test_raises_value_error_when_no_criteria(self) -> None:
"""Test that ValueError is raised when no validation criteria provided."""
with pytest.raises(ValueError, match="At least one validation criterion"):
validate_text("Some text")
def test_raises_value_error_when_bleu_without_reference(self) -> None:
"""Test that ValueError is raised when BLEU requested without reference."""
with pytest.raises(ValueError, match="Reference text required"):
validate_text("Some text", min_bleu=0.5)
def test_raises_value_error_when_rouge_without_reference(self) -> None:
"""Test that ValueError is raised when ROUGE requested without reference."""
with pytest.raises(ValueError, match="Reference text required"):
validate_text("Some text", min_rouge=0.5)
def test_raises_value_error_when_semantic_without_reference(self) -> None:
"""Test that ValueError is raised for semantic without reference."""
with pytest.raises(ValueError, match="Reference text required"):
validate_text("Some text", min_semantic=0.5)
class TestValidateTextMultipleCriteria:
"""Test validation with multiple criteria combined."""
def test_passes_all_criteria(self) -> None:
"""Test validation passes when all criteria are met."""
reference = "The quick brown fox jumps over the lazy dog."
text = "The quick brown fox jumps over the lazy dog."
validate_text(
text,
reference=reference,
min_bleu=0.9,
min_length=10,
max_length=100,
)
def test_fails_when_one_criterion_fails(self) -> None:
"""Test validation fails when any criterion fails."""
reference = "The quick brown fox jumps over the lazy dog."
text = "The quick brown fox jumps over the lazy dog."
with pytest.raises(AssertionError):
validate_text(
text,
reference=reference,
min_bleu=0.9,
max_length=10, # This will fail
)
class TestValidateTextFailureMessage:
"""Test failure message formatting."""
def test_failure_message_includes_text_preview(self) -> None:
"""Test that failure message includes preview of the text."""
text = "Short text"
with pytest.raises(AssertionError) as exc_info:
validate_text(text, min_length=100)
assert "Short text" in str(exc_info.value)
def test_failure_message_truncates_long_text(self) -> None:
"""Test that long text is truncated in failure message."""
text = "A" * 200
with pytest.raises(AssertionError) as exc_info:
validate_text(text, max_length=50)
message = str(exc_info.value)
assert "..." in message
assert "A" * 200 not in message
def test_failure_message_includes_check_details(self) -> None:
"""Test that failure message includes check name and details."""
text = "Short"
with pytest.raises(AssertionError) as exc_info:
validate_text(text, min_length=100)
message = str(exc_info.value)
assert "Failed checks:" in message
assert "length" in message.lower()
class TestValidateTextListReference:
"""Test validation with list of reference texts."""
def test_bleu_with_multiple_references(self) -> None:
"""Test BLEU validation accepts multiple reference texts."""
references = [
"The quick brown fox jumps over the lazy dog.",
"A fast brown fox leaps over a sleepy dog.",
]
text = "The quick brown fox jumps over the lazy dog."
validate_text(text, reference=references, min_bleu=0.9)
def test_rouge_with_multiple_references(self) -> None:
"""Test ROUGE validation accepts multiple reference texts."""
references = [
"Machine learning requires data.",
"ML models need training data.",
]
text = "Machine learning models require training data."
validate_text(text, reference=references, min_rouge=0.3)