mirror of
https://github.com/kennethreitz/kjvstudy.org.git
synced 2026-06-05 23:00:16 +00:00
c77b13c2b8
- Remove unused dropdown CSS and JS from homepage (~200 lines) - Fix API error handling to return 404 instead of 500 for invalid inputs - Add book existence and chapter/verse validation - Add 10 tests for universal search endpoint (110 total tests now) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
373 lines
13 KiB
Python
373 lines
13 KiB
Python
"""
|
|
Unit tests for KJV Study API endpoints
|
|
|
|
Fixtures are imported from conftest.py
|
|
"""
|
|
import pytest
|
|
|
|
|
|
class TestAPIHealth:
|
|
"""Tests for health check endpoints"""
|
|
|
|
def test_api_health(self, client):
|
|
"""Test /api/health endpoint"""
|
|
response = client.get("/api/health")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["status"] == "healthy"
|
|
assert "service" in data
|
|
assert "version" in data
|
|
|
|
|
|
class TestAPIIndex:
|
|
"""Tests for API index endpoint"""
|
|
|
|
def test_api_index(self, client):
|
|
"""Test /api/ endpoint returns index"""
|
|
response = client.get("/api/")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["name"] == "KJV Study API"
|
|
assert "version" in data
|
|
assert "documentation" in data
|
|
assert "endpoints" in data
|
|
|
|
|
|
class TestVerseEndpoints:
|
|
"""Tests for verse-related endpoints"""
|
|
|
|
def test_get_single_verse(self, client):
|
|
"""Test /api/verse/{book}/{chapter}/{verse}"""
|
|
response = client.get("/api/verse/John/3/16")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["book"] == "John"
|
|
assert data["chapter"] == 3
|
|
assert data["verse"] == 16
|
|
assert "text" in data
|
|
assert "God so loved the world" in data["text"]
|
|
|
|
def test_get_verse_with_abbreviation(self, client):
|
|
"""Test verse endpoint with book abbreviation"""
|
|
response = client.get("/api/verse/Gen/1/1")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["book"] == "Genesis"
|
|
assert data["chapter"] == 1
|
|
assert data["verse"] == 1
|
|
assert "In the beginning" in data["text"]
|
|
|
|
def test_get_nonexistent_verse(self, client):
|
|
"""Test verse endpoint with invalid verse"""
|
|
response = client.get("/api/verse/John/3/999")
|
|
# Currently returns 500, should return 404
|
|
assert response.status_code in [404, 500]
|
|
|
|
def test_get_verse_range(self, client):
|
|
"""Test /api/verse-range/{book}/{chapter}/{start}/{end}"""
|
|
response = client.get("/api/verse-range/Psalms/23/1/6")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["book"] == "Psalms"
|
|
assert data["chapter"] == 23
|
|
assert data["start"] == 1
|
|
assert data["end"] == 6
|
|
assert "verses" in data
|
|
assert len(data["verses"]) == 6
|
|
|
|
def test_verse_of_the_day(self, client):
|
|
"""Test /api/verse-of-the-day"""
|
|
response = client.get("/api/verse-of-the-day")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "text" in data
|
|
assert "reference" in data
|
|
assert "book" in data
|
|
assert "chapter" in data
|
|
assert "verse" in data
|
|
|
|
|
|
class TestBookEndpoints:
|
|
"""Tests for book-related endpoints"""
|
|
|
|
def test_get_all_books(self, client):
|
|
"""Test /api/books"""
|
|
response = client.get("/api/books")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "total_books" in data
|
|
assert data["total_books"] == 66
|
|
assert "old_testament" in data
|
|
assert "new_testament" in data
|
|
|
|
def test_get_book_details(self, client):
|
|
"""Test /api/books/{book}"""
|
|
response = client.get("/api/books/Genesis")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["name"] == "Genesis"
|
|
assert data["total_chapters"] == 50
|
|
assert "chapters" in data
|
|
|
|
def test_get_chapter(self, client):
|
|
"""Test /api/books/{book}/chapters/{chapter}"""
|
|
response = client.get("/api/books/John/chapters/1")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["book"] == "John"
|
|
assert data["chapter"] == 1
|
|
assert "verses" in data
|
|
assert data["total_verses"] > 0
|
|
|
|
def test_get_book_text(self, client):
|
|
"""Test /api/books/{book}/text"""
|
|
response = client.get("/api/books/Philemon/text")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["book"] == "Philemon"
|
|
assert data["total_chapters"] == 1
|
|
assert data["total_verses"] == 25
|
|
assert "chapters" in data
|
|
|
|
def test_get_nonexistent_book(self, client):
|
|
"""Test book endpoint with invalid book"""
|
|
response = client.get("/api/books/FakeBook")
|
|
assert response.status_code == 404
|
|
|
|
|
|
class TestBibleEndpoint:
|
|
"""Tests for complete Bible endpoint"""
|
|
|
|
def test_get_entire_bible(self, client):
|
|
"""Test /api/bible"""
|
|
response = client.get("/api/bible")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["total_books"] == 66
|
|
assert data["total_verses"] == 31102
|
|
assert "books" in data
|
|
assert len(data["books"]) == 66
|
|
|
|
|
|
class TestSearchEndpoint:
|
|
"""Tests for search endpoint"""
|
|
|
|
def test_search_with_query(self, client):
|
|
"""Test /api/search with query"""
|
|
response = client.get("/api/search?q=love")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "results" in data
|
|
assert "total" in data
|
|
|
|
def test_search_with_limit(self, client):
|
|
"""Test /api/search with limit"""
|
|
response = client.get("/api/search?q=faith&limit=5")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "results" in data
|
|
assert len(data["results"]) <= 5
|
|
|
|
def test_search_empty_query(self, client):
|
|
"""Test /api/search with empty query"""
|
|
response = client.get("/api/search?q=")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["total"] == 0
|
|
|
|
|
|
class TestUniversalSearchEndpoint:
|
|
"""Tests for universal search endpoint"""
|
|
|
|
def test_universal_search_basic(self, client):
|
|
"""Test /api/universal-search with basic query"""
|
|
response = client.get("/api/universal-search?q=love")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "query" in data
|
|
assert "results" in data
|
|
assert data["query"] == "love"
|
|
|
|
def test_universal_search_finds_books(self, client):
|
|
"""Test universal search finds books"""
|
|
response = client.get("/api/universal-search?q=genesis")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "books" in data["results"]
|
|
assert any(b["name"] == "Genesis" for b in data["results"]["books"])
|
|
|
|
def test_universal_search_finds_topics(self, client):
|
|
"""Test universal search finds topics"""
|
|
response = client.get("/api/universal-search?q=faith")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
# Topics may or may not be present depending on data
|
|
assert "results" in data
|
|
|
|
def test_universal_search_finds_verses(self, client):
|
|
"""Test universal search finds verses"""
|
|
response = client.get("/api/universal-search?q=beginning")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "verses" in data["results"]
|
|
assert len(data["results"]["verses"]) > 0
|
|
|
|
def test_universal_search_with_limit(self, client):
|
|
"""Test universal search respects limit"""
|
|
response = client.get("/api/universal-search?q=god&limit=3")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
# Check that each category respects the limit
|
|
for category in data["results"].values():
|
|
assert len(category) <= 3
|
|
|
|
def test_universal_search_short_query(self, client):
|
|
"""Test universal search with very short query"""
|
|
response = client.get("/api/universal-search?q=a")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
# Short queries return empty results
|
|
assert data["results"] == {}
|
|
|
|
def test_universal_search_book_synonyms(self, client):
|
|
"""Test universal search handles book synonyms"""
|
|
response = client.get("/api/universal-search?q=revelations")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
# Should find Revelation book
|
|
if "books" in data["results"]:
|
|
assert any(b["name"] == "Revelation" for b in data["results"]["books"])
|
|
|
|
def test_universal_search_finds_resources(self, client):
|
|
"""Test universal search finds resources"""
|
|
response = client.get("/api/universal-search?q=trinity")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "resources" in data["results"]
|
|
assert len(data["results"]["resources"]) > 0
|
|
|
|
def test_universal_search_theological_synonyms(self, client):
|
|
"""Test universal search handles theological synonyms"""
|
|
response = client.get("/api/universal-search?q=holy%20spirit")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
# Should find Pneumatology via synonym
|
|
if "resources" in data["results"]:
|
|
names = [r["name"] for r in data["results"]["resources"]]
|
|
assert "Pneumatology" in names
|
|
|
|
def test_universal_search_finds_study_guides(self, client):
|
|
"""Test universal search finds study guides"""
|
|
response = client.get("/api/universal-search?q=prayer")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
if "resources" in data["results"]:
|
|
# Should find prayer-related study guides
|
|
urls = [r["url"] for r in data["results"]["resources"]]
|
|
assert any("study-guides" in url or "prayer" in url for url in urls)
|
|
|
|
|
|
class TestInterlinearEndpoint:
|
|
"""Tests for interlinear data endpoint"""
|
|
|
|
def test_get_interlinear_new_testament(self, client):
|
|
"""Test /api/interlinear for NT verse"""
|
|
response = client.get("/api/interlinear/John/1/1")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["book"] == "John"
|
|
assert data["chapter"] == 1
|
|
assert data["verse"] == 1
|
|
assert "interlinear_available" in data
|
|
|
|
def test_get_interlinear_nonexistent_verse(self, client):
|
|
"""Test interlinear with invalid verse"""
|
|
response = client.get("/api/interlinear/John/1/999")
|
|
# Currently returns 500, should return 404
|
|
assert response.status_code in [404, 500]
|
|
|
|
|
|
class TestCrossReferencesEndpoint:
|
|
"""Tests for cross-references endpoint"""
|
|
|
|
def test_get_cross_references(self, client):
|
|
"""Test /api/cross-references/{book}/{chapter}/{verse}"""
|
|
response = client.get("/api/cross-references/John/3/16")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["book"] == "John"
|
|
assert data["chapter"] == 3
|
|
assert data["verse"] == 16
|
|
assert "cross_references" in data
|
|
|
|
|
|
class TestTopicsEndpoints:
|
|
"""Tests for topics endpoints"""
|
|
|
|
def test_get_all_topics(self, client):
|
|
"""Test /api/topics"""
|
|
response = client.get("/api/topics")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "total_topics" in data
|
|
assert "topics" in data
|
|
|
|
def test_get_specific_topic(self, client):
|
|
"""Test /api/topics/{topic_name}"""
|
|
response = client.get("/api/topics/faith")
|
|
# Topic might not exist, accept both 200 and 404
|
|
assert response.status_code in [200, 404]
|
|
if response.status_code == 200:
|
|
data = response.json()
|
|
assert "name" in data
|
|
|
|
|
|
class TestReadingPlansEndpoints:
|
|
"""Tests for reading plans endpoints"""
|
|
|
|
def test_get_all_reading_plans(self, client):
|
|
"""Test /api/reading-plans"""
|
|
response = client.get("/api/reading-plans")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "total_plans" in data
|
|
assert "plans" in data
|
|
assert data["total_plans"] > 0
|
|
|
|
def test_get_specific_reading_plan(self, client):
|
|
"""Test /api/reading-plans/{plan_id}"""
|
|
response = client.get("/api/reading-plans/chronological")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "name" in data
|
|
assert "description" in data
|
|
|
|
def test_get_nonexistent_reading_plan(self, client):
|
|
"""Test reading plan endpoint with invalid plan"""
|
|
response = client.get("/api/reading-plans/fake-plan")
|
|
assert response.status_code == 404
|
|
|
|
|
|
class TestBookNameNormalization:
|
|
"""Tests for book name normalization"""
|
|
|
|
def test_abbreviations(self, client):
|
|
"""Test various book abbreviations"""
|
|
# Test Genesis abbreviations
|
|
for abbrev in ["Gen", "Ge"]:
|
|
response = client.get(f"/api/verse/{abbrev}/1/1")
|
|
# Some abbreviations might not work, accept 200, 404, or 500
|
|
if response.status_code == 200:
|
|
data = response.json()
|
|
assert data["book"] == "Genesis"
|
|
|
|
# Test Matthew abbreviations - Matt should work
|
|
response = client.get("/api/verse/Matt/1/1")
|
|
if response.status_code == 200:
|
|
data = response.json()
|
|
assert data["book"] == "Matthew"
|
|
|
|
|
|
if __name__ == "__main__":
|
|
pytest.main([__file__, "-v"])
|