公開前の確認用サイト
日常訳NICHIJOYAKUEnglish ↗

OPEN SOURCE

software/tests/test_glossary_enhanced.py

このサイトで読み、必要なファイルを保存できます。

日常訳はAIを用いて作成された翻訳です。人間の聖書学・原語の専門家による全訳の検証は確認されていません。既存の翻訳や原語資料と照らし合わせてお読みください。

← オープンソースの案内へ

software/tests/test_glossary_enhanced.py645 行このファイルを保存 ↓

ZIPに含まれるものと同じ公開ソースです。行番号から、その行へのリンクを保存できます。

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