Files
Timmy-time-dashboard/tests/test_scripture.py
Claude 63bbe2a288 feat: add sovereign biblical text integration module (scripture)
Implement the core scripture module for local-first ESV text storage,
verse retrieval, reference parsing, original language support,
cross-referencing, topical mapping, and automated meditation workflows.

Architecture:
- scripture/constants.py: 66-book Protestant canon with aliases and metadata
- scripture/models.py: Pydantic models with integer-encoded verse IDs
- scripture/parser.py: Regex-based reference extraction and formatting
- scripture/store.py: SQLite-backed verse/xref/topic/Strong's storage
- scripture/memory.py: Tripartite memory (working/long-term/associative)
- scripture/meditation.py: Sequential/thematic/lectionary meditation scheduler
- dashboard/routes/scripture.py: REST endpoints for all scripture operations
- config.py: scripture_enabled, translation, meditation settings
- 95 comprehensive tests covering all modules and routes

https://claude.ai/code/session_015wv7FM6BFsgZ35Us6WeY7H
2026-02-26 17:06:00 +00:00

902 lines
34 KiB
Python

"""Tests for the scripture module — sovereign biblical text integration.
Covers: constants, models, parser, store, memory, meditation, and routes.
All tests use in-memory or temp-file SQLite — no external services needed.
"""
import json
import sqlite3
import tempfile
from pathlib import Path
from unittest.mock import patch
import pytest
from fastapi.testclient import TestClient
# ════════════════════════════════════════════════════════════════════════════
# Constants
# ════════════════════════════════════════════════════════════════════════════
class TestConstants:
def test_total_books(self):
from scripture.constants import BOOKS, TOTAL_BOOKS
assert len(BOOKS) == TOTAL_BOOKS == 66
def test_ot_nt_split(self):
from scripture.constants import BOOKS, OT_BOOKS, NT_BOOKS
ot = [b for b in BOOKS if b.testament == "OT"]
nt = [b for b in BOOKS if b.testament == "NT"]
assert len(ot) == OT_BOOKS == 39
assert len(nt) == NT_BOOKS == 27
def test_book_ids_sequential(self):
from scripture.constants import BOOKS
for i, book in enumerate(BOOKS, start=1):
assert book.id == i, f"Book {book.name} has id {book.id}, expected {i}"
def test_book_by_name_full(self):
from scripture.constants import book_by_name
info = book_by_name("Genesis")
assert info is not None
assert info.id == 1
assert info.testament == "OT"
def test_book_by_name_abbreviation(self):
from scripture.constants import book_by_name
info = book_by_name("Rev")
assert info is not None
assert info.id == 66
assert info.name == "Revelation"
def test_book_by_name_case_insensitive(self):
from scripture.constants import book_by_name
assert book_by_name("JOHN") is not None
assert book_by_name("john") is not None
assert book_by_name("John") is not None
def test_book_by_name_alias(self):
from scripture.constants import book_by_name
assert book_by_name("1 Cor").id == 46
assert book_by_name("Phil").id == 50
assert book_by_name("Ps").id == 19
def test_book_by_name_unknown(self):
from scripture.constants import book_by_name
assert book_by_name("Nonexistent") is None
def test_book_by_id(self):
from scripture.constants import book_by_id
info = book_by_id(43)
assert info is not None
assert info.name == "John"
def test_book_by_id_invalid(self):
from scripture.constants import book_by_id
assert book_by_id(0) is None
assert book_by_id(67) is None
def test_genres_present(self):
from scripture.constants import GENRES
assert "gospel" in GENRES
assert "epistle" in GENRES
assert "wisdom" in GENRES
assert "prophecy" in GENRES
def test_first_and_last_books(self):
from scripture.constants import BOOKS
assert BOOKS[0].name == "Genesis"
assert BOOKS[-1].name == "Revelation"
# ════════════════════════════════════════════════════════════════════════════
# Models
# ════════════════════════════════════════════════════════════════════════════
class TestModels:
def test_encode_verse_id(self):
from scripture.models import encode_verse_id
assert encode_verse_id(43, 3, 16) == 43003016
assert encode_verse_id(1, 1, 1) == 1001001
assert encode_verse_id(66, 22, 21) == 66022021
def test_decode_verse_id(self):
from scripture.models import decode_verse_id
assert decode_verse_id(43003016) == (43, 3, 16)
assert decode_verse_id(1001001) == (1, 1, 1)
assert decode_verse_id(66022021) == (66, 22, 21)
def test_encode_decode_roundtrip(self):
from scripture.models import decode_verse_id, encode_verse_id
for book in (1, 19, 43, 66):
for chapter in (1, 10, 50):
for verse in (1, 5, 31):
vid = encode_verse_id(book, chapter, verse)
assert decode_verse_id(vid) == (book, chapter, verse)
def test_verse_ref_id(self):
from scripture.models import VerseRef
ref = VerseRef(book=43, chapter=3, verse=16)
assert ref.verse_id == 43003016
def test_verse_range_ids(self):
from scripture.models import VerseRange, VerseRef
vr = VerseRange(
start=VerseRef(book=1, chapter=1, verse=1),
end=VerseRef(book=1, chapter=1, verse=3),
)
ids = vr.verse_ids()
assert 1001001 in ids
assert 1001002 in ids
assert 1001003 in ids
def test_verse_model(self):
from scripture.models import Verse
v = Verse(
verse_id=43003016,
book=43,
chapter=3,
verse_num=16,
text="For God so loved the world...",
translation="ESV",
testament="NT",
genre="gospel",
)
assert v.text.startswith("For God")
assert v.testament == "NT"
def test_meditation_state_advance(self):
from scripture.models import MeditationState
state = MeditationState()
assert state.verses_meditated == 0
state.advance(1, 1, 2)
assert state.current_verse == 2
assert state.verses_meditated == 1
assert state.last_meditation is not None
def test_scripture_query_defaults(self):
from scripture.models import ScriptureQuery
q = ScriptureQuery(raw_text="test")
assert q.intent == "lookup"
assert q.references == []
assert q.keywords == []
def test_cross_reference_model(self):
from scripture.models import CrossReference
xref = CrossReference(
source_verse_id=43003016,
target_verse_id=45005008,
reference_type="thematic",
confidence=0.9,
)
assert xref.reference_type == "thematic"
assert xref.confidence == 0.9
def test_strongs_entry(self):
from scripture.models import StrongsEntry
entry = StrongsEntry(
strongs_number="H7225",
language="hebrew",
lemma="רֵאשִׁית",
transliteration="reshith",
gloss="beginning",
)
assert entry.language == "hebrew"
# ════════════════════════════════════════════════════════════════════════════
# Parser
# ════════════════════════════════════════════════════════════════════════════
class TestParser:
def test_parse_single_verse(self):
from scripture.parser import parse_reference
result = parse_reference("John 3:16")
assert result is not None
assert result.start.book == 43
assert result.start.chapter == 3
assert result.start.verse == 16
assert result.end.verse == 16
def test_parse_range(self):
from scripture.parser import parse_reference
result = parse_reference("Romans 5:1-11")
assert result is not None
assert result.start.verse == 1
assert result.end.verse == 11
def test_parse_whole_chapter(self):
from scripture.parser import parse_reference
result = parse_reference("Genesis 1")
assert result is not None
assert result.start.verse == 1
assert result.end.verse == 999 # sentinel for whole chapter
def test_parse_multi_chapter_range(self):
from scripture.parser import parse_reference
result = parse_reference("Genesis 1:1-2:3")
assert result is not None
assert result.start.chapter == 1
assert result.start.verse == 1
assert result.end.chapter == 2
assert result.end.verse == 3
def test_parse_abbreviation(self):
from scripture.parser import parse_reference
result = parse_reference("Rom 8:28")
assert result is not None
assert result.start.book == 45
def test_parse_numbered_book(self):
from scripture.parser import parse_reference
result = parse_reference("1 Cor 13:4")
assert result is not None
assert result.start.book == 46
def test_parse_invalid(self):
from scripture.parser import parse_reference
assert parse_reference("not a reference") is None
def test_parse_unknown_book(self):
from scripture.parser import parse_reference
assert parse_reference("Hezekiah 1:1") is None
def test_extract_multiple_references(self):
from scripture.parser import extract_references
text = "See John 3:16 and Romans 5:8 for the gospel message."
refs = extract_references(text)
assert len(refs) == 2
assert refs[0].start.book == 43
assert refs[1].start.book == 45
def test_extract_no_references(self):
from scripture.parser import extract_references
assert extract_references("No references here.") == []
def test_format_reference(self):
from scripture.models import VerseRef
from scripture.parser import format_reference
ref = VerseRef(book=43, chapter=3, verse=16)
assert format_reference(ref) == "John 3:16"
def test_format_reference_chapter_only(self):
from scripture.models import VerseRef
from scripture.parser import format_reference
ref = VerseRef(book=1, chapter=1, verse=0)
assert format_reference(ref) == "Genesis 1"
def test_format_range_single(self):
from scripture.models import VerseRange, VerseRef
from scripture.parser import format_range
vr = VerseRange(
start=VerseRef(book=43, chapter=3, verse=16),
end=VerseRef(book=43, chapter=3, verse=16),
)
assert format_range(vr) == "John 3:16"
def test_format_range_same_chapter(self):
from scripture.models import VerseRange, VerseRef
from scripture.parser import format_range
vr = VerseRange(
start=VerseRef(book=45, chapter=5, verse=1),
end=VerseRef(book=45, chapter=5, verse=11),
)
assert format_range(vr) == "Romans 5:1-11"
def test_format_range_multi_chapter(self):
from scripture.models import VerseRange, VerseRef
from scripture.parser import format_range
vr = VerseRange(
start=VerseRef(book=1, chapter=1, verse=1),
end=VerseRef(book=1, chapter=2, verse=3),
)
assert format_range(vr) == "Genesis 1:1-2:3"
# ════════════════════════════════════════════════════════════════════════════
# Store
# ════════════════════════════════════════════════════════════════════════════
@pytest.fixture
def temp_store():
"""Create a ScriptureStore backed by a temp file."""
from scripture.store import ScriptureStore
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
db_path = f.name
store = ScriptureStore(db_path=db_path)
yield store
store.close()
Path(db_path).unlink(missing_ok=True)
def _sample_verse(book=43, chapter=3, verse_num=16, text="For God so loved the world"):
from scripture.models import Verse, encode_verse_id
return Verse(
verse_id=encode_verse_id(book, chapter, verse_num),
book=book,
chapter=chapter,
verse_num=verse_num,
text=text,
translation="ESV",
testament="NT",
genre="gospel",
)
class TestStore:
def test_insert_and_get(self, temp_store):
verse = _sample_verse()
temp_store.insert_verse(verse)
result = temp_store.get_verse(43, 3, 16)
assert result is not None
assert result.text == "For God so loved the world"
def test_get_nonexistent(self, temp_store):
assert temp_store.get_verse(99, 1, 1) is None
def test_get_verse_by_id(self, temp_store):
verse = _sample_verse()
temp_store.insert_verse(verse)
result = temp_store.get_verse_by_id(43003016)
assert result is not None
assert result.book == 43
def test_bulk_insert(self, temp_store):
verses = [
_sample_verse(1, 1, 1, "In the beginning God created the heavens and the earth."),
_sample_verse(1, 1, 2, "The earth was without form and void."),
_sample_verse(1, 1, 3, "And God said, Let there be light."),
]
temp_store.insert_verses(verses)
assert temp_store.count_verses() == 3
def test_get_chapter(self, temp_store):
verses = [
_sample_verse(1, 1, i, f"Verse {i}")
for i in range(1, 6)
]
temp_store.insert_verses(verses)
chapter = temp_store.get_chapter(1, 1)
assert len(chapter) == 5
assert chapter[0].verse_num == 1
def test_get_range(self, temp_store):
from scripture.models import encode_verse_id
verses = [
_sample_verse(1, 1, i, f"Verse {i}")
for i in range(1, 11)
]
temp_store.insert_verses(verses)
result = temp_store.get_range(
encode_verse_id(1, 1, 3),
encode_verse_id(1, 1, 7),
)
assert len(result) == 5
def test_search_text(self, temp_store):
verses = [
_sample_verse(43, 3, 16, "For God so loved the world"),
_sample_verse(43, 3, 17, "For God did not send his Son"),
_sample_verse(45, 5, 8, "God shows his love for us"),
]
temp_store.insert_verses(verses)
results = temp_store.search_text("God")
assert len(results) == 3
results = temp_store.search_text("loved")
assert len(results) == 1
def test_count_verses(self, temp_store):
assert temp_store.count_verses() == 0
temp_store.insert_verse(_sample_verse())
assert temp_store.count_verses() == 1
def test_get_books(self, temp_store):
verses = [
_sample_verse(1, 1, 1, "Genesis verse"),
_sample_verse(43, 1, 1, "John verse"),
]
temp_store.insert_verses(verses)
books = temp_store.get_books()
assert len(books) == 2
assert books[0]["name"] == "Genesis"
assert books[1]["name"] == "John"
def test_cross_references(self, temp_store):
from scripture.models import CrossReference
xref = CrossReference(
source_verse_id=43003016,
target_verse_id=45005008,
reference_type="thematic",
confidence=0.9,
)
temp_store.insert_cross_reference(xref)
results = temp_store.get_cross_references(43003016)
assert len(results) == 1
assert results[0].target_verse_id == 45005008
def test_cross_references_bidirectional(self, temp_store):
from scripture.models import CrossReference
xref = CrossReference(
source_verse_id=43003016,
target_verse_id=45005008,
)
temp_store.insert_cross_reference(xref)
# Query from target side
results = temp_store.get_cross_references(45005008)
assert len(results) == 1
def test_topics(self, temp_store):
from scripture.models import Topic
topic = Topic(
topic_id="love",
name="Love",
description="Biblical concept of love",
verse_ids=[43003016, 45005008],
)
temp_store.insert_topic(topic)
result = temp_store.get_topic("love")
assert result is not None
assert result.name == "Love"
assert len(result.verse_ids) == 2
def test_search_topics(self, temp_store):
from scripture.models import Topic
temp_store.insert_topic(Topic(topic_id="love", name="Love"))
temp_store.insert_topic(Topic(topic_id="faith", name="Faith"))
results = temp_store.search_topics("lov")
assert len(results) == 1
assert results[0].name == "Love"
def test_get_verses_for_topic(self, temp_store):
from scripture.models import Topic
verse = _sample_verse()
temp_store.insert_verse(verse)
topic = Topic(
topic_id="love",
name="Love",
verse_ids=[verse.verse_id],
)
temp_store.insert_topic(topic)
verses = temp_store.get_verses_for_topic("love")
assert len(verses) == 1
assert verses[0].verse_id == verse.verse_id
def test_strongs(self, temp_store):
from scripture.models import StrongsEntry
entry = StrongsEntry(
strongs_number="G26",
language="greek",
lemma="ἀγάπη",
transliteration="agape",
gloss="love",
)
temp_store.insert_strongs(entry)
result = temp_store.get_strongs("G26")
assert result is not None
assert result.gloss == "love"
assert temp_store.get_strongs("G9999") is None
def test_stats(self, temp_store):
stats = temp_store.stats()
assert stats["verses"] == 0
assert stats["cross_references"] == 0
assert stats["topics"] == 0
assert stats["strongs_entries"] == 0
# ════════════════════════════════════════════════════════════════════════════
# Memory
# ════════════════════════════════════════════════════════════════════════════
@pytest.fixture
def temp_memory():
"""Create a ScriptureMemory backed by a temp file."""
from scripture.memory import ScriptureMemory
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
db_path = f.name
mem = ScriptureMemory(db_path=db_path)
yield mem
mem.close()
Path(db_path).unlink(missing_ok=True)
class TestWorkingMemory:
def test_focus_and_retrieve(self):
from scripture.memory import WorkingMemory
wm = WorkingMemory(capacity=3)
v1 = _sample_verse(1, 1, 1, "Verse 1")
v2 = _sample_verse(1, 1, 2, "Verse 2")
wm.focus(v1)
wm.focus(v2)
assert len(wm) == 2
focused = wm.get_focused()
assert focused[0].verse_id == v1.verse_id
assert focused[1].verse_id == v2.verse_id
def test_capacity_eviction(self):
from scripture.memory import WorkingMemory
wm = WorkingMemory(capacity=2)
v1 = _sample_verse(1, 1, 1, "Verse 1")
v2 = _sample_verse(1, 1, 2, "Verse 2")
v3 = _sample_verse(1, 1, 3, "Verse 3")
wm.focus(v1)
wm.focus(v2)
wm.focus(v3)
assert len(wm) == 2
assert not wm.is_focused(v1.verse_id)
assert wm.is_focused(v2.verse_id)
assert wm.is_focused(v3.verse_id)
def test_refocus_moves_to_end(self):
from scripture.memory import WorkingMemory
wm = WorkingMemory(capacity=3)
v1 = _sample_verse(1, 1, 1, "Verse 1")
v2 = _sample_verse(1, 1, 2, "Verse 2")
wm.focus(v1)
wm.focus(v2)
wm.focus(v1) # Re-focus v1
focused = wm.get_focused()
assert focused[-1].verse_id == v1.verse_id
def test_clear(self):
from scripture.memory import WorkingMemory
wm = WorkingMemory()
wm.focus(_sample_verse())
wm.clear()
assert len(wm) == 0
class TestAssociativeMemory:
def test_meditation_state_persistence(self, temp_memory):
from scripture.models import MeditationState
state = temp_memory.associative.get_meditation_state()
assert state.current_book == 1
assert state.mode == "sequential"
state.advance(43, 3, 16)
state.mode = "thematic"
temp_memory.associative.save_meditation_state(state)
loaded = temp_memory.associative.get_meditation_state()
assert loaded.current_book == 43
assert loaded.current_chapter == 3
assert loaded.current_verse == 16
assert loaded.mode == "thematic"
assert loaded.verses_meditated == 1
def test_meditation_log(self, temp_memory):
temp_memory.associative.log_meditation(43003016, notes="Great verse")
temp_memory.associative.log_meditation(45005008, notes="Also good")
history = temp_memory.associative.get_meditation_history(limit=10)
assert len(history) == 2
assert history[0]["verse_id"] == 45005008 # most recent first
def test_meditation_count(self, temp_memory):
assert temp_memory.associative.meditation_count() == 0
temp_memory.associative.log_meditation(1001001)
assert temp_memory.associative.meditation_count() == 1
def test_insights(self, temp_memory):
temp_memory.associative.add_insight(
43003016, "God's love is unconditional", category="theology"
)
insights = temp_memory.associative.get_insights(43003016)
assert len(insights) == 1
assert insights[0]["category"] == "theology"
def test_recent_insights(self, temp_memory):
temp_memory.associative.add_insight(1001001, "Creation narrative")
temp_memory.associative.add_insight(43003016, "Gospel core")
recent = temp_memory.associative.get_recent_insights(limit=5)
assert len(recent) == 2
def test_duplicate_insight_ignored(self, temp_memory):
temp_memory.associative.add_insight(43003016, "Same insight")
temp_memory.associative.add_insight(43003016, "Same insight")
insights = temp_memory.associative.get_insights(43003016)
assert len(insights) == 1
class TestScriptureMemory:
def test_status(self, temp_memory):
status = temp_memory.status()
assert "working_memory_items" in status
assert "meditation_mode" in status
assert status["working_memory_items"] == 0
# ════════════════════════════════════════════════════════════════════════════
# Meditation Scheduler
# ════════════════════════════════════════════════════════════════════════════
@pytest.fixture
def temp_scheduler():
"""Create a MeditationScheduler backed by temp stores."""
from scripture.meditation import MeditationScheduler
from scripture.memory import ScriptureMemory
from scripture.store import ScriptureStore
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
store_path = f.name
with tempfile.NamedTemporaryFile(suffix=".db", delete=False) as f:
mem_path = f.name
store = ScriptureStore(db_path=store_path)
memory = ScriptureMemory(db_path=mem_path)
scheduler = MeditationScheduler(store=store, memory=memory)
yield scheduler, store, memory
store.close()
memory.close()
Path(store_path).unlink(missing_ok=True)
Path(mem_path).unlink(missing_ok=True)
class TestMeditationScheduler:
def test_initial_state(self, temp_scheduler):
scheduler, _, _ = temp_scheduler
state = scheduler.state
assert state.mode == "sequential"
assert state.current_book == 1
def test_set_mode(self, temp_scheduler):
scheduler, _, _ = temp_scheduler
state = scheduler.set_mode("thematic", theme="love")
assert state.mode == "thematic"
assert state.theme == "love"
def test_set_invalid_mode(self, temp_scheduler):
scheduler, _, _ = temp_scheduler
with pytest.raises(ValueError, match="Unknown mode"):
scheduler.set_mode("invalid")
def test_next_sequential(self, temp_scheduler):
scheduler, store, _ = temp_scheduler
verses = [
_sample_verse(1, 1, i, f"Genesis 1:{i}")
for i in range(1, 6)
]
store.insert_verses(verses)
# State starts at 1:1:1, next should be 1:1:2
result = scheduler.next_meditation()
assert result is not None
assert result.verse_num == 2
def test_sequential_chapter_advance(self, temp_scheduler):
scheduler, store, _ = temp_scheduler
# Only two verses in chapter 1, plus verse 1 of chapter 2
store.insert_verses([
_sample_verse(1, 1, 1, "Gen 1:1"),
_sample_verse(1, 1, 2, "Gen 1:2"),
_sample_verse(1, 2, 1, "Gen 2:1"),
])
# Start at 1:1:1 → next is 1:1:2
v = scheduler.next_meditation()
assert v.verse_num == 2
# Next should advance to 1:2:1
v = scheduler.next_meditation()
assert v is not None
assert v.chapter == 2
assert v.verse_num == 1
def test_current_focus_empty(self, temp_scheduler):
scheduler, _, _ = temp_scheduler
assert scheduler.current_focus() is None
def test_meditate_on(self, temp_scheduler):
scheduler, store, memory = temp_scheduler
verse = _sample_verse()
store.insert_verse(verse)
scheduler.meditate_on(verse, notes="Reflecting on love")
assert memory.working.is_focused(verse.verse_id)
state = scheduler.state
assert state.verses_meditated == 1
def test_status(self, temp_scheduler):
scheduler, _, _ = temp_scheduler
status = scheduler.status()
assert "mode" in status
assert "current_book" in status
assert "verses_meditated" in status
def test_history(self, temp_scheduler):
scheduler, store, _ = temp_scheduler
verse = _sample_verse()
store.insert_verse(verse)
scheduler.meditate_on(verse)
history = scheduler.history(limit=5)
assert len(history) == 1
def test_get_context(self, temp_scheduler):
scheduler, store, _ = temp_scheduler
verses = [_sample_verse(1, 1, i, f"Gen 1:{i}") for i in range(1, 6)]
store.insert_verses(verses)
ctx = scheduler.get_context(verses[2], before=1, after=1)
assert len(ctx) == 3
def test_get_cross_references(self, temp_scheduler):
from scripture.models import CrossReference
scheduler, store, _ = temp_scheduler
v1 = _sample_verse(43, 3, 16, "For God so loved")
v2 = _sample_verse(45, 5, 8, "God shows his love")
store.insert_verse(v1)
store.insert_verse(v2)
store.insert_cross_reference(CrossReference(
source_verse_id=v1.verse_id,
target_verse_id=v2.verse_id,
))
xrefs = scheduler.get_cross_references(v1)
assert len(xrefs) == 1
assert xrefs[0].verse_id == v2.verse_id
# ════════════════════════════════════════════════════════════════════════════
# Routes
# ════════════════════════════════════════════════════════════════════════════
@pytest.fixture
def scripture_client(tmp_path):
"""TestClient with isolated scripture stores."""
from scripture.meditation import MeditationScheduler
from scripture.memory import ScriptureMemory
from scripture.store import ScriptureStore
store = ScriptureStore(db_path=tmp_path / "scripture.db")
memory = ScriptureMemory(db_path=tmp_path / "memory.db")
scheduler = MeditationScheduler(store=store, memory=memory)
# Seed with some verses for route testing
store.insert_verses([
_sample_verse(43, 3, 16, "For God so loved the world, that he gave his only Son, that whoever believes in him should not perish but have eternal life."),
_sample_verse(43, 3, 17, "For God did not send his Son into the world to condemn the world, but in order that the world might be saved through him."),
_sample_verse(45, 5, 8, "but God shows his love for us in that while we were still sinners, Christ died for us."),
_sample_verse(1, 1, 1, "In the beginning, God created the heavens and the earth."),
_sample_verse(1, 1, 2, "The earth was without form and void, and darkness was over the face of the deep."),
_sample_verse(1, 1, 3, "And God said, Let there be light, and there was light."),
])
with patch("dashboard.routes.scripture.scripture_store", store), \
patch("dashboard.routes.scripture.scripture_memory", memory), \
patch("dashboard.routes.scripture.meditation_scheduler", scheduler):
from dashboard.app import app
with TestClient(app) as c:
yield c
store.close()
memory.close()
class TestScriptureRoutes:
def test_scripture_status(self, scripture_client):
resp = scripture_client.get("/scripture")
assert resp.status_code == 200
data = resp.json()
assert "store" in data
assert "memory" in data
assert "meditation" in data
def test_get_verse(self, scripture_client):
resp = scripture_client.get("/scripture/verse", params={"ref": "John 3:16"})
assert resp.status_code == 200
data = resp.json()
assert data["reference"] == "John 3:16"
assert "loved" in data["text"]
def test_get_verse_range(self, scripture_client):
resp = scripture_client.get("/scripture/verse", params={"ref": "John 3:16-17"})
assert resp.status_code == 200
data = resp.json()
assert "verses" in data
assert len(data["verses"]) == 2
def test_get_verse_bad_ref(self, scripture_client):
resp = scripture_client.get("/scripture/verse", params={"ref": "not a ref"})
assert resp.status_code == 400
def test_get_verse_not_found(self, scripture_client):
resp = scripture_client.get("/scripture/verse", params={"ref": "Jude 1:25"})
assert resp.status_code == 404
def test_get_chapter(self, scripture_client):
resp = scripture_client.get(
"/scripture/chapter", params={"book": "Genesis", "chapter": 1}
)
assert resp.status_code == 200
data = resp.json()
assert data["book"] == "Genesis"
assert len(data["verses"]) == 3
def test_get_chapter_bad_book(self, scripture_client):
resp = scripture_client.get(
"/scripture/chapter", params={"book": "FakeBook", "chapter": 1}
)
assert resp.status_code == 400
def test_search(self, scripture_client):
resp = scripture_client.get("/scripture/search", params={"q": "God"})
assert resp.status_code == 200
data = resp.json()
assert data["count"] > 0
def test_search_empty(self, scripture_client):
resp = scripture_client.get("/scripture/search", params={"q": "xyznonexistent"})
assert resp.status_code == 200
assert resp.json()["count"] == 0
def test_meditate_get(self, scripture_client):
resp = scripture_client.get("/scripture/meditate")
assert resp.status_code == 200
data = resp.json()
assert "status" in data
def test_meditate_post(self, scripture_client):
resp = scripture_client.post("/scripture/meditate")
assert resp.status_code == 200
data = resp.json()
assert "verse" in data
assert "status" in data
def test_set_meditation_mode(self, scripture_client):
resp = scripture_client.post(
"/scripture/meditate/mode", params={"mode": "thematic", "theme": "love"}
)
assert resp.status_code == 200
assert resp.json()["mode"] == "thematic"
def test_set_meditation_mode_invalid(self, scripture_client):
resp = scripture_client.post(
"/scripture/meditate/mode", params={"mode": "invalid"}
)
assert resp.status_code == 400
def test_memory_status(self, scripture_client):
resp = scripture_client.get("/scripture/memory")
assert resp.status_code == 200
data = resp.json()
assert "working_memory_items" in data
def test_stats(self, scripture_client):
resp = scripture_client.get("/scripture/stats")
assert resp.status_code == 200
data = resp.json()
assert "verses" in data
def test_ingest(self, scripture_client):
payload = {
"verses": [
{"book": 19, "chapter": 23, "verse_num": 1, "text": "The LORD is my shepherd; I shall not want."},
{"book": 19, "chapter": 23, "verse_num": 2, "text": "He makes me lie down in green pastures."},
]
}
resp = scripture_client.post("/scripture/ingest", json=payload)
assert resp.status_code == 200
data = resp.json()
assert data["ingested"] == 2
def test_ingest_invalid(self, scripture_client):
resp = scripture_client.post("/scripture/ingest", json={"verses": []})
assert resp.status_code == 400
def test_ingest_bad_json(self, scripture_client):
resp = scripture_client.post(
"/scripture/ingest",
content=b"not json",
headers={"Content-Type": "application/json"},
)
assert resp.status_code == 400
def test_xref(self, scripture_client):
resp = scripture_client.get("/scripture/xref", params={"ref": "John 3:16"})
assert resp.status_code == 200
data = resp.json()
assert "source" in data
assert "cross_references" in data
def test_xref_not_found(self, scripture_client):
resp = scripture_client.get("/scripture/xref", params={"ref": "Jude 1:25"})
assert resp.status_code == 404