mirror of
https://github.com/kennethreitz/kjvstudy.org.git
synced 2026-06-05 23:00:16 +00:00
8322a88edd
- Extracted ~1250 lines of JavaScript from base.html to static/base.js - Reduced base.html from ~2700 lines to ~1440 lines - Updated tests to expect 404 (not 500) for invalid resources - Updated CLAUDE.md to reflect proper error handling 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
393 lines
14 KiB
Python
393 lines
14 KiB
Python
"""
|
|
Edge case and error handling tests for KJV Study API
|
|
|
|
Fixtures are imported from conftest.py
|
|
"""
|
|
import pytest
|
|
|
|
|
|
class TestErrorHandling:
|
|
"""Tests for error handling and edge cases"""
|
|
|
|
def test_invalid_book_name(self, client):
|
|
"""Test verse endpoint with non-existent book"""
|
|
response = client.get("/api/verse/NotABook/1/1")
|
|
assert response.status_code == 404
|
|
|
|
def test_invalid_chapter_number(self, client):
|
|
"""Test verse endpoint with invalid chapter"""
|
|
response = client.get("/api/verse/Genesis/999/1")
|
|
assert response.status_code == 404
|
|
|
|
def test_invalid_verse_number(self, client):
|
|
"""Test verse endpoint with invalid verse"""
|
|
response = client.get("/api/verse/John/3/999")
|
|
assert response.status_code == 404
|
|
|
|
def test_negative_chapter(self, client):
|
|
"""Test verse endpoint with negative chapter"""
|
|
response = client.get("/api/verse/Genesis/-1/1")
|
|
assert response.status_code in [404, 422, 500]
|
|
|
|
def test_negative_verse(self, client):
|
|
"""Test verse endpoint with negative verse"""
|
|
response = client.get("/api/verse/Genesis/1/-1")
|
|
assert response.status_code in [404, 422, 500]
|
|
|
|
def test_zero_chapter(self, client):
|
|
"""Test verse endpoint with zero chapter"""
|
|
response = client.get("/api/verse/Genesis/0/1")
|
|
assert response.status_code in [404, 422, 500]
|
|
|
|
def test_zero_verse(self, client):
|
|
"""Test verse endpoint with zero verse"""
|
|
response = client.get("/api/verse/Genesis/1/0")
|
|
assert response.status_code in [404, 422, 500]
|
|
|
|
|
|
class TestVerseRangeEdgeCases:
|
|
"""Tests for verse range edge cases"""
|
|
|
|
def test_reversed_verse_range(self, client):
|
|
"""Test verse range with start > end"""
|
|
response = client.get("/api/verse-range/John/3/16/1")
|
|
# Should handle reversed ranges gracefully - 404 is acceptable (no verses found)
|
|
assert response.status_code in [200, 400, 404, 422, 500]
|
|
|
|
def test_single_verse_range(self, client):
|
|
"""Test verse range with start = end"""
|
|
response = client.get("/api/verse-range/John/3/16/16")
|
|
assert response.status_code == 200
|
|
if response.status_code == 200:
|
|
data = response.json()
|
|
assert len(data["verses"]) == 1
|
|
|
|
def test_large_verse_range(self, client):
|
|
"""Test verse range spanning many verses"""
|
|
response = client.get("/api/verse-range/Psalms/119/1/176")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert len(data["verses"]) == 176
|
|
|
|
def test_verse_range_exceeds_chapter(self, client):
|
|
"""Test verse range that goes beyond chapter length"""
|
|
response = client.get("/api/verse-range/John/3/1/999")
|
|
# Should handle gracefully, possibly returning up to last verse
|
|
assert response.status_code in [200, 404, 422]
|
|
|
|
|
|
class TestBookAbbreviations:
|
|
"""Comprehensive tests for book name abbreviations"""
|
|
|
|
def test_old_testament_abbreviations(self, client):
|
|
"""Test various Old Testament book abbreviations"""
|
|
abbreviations = {
|
|
"Gen": "Genesis",
|
|
"Exo": "Exodus",
|
|
"Lev": "Leviticus",
|
|
"Num": "Numbers",
|
|
"Deut": "Deuteronomy",
|
|
"Josh": "Joshua",
|
|
"Ps": "Psalms",
|
|
"Prov": "Proverbs",
|
|
"Isa": "Isaiah",
|
|
"Jer": "Jeremiah",
|
|
"Ezek": "Ezekiel",
|
|
"Dan": "Daniel",
|
|
}
|
|
|
|
for abbrev, full_name in abbreviations.items():
|
|
response = client.get(f"/api/books/{abbrev}")
|
|
assert response.status_code in [200, 404]
|
|
if response.status_code == 200:
|
|
data = response.json()
|
|
# Response has "name" field, not "book"
|
|
assert data["name"] == full_name
|
|
|
|
def test_new_testament_abbreviations(self, client):
|
|
"""Test various New Testament book abbreviations"""
|
|
abbreviations = {
|
|
"Matt": "Matthew",
|
|
"Rom": "Romans",
|
|
"Cor": "Corinthians",
|
|
"Gal": "Galatians",
|
|
"Eph": "Ephesians",
|
|
"Phil": "Philippians",
|
|
"Col": "Colossians",
|
|
"Thess": "Thessalonians",
|
|
"Heb": "Hebrews",
|
|
"Jas": "James",
|
|
"Rev": "Revelation",
|
|
}
|
|
|
|
for abbrev, full_name in abbreviations.items():
|
|
response = client.get(f"/api/books/{abbrev}")
|
|
# Some abbreviations might need more context (like 1 Cor vs 2 Cor)
|
|
assert response.status_code in [200, 404]
|
|
|
|
def test_numbered_book_abbreviations(self, client):
|
|
"""Test abbreviations for numbered books"""
|
|
test_cases = [
|
|
("1 Sam", "I Samuel"),
|
|
("2 Sam", "II Samuel"),
|
|
("1 Kings", "I Kings"),
|
|
("2 Kings", "II Kings"),
|
|
("1 Cor", "I Corinthians"),
|
|
("2 Cor", "II Corinthians"),
|
|
]
|
|
|
|
for abbrev, expected in test_cases:
|
|
response = client.get(f"/api/verse/{abbrev}/1/1")
|
|
if response.status_code == 200:
|
|
data = response.json()
|
|
# Book name might be in Roman numerals or Arabic numerals
|
|
assert "Samuel" in data["book"] or "Corinthians" in data["book"] or "Kings" in data["book"]
|
|
|
|
|
|
class TestSearchFunctionality:
|
|
"""Comprehensive search tests"""
|
|
|
|
def test_search_common_words(self, client):
|
|
"""Test search for common biblical words"""
|
|
common_words = ["love", "faith", "hope", "peace", "righteousness"]
|
|
|
|
for word in common_words:
|
|
response = client.get(f"/api/search?q={word}")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["total"] > 0
|
|
assert len(data["results"]) > 0
|
|
|
|
def test_search_phrase(self, client):
|
|
"""Test search for a phrase"""
|
|
response = client.get("/api/search?q=God so loved the world")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["total"] > 0
|
|
|
|
def test_search_case_insensitive(self, client):
|
|
"""Test search is case insensitive"""
|
|
response1 = client.get("/api/search?q=LOVE&limit=10")
|
|
response2 = client.get("/api/search?q=love&limit=10")
|
|
|
|
assert response1.status_code == 200
|
|
assert response2.status_code == 200
|
|
|
|
# Results should be similar for case-insensitive search
|
|
data1 = response1.json()
|
|
data2 = response2.json()
|
|
assert data1["total"] > 0
|
|
assert data2["total"] > 0
|
|
|
|
def test_search_very_short_query(self, client):
|
|
"""Test search with very short query"""
|
|
response = client.get("/api/search?q=a")
|
|
assert response.status_code == 200
|
|
# Might return empty or filtered results for very short queries
|
|
|
|
def test_search_special_characters(self, client):
|
|
"""Test search with special characters"""
|
|
response = client.get("/api/search?q=love?")
|
|
assert response.status_code == 200
|
|
|
|
def test_search_with_zero_limit(self, client):
|
|
"""Test search with limit=0"""
|
|
response = client.get("/api/search?q=love&limit=0")
|
|
assert response.status_code == 200
|
|
|
|
def test_search_with_large_limit(self, client):
|
|
"""Test search with very large limit"""
|
|
response = client.get("/api/search?q=love&limit=10000")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
# Should handle large limits gracefully
|
|
|
|
|
|
class TestBookBoundaries:
|
|
"""Tests for book boundaries and edge cases"""
|
|
|
|
def test_shortest_book(self, client):
|
|
"""Test the shortest book (2 John or 3 John)"""
|
|
response = client.get("/api/books/III John/text")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["total_chapters"] == 1
|
|
assert data["total_verses"] > 0
|
|
|
|
def test_longest_book(self, client):
|
|
"""Test the longest book (Psalms)"""
|
|
response = client.get("/api/books/Psalms")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["total_chapters"] == 150
|
|
|
|
def test_first_book(self, client):
|
|
"""Test first book of the Bible"""
|
|
response = client.get("/api/books/Genesis")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["name"] == "Genesis"
|
|
|
|
def test_last_book(self, client):
|
|
"""Test last book of the Bible"""
|
|
response = client.get("/api/books/Revelation")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["name"] == "Revelation"
|
|
|
|
def test_longest_chapter(self, client):
|
|
"""Test longest chapter (Psalms 119)"""
|
|
response = client.get("/api/books/Psalms/chapters/119")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["total_verses"] == 176
|
|
|
|
|
|
class TestCrossReferencesEdgeCases:
|
|
"""Tests for cross-references edge cases"""
|
|
|
|
def test_verse_with_no_cross_references(self, client):
|
|
"""Test verse that might have no cross-references"""
|
|
response = client.get("/api/cross-references/III John/1/1")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "cross_references" in data
|
|
# Might be empty list
|
|
|
|
def test_verse_with_many_cross_references(self, client):
|
|
"""Test verse with many cross-references"""
|
|
response = client.get("/api/cross-references/John/3/16")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "cross_references" in data
|
|
|
|
|
|
class TestInterlinearEdgeCases:
|
|
"""Tests for interlinear data edge cases"""
|
|
|
|
def test_old_testament_interlinear(self, client):
|
|
"""Test interlinear for Old Testament (Hebrew)"""
|
|
response = client.get("/api/interlinear/Genesis/1/1")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "interlinear_available" in data
|
|
|
|
def test_new_testament_interlinear(self, client):
|
|
"""Test interlinear for New Testament (Greek)"""
|
|
response = client.get("/api/interlinear/John/1/1")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert "interlinear_available" in data
|
|
|
|
|
|
class TestTopicsEdgeCases:
|
|
"""Tests for topics edge cases"""
|
|
|
|
def test_nonexistent_topic(self, client):
|
|
"""Test non-existent topic"""
|
|
response = client.get("/api/topics/notarealtopic123456")
|
|
assert response.status_code == 404
|
|
|
|
def test_topic_case_sensitivity(self, client):
|
|
"""Test topic name case sensitivity"""
|
|
response1 = client.get("/api/topics/faith")
|
|
response2 = client.get("/api/topics/Faith")
|
|
response3 = client.get("/api/topics/FAITH")
|
|
|
|
# At least one should work
|
|
assert any(r.status_code == 200 for r in [response1, response2, response3])
|
|
|
|
|
|
class TestReadingPlansEdgeCases:
|
|
"""Tests for reading plans edge cases"""
|
|
|
|
def test_nonexistent_reading_plan(self, client):
|
|
"""Test non-existent reading plan"""
|
|
response = client.get("/api/reading-plans/not-a-real-plan")
|
|
assert response.status_code == 404
|
|
|
|
def test_all_reading_plans_structure(self, client):
|
|
"""Test structure of all reading plans"""
|
|
response = client.get("/api/reading-plans")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
|
|
assert "plans" in data
|
|
for plan in data["plans"]:
|
|
assert "id" in plan
|
|
assert "name" in plan
|
|
assert "description" in plan
|
|
assert "days" in plan
|
|
|
|
|
|
class TestAPIPerformance:
|
|
"""Tests for API performance and large data handling"""
|
|
|
|
def test_entire_bible_response_size(self, client):
|
|
"""Test that entire Bible endpoint returns valid data"""
|
|
response = client.get("/api/bible")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
|
|
# Verify structure
|
|
assert "total_books" in data
|
|
assert "total_verses" in data
|
|
assert "books" in data
|
|
|
|
# Verify counts
|
|
assert data["total_books"] == 66
|
|
assert data["total_verses"] > 31000 # KJV has 31,102 verses
|
|
|
|
def test_long_book_response(self, client):
|
|
"""Test response for longest books"""
|
|
long_books = ["Psalms", "Genesis", "Isaiah", "Jeremiah"]
|
|
|
|
for book in long_books:
|
|
response = client.get(f"/api/books/{book}/text")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["total_verses"] > 100
|
|
|
|
|
|
class TestContentValidation:
|
|
"""Tests for content validation"""
|
|
|
|
def test_john_3_16_content(self, client):
|
|
"""Test that John 3:16 has correct content"""
|
|
response = client.get("/api/verse/John/3/16")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
|
|
text = data["text"].lower()
|
|
assert "god" in text
|
|
assert "loved" in text
|
|
assert "world" in text
|
|
assert "son" in text
|
|
|
|
def test_genesis_1_1_content(self, client):
|
|
"""Test that Genesis 1:1 has correct content"""
|
|
response = client.get("/api/verse/Genesis/1/1")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
|
|
text = data["text"].lower()
|
|
assert "beginning" in text
|
|
assert "god" in text
|
|
assert "created" in text
|
|
assert "heaven" in text
|
|
assert "earth" in text
|
|
|
|
def test_psalms_23_1_content(self, client):
|
|
"""Test that Psalms 23:1 has correct content"""
|
|
response = client.get("/api/verse/Psalms/23/1")
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
|
|
text = data["text"].lower()
|
|
assert "lord" in text
|
|
assert "shepherd" in text
|
|
|
|
|
|
if __name__ == "__main__":
|
|
pytest.main([__file__, "-v"])
|