OPEN SOURCE
software/tests/test_style_config.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 translation style configuration."""23import json4import pytest5from pathlib import Path67from src.config.style_config import StyleConfigManager8from src.context.glossary import GlossaryManager9from src.models.style import (10CONSISTENCY_PROMPTS,11EQUIVALENCE_PROMPTS,12FORMALITY_PROMPTS,13GENDER_PROMPTS,14READABILITY_PROMPTS,15TRADITION_PROMPTS,16EquivalenceApproach,17FormalityLevel,18GenderLanguage,19ReadabilityLevel,20TermConsistency,21TheologicalTradition,22TranslationStyle,23build_system_prompt,24)25from src.pipeline.prompt import SYSTEM_PROMPT, PromptBuilder262728class TestEquivalenceApproach:29"""Tests for EquivalenceApproach enum."""3031def test_all_values_exist(self) -> None:32"""Test all expected values exist."""33assert EquivalenceApproach.FORMAL.value == "formal"34assert EquivalenceApproach.DYNAMIC.value == "dynamic"35assert EquivalenceApproach.OPTIMAL.value == "optimal"3637def test_has_prompt_template(self) -> None:38"""Test each approach has a prompt template."""39for approach in EquivalenceApproach:40assert approach in EQUIVALENCE_PROMPTS41assert len(EQUIVALENCE_PROMPTS[approach]) > 0424344class TestGenderLanguage:45"""Tests for GenderLanguage enum."""4647def test_all_values_exist(self) -> None:48"""Test all expected values exist."""49assert GenderLanguage.TRADITIONAL.value == "traditional"50assert GenderLanguage.MODERATE_INCLUSIVE.value == "moderate_inclusive"51assert GenderLanguage.FULLY_INCLUSIVE.value == "fully_inclusive"5253def test_has_prompt_template(self) -> None:54"""Test each option has a prompt template."""55for option in GenderLanguage:56assert option in GENDER_PROMPTS575859class TestReadabilityLevel:60"""Tests for ReadabilityLevel enum."""6162def test_all_values_exist(self) -> None:63"""Test all expected values exist."""64assert ReadabilityLevel.SCHOLARLY.value == "scholarly"65assert ReadabilityLevel.GENERAL_PUBLIC.value == "general_public"66assert ReadabilityLevel.CHILDREN.value == "children"6768def test_has_prompt_template(self) -> None:69"""Test each level has a prompt template."""70for level in ReadabilityLevel:71assert level in READABILITY_PROMPTS727374class TestTheologicalTradition:75"""Tests for TheologicalTradition enum."""7677def test_all_values_exist(self) -> None:78"""Test all expected values exist."""79assert TheologicalTradition.EVANGELICAL.value == "evangelical"80assert TheologicalTradition.CATHOLIC.value == "catholic"81assert TheologicalTradition.ORTHODOX.value == "orthodox"82assert TheologicalTradition.ECUMENICAL.value == "ecumenical"8384def test_has_prompt_template(self) -> None:85"""Test each tradition has a prompt template."""86for tradition in TheologicalTradition:87assert tradition in TRADITION_PROMPTS888990class TestTermConsistency:91"""Tests for TermConsistency enum."""9293def test_all_values_exist(self) -> None:94"""Test all expected values exist."""95assert TermConsistency.STRICT.value == "strict"96assert TermConsistency.BALANCED.value == "balanced"97assert TermConsistency.CONTEXTUAL.value == "contextual"9899def test_has_prompt_template(self) -> None:100"""Test each option has a prompt template."""101for option in TermConsistency:102assert option in CONSISTENCY_PROMPTS103104105class TestFormalityLevel:106"""Tests for FormalityLevel enum."""107108def test_all_values_exist(self) -> None:109"""Test all expected values exist."""110assert FormalityLevel.CASUAL.value == "casual"111assert FormalityLevel.STANDARD.value == "standard"112assert FormalityLevel.FORMAL.value == "formal"113114def test_has_prompt_template(self) -> None:115"""Test each level has a prompt template."""116for level in FormalityLevel:117assert level in FORMALITY_PROMPTS118119120class TestTranslationStyle:121"""Tests for TranslationStyle dataclass."""122123def test_default_values(self) -> None:124"""Test default values are set correctly."""125style = TranslationStyle()126assert style.equivalence_approach == EquivalenceApproach.DYNAMIC127assert style.gender_language == GenderLanguage.MODERATE_INCLUSIVE128assert style.readability_level == ReadabilityLevel.WEB_NATIVE129assert style.theological_tradition == TheologicalTradition.EVANGELICAL130assert style.textual_basis == "MorphGNT/SBLGNT"131assert style.term_consistency == TermConsistency.BALANCED132assert style.formality_level == FormalityLevel.STANDARD133assert "なり" in style.avoid_archaic134assert style.target_style_reference == "現代のウェブ記事・ナレーションのような平易な日本語"135assert style.additional_instructions is None136137def test_custom_values(self) -> None:138"""Test creating with custom values."""139style = TranslationStyle(140equivalence_approach=EquivalenceApproach.FORMAL,141gender_language=GenderLanguage.TRADITIONAL,142readability_level=ReadabilityLevel.SCHOLARLY,143theological_tradition=TheologicalTradition.CATHOLIC,144textual_basis="Textus Receptus",145term_consistency=TermConsistency.STRICT,146formality_level=FormalityLevel.FORMAL,147avoid_archaic=["候"],148target_style_reference="文語訳-style",149additional_instructions="Focus on liturgical usage",150)151assert style.equivalence_approach == EquivalenceApproach.FORMAL152assert style.theological_tradition == TheologicalTradition.CATHOLIC153assert style.textual_basis == "Textus Receptus"154assert style.additional_instructions == "Focus on liturgical usage"155156def test_to_dict(self) -> None:157"""Test conversion to dictionary."""158style = TranslationStyle()159data = style.to_dict()160assert data["equivalence_approach"] == "dynamic"161assert data["gender_language"] == "moderate_inclusive"162assert data["readability_level"] == "web_native"163assert data["theological_tradition"] == "evangelical"164assert data["term_consistency"] == "balanced"165assert data["formality_level"] == "standard"166assert isinstance(data["avoid_archaic"], list)167168def test_from_dict(self) -> None:169"""Test creation from dictionary."""170data = {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}182style = TranslationStyle.from_dict(data)183assert style.equivalence_approach == EquivalenceApproach.FORMAL184assert style.gender_language == GenderLanguage.TRADITIONAL185assert style.theological_tradition == TheologicalTradition.CATHOLIC186assert style.additional_instructions == "Test instructions"187188def test_from_dict_with_defaults(self) -> None:189"""Test creation from partial dictionary uses defaults."""190data = {"equivalence_approach": "optimal"}191style = TranslationStyle.from_dict(data)192assert style.equivalence_approach == EquivalenceApproach.OPTIMAL193assert style.gender_language == GenderLanguage.MODERATE_INCLUSIVE # Default194195def test_roundtrip(self) -> None:196"""Test to_dict and from_dict roundtrip."""197original = TranslationStyle(198equivalence_approach=EquivalenceApproach.OPTIMAL,199gender_language=GenderLanguage.FULLY_INCLUSIVE,200additional_instructions="Test",201)202data = original.to_dict()203restored = TranslationStyle.from_dict(data)204assert restored.equivalence_approach == original.equivalence_approach205assert restored.gender_language == original.gender_language206assert restored.additional_instructions == original.additional_instructions207208209class TestBuildSystemPrompt:210"""Tests for build_system_prompt function."""211212def test_default_style_prompt(self) -> None:213"""Test prompt with default style."""214style = TranslationStyle()215prompt = build_system_prompt(style)216assert "Dynamic/Functional" in prompt217assert "Moderate Inclusive" in prompt218assert "Web-native" in prompt219assert "Protestant Evangelical" in prompt220assert "MorphGNT/SBLGNT" in prompt221222def test_formal_equivalence_prompt(self) -> None:223"""Test prompt with formal equivalence."""224style = TranslationStyle(equivalence_approach=EquivalenceApproach.FORMAL)225prompt = build_system_prompt(style)226assert "word-for-word" in prompt227assert "Formal" in prompt228229def test_traditional_gender_prompt(self) -> None:230"""Test prompt with traditional gender language."""231style = TranslationStyle(gender_language=GenderLanguage.TRADITIONAL)232prompt = build_system_prompt(style)233assert "Traditional" in prompt234assert "masculine generic" in prompt235236def test_scholarly_readability_prompt(self) -> None:237"""Test prompt with scholarly readability."""238style = TranslationStyle(readability_level=ReadabilityLevel.SCHOLARLY)239prompt = build_system_prompt(style)240assert "Scholarly" in prompt241assert "academic" in prompt242243def test_catholic_tradition_prompt(self) -> None:244"""Test prompt with Catholic tradition."""245style = TranslationStyle(246theological_tradition=TheologicalTradition.CATHOLIC247)248prompt = build_system_prompt(style)249assert "Catholic" in prompt250251def test_strict_consistency_prompt(self) -> None:252"""Test prompt with strict term consistency."""253style = TranslationStyle(term_consistency=TermConsistency.STRICT)254prompt = build_system_prompt(style)255assert "Strict" in prompt256assert "same word" in prompt257258def test_formal_formality_prompt(self) -> None:259"""Test prompt with formal formality level."""260style = TranslationStyle(formality_level=FormalityLevel.FORMAL)261prompt = build_system_prompt(style)262assert "Formal/Literary" in prompt or "formal literary" in prompt263264def test_avoid_archaic_included(self) -> None:265"""Test archaic expressions are included."""266style = TranslationStyle(avoid_archaic=["なり", "候"])267prompt = build_system_prompt(style)268assert "なり" in prompt269assert "候" in prompt270271def test_additional_instructions_included(self) -> None:272"""Test additional instructions are included."""273style = TranslationStyle(274additional_instructions="Focus on poetic language"275)276prompt = build_system_prompt(style)277assert "ADDITIONAL INSTRUCTIONS" in prompt278assert "poetic language" in prompt279280def test_additional_instructions_not_included_when_none(self) -> None:281"""Test no additional instructions section when None."""282style = TranslationStyle(additional_instructions=None)283prompt = build_system_prompt(style)284assert "ADDITIONAL INSTRUCTIONS" not in prompt285286def test_input_format_section(self) -> None:287"""Test input format section is always included."""288style = TranslationStyle()289prompt = build_system_prompt(style)290assert "INPUT FORMAT" in prompt291assert "Source text" in prompt292assert "Japanese translation" in prompt293294295class TestStyleConfigManager:296"""Tests for StyleConfigManager."""297298def test_init_default_path(self) -> None:299"""Test initialization with default path."""300manager = StyleConfigManager()301assert manager.config_path == Path("data/config/style.json")302303def test_init_custom_path(self, tmp_path: Path) -> None:304"""Test initialization with custom path."""305custom_path = tmp_path / "custom_style.json"306manager = StyleConfigManager(custom_path)307assert manager.config_path == custom_path308309def test_load_nonexistent_returns_default(self, tmp_path: Path) -> None:310"""Test loading from nonexistent file returns default."""311path = tmp_path / "nonexistent.json"312manager = StyleConfigManager(path)313style = manager.load()314assert style.equivalence_approach == EquivalenceApproach.DYNAMIC315316def test_save_creates_file(self, tmp_path: Path) -> None:317"""Test save creates the config file."""318path = tmp_path / "style.json"319manager = StyleConfigManager(path)320style = TranslationStyle(equivalence_approach=EquivalenceApproach.FORMAL)321manager.save(style)322assert path.exists()323324def test_save_and_load_roundtrip(self, tmp_path: Path) -> None:325"""Test save and load roundtrip."""326path = tmp_path / "style.json"327manager = StyleConfigManager(path)328329original = TranslationStyle(330equivalence_approach=EquivalenceApproach.OPTIMAL,331gender_language=GenderLanguage.FULLY_INCLUSIVE,332additional_instructions="Test roundtrip",333)334manager.save(original)335336# Create new manager and load337manager2 = StyleConfigManager(path)338loaded = manager2.load()339340assert loaded.equivalence_approach == original.equivalence_approach341assert loaded.gender_language == original.gender_language342assert loaded.additional_instructions == original.additional_instructions343344def test_get_style_loads_lazily(self, tmp_path: Path) -> None:345"""Test get_style loads on first call."""346path = tmp_path / "style.json"347manager = StyleConfigManager(path)348349# First call loads default (file doesn't exist)350style1 = manager.get_style()351assert style1 is not None352353# Second call returns cached354style2 = manager.get_style()355assert style2 is style1356357def test_set_style_in_memory(self, tmp_path: Path) -> None:358"""Test set_style only updates memory."""359path = tmp_path / "style.json"360manager = StyleConfigManager(path)361362new_style = TranslationStyle(363equivalence_approach=EquivalenceApproach.FORMAL364)365manager.set_style(new_style)366367# Memory updated368assert manager.get_style().equivalence_approach == EquivalenceApproach.FORMAL369370# File not created yet371assert not path.exists()372373def test_reset_to_default(self, tmp_path: Path) -> None:374"""Test reset_to_default returns default style."""375path = tmp_path / "style.json"376manager = StyleConfigManager(path)377378# Set custom style379custom = TranslationStyle(equivalence_approach=EquivalenceApproach.FORMAL)380manager.set_style(custom)381382# Reset383default = manager.reset_to_default()384assert default.equivalence_approach == EquivalenceApproach.DYNAMIC385386def test_reload_discards_changes(self, tmp_path: Path) -> None:387"""Test reload discards in-memory changes."""388path = tmp_path / "style.json"389390# Save a style to file391initial = TranslationStyle(equivalence_approach=EquivalenceApproach.OPTIMAL)392with path.open("w") as f:393json.dump(initial.to_dict(), f)394395manager = StyleConfigManager(path)396manager.load()397398# Make in-memory change399manager.set_style(400TranslationStyle(equivalence_approach=EquivalenceApproach.FORMAL)401)402assert manager.get_style().equivalence_approach == EquivalenceApproach.FORMAL403404# Reload from file405reloaded = manager.reload()406assert reloaded.equivalence_approach == EquivalenceApproach.OPTIMAL407408def test_save_creates_directories(self, tmp_path: Path) -> None:409"""Test save creates parent directories."""410path = tmp_path / "nested" / "dir" / "style.json"411manager = StyleConfigManager(path)412manager.save(TranslationStyle())413assert path.exists()414415def test_save_without_argument_uses_current(self, tmp_path: Path) -> None:416"""Test save without argument uses current style."""417path = tmp_path / "style.json"418manager = StyleConfigManager(path)419420# Load default421manager.load()422423# Save without argument424manager.save()425426# Verify file exists and has default values427assert path.exists()428with path.open() as f:429data = json.load(f)430assert data["equivalence_approach"] == "dynamic"431432def test_load_returns_cached_style(self, tmp_path: Path) -> None:433"""Test load returns cached style on subsequent calls."""434path = tmp_path / "style.json"435manager = StyleConfigManager(path)436437# First load creates default438style1 = manager.load()439440# Second load returns cached441style2 = manager.load()442443assert style1 is style2444445def test_save_without_loaded_style(self, tmp_path: Path) -> None:446"""Test save creates default when no style loaded."""447path = tmp_path / "style.json"448manager = StyleConfigManager(path)449450# Save without loading first (no argument)451manager.save()452453# Verify file exists and has default values454assert path.exists()455with path.open() as f:456data = json.load(f)457assert data["equivalence_approach"] == "dynamic"458459460class TestPromptBuilderWithStyle:461"""Tests for PromptBuilder with style configuration."""462463def test_default_system_prompt_without_style(self) -> None:464"""Test default system prompt when no style is set."""465glossary = GlossaryManager()466builder = PromptBuilder(glossary)467prompt = builder.get_system_prompt()468assert prompt == SYSTEM_PROMPT469470def test_system_prompt_with_style(self) -> None:471"""Test system prompt with custom style."""472glossary = GlossaryManager()473style = TranslationStyle(equivalence_approach=EquivalenceApproach.FORMAL)474builder = PromptBuilder(glossary, style=style)475prompt = builder.get_system_prompt()476assert "word-for-word" in prompt477assert prompt != SYSTEM_PROMPT478479def test_style_property_creates_default(self) -> None:480"""Test style property creates default when None."""481glossary = GlossaryManager()482builder = PromptBuilder(glossary)483style = builder.style484assert style.equivalence_approach == EquivalenceApproach.DYNAMIC485486def test_style_setter_invalidates_cache(self) -> None:487"""Test setting style invalidates cached prompt."""488glossary = GlossaryManager()489style1 = TranslationStyle(equivalence_approach=EquivalenceApproach.FORMAL)490builder = PromptBuilder(glossary, style=style1)491492# Get initial prompt493prompt1 = builder.get_system_prompt()494assert "word-for-word" in prompt1495496# Change style497style2 = TranslationStyle(equivalence_approach=EquivalenceApproach.DYNAMIC)498builder.style = style2499500# Get new prompt501prompt2 = builder.get_system_prompt()502assert "thought-for-thought" in prompt2503assert prompt1 != prompt2504505def test_build_system_prompt_no_cache(self) -> None:506"""Test build_system_prompt always regenerates."""507glossary = GlossaryManager()508builder = PromptBuilder(glossary)509510# Access style to initialize it511_ = builder.style512513prompt = builder.build_system_prompt()514assert "Dynamic/Functional" in prompt515516def test_system_prompt_cached(self) -> None:517"""Test system prompt is cached."""518glossary = GlossaryManager()519style = TranslationStyle()520builder = PromptBuilder(glossary, style=style)521522prompt1 = builder.get_system_prompt()523prompt2 = builder.get_system_prompt()524assert prompt1 is prompt2 # Same object (cached)525