OPEN SOURCE
software/tests/test_context.py
Read on this site and download the files you need.
Nichijoyaku was created using AI. A complete review by human biblical-language and biblical-studies specialists has not been established. Read alongside established translations and original-language resources.
← Back to the open-source guide
1"""Tests for context building and glossary management."""23import json4import pytest5from pathlib import Path67from src.context.builder import ContextBuilder8from src.context.glossary import GlossaryManager9from src.data.loader import DataLoader10from src.models.verse import GlossaryTerm, VerseReference111213@pytest.fixture14def glossary_terms() -> list[GlossaryTerm]:15"""Create test glossary terms."""16return [17GlossaryTerm(18original="λόγος",19transliteration="logos",20japanese="言葉",21english="word",22notes="The Word",23),24GlossaryTerm(25original="θεός",26transliteration="theos",27japanese="神",28english="God",29),30GlossaryTerm(31original="ἀγάπη",32transliteration="agape",33japanese="愛",34english="love",35),36]373839@pytest.fixture40def temp_data_dir(tmp_path: Path) -> Path:41"""Create temporary data directory with test data."""42sources_dir = tmp_path / "sources"43(sources_dir / "greek").mkdir(parents=True)44(sources_dir / "japanese" / "meiji").mkdir(parents=True)45(sources_dir / "japanese" / "taisho").mkdir(parents=True)46(sources_dir / "english" / "kjv").mkdir(parents=True)47(sources_dir / "english" / "asv").mkdir(parents=True)48(tmp_path / "metadata" / "john").mkdir(parents=True)4950# Greek data with 5 verses51greek_data = {52"1:1": {"text": "Ἐν ἀρχῇ ἦν ὁ λόγος", "translit": "En archē ēn ho logos"},53"1:2": {"text": "οὗτος ἦν ἐν ἀρχῇ", "translit": "houtos ēn en archē"},54"1:3": {"text": "πάντα δι' αὐτοῦ ἐγένετο", "translit": "panta di autou egeneto"},55"1:4": {"text": "ἐν αὐτῷ ζωὴ ἦν", "translit": "en autō zōē ēn"},56"1:5": {"text": "τὸ φῶς ἐν τῇ σκοτίᾳ", "translit": "to phōs en tē skotia"},57}58with (sources_dir / "greek" / "john.json").open("w") as f:59json.dump(greek_data, f)6061# KJV data62kjv_data = {63"1:1": "In the beginning was the Word",64"1:2": "The same was in the beginning with God",65"1:3": "All things were made by him",66"1:4": "In him was life",67"1:5": "And the light shineth in darkness",68}69with (sources_dir / "english" / "kjv" / "john.json").open("w") as f:70json.dump(kjv_data, f)7172# Book metadata73book_metadata = {74"book": "john",75"name_japanese": "ヨハネによる福音書",76"name_english": "Gospel of John",77"testament": "NT",78"author": "John the Apostle",79"themes": ["love", "light"],80"description": "The fourth Gospel",81"chapter_count": 21,82"verse_count": 879,83}84with (tmp_path / "metadata" / "john" / "book_context.json").open("w") as f:85json.dump(book_metadata, f)8687# Chapter summaries88chapter_summaries = {89"1": {90"summary": "The prologue introduces Jesus as the Word",91"key_events": ["Word becomes flesh"],92"key_characters": ["Jesus", "John"],93"key_themes": ["Light", "Word"],94}95}96with (tmp_path / "metadata" / "john" / "chapter_summaries.json").open("w") as f:97json.dump(chapter_summaries, f)9899# Glossary100glossary = {101"terms": [102{103"original": "λόγος",104"transliteration": "logos",105"japanese": "言葉",106"english": "word",107"notes": "The Word",108}109]110}111with (tmp_path / "metadata" / "glossary.json").open("w") as f:112json.dump(glossary, f)113114return tmp_path115116117class TestGlossaryManager:118"""Tests for GlossaryManager."""119120def test_init_empty(self) -> None:121"""Test initializing empty glossary."""122glossary = GlossaryManager()123assert len(glossary) == 0124125def test_init_with_terms(self, glossary_terms: list[GlossaryTerm]) -> None:126"""Test initializing with terms."""127glossary = GlossaryManager(glossary_terms)128assert len(glossary) == 3129130def test_add_term(self) -> None:131"""Test adding a term."""132glossary = GlossaryManager()133term = GlossaryTerm(134original="λόγος",135transliteration="logos",136japanese="言葉",137english="word",138)139glossary.add_term(term)140assert len(glossary) == 1141142def test_get_by_original(self, glossary_terms: list[GlossaryTerm]) -> None:143"""Test getting term by original."""144glossary = GlossaryManager(glossary_terms)145term = glossary.get_by_original("λόγος")146assert term is not None147assert term.japanese == "言葉"148149def test_get_by_original_not_found(150self, glossary_terms: list[GlossaryTerm]151) -> None:152"""Test getting non-existent term."""153glossary = GlossaryManager(glossary_terms)154term = glossary.get_by_original("nonexistent")155assert term is None156157def test_get_by_transliteration(158self, glossary_terms: list[GlossaryTerm]159) -> None:160"""Test getting term by transliteration."""161glossary = GlossaryManager(glossary_terms)162term = glossary.get_by_transliteration("logos")163assert term is not None164assert term.original == "λόγος"165166def test_get_by_transliteration_case_insensitive(167self, glossary_terms: list[GlossaryTerm]168) -> None:169"""Test transliteration lookup is case insensitive."""170glossary = GlossaryManager(glossary_terms)171term = glossary.get_by_transliteration("LOGOS")172assert term is not None173174def test_find_terms_in_text_by_original(175self, glossary_terms: list[GlossaryTerm]176) -> None:177"""Test finding terms by original in text."""178glossary = GlossaryManager(glossary_terms)179found = glossary.find_terms_in_text("Ἐν ἀρχῇ ἦν ὁ λόγος")180assert len(found) == 1181assert found[0].original == "λόγος"182183def test_find_terms_in_text_by_transliteration(184self, glossary_terms: list[GlossaryTerm]185) -> None:186"""Test finding terms by transliteration in text."""187glossary = GlossaryManager(glossary_terms)188found = glossary.find_terms_in_text("en archē ēn ho logos")189assert len(found) == 1190assert found[0].original == "λόγος"191192def test_find_terms_in_text_multiple(193self, glossary_terms: list[GlossaryTerm]194) -> None:195"""Test finding multiple terms."""196glossary = GlossaryManager(glossary_terms)197found = glossary.find_terms_in_text("λόγος and θεός")198assert len(found) == 2199200def test_find_terms_no_duplicates(201self, glossary_terms: list[GlossaryTerm]202) -> None:203"""Test no duplicate terms returned."""204glossary = GlossaryManager(glossary_terms)205# Text with both original and transliteration206found = glossary.find_terms_in_text("λόγος logos")207assert len(found) == 1208209def test_get_all_terms(self, glossary_terms: list[GlossaryTerm]) -> None:210"""Test getting all terms."""211glossary = GlossaryManager(glossary_terms)212all_terms = glossary.get_all_terms()213assert len(all_terms) == 3214215def test_format_for_prompt_with_terms(216self, glossary_terms: list[GlossaryTerm]217) -> None:218"""Test formatting terms for prompt."""219glossary = GlossaryManager(glossary_terms)220formatted = glossary.format_for_prompt(glossary_terms[:2])221assert "λόγος" in formatted222assert "言葉" in formatted223assert "[The Word]" in formatted # Notes should be included224225def test_format_for_prompt_empty(self) -> None:226"""Test formatting empty list."""227glossary = GlossaryManager()228formatted = glossary.format_for_prompt([])229assert "No specific glossary" in formatted230231232class TestContextBuilder:233"""Tests for ContextBuilder."""234235def test_build_context(self, temp_data_dir: Path) -> None:236"""Test building translation context."""237loader = DataLoader(temp_data_dir)238glossary = GlossaryManager(loader.load_glossary())239builder = ContextBuilder(loader, glossary)240241ref = VerseReference(book="john", chapter=1, verse=3)242context = builder.build_context(ref)243244assert context.target_verse.reference == ref245assert len(context.preceding_verses) > 0246assert len(context.following_verses) > 0247248def test_build_context_first_verse(self, temp_data_dir: Path) -> None:249"""Test context for first verse has no preceding."""250loader = DataLoader(temp_data_dir)251glossary = GlossaryManager(loader.load_glossary())252builder = ContextBuilder(loader, glossary)253254ref = VerseReference(book="john", chapter=1, verse=1)255context = builder.build_context(ref)256257assert len(context.preceding_verses) == 0258259def test_build_context_with_metadata(self, temp_data_dir: Path) -> None:260"""Test context includes metadata."""261loader = DataLoader(temp_data_dir)262glossary = GlossaryManager(loader.load_glossary())263builder = ContextBuilder(loader, glossary)264265ref = VerseReference(book="john", chapter=1, verse=1)266context = builder.build_context(ref)267268assert context.book_metadata is not None269assert context.chapter_summary is not None270271def test_build_context_finds_glossary_terms(self, temp_data_dir: Path) -> None:272"""Test context finds glossary terms in verse."""273loader = DataLoader(temp_data_dir)274glossary = GlossaryManager(loader.load_glossary())275builder = ContextBuilder(loader, glossary)276277ref = VerseReference(book="john", chapter=1, verse=1)278context = builder.build_context(ref)279280# John 1:1 has λόγος which is in our glossary281assert len(context.glossary_terms) > 0282283def test_context_window_size(self, temp_data_dir: Path) -> None:284"""Test context window size parameter."""285loader = DataLoader(temp_data_dir)286glossary = GlossaryManager()287builder = ContextBuilder(loader, glossary, context_window=2)288289ref = VerseReference(book="john", chapter=1, verse=3)290context = builder.build_context(ref)291292# Should have at most 2 preceding and 2 following293assert len(context.preceding_verses) <= 2294assert len(context.following_verses) <= 2295