Files
kjvstudy.org/tests/test_strongs.py
kennethreitz a1f2228c1a Add comprehensive Strong's Concordance test suite
62 new tests covering:
- Data loading and basic lookup (Hebrew/Greek entries)
- Helper functions (definition, word, transliteration, KJV usage)
- format_strongs_entry function
- Search functionality (both languages, limits, case-insensitive)
- Paginated listing (get_all_strongs)
- Web routes (index, search, entry, Hebrew/Greek indexes, pagination)
- API endpoints
- Edge cases (boundaries, common words, derivation links)
- Integration tests (search flow, navigation, breadcrumbs)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-29 17:11:19 -05:00

491 lines
17 KiB
Python

"""
Tests for Strong's Concordance functionality.
Tests cover:
- Strong's data loading and lookup
- Hebrew and Greek entry retrieval
- Search functionality
- Pagination
- Web routes
- API endpoints
"""
import pytest
from fastapi.testclient import TestClient
from kjvstudy_org.server import app
from kjvstudy_org.strongs import (
get_strongs_entry,
get_strongs_definition,
get_strongs_word,
get_strongs_transliteration,
get_strongs_kjv_usage,
format_strongs_entry,
search_strongs,
get_all_strongs,
)
@pytest.fixture
def client():
"""Create test client"""
return TestClient(app)
class TestStrongsDataLoading:
"""Tests for Strong's data loading and basic lookup"""
def test_get_hebrew_entry(self):
"""Test retrieving a Hebrew Strong's entry"""
entry = get_strongs_entry("H1")
assert entry is not None
assert "lemma" in entry
def test_get_greek_entry(self):
"""Test retrieving a Greek Strong's entry"""
entry = get_strongs_entry("G1")
assert entry is not None
assert "lemma" in entry
def test_get_entry_case_insensitive(self):
"""Test Strong's lookup is case-insensitive"""
entry1 = get_strongs_entry("H430")
entry2 = get_strongs_entry("h430")
assert entry1 == entry2
def test_get_entry_with_whitespace(self):
"""Test Strong's lookup handles whitespace"""
entry = get_strongs_entry(" H1 ")
assert entry is not None
def test_get_invalid_entry(self):
"""Test invalid Strong's number returns None"""
entry = get_strongs_entry("X9999")
assert entry is None
def test_get_nonexistent_entry(self):
"""Test nonexistent Strong's number returns None"""
entry = get_strongs_entry("H99999")
assert entry is None
def test_get_empty_entry(self):
"""Test empty string returns None"""
entry = get_strongs_entry("")
assert entry is None
def test_get_none_entry(self):
"""Test None input returns None"""
entry = get_strongs_entry(None)
assert entry is None
class TestStrongsHelperFunctions:
"""Tests for Strong's helper functions"""
def test_get_definition(self):
"""Test getting Strong's definition"""
definition = get_strongs_definition("G26")
assert definition is not None
assert len(definition) > 0
def test_get_word(self):
"""Test getting original word (lemma)"""
word = get_strongs_word("G26")
assert word is not None
# Greek agape
assert "γάπ" in word or "agap" in word.lower()
def test_get_transliteration(self):
"""Test getting transliteration"""
translit = get_strongs_transliteration("G26")
assert translit is not None
# Transliteration is "agápē" - check for base characters
assert "ag" in translit.lower() and "p" in translit.lower()
def test_get_kjv_usage(self):
"""Test getting KJV usage"""
usage = get_strongs_kjv_usage("G26")
assert usage is not None
assert "love" in usage.lower()
def test_get_hebrew_transliteration(self):
"""Test Hebrew transliteration uses xlit field"""
translit = get_strongs_transliteration("H1")
assert translit is not None
def test_helper_functions_invalid_number(self):
"""Test helper functions handle invalid numbers"""
assert get_strongs_definition("X9999") is None
assert get_strongs_word("X9999") is None
assert get_strongs_transliteration("X9999") is None
assert get_strongs_kjv_usage("X9999") is None
class TestFormatStrongsEntry:
"""Tests for format_strongs_entry function"""
def test_format_greek_entry(self):
"""Test formatting Greek entry"""
entry = format_strongs_entry("G26")
assert entry is not None
assert entry["strongs"] == "G26"
assert entry["language"] == "Greek"
assert "word" in entry
assert "definition" in entry
def test_format_hebrew_entry(self):
"""Test formatting Hebrew entry"""
entry = format_strongs_entry("H430")
assert entry is not None
assert entry["strongs"] == "H430"
assert entry["language"] == "Hebrew"
assert "word" in entry
assert "definition" in entry
def test_format_entry_has_all_fields(self):
"""Test formatted entry has all expected fields"""
entry = format_strongs_entry("G26")
expected_fields = [
"strongs", "language", "word", "transliteration",
"definition", "kjv_usage", "derivation"
]
for field in expected_fields:
assert field in entry
def test_format_hebrew_has_pronunciation(self):
"""Test Hebrew entries include pronunciation"""
entry = format_strongs_entry("H1")
assert "pronunciation" in entry
def test_format_invalid_entry(self):
"""Test formatting invalid entry returns None"""
entry = format_strongs_entry("X9999")
assert entry is None
class TestSearchStrongs:
"""Tests for Strong's search functionality"""
def test_search_finds_results(self):
"""Test search returns results"""
results = search_strongs("love")
assert len(results) > 0
def test_search_result_structure(self):
"""Test search results have correct structure"""
results = search_strongs("love", limit=1)
assert len(results) > 0
result = results[0]
assert "strongs" in result
assert "language" in result
assert "word" in result
assert "definition" in result
def test_search_hebrew_only(self):
"""Test search Hebrew only"""
results = search_strongs("God", language="hebrew")
for result in results:
assert result["language"] == "Hebrew"
def test_search_greek_only(self):
"""Test search Greek only"""
results = search_strongs("love", language="greek")
for result in results:
assert result["language"] == "Greek"
def test_search_both_languages(self):
"""Test search both languages"""
results = search_strongs("peace", language="both")
languages = set(r["language"] for r in results)
# Should have at least one result
assert len(results) > 0
def test_search_respects_limit(self):
"""Test search respects limit parameter"""
results = search_strongs("the", limit=5)
assert len(results) <= 5
def test_search_case_insensitive(self):
"""Test search is case-insensitive"""
results1 = search_strongs("love")
results2 = search_strongs("LOVE")
assert len(results1) == len(results2)
def test_search_no_results(self):
"""Test search with no results"""
results = search_strongs("xyznonexistent123")
assert len(results) == 0
class TestGetAllStrongs:
"""Tests for paginated Strong's listing"""
def test_get_all_hebrew(self):
"""Test getting all Hebrew entries"""
data = get_all_strongs("hebrew", page=1, per_page=100)
assert "entries" in data
assert "total" in data
assert "page" in data
assert "total_pages" in data
assert len(data["entries"]) == 100
assert data["total"] > 8000 # Should have ~8674 Hebrew entries
def test_get_all_greek(self):
"""Test getting all Greek entries"""
data = get_all_strongs("greek", page=1, per_page=100)
assert len(data["entries"]) == 100
assert data["total"] > 5000 # Should have ~5523 Greek entries
def test_pagination_page_2(self):
"""Test pagination returns different entries"""
page1 = get_all_strongs("hebrew", page=1, per_page=10)
page2 = get_all_strongs("hebrew", page=2, per_page=10)
# Entries should be different
page1_ids = [e["strongs"] for e in page1["entries"]]
page2_ids = [e["strongs"] for e in page2["entries"]]
assert page1_ids != page2_ids
def test_pagination_total_pages(self):
"""Test total pages calculation"""
data = get_all_strongs("hebrew", page=1, per_page=100)
expected_pages = (data["total"] + 99) // 100
assert data["total_pages"] == expected_pages
def test_entries_sorted_by_number(self):
"""Test entries are sorted by Strong's number"""
data = get_all_strongs("hebrew", page=1, per_page=10)
numbers = [int(e["strongs"][1:]) for e in data["entries"]]
assert numbers == sorted(numbers)
def test_invalid_language(self):
"""Test invalid language returns empty"""
data = get_all_strongs("invalid", page=1)
assert data["entries"] == []
assert data["total"] == 0
def test_entry_has_required_fields(self):
"""Test paginated entries have required fields"""
data = get_all_strongs("greek", page=1, per_page=1)
entry = data["entries"][0]
assert "strongs" in entry
assert "language" in entry
assert "word" in entry
assert "transliteration" in entry
assert "definition" in entry
class TestStrongsWebRoutes:
"""Tests for Strong's web routes"""
def test_strongs_index_page(self, client):
"""Test Strong's index page loads"""
response = client.get("/strongs")
assert response.status_code == 200
content = response.content.decode()
assert "Strong" in content
def test_strongs_search_page(self, client):
"""Test Strong's search with query"""
response = client.get("/strongs?q=love")
assert response.status_code == 200
content = response.content.decode()
assert "love" in content.lower()
def test_strongs_entry_page_greek(self, client):
"""Test Greek Strong's entry page"""
response = client.get("/strongs/G26")
assert response.status_code == 200
content = response.content.decode()
assert "G26" in content
assert "Greek" in content
def test_strongs_entry_page_hebrew(self, client):
"""Test Hebrew Strong's entry page"""
response = client.get("/strongs/H430")
assert response.status_code == 200
content = response.content.decode()
assert "H430" in content
assert "Hebrew" in content
def test_strongs_entry_case_insensitive(self, client):
"""Test entry page handles lowercase"""
response = client.get("/strongs/g26")
assert response.status_code == 200
def test_strongs_invalid_entry(self, client):
"""Test invalid entry returns 404"""
response = client.get("/strongs/X9999")
assert response.status_code == 404
def test_strongs_hebrew_index(self, client):
"""Test Hebrew index page"""
response = client.get("/strongs/hebrew")
assert response.status_code == 200
content = response.content.decode()
assert "Hebrew" in content
assert "H1" in content
def test_strongs_greek_index(self, client):
"""Test Greek index page"""
response = client.get("/strongs/greek")
assert response.status_code == 200
content = response.content.decode()
assert "Greek" in content
assert "G1" in content
def test_strongs_hebrew_pagination(self, client):
"""Test Hebrew index pagination"""
response = client.get("/strongs/hebrew?page=2")
assert response.status_code == 200
content = response.content.decode()
assert "Page 2" in content
def test_strongs_greek_pagination(self, client):
"""Test Greek index pagination"""
response = client.get("/strongs/greek?page=2")
assert response.status_code == 200
def test_strongs_entry_has_verse_occurrences(self, client):
"""Test entry page shows verse occurrences"""
# Use a common word that should have occurrences
response = client.get("/strongs/G2316") # theos (God)
assert response.status_code == 200
content = response.content.decode()
# Should have occurrences section if interlinear data exists
assert "Occurrence" in content or "verse" in content.lower()
class TestStrongsAPIRoutes:
"""Tests for Strong's API endpoints"""
def test_api_strongs_entry(self, client):
"""Test API Strong's entry endpoint"""
response = client.get("/api/strongs/G26")
assert response.status_code == 200
data = response.json()
assert data["strongs"] == "G26"
assert data["language"] == "Greek"
def test_api_strongs_hebrew_entry(self, client):
"""Test API Hebrew Strong's entry"""
response = client.get("/api/strongs/H430")
assert response.status_code == 200
data = response.json()
assert data["strongs"] == "H430"
assert data["language"] == "Hebrew"
def test_api_strongs_invalid(self, client):
"""Test API invalid Strong's number"""
response = client.get("/api/strongs/X9999")
assert response.status_code == 404
def test_api_strongs_search(self, client):
"""Test API Strong's search endpoint"""
response = client.get("/api/strongs?q=love")
assert response.status_code == 200
data = response.json()
assert "results" in data
assert len(data["results"]) > 0
class TestStrongsEdgeCases:
"""Edge case tests for Strong's functionality"""
def test_first_hebrew_entry(self, client):
"""Test first Hebrew entry (H1)"""
response = client.get("/strongs/H1")
assert response.status_code == 200
def test_last_hebrew_entry(self, client):
"""Test last Hebrew entry (H8674)"""
response = client.get("/strongs/H8674")
assert response.status_code == 200
def test_first_greek_entry(self, client):
"""Test first Greek entry (G1)"""
response = client.get("/strongs/G1")
assert response.status_code == 200
def test_last_greek_entry(self, client):
"""Test last Greek entry (G5624)"""
response = client.get("/strongs/G5624")
assert response.status_code == 200
def test_hebrew_boundary_plus_one(self, client):
"""Test beyond Hebrew range returns 404"""
response = client.get("/strongs/H9999")
assert response.status_code == 404
def test_greek_boundary_plus_one(self, client):
"""Test beyond Greek range returns 404"""
response = client.get("/strongs/G9999")
assert response.status_code == 404
def test_common_hebrew_words(self, client):
"""Test common Hebrew Strong's numbers"""
common = ["H430", "H3068", "H1", "H7965", "H2617"]
for num in common:
response = client.get(f"/strongs/{num}")
assert response.status_code == 200, f"Failed for {num}"
def test_common_greek_words(self, client):
"""Test common Greek Strong's numbers"""
common = ["G26", "G2316", "G3056", "G4102", "G5485"]
for num in common:
response = client.get(f"/strongs/{num}")
assert response.status_code == 200, f"Failed for {num}"
def test_strongs_with_derivation_links(self, client):
"""Test entry with derivation containing Strong's references"""
# H1580 has derivation with references to other Strong's numbers
response = client.get("/strongs/H1580")
assert response.status_code == 200
content = response.content.decode()
# Should have linked Strong's references in derivation
assert "/strongs/H" in content or "derivation" in content.lower()
class TestStrongsIntegration:
"""Integration tests for Strong's functionality"""
def test_search_then_view_entry(self, client):
"""Test searching and then viewing an entry"""
# Search for love
search_response = client.get("/strongs?q=love")
assert search_response.status_code == 200
# View G26 (agape)
entry_response = client.get("/strongs/G26")
assert entry_response.status_code == 200
content = entry_response.content.decode()
assert "love" in content.lower()
def test_browse_hebrew_then_entry(self, client):
"""Test browsing Hebrew index then viewing entry"""
# Browse Hebrew
index_response = client.get("/strongs/hebrew")
assert index_response.status_code == 200
# View first entry
entry_response = client.get("/strongs/H1")
assert entry_response.status_code == 200
def test_navigate_between_entries(self, client):
"""Test navigation between Strong's entries"""
response = client.get("/strongs/H100")
assert response.status_code == 200
content = response.content.decode()
# Should have navigation to adjacent entries
assert "H99" in content or "H101" in content
def test_strongs_page_has_breadcrumbs(self, client):
"""Test Strong's pages have breadcrumb navigation"""
response = client.get("/strongs/G26")
assert response.status_code == 200
content = response.content.decode()
# Should have link back to Strong's index
assert "/strongs" in content