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

OPEN SOURCE

software/tests/test_style_config.py

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

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

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

software/tests/test_style_config.py525 行このファイルを保存 ↓

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

ファイルの確認情報SHA-256 9ae542fc89bce2cb6a0b1a2ca7b3fd123ceea51a3c4d0aa8ceeeb2908464bf05
1"""Tests for translation style configuration."""23import json4import pytest5from pathlib import Path67from src.config.style_config import StyleConfigManager8from src.context.glossary import GlossaryManager9from src.models.style import (10    CONSISTENCY_PROMPTS,11    EQUIVALENCE_PROMPTS,12    FORMALITY_PROMPTS,13    GENDER_PROMPTS,14    READABILITY_PROMPTS,15    TRADITION_PROMPTS,16    EquivalenceApproach,17    FormalityLevel,18    GenderLanguage,19    ReadabilityLevel,20    TermConsistency,21    TheologicalTradition,22    TranslationStyle,23    build_system_prompt,24)25from src.pipeline.prompt import SYSTEM_PROMPT, PromptBuilder262728class TestEquivalenceApproach:29    """Tests for EquivalenceApproach enum."""3031    def test_all_values_exist(self) -> None:32        """Test all expected values exist."""33        assert EquivalenceApproach.FORMAL.value == "formal"34        assert EquivalenceApproach.DYNAMIC.value == "dynamic"35        assert EquivalenceApproach.OPTIMAL.value == "optimal"3637    def test_has_prompt_template(self) -> None:38        """Test each approach has a prompt template."""39        for approach in EquivalenceApproach:40            assert approach in EQUIVALENCE_PROMPTS41            assert len(EQUIVALENCE_PROMPTS[approach]) > 0424344class TestGenderLanguage:45    """Tests for GenderLanguage enum."""4647    def test_all_values_exist(self) -> None:48        """Test all expected values exist."""49        assert GenderLanguage.TRADITIONAL.value == "traditional"50        assert GenderLanguage.MODERATE_INCLUSIVE.value == "moderate_inclusive"51        assert GenderLanguage.FULLY_INCLUSIVE.value == "fully_inclusive"5253    def test_has_prompt_template(self) -> None:54        """Test each option has a prompt template."""55        for option in GenderLanguage:56            assert option in GENDER_PROMPTS575859class TestReadabilityLevel:60    """Tests for ReadabilityLevel enum."""6162    def test_all_values_exist(self) -> None:63        """Test all expected values exist."""64        assert ReadabilityLevel.SCHOLARLY.value == "scholarly"65        assert ReadabilityLevel.GENERAL_PUBLIC.value == "general_public"66        assert ReadabilityLevel.CHILDREN.value == "children"6768    def test_has_prompt_template(self) -> None:69        """Test each level has a prompt template."""70        for level in ReadabilityLevel:71            assert level in READABILITY_PROMPTS727374class TestTheologicalTradition:75    """Tests for TheologicalTradition enum."""7677    def test_all_values_exist(self) -> None:78        """Test all expected values exist."""79        assert TheologicalTradition.EVANGELICAL.value == "evangelical"80        assert TheologicalTradition.CATHOLIC.value == "catholic"81        assert TheologicalTradition.ORTHODOX.value == "orthodox"82        assert TheologicalTradition.ECUMENICAL.value == "ecumenical"8384    def test_has_prompt_template(self) -> None:85        """Test each tradition has a prompt template."""86        for tradition in TheologicalTradition:87            assert tradition in TRADITION_PROMPTS888990class TestTermConsistency:91    """Tests for TermConsistency enum."""9293    def test_all_values_exist(self) -> None:94        """Test all expected values exist."""95        assert TermConsistency.STRICT.value == "strict"96        assert TermConsistency.BALANCED.value == "balanced"97        assert TermConsistency.CONTEXTUAL.value == "contextual"9899    def test_has_prompt_template(self) -> None:100        """Test each option has a prompt template."""101        for option in TermConsistency:102            assert option in CONSISTENCY_PROMPTS103104105class TestFormalityLevel:106    """Tests for FormalityLevel enum."""107108    def test_all_values_exist(self) -> None:109        """Test all expected values exist."""110        assert FormalityLevel.CASUAL.value == "casual"111        assert FormalityLevel.STANDARD.value == "standard"112        assert FormalityLevel.FORMAL.value == "formal"113114    def test_has_prompt_template(self) -> None:115        """Test each level has a prompt template."""116        for level in FormalityLevel:117            assert level in FORMALITY_PROMPTS118119120class TestTranslationStyle:121    """Tests for TranslationStyle dataclass."""122123    def test_default_values(self) -> None:124        """Test default values are set correctly."""125        style = TranslationStyle()126        assert style.equivalence_approach == EquivalenceApproach.DYNAMIC127        assert style.gender_language == GenderLanguage.MODERATE_INCLUSIVE128        assert style.readability_level == ReadabilityLevel.WEB_NATIVE129        assert style.theological_tradition == TheologicalTradition.EVANGELICAL130        assert style.textual_basis == "MorphGNT/SBLGNT"131        assert style.term_consistency == TermConsistency.BALANCED132        assert style.formality_level == FormalityLevel.STANDARD133        assert "なり" in style.avoid_archaic134        assert style.target_style_reference == "現代のウェブ記事・ナレーションのような平易な日本語"135        assert style.additional_instructions is None136137    def test_custom_values(self) -> None:138        """Test creating with custom values."""139        style = TranslationStyle(140            equivalence_approach=EquivalenceApproach.FORMAL,141            gender_language=GenderLanguage.TRADITIONAL,142            readability_level=ReadabilityLevel.SCHOLARLY,143            theological_tradition=TheologicalTradition.CATHOLIC,144            textual_basis="Textus Receptus",145            term_consistency=TermConsistency.STRICT,146            formality_level=FormalityLevel.FORMAL,147            avoid_archaic=["候"],148            target_style_reference="文語訳-style",149            additional_instructions="Focus on liturgical usage",150        )151        assert style.equivalence_approach == EquivalenceApproach.FORMAL152        assert style.theological_tradition == TheologicalTradition.CATHOLIC153        assert style.textual_basis == "Textus Receptus"154        assert style.additional_instructions == "Focus on liturgical usage"155156    def test_to_dict(self) -> None:157        """Test conversion to dictionary."""158        style = TranslationStyle()159        data = style.to_dict()160        assert data["equivalence_approach"] == "dynamic"161        assert data["gender_language"] == "moderate_inclusive"162        assert data["readability_level"] == "web_native"163        assert data["theological_tradition"] == "evangelical"164        assert data["term_consistency"] == "balanced"165        assert data["formality_level"] == "standard"166        assert isinstance(data["avoid_archaic"], list)167168    def test_from_dict(self) -> None:169        """Test creation from dictionary."""170        data = {171            "equivalence_approach": "formal",172            "gender_language": "traditional",173            "readability_level": "scholarly",174            "theological_tradition": "catholic",175            "textual_basis": "Custom",176            "term_consistency": "strict",177            "formality_level": "formal",178            "avoid_archaic": ["候"],179            "target_style_reference": "custom-style",180            "additional_instructions": "Test instructions",181        }182        style = TranslationStyle.from_dict(data)183        assert style.equivalence_approach == EquivalenceApproach.FORMAL184        assert style.gender_language == GenderLanguage.TRADITIONAL185        assert style.theological_tradition == TheologicalTradition.CATHOLIC186        assert style.additional_instructions == "Test instructions"187188    def test_from_dict_with_defaults(self) -> None:189        """Test creation from partial dictionary uses defaults."""190        data = {"equivalence_approach": "optimal"}191        style = TranslationStyle.from_dict(data)192        assert style.equivalence_approach == EquivalenceApproach.OPTIMAL193        assert style.gender_language == GenderLanguage.MODERATE_INCLUSIVE  # Default194195    def test_roundtrip(self) -> None:196        """Test to_dict and from_dict roundtrip."""197        original = TranslationStyle(198            equivalence_approach=EquivalenceApproach.OPTIMAL,199            gender_language=GenderLanguage.FULLY_INCLUSIVE,200            additional_instructions="Test",201        )202        data = original.to_dict()203        restored = TranslationStyle.from_dict(data)204        assert restored.equivalence_approach == original.equivalence_approach205        assert restored.gender_language == original.gender_language206        assert restored.additional_instructions == original.additional_instructions207208209class TestBuildSystemPrompt:210    """Tests for build_system_prompt function."""211212    def test_default_style_prompt(self) -> None:213        """Test prompt with default style."""214        style = TranslationStyle()215        prompt = build_system_prompt(style)216        assert "Dynamic/Functional" in prompt217        assert "Moderate Inclusive" in prompt218        assert "Web-native" in prompt219        assert "Protestant Evangelical" in prompt220        assert "MorphGNT/SBLGNT" in prompt221222    def test_formal_equivalence_prompt(self) -> None:223        """Test prompt with formal equivalence."""224        style = TranslationStyle(equivalence_approach=EquivalenceApproach.FORMAL)225        prompt = build_system_prompt(style)226        assert "word-for-word" in prompt227        assert "Formal" in prompt228229    def test_traditional_gender_prompt(self) -> None:230        """Test prompt with traditional gender language."""231        style = TranslationStyle(gender_language=GenderLanguage.TRADITIONAL)232        prompt = build_system_prompt(style)233        assert "Traditional" in prompt234        assert "masculine generic" in prompt235236    def test_scholarly_readability_prompt(self) -> None:237        """Test prompt with scholarly readability."""238        style = TranslationStyle(readability_level=ReadabilityLevel.SCHOLARLY)239        prompt = build_system_prompt(style)240        assert "Scholarly" in prompt241        assert "academic" in prompt242243    def test_catholic_tradition_prompt(self) -> None:244        """Test prompt with Catholic tradition."""245        style = TranslationStyle(246            theological_tradition=TheologicalTradition.CATHOLIC247        )248        prompt = build_system_prompt(style)249        assert "Catholic" in prompt250251    def test_strict_consistency_prompt(self) -> None:252        """Test prompt with strict term consistency."""253        style = TranslationStyle(term_consistency=TermConsistency.STRICT)254        prompt = build_system_prompt(style)255        assert "Strict" in prompt256        assert "same word" in prompt257258    def test_formal_formality_prompt(self) -> None:259        """Test prompt with formal formality level."""260        style = TranslationStyle(formality_level=FormalityLevel.FORMAL)261        prompt = build_system_prompt(style)262        assert "Formal/Literary" in prompt or "formal literary" in prompt263264    def test_avoid_archaic_included(self) -> None:265        """Test archaic expressions are included."""266        style = TranslationStyle(avoid_archaic=["なり", "候"])267        prompt = build_system_prompt(style)268        assert "なり" in prompt269        assert "候" in prompt270271    def test_additional_instructions_included(self) -> None:272        """Test additional instructions are included."""273        style = TranslationStyle(274            additional_instructions="Focus on poetic language"275        )276        prompt = build_system_prompt(style)277        assert "ADDITIONAL INSTRUCTIONS" in prompt278        assert "poetic language" in prompt279280    def test_additional_instructions_not_included_when_none(self) -> None:281        """Test no additional instructions section when None."""282        style = TranslationStyle(additional_instructions=None)283        prompt = build_system_prompt(style)284        assert "ADDITIONAL INSTRUCTIONS" not in prompt285286    def test_input_format_section(self) -> None:287        """Test input format section is always included."""288        style = TranslationStyle()289        prompt = build_system_prompt(style)290        assert "INPUT FORMAT" in prompt291        assert "Source text" in prompt292        assert "Japanese translation" in prompt293294295class TestStyleConfigManager:296    """Tests for StyleConfigManager."""297298    def test_init_default_path(self) -> None:299        """Test initialization with default path."""300        manager = StyleConfigManager()301        assert manager.config_path == Path("data/config/style.json")302303    def test_init_custom_path(self, tmp_path: Path) -> None:304        """Test initialization with custom path."""305        custom_path = tmp_path / "custom_style.json"306        manager = StyleConfigManager(custom_path)307        assert manager.config_path == custom_path308309    def test_load_nonexistent_returns_default(self, tmp_path: Path) -> None:310        """Test loading from nonexistent file returns default."""311        path = tmp_path / "nonexistent.json"312        manager = StyleConfigManager(path)313        style = manager.load()314        assert style.equivalence_approach == EquivalenceApproach.DYNAMIC315316    def test_save_creates_file(self, tmp_path: Path) -> None:317        """Test save creates the config file."""318        path = tmp_path / "style.json"319        manager = StyleConfigManager(path)320        style = TranslationStyle(equivalence_approach=EquivalenceApproach.FORMAL)321        manager.save(style)322        assert path.exists()323324    def test_save_and_load_roundtrip(self, tmp_path: Path) -> None:325        """Test save and load roundtrip."""326        path = tmp_path / "style.json"327        manager = StyleConfigManager(path)328329        original = TranslationStyle(330            equivalence_approach=EquivalenceApproach.OPTIMAL,331            gender_language=GenderLanguage.FULLY_INCLUSIVE,332            additional_instructions="Test roundtrip",333        )334        manager.save(original)335336        # Create new manager and load337        manager2 = StyleConfigManager(path)338        loaded = manager2.load()339340        assert loaded.equivalence_approach == original.equivalence_approach341        assert loaded.gender_language == original.gender_language342        assert loaded.additional_instructions == original.additional_instructions343344    def test_get_style_loads_lazily(self, tmp_path: Path) -> None:345        """Test get_style loads on first call."""346        path = tmp_path / "style.json"347        manager = StyleConfigManager(path)348349        # First call loads default (file doesn't exist)350        style1 = manager.get_style()351        assert style1 is not None352353        # Second call returns cached354        style2 = manager.get_style()355        assert style2 is style1356357    def test_set_style_in_memory(self, tmp_path: Path) -> None:358        """Test set_style only updates memory."""359        path = tmp_path / "style.json"360        manager = StyleConfigManager(path)361362        new_style = TranslationStyle(363            equivalence_approach=EquivalenceApproach.FORMAL364        )365        manager.set_style(new_style)366367        # Memory updated368        assert manager.get_style().equivalence_approach == EquivalenceApproach.FORMAL369370        # File not created yet371        assert not path.exists()372373    def test_reset_to_default(self, tmp_path: Path) -> None:374        """Test reset_to_default returns default style."""375        path = tmp_path / "style.json"376        manager = StyleConfigManager(path)377378        # Set custom style379        custom = TranslationStyle(equivalence_approach=EquivalenceApproach.FORMAL)380        manager.set_style(custom)381382        # Reset383        default = manager.reset_to_default()384        assert default.equivalence_approach == EquivalenceApproach.DYNAMIC385386    def test_reload_discards_changes(self, tmp_path: Path) -> None:387        """Test reload discards in-memory changes."""388        path = tmp_path / "style.json"389390        # Save a style to file391        initial = TranslationStyle(equivalence_approach=EquivalenceApproach.OPTIMAL)392        with path.open("w") as f:393            json.dump(initial.to_dict(), f)394395        manager = StyleConfigManager(path)396        manager.load()397398        # Make in-memory change399        manager.set_style(400            TranslationStyle(equivalence_approach=EquivalenceApproach.FORMAL)401        )402        assert manager.get_style().equivalence_approach == EquivalenceApproach.FORMAL403404        # Reload from file405        reloaded = manager.reload()406        assert reloaded.equivalence_approach == EquivalenceApproach.OPTIMAL407408    def test_save_creates_directories(self, tmp_path: Path) -> None:409        """Test save creates parent directories."""410        path = tmp_path / "nested" / "dir" / "style.json"411        manager = StyleConfigManager(path)412        manager.save(TranslationStyle())413        assert path.exists()414415    def test_save_without_argument_uses_current(self, tmp_path: Path) -> None:416        """Test save without argument uses current style."""417        path = tmp_path / "style.json"418        manager = StyleConfigManager(path)419420        # Load default421        manager.load()422423        # Save without argument424        manager.save()425426        # Verify file exists and has default values427        assert path.exists()428        with path.open() as f:429            data = json.load(f)430        assert data["equivalence_approach"] == "dynamic"431432    def test_load_returns_cached_style(self, tmp_path: Path) -> None:433        """Test load returns cached style on subsequent calls."""434        path = tmp_path / "style.json"435        manager = StyleConfigManager(path)436437        # First load creates default438        style1 = manager.load()439440        # Second load returns cached441        style2 = manager.load()442443        assert style1 is style2444445    def test_save_without_loaded_style(self, tmp_path: Path) -> None:446        """Test save creates default when no style loaded."""447        path = tmp_path / "style.json"448        manager = StyleConfigManager(path)449450        # Save without loading first (no argument)451        manager.save()452453        # Verify file exists and has default values454        assert path.exists()455        with path.open() as f:456            data = json.load(f)457        assert data["equivalence_approach"] == "dynamic"458459460class TestPromptBuilderWithStyle:461    """Tests for PromptBuilder with style configuration."""462463    def test_default_system_prompt_without_style(self) -> None:464        """Test default system prompt when no style is set."""465        glossary = GlossaryManager()466        builder = PromptBuilder(glossary)467        prompt = builder.get_system_prompt()468        assert prompt == SYSTEM_PROMPT469470    def test_system_prompt_with_style(self) -> None:471        """Test system prompt with custom style."""472        glossary = GlossaryManager()473        style = TranslationStyle(equivalence_approach=EquivalenceApproach.FORMAL)474        builder = PromptBuilder(glossary, style=style)475        prompt = builder.get_system_prompt()476        assert "word-for-word" in prompt477        assert prompt != SYSTEM_PROMPT478479    def test_style_property_creates_default(self) -> None:480        """Test style property creates default when None."""481        glossary = GlossaryManager()482        builder = PromptBuilder(glossary)483        style = builder.style484        assert style.equivalence_approach == EquivalenceApproach.DYNAMIC485486    def test_style_setter_invalidates_cache(self) -> None:487        """Test setting style invalidates cached prompt."""488        glossary = GlossaryManager()489        style1 = TranslationStyle(equivalence_approach=EquivalenceApproach.FORMAL)490        builder = PromptBuilder(glossary, style=style1)491492        # Get initial prompt493        prompt1 = builder.get_system_prompt()494        assert "word-for-word" in prompt1495496        # Change style497        style2 = TranslationStyle(equivalence_approach=EquivalenceApproach.DYNAMIC)498        builder.style = style2499500        # Get new prompt501        prompt2 = builder.get_system_prompt()502        assert "thought-for-thought" in prompt2503        assert prompt1 != prompt2504505    def test_build_system_prompt_no_cache(self) -> None:506        """Test build_system_prompt always regenerates."""507        glossary = GlossaryManager()508        builder = PromptBuilder(glossary)509510        # Access style to initialize it511        _ = builder.style512513        prompt = builder.build_system_prompt()514        assert "Dynamic/Functional" in prompt515516    def test_system_prompt_cached(self) -> None:517        """Test system prompt is cached."""518        glossary = GlossaryManager()519        style = TranslationStyle()520        builder = PromptBuilder(glossary, style=style)521522        prompt1 = builder.get_system_prompt()523        prompt2 = builder.get_system_prompt()524        assert prompt1 is prompt2  # Same object (cached)525