OPEN SOURCE
software/tests/test_glossary_enhanced.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 enhanced glossary models and migration utilities."""23import json4import pytest5from pathlib import Path67from src.context.glossary import GlossaryManager8from src.data.migrations import (9infer_domain_from_notes,10load_glossary_any_version,11migrate_glossary_file,12migrate_glossary_v1_to_v2,13)14from src.models.glossary import (15EnhancedGlossaryTerm,16GlossaryTerm,17TheologicalDomain,18)192021class TestTheologicalDomain:22"""Tests for TheologicalDomain enum."""2324def test_all_domains_exist(self) -> None:25"""Test all expected domains are defined."""26expected = [27"christology",28"soteriology",29"eschatology",30"pneumatology",31"ecclesiology",32"hamartiology",33"theology_proper",34"anthropology",35"general",36]37domain_values = [d.value for d in TheologicalDomain]38for exp in expected:39assert exp in domain_values4041def test_domain_string_value(self) -> None:42"""Test domain values are strings."""43assert TheologicalDomain.CHRISTOLOGY.value == "christology"44assert TheologicalDomain.GENERAL.value == "general"454647class TestEnhancedGlossaryTerm:48"""Tests for EnhancedGlossaryTerm model."""4950def test_create_basic_term(self) -> None:51"""Test creating term with only required fields."""52term = EnhancedGlossaryTerm(53original="λόγος",54transliteration="logos",55japanese="言葉",56english="word",57)58assert term.original == "λόγος"59assert term.domain == TheologicalDomain.GENERAL60assert term.semantic_range == []61assert term.frequency == 06263def test_create_enhanced_term(self) -> None:64"""Test creating term with all enhanced fields."""65term = EnhancedGlossaryTerm(66original="λόγος",67transliteration="logos",68japanese="言葉",69english="word",70notes="The divine Word",71definition="The Greek concept of reason and word",72semantic_range=["word", "reason", "message"],73domain=TheologicalDomain.CHRISTOLOGY,74usage_examples=["John 1:1", "John 1:14"],75frequency=330,76related_terms=["θεός", "σάρξ"],77avoid_translations=["ことば"],78)79assert term.definition is not None80assert len(term.semantic_range) == 381assert term.domain == TheologicalDomain.CHRISTOLOGY82assert term.frequency == 33083assert len(term.related_terms) == 284assert len(term.avoid_translations) == 18586def test_has_enhanced_metadata_false(self) -> None:87"""Test has_enhanced_metadata returns False for basic term."""88term = EnhancedGlossaryTerm(89original="λόγος",90transliteration="logos",91japanese="言葉",92english="word",93)94assert term.has_enhanced_metadata() is False9596def test_has_enhanced_metadata_true_with_definition(self) -> None:97"""Test has_enhanced_metadata returns True with definition."""98term = EnhancedGlossaryTerm(99original="λόγος",100transliteration="logos",101japanese="言葉",102english="word",103definition="Test definition",104)105assert term.has_enhanced_metadata() is True106107def test_has_enhanced_metadata_true_with_domain(self) -> None:108"""Test has_enhanced_metadata returns True with non-general domain."""109term = EnhancedGlossaryTerm(110original="λόγος",111transliteration="logos",112japanese="言葉",113english="word",114domain=TheologicalDomain.CHRISTOLOGY,115)116assert term.has_enhanced_metadata() is True117118def test_has_enhanced_metadata_true_with_semantic_range(self) -> None:119"""Test has_enhanced_metadata returns True with semantic range."""120term = EnhancedGlossaryTerm(121original="λόγος",122transliteration="logos",123japanese="言葉",124english="word",125semantic_range=["word", "reason"],126)127assert term.has_enhanced_metadata() is True128129def test_has_enhanced_metadata_true_with_frequency(self) -> None:130"""Test has_enhanced_metadata returns True with frequency > 0."""131term = EnhancedGlossaryTerm(132original="λόγος",133transliteration="logos",134japanese="言葉",135english="word",136frequency=10,137)138assert term.has_enhanced_metadata() is True139140def test_has_enhanced_metadata_true_with_avoid(self) -> None:141"""Test has_enhanced_metadata returns True with avoid_translations."""142term = EnhancedGlossaryTerm(143original="λόγος",144transliteration="logos",145japanese="言葉",146english="word",147avoid_translations=["ことば"],148)149assert term.has_enhanced_metadata() is True150151def test_backward_compatibility_alias(self) -> None:152"""Test GlossaryTerm is an alias for EnhancedGlossaryTerm."""153assert GlossaryTerm is EnhancedGlossaryTerm154155def test_frequency_non_negative(self) -> None:156"""Test frequency must be non-negative."""157with pytest.raises(ValueError):158EnhancedGlossaryTerm(159original="λόγος",160transliteration="logos",161japanese="言葉",162english="word",163frequency=-1,164)165166167class TestGlossaryManagerDomain:168"""Tests for GlossaryManager domain functionality."""169170@pytest.fixture171def enhanced_terms(self) -> list[EnhancedGlossaryTerm]:172"""Create test terms with domains."""173return [174EnhancedGlossaryTerm(175original="λόγος",176transliteration="logos",177japanese="言葉",178english="word",179domain=TheologicalDomain.CHRISTOLOGY,180related_terms=["θεός"],181),182EnhancedGlossaryTerm(183original="θεός",184transliteration="theos",185japanese="神",186english="God",187domain=TheologicalDomain.THEOLOGY_PROPER,188related_terms=["λόγος", "πνεῦμα"],189),190EnhancedGlossaryTerm(191original="πνεῦμα",192transliteration="pneuma",193japanese="霊",194english="spirit",195domain=TheologicalDomain.PNEUMATOLOGY,196),197EnhancedGlossaryTerm(198original="ἁμαρτία",199transliteration="hamartia",200japanese="罪",201english="sin",202domain=TheologicalDomain.HAMARTIOLOGY,203),204]205206def test_get_by_domain(207self, enhanced_terms: list[EnhancedGlossaryTerm]208) -> None:209"""Test getting terms by domain."""210manager = GlossaryManager(enhanced_terms)211christology_terms = manager.get_by_domain(TheologicalDomain.CHRISTOLOGY)212assert len(christology_terms) == 1213assert christology_terms[0].original == "λόγος"214215def test_get_by_domain_empty(216self, enhanced_terms: list[EnhancedGlossaryTerm]217) -> None:218"""Test getting terms from empty domain."""219manager = GlossaryManager(enhanced_terms)220eschatology_terms = manager.get_by_domain(TheologicalDomain.ESCHATOLOGY)221assert len(eschatology_terms) == 0222223def test_get_by_domain_returns_copy(224self, enhanced_terms: list[EnhancedGlossaryTerm]225) -> None:226"""Test get_by_domain returns a copy."""227manager = GlossaryManager(enhanced_terms)228terms = manager.get_by_domain(TheologicalDomain.CHRISTOLOGY)229terms.clear()230# Original should still have the term231assert len(manager.get_by_domain(TheologicalDomain.CHRISTOLOGY)) == 1232233def test_get_related_terms(234self, enhanced_terms: list[EnhancedGlossaryTerm]235) -> None:236"""Test getting related terms."""237manager = GlossaryManager(enhanced_terms)238theos_term = manager.get_by_original("θεός")239assert theos_term is not None240related = manager.get_related_terms(theos_term)241assert len(related) == 2242originals = [t.original for t in related]243assert "λόγος" in originals244assert "πνεῦμα" in originals245246def test_get_related_terms_not_found(247self, enhanced_terms: list[EnhancedGlossaryTerm]248) -> None:249"""Test related terms with missing references."""250term = EnhancedGlossaryTerm(251original="test",252transliteration="test",253japanese="テスト",254english="test",255related_terms=["nonexistent"],256)257manager = GlossaryManager([term])258related = manager.get_related_terms(term)259assert len(related) == 0260261def test_domain_index_updated_on_add(self) -> None:262"""Test domain index is updated when adding terms."""263manager = GlossaryManager()264term = EnhancedGlossaryTerm(265original="λόγος",266transliteration="logos",267japanese="言葉",268english="word",269domain=TheologicalDomain.CHRISTOLOGY,270)271manager.add_term(term)272assert len(manager.get_by_domain(TheologicalDomain.CHRISTOLOGY)) == 1273274def test_get_domain_summary(275self, enhanced_terms: list[EnhancedGlossaryTerm]276) -> None:277"""Test getting domain summary."""278manager = GlossaryManager(enhanced_terms)279summary = manager.get_domain_summary()280assert summary["christology"] == 1281assert summary["theology_proper"] == 1282assert summary["pneumatology"] == 1283assert summary["hamartiology"] == 1284assert summary["general"] == 0285286287class TestGlossaryManagerEnhancedFormatting:288"""Tests for enhanced prompt formatting."""289290def test_format_with_semantic_range(self) -> None:291"""Test formatting includes semantic range."""292term = EnhancedGlossaryTerm(293original="λόγος",294transliteration="logos",295japanese="言葉",296english="word",297semantic_range=["word", "reason", "message"],298)299manager = GlossaryManager([term])300formatted = manager.format_for_prompt([term])301assert "Semantic range: word, reason, message" in formatted302303def test_format_with_avoid_translations(self) -> None:304"""Test formatting includes avoid translations."""305term = EnhancedGlossaryTerm(306original="λόγος",307transliteration="logos",308japanese="言葉",309english="word",310avoid_translations=["ことば", "話"],311)312manager = GlossaryManager([term])313formatted = manager.format_for_prompt([term])314assert "Avoid: ことば, 話" in formatted315316def test_format_with_definition(self) -> None:317"""Test formatting includes definition."""318term = EnhancedGlossaryTerm(319original="λόγος",320transliteration="logos",321japanese="言葉",322english="word",323definition="The Greek concept of word and reason",324)325manager = GlossaryManager([term])326formatted = manager.format_for_prompt([term])327assert "Definition: The Greek concept" in formatted328329def test_format_without_enhanced(self) -> None:330"""Test formatting without enhanced metadata."""331term = EnhancedGlossaryTerm(332original="λόγος",333transliteration="logos",334japanese="言葉",335english="word",336semantic_range=["word", "reason"],337)338manager = GlossaryManager([term])339formatted = manager.format_for_prompt([term], include_enhanced=False)340assert "Semantic range" not in formatted341342def test_format_core_always_included(self) -> None:343"""Test core fields always included."""344term = EnhancedGlossaryTerm(345original="λόγος",346transliteration="logos",347japanese="言葉",348english="word",349notes="The Word",350)351manager = GlossaryManager([term])352formatted = manager.format_for_prompt([term])353assert "λόγος" in formatted354assert "logos" in formatted355assert "言葉" in formatted356assert "[The Word]" in formatted357358359class TestInferDomainFromNotes:360"""Tests for domain inference from notes."""361362def test_infer_christology_from_son(self) -> None:363"""Test inferring Christology from 'Son'."""364domain = infer_domain_from_notes("Son of God", "Son")365assert domain == TheologicalDomain.CHRISTOLOGY366367def test_infer_christology_from_word(self) -> None:368"""Test inferring Christology from 'Word'."""369domain = infer_domain_from_notes("The divine Word", "Word")370assert domain == TheologicalDomain.CHRISTOLOGY371372def test_infer_pneumatology_from_spirit(self) -> None:373"""Test inferring Pneumatology from 'Spirit'."""374domain = infer_domain_from_notes("Holy Spirit", "Spirit")375assert domain == TheologicalDomain.PNEUMATOLOGY376377def test_infer_soteriology_from_grace(self) -> None:378"""Test inferring Soteriology from 'grace'."""379domain = infer_domain_from_notes("Divine grace", "grace")380assert domain == TheologicalDomain.SOTERIOLOGY381382def test_infer_hamartiology_from_sin(self) -> None:383"""Test inferring Hamartiology from 'sin'."""384domain = infer_domain_from_notes("Missing the mark", "sin")385assert domain == TheologicalDomain.HAMARTIOLOGY386387def test_infer_theology_proper_from_god(self) -> None:388"""Test inferring Theology Proper from 'God'."""389domain = infer_domain_from_notes("Standard translation for God", "God")390assert domain == TheologicalDomain.THEOLOGY_PROPER391392def test_infer_general_default(self) -> None:393"""Test default to GENERAL when no keywords match."""394domain = infer_domain_from_notes("Some random note", "something")395assert domain == TheologicalDomain.GENERAL396397def test_infer_with_none_notes(self) -> None:398"""Test inference with None notes."""399domain = infer_domain_from_notes(None, "general")400assert domain == TheologicalDomain.GENERAL401402403class TestMigrateGlossaryV1ToV2:404"""Tests for glossary migration."""405406def test_migrate_basic(self) -> None:407"""Test basic v1 to v2 migration."""408v1_data = {409"terms": [410{411"original": "λόγος",412"transliteration": "logos",413"japanese": "言葉",414"english": "Word",415"notes": "The divine Word",416}417]418}419v2_data = migrate_glossary_v1_to_v2(v1_data)420assert v2_data["version"] == "2.0"421assert len(v2_data["terms"]) == 1422term = v2_data["terms"][0]423assert term["domain"] == "christology" # Inferred from "Word"424assert term["semantic_range"] == []425assert term["avoid_translations"] == []426427def test_migrate_preserves_core_fields(self) -> None:428"""Test migration preserves all core fields."""429v1_data = {430"terms": [431{432"original": "θεός",433"transliteration": "theos",434"japanese": "神",435"english": "God",436"notes": "Standard translation",437}438]439}440v2_data = migrate_glossary_v1_to_v2(v1_data)441term = v2_data["terms"][0]442assert term["original"] == "θεός"443assert term["transliteration"] == "theos"444assert term["japanese"] == "神"445assert term["english"] == "God"446assert term["notes"] == "Standard translation"447448def test_migrate_multiple_terms(self) -> None:449"""Test migrating multiple terms."""450v1_data = {451"terms": [452{453"original": "λόγος",454"transliteration": "logos",455"japanese": "言葉",456"english": "Word",457},458{459"original": "θεός",460"transliteration": "theos",461"japanese": "神",462"english": "God",463},464]465}466v2_data = migrate_glossary_v1_to_v2(v1_data)467assert len(v2_data["terms"]) == 2468469def test_migrate_empty_terms(self) -> None:470"""Test migrating empty terms list."""471v1_data = {"terms": []}472v2_data = migrate_glossary_v1_to_v2(v1_data)473assert v2_data["version"] == "2.0"474assert v2_data["terms"] == []475476477class TestLoadGlossaryAnyVersion:478"""Tests for loading glossary from any version."""479480def test_load_v1_format(self, tmp_path: Path) -> None:481"""Test loading v1 format file."""482v1_data = {483"terms": [484{485"original": "λόγος",486"transliteration": "logos",487"japanese": "言葉",488"english": "Word",489"notes": "The divine Word",490}491]492}493path = tmp_path / "glossary.json"494with path.open("w", encoding="utf-8") as f:495json.dump(v1_data, f)496497terms = load_glossary_any_version(path)498assert len(terms) == 1499assert isinstance(terms[0], EnhancedGlossaryTerm)500assert terms[0].domain == TheologicalDomain.CHRISTOLOGY501502def test_load_v2_format(self, tmp_path: Path) -> None:503"""Test loading v2 format file."""504v2_data = {505"version": "2.0",506"terms": [507{508"original": "λόγος",509"transliteration": "logos",510"japanese": "言葉",511"english": "Word",512"domain": "christology",513"semantic_range": ["word", "reason"],514}515],516}517path = tmp_path / "glossary.json"518with path.open("w", encoding="utf-8") as f:519json.dump(v2_data, f)520521terms = load_glossary_any_version(path)522assert len(terms) == 1523assert terms[0].semantic_range == ["word", "reason"]524525def test_load_nonexistent_file(self, tmp_path: Path) -> None:526"""Test loading nonexistent file returns empty list."""527path = tmp_path / "nonexistent.json"528terms = load_glossary_any_version(path)529assert terms == []530531532class TestMigrateGlossaryFile:533"""Tests for file-based migration."""534535def test_migrate_file_creates_backup(self, tmp_path: Path) -> None:536"""Test migration creates backup file."""537v1_data = {538"terms": [539{540"original": "λόγος",541"transliteration": "logos",542"japanese": "言葉",543"english": "Word",544}545]546}547path = tmp_path / "glossary.json"548with path.open("w", encoding="utf-8") as f:549json.dump(v1_data, f)550551migrate_glossary_file(path)552553backup_path = path.with_suffix(".json.v1.bak")554assert backup_path.exists()555556def test_migrate_file_updates_content(self, tmp_path: Path) -> None:557"""Test migration updates file content."""558v1_data = {559"terms": [560{561"original": "λόγος",562"transliteration": "logos",563"japanese": "言葉",564"english": "Word",565}566]567}568path = tmp_path / "glossary.json"569with path.open("w", encoding="utf-8") as f:570json.dump(v1_data, f)571572migrate_glossary_file(path)573574with path.open("r", encoding="utf-8") as f:575migrated = json.load(f)576assert migrated["version"] == "2.0"577578def test_migrate_file_already_v2(self, tmp_path: Path) -> None:579"""Test migration of already v2 file returns terms."""580v2_data = {581"version": "2.0",582"terms": [583{584"original": "λόγος",585"transliteration": "logos",586"japanese": "言葉",587"english": "Word",588"domain": "christology",589}590],591}592path = tmp_path / "glossary.json"593with path.open("w", encoding="utf-8") as f:594json.dump(v2_data, f)595596terms = migrate_glossary_file(path)597assert len(terms) == 1598# No backup should be created599assert not path.with_suffix(".json.v1.bak").exists()600601def test_migrate_file_to_different_output(self, tmp_path: Path) -> None:602"""Test migration to different output file."""603v1_data = {604"terms": [605{606"original": "λόγος",607"transliteration": "logos",608"japanese": "言葉",609"english": "Word",610}611]612}613input_path = tmp_path / "v1_glossary.json"614output_path = tmp_path / "v2_glossary.json"615with input_path.open("w", encoding="utf-8") as f:616json.dump(v1_data, f)617618migrate_glossary_file(input_path, output_path)619620assert output_path.exists()621with output_path.open("r", encoding="utf-8") as f:622migrated = json.load(f)623assert migrated["version"] == "2.0"624625def test_migrate_file_no_backup(self, tmp_path: Path) -> None:626"""Test migration without backup."""627v1_data = {628"terms": [629{630"original": "λόγος",631"transliteration": "logos",632"japanese": "言葉",633"english": "Word",634}635]636}637path = tmp_path / "glossary.json"638with path.open("w", encoding="utf-8") as f:639json.dump(v1_data, f)640641migrate_glossary_file(path, backup=False)642643backup_path = path.with_suffix(".json.v1.bak")644assert not backup_path.exists()645