"""Tests for data module validation.""" import os import sys sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) from data import ( get_categories, get_verse_by_id, get_verses_by_category, get_verses_by_max_difficulty, load_verses, normalize_text, ) class TestLoadVerses: """Tests for verse data loading.""" def test_load_returns_list(self) -> None: """Verses load as a list.""" verses = load_verses() assert isinstance(verses, list) def test_minimum_verses(self) -> None: """At least 12 verses loaded.""" verses = load_verses() assert len(verses) >= 12 def test_verse_structure(self) -> None: """Each verse has required fields: id, text, category, difficulty, reference.""" verses = load_verses() required_fields = {"id", "text", "category", "difficulty", "reference"} for verse in verses: assert required_fields.issubset(verse.keys()), ( f"Missing fields in {verse.get('id', 'unknown')}" ) def test_unique_ids(self) -> None: """All verse IDs are unique.""" verses = load_verses() ids = [v["id"] for v in verses] assert len(ids) == len(set(ids)) def test_categories_present(self) -> None: """All four categories represented: Binding, Healing, Exorcism, Protection, Judgment.""" verses = load_verses() categories = get_categories(verses) expected = {"Binding", "Healing", "Exorcism", "Protection", "Judgment"} assert expected.issubset(set(categories)) def test_difficulty_range(self) -> None: """All difficulties are between 1 and 5.""" verses = load_verses() for verse in verses: assert 1 <= verse["difficulty"] <= 5 def test_easy_verses_count(self) -> None: """At least 4 easy (difficulty 1) verses for starting players.""" verses = load_verses() easy = get_verses_by_max_difficulty(verses, 1) assert len(easy) >= 4 class TestNormalizeText: """Tests for text normalization used in verse matching.""" def test_removes_punctuation(self) -> None: """Punctuation is stripped from text.""" result = normalize_text("Hello, world! How are you?") assert result == "hello world how are you" def test_lowercases(self) -> None: """Text is converted to lowercase.""" result = normalize_text("HELLO WORLD") assert result == "hello world" def test_collapses_whitespace(self) -> None: """Multiple spaces are collapsed to single space.""" result = normalize_text("Hello world") assert result == "hello world" def test_combined(self) -> None: """Combined punctuation, case, and whitespace normalization.""" result = normalize_text(" Hello, World! ") assert result == "hello world" def test_empty_string(self) -> None: """Empty string returns empty string.""" assert normalize_text("") == "" def test_identical_verses_match(self) -> None: """Same verse text normalizes to identical strings.""" verse = "I have given you authority to trample on snakes and scorpions." typed = "I have given you authority to trample on snakes and scorpions." assert normalize_text(verse) == normalize_text(typed) def test_punctuation_difference_ignored(self) -> None: """Punctuation differences are ignored in normalization.""" verse = "He heals the brokenhearted and binds up their wounds." typed = "He heals the brokenhearted, and binds up their wounds!" assert normalize_text(verse) == normalize_text(typed) class TestGetVerseById: """Tests for verse lookup by ID.""" def test_finds_existing_verse(self) -> None: """Existing verse ID returns the verse.""" verses = load_verses() verse = get_verse_by_id(verses, "binding_001") assert verse is not None assert verse["id"] == "binding_001" def test_returns_none_for_missing(self) -> None: """Non-existent ID returns None.""" verses = load_verses() assert get_verse_by_id(verses, "nonexistent_999") is None class TestGetVersesByCategory: """Tests for filtering verses by category.""" def test_filter_by_category(self) -> None: """Returns only verses matching the category.""" verses = load_verses() binding = get_verses_by_category(verses, "Binding") assert all(v["category"] == "Binding" for v in binding) assert len(binding) >= 3 def test_case_insensitive(self) -> None: """Category filtering is case-insensitive.""" verses = load_verses() result1 = get_verses_by_category(verses, "Binding") result2 = get_verses_by_category(verses, "binding") assert result1 == result2 class TestGetVersesByMaxDifficulty: """Tests for filtering verses by maximum difficulty.""" def test_filters_correctly(self) -> None: """Returns only verses at or below max difficulty.""" verses = load_verses() easy = get_verses_by_max_difficulty(verses, 1) assert all(v["difficulty"] <= 1 for v in easy) def test_includes_all_at_level(self) -> None: """All verses up to max difficulty are included.""" verses = load_verses() all_verses = load_verses() result = get_verses_by_max_difficulty(verses, 5) assert len(result) == len(all_verses)