OPEN SOURCE
software/tests/test_proto_converters.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 Protocol Buffer converter functions."""23from __future__ import annotations45from datetime import datetime, timezone67import pytest89from src.models.glossary import EnhancedGlossaryTerm, TheologicalDomain10from src.models.style import (11EquivalenceApproach,12FormalityLevel,13GenderLanguage,14ReadabilityLevel,15TermConsistency,16TheologicalTradition,17TranslationStyle,18)19from src.models.verse import (20BookMetadata,21ChapterSummary,22TranslationContext,23TranslationOutput,24Verse,25VerseReference,26)27from src.protos.converters import (28AnnotatedWordProto,29BookMetadataProto,30ChapterSummaryProto,31ConfidenceLevel,32FuriganaAnnotationProto,33GlossaryTermProto,34TranslationPromptInputProto,35TranslationResultProto,36TranslationStyleProto,37VerseProto,38VerseReferenceProto,39annotated_word_to_proto,40book_metadata_to_proto,41chapter_summary_to_proto,42furigana_annotation_to_proto,43glossary_term_to_proto,44proto_to_translation_result,45proto_to_verse,46proto_to_verse_reference,47translation_context_to_prompt_input,48translation_result_to_proto,49translation_style_to_proto,50verse_reference_to_proto,51verse_to_proto,52)53from src.protos.converters import EquivalenceApproach as EquivalenceApproachProto54from src.protos.converters import FormalityLevel as FormalityLevelProto55from src.protos.converters import GenderLanguage as GenderLanguageProto56from src.protos.converters import MatchType as MatchTypeProto57from src.protos.converters import ReadabilityLevel as ReadabilityLevelProto58from src.protos.converters import TermConsistency as TermConsistencyProto59from src.protos.converters import BiblicalTestament60from src.protos.converters import TheologicalDomain as TheologicalDomainProto61from src.protos.converters import TheologicalTradition as TheologicalTraditionProto62from src.utils.furigana import FuriganaAnnotation636465class TestVerseReferenceConversion:66"""Tests for VerseReference conversions."""6768def test_verse_reference_to_proto(self) -> None:69"""Test converting VerseReference to proto."""70ref = VerseReference(book="john", chapter=1, verse=1)71proto = verse_reference_to_proto(ref)7273assert isinstance(proto, VerseReferenceProto)74assert proto.book == "john"75assert proto.chapter == 176assert proto.verse == 17778def test_proto_to_verse_reference(self) -> None:79"""Test converting proto to VerseReference."""80proto = VerseReferenceProto(book="genesis", chapter=1, verse=1)81ref = proto_to_verse_reference(proto)8283assert isinstance(ref, VerseReference)84assert ref.book == "genesis"85assert ref.chapter == 186assert ref.verse == 18788def test_verse_reference_roundtrip(self) -> None:89"""Test roundtrip conversion preserves data."""90original = VerseReference(book="romans", chapter=8, verse=28)91proto = verse_reference_to_proto(original)92result = proto_to_verse_reference(proto)9394assert result.book == original.book95assert result.chapter == original.chapter96assert result.verse == original.verse979899class TestVerseConversion:100"""Tests for Verse conversions."""101102def test_verse_to_proto_full(self) -> None:103"""Test converting Verse with all fields to proto."""104verse = Verse(105reference=VerseReference(book="john", chapter=1, verse=1),106greek_text="Ἐν ἀρχῇ ἦν ὁ λόγος",107greek_translit="En arche en ho logos",108hebrew_text=None,109hebrew_translit=None,110japanese_meiji="太初に言あり",111japanese_taisho="太初に言あり、言は神と共にあり",112english_kjv="In the beginning was the Word",113english_asv="In the beginning was the Word",114)115proto = verse_to_proto(verse)116117assert isinstance(proto, VerseProto)118assert proto.reference.book == "john"119assert proto.greek_text == "Ἐν ἀρχῇ ἦν ὁ λόγος"120assert proto.greek_translit == "En arche en ho logos"121assert proto.hebrew_text is None122assert proto.japanese_meiji == "太初に言あり"123124def test_verse_to_proto_minimal(self) -> None:125"""Test converting Verse with minimal fields to proto."""126verse = Verse(127reference=VerseReference(book="john", chapter=1, verse=1),128)129proto = verse_to_proto(verse)130131assert proto.greek_text is None132assert proto.hebrew_text is None133assert proto.english_kjv is None134135def test_proto_to_verse(self) -> None:136"""Test converting proto to Verse."""137proto = VerseProto(138reference=VerseReferenceProto(book="john", chapter=1, verse=1),139greek_text="Ἐν ἀρχῇ",140english_kjv="In the beginning",141)142verse = proto_to_verse(proto)143144assert isinstance(verse, Verse)145assert verse.reference.book == "john"146assert verse.greek_text == "Ἐν ἀρχῇ"147assert verse.english_kjv == "In the beginning"148149def test_verse_roundtrip(self) -> None:150"""Test roundtrip conversion preserves data."""151original = Verse(152reference=VerseReference(book="matthew", chapter=5, verse=3),153greek_text="Μακάριοι οἱ πτωχοὶ τῷ πνεύματι",154english_kjv="Blessed are the poor in spirit",155)156proto = verse_to_proto(original)157result = proto_to_verse(proto)158159assert result.reference.book == original.reference.book160assert result.greek_text == original.greek_text161assert result.english_kjv == original.english_kjv162163164class TestBookMetadataConversion:165"""Tests for BookMetadata conversions."""166167def test_book_metadata_to_proto_nt(self) -> None:168"""Test converting NT BookMetadata to proto."""169metadata = BookMetadata(170book="john",171name_japanese="ヨハネによる福音書",172name_english="Gospel of John",173testament="NT",174author="John the Apostle",175themes=["Word", "Light", "Life", "Love"],176description="The fourth Gospel",177chapter_count=21,178verse_count=879,179)180proto = book_metadata_to_proto(metadata)181182assert isinstance(proto, BookMetadataProto)183assert proto.book == "john"184assert proto.name_japanese == "ヨハネによる福音書"185assert proto.testament == BiblicalTestament.NEW_TESTAMENT186assert proto.themes == ["Word", "Light", "Life", "Love"]187assert proto.chapter_count == 21188189def test_book_metadata_to_proto_ot(self) -> None:190"""Test converting OT BookMetadata to proto."""191metadata = BookMetadata(192book="genesis",193name_japanese="創世記",194name_english="Genesis",195testament="OT",196author="Moses",197themes=["Creation", "Covenant"],198description="The first book",199chapter_count=50,200verse_count=1533,201)202proto = book_metadata_to_proto(metadata)203204assert proto.testament == BiblicalTestament.OLD_TESTAMENT205206207class TestChapterSummaryConversion:208"""Tests for ChapterSummary conversions."""209210def test_chapter_summary_to_proto(self) -> None:211"""Test converting ChapterSummary to proto."""212summary = ChapterSummary(213book="john",214chapter=1,215summary="The prologue introduces the Word",216key_events=["Word became flesh", "John testifies"],217key_characters=["Jesus", "John the Baptist"],218key_themes=["Word", "Light", "Witness"],219)220proto = chapter_summary_to_proto(summary)221222assert isinstance(proto, ChapterSummaryProto)223assert proto.book == "john"224assert proto.chapter == 1225assert proto.summary == "The prologue introduces the Word"226assert "Word became flesh" in proto.key_events227assert "Jesus" in proto.key_characters228assert "Light" in proto.key_themes229230231class TestGlossaryTermConversion:232"""Tests for GlossaryTerm conversions."""233234def test_glossary_term_to_proto_full(self) -> None:235"""Test converting full GlossaryTerm to proto."""236term = EnhancedGlossaryTerm(237original="λόγος",238transliteration="logos",239japanese="言",240english="Word",241notes="Theological term for divine Word",242lemma="λόγος",243inflected_forms=["λόγον", "λόγου"],244strongs_number="G3056",245definition="Word, reason, speech",246semantic_range=["word", "reason", "account"],247domain=TheologicalDomain.CHRISTOLOGY,248usage_examples=["John 1:1"],249frequency=330,250related_terms=["ῥῆμα"],251avoid_translations=["言葉だけ"],252)253proto = glossary_term_to_proto(term)254255assert isinstance(proto, GlossaryTermProto)256assert proto.original == "λόγος"257assert proto.transliteration == "logos"258assert proto.japanese == "言"259assert proto.strongs_number == "G3056"260assert proto.domain == TheologicalDomainProto.CHRISTOLOGY261assert "λόγον" in proto.inflected_forms262assert proto.frequency == 330263264def test_glossary_term_to_proto_minimal(self) -> None:265"""Test converting minimal GlossaryTerm to proto."""266term = EnhancedGlossaryTerm(267original="ἀγάπη",268transliteration="agape",269japanese="愛",270english="love",271)272proto = glossary_term_to_proto(term)273274assert proto.original == "ἀγάπη"275assert proto.domain == TheologicalDomainProto.GENERAL276assert proto.notes is None277assert proto.inflected_forms == []278279def test_all_theological_domains(self) -> None:280"""Test all theological domain conversions."""281domains = [282(TheologicalDomain.CHRISTOLOGY, TheologicalDomainProto.CHRISTOLOGY),283(TheologicalDomain.SOTERIOLOGY, TheologicalDomainProto.SOTERIOLOGY),284(TheologicalDomain.ESCHATOLOGY, TheologicalDomainProto.ESCHATOLOGY),285(TheologicalDomain.PNEUMATOLOGY, TheologicalDomainProto.PNEUMATOLOGY),286(TheologicalDomain.ECCLESIOLOGY, TheologicalDomainProto.ECCLESIOLOGY),287(TheologicalDomain.HAMARTIOLOGY, TheologicalDomainProto.HAMARTIOLOGY),288(TheologicalDomain.THEOLOGY_PROPER, TheologicalDomainProto.THEOLOGY_PROPER),289(TheologicalDomain.ANTHROPOLOGY, TheologicalDomainProto.ANTHROPOLOGY),290(TheologicalDomain.GENERAL, TheologicalDomainProto.GENERAL),291]292293for model_domain, expected_proto in domains:294term = EnhancedGlossaryTerm(295original="test",296transliteration="test",297japanese="テスト",298english="test",299domain=model_domain,300)301proto = glossary_term_to_proto(term)302assert proto.domain == expected_proto303304305class TestTranslationStyleConversion:306"""Tests for TranslationStyle conversions."""307308def test_translation_style_to_proto_default(self) -> None:309"""Test converting default TranslationStyle to proto."""310style = TranslationStyle()311proto = translation_style_to_proto(style)312313assert isinstance(proto, TranslationStyleProto)314assert proto.equivalence_approach == EquivalenceApproachProto.DYNAMIC315assert proto.gender_language == GenderLanguageProto.MODERATE_INCLUSIVE316assert proto.readability_level == ReadabilityLevelProto.WEB_NATIVE317assert proto.theological_tradition == TheologicalTraditionProto.EVANGELICAL318assert proto.term_consistency == TermConsistencyProto.BALANCED319assert proto.formality_level == FormalityLevelProto.STANDARD320assert "なり" in proto.avoid_archaic321322def test_translation_style_to_proto_custom(self) -> None:323"""Test converting custom TranslationStyle to proto."""324style = TranslationStyle(325equivalence_approach=EquivalenceApproach.FORMAL,326gender_language=GenderLanguage.TRADITIONAL,327readability_level=ReadabilityLevel.SCHOLARLY,328theological_tradition=TheologicalTradition.CATHOLIC,329textual_basis="Majority Text",330term_consistency=TermConsistency.STRICT,331formality_level=FormalityLevel.FORMAL,332avoid_archaic=[],333target_style_reference="Literary",334additional_instructions="Use formal language",335)336proto = translation_style_to_proto(style)337338assert proto.equivalence_approach == EquivalenceApproachProto.FORMAL339assert proto.gender_language == GenderLanguageProto.TRADITIONAL340assert proto.readability_level == ReadabilityLevelProto.SCHOLARLY341assert proto.theological_tradition == TheologicalTraditionProto.CATHOLIC342assert proto.textual_basis == "Majority Text"343assert proto.term_consistency == TermConsistencyProto.STRICT344assert proto.formality_level == FormalityLevelProto.FORMAL_LEVEL345assert proto.additional_instructions == "Use formal language"346347def test_all_equivalence_approaches(self) -> None:348"""Test all equivalence approach conversions."""349approaches = [350(EquivalenceApproach.FORMAL, EquivalenceApproachProto.FORMAL),351(EquivalenceApproach.DYNAMIC, EquivalenceApproachProto.DYNAMIC),352(EquivalenceApproach.OPTIMAL, EquivalenceApproachProto.OPTIMAL),353]354for model_approach, expected_proto in approaches:355style = TranslationStyle(equivalence_approach=model_approach)356proto = translation_style_to_proto(style)357assert proto.equivalence_approach == expected_proto358359def test_all_gender_languages(self) -> None:360"""Test all gender language conversions."""361genders = [362(GenderLanguage.TRADITIONAL, GenderLanguageProto.TRADITIONAL),363(GenderLanguage.MODERATE_INCLUSIVE, GenderLanguageProto.MODERATE_INCLUSIVE),364(GenderLanguage.FULLY_INCLUSIVE, GenderLanguageProto.FULLY_INCLUSIVE),365]366for model_gender, expected_proto in genders:367style = TranslationStyle(gender_language=model_gender)368proto = translation_style_to_proto(style)369assert proto.gender_language == expected_proto370371def test_all_readability_levels(self) -> None:372"""Test all readability level conversions."""373levels = [374(ReadabilityLevel.SCHOLARLY, ReadabilityLevelProto.SCHOLARLY),375(ReadabilityLevel.GENERAL_PUBLIC, ReadabilityLevelProto.GENERAL_PUBLIC),376(ReadabilityLevel.CHILDREN, ReadabilityLevelProto.CHILDREN),377]378for model_level, expected_proto in levels:379style = TranslationStyle(readability_level=model_level)380proto = translation_style_to_proto(style)381assert proto.readability_level == expected_proto382383def test_all_theological_traditions(self) -> None:384"""Test all theological tradition conversions."""385traditions = [386(TheologicalTradition.EVANGELICAL, TheologicalTraditionProto.EVANGELICAL),387(TheologicalTradition.CATHOLIC, TheologicalTraditionProto.CATHOLIC),388(TheologicalTradition.ORTHODOX, TheologicalTraditionProto.ORTHODOX),389(TheologicalTradition.ECUMENICAL, TheologicalTraditionProto.ECUMENICAL),390]391for model_tradition, expected_proto in traditions:392style = TranslationStyle(theological_tradition=model_tradition)393proto = translation_style_to_proto(style)394assert proto.theological_tradition == expected_proto395396def test_all_term_consistencies(self) -> None:397"""Test all term consistency conversions."""398consistencies = [399(TermConsistency.STRICT, TermConsistencyProto.STRICT),400(TermConsistency.BALANCED, TermConsistencyProto.BALANCED),401(TermConsistency.CONTEXTUAL, TermConsistencyProto.CONTEXTUAL),402]403for model_consistency, expected_proto in consistencies:404style = TranslationStyle(term_consistency=model_consistency)405proto = translation_style_to_proto(style)406assert proto.term_consistency == expected_proto407408def test_all_formality_levels(self) -> None:409"""Test all formality level conversions."""410levels = [411(FormalityLevel.CASUAL, FormalityLevelProto.CASUAL),412(FormalityLevel.STANDARD, FormalityLevelProto.STANDARD),413(FormalityLevel.FORMAL, FormalityLevelProto.FORMAL_LEVEL),414]415for model_level, expected_proto in levels:416style = TranslationStyle(formality_level=model_level)417proto = translation_style_to_proto(style)418assert proto.formality_level == expected_proto419420421class TestFuriganaAnnotationConversion:422"""Tests for FuriganaAnnotation conversions."""423424def test_furigana_annotation_to_proto(self) -> None:425"""Test converting FuriganaAnnotation to proto."""426annotation = FuriganaAnnotation(427start_offset=0,428end_offset=1,429base_text="神",430reading="かみ",431is_standard_reading=True,432reading_source="dictionary",433)434proto = furigana_annotation_to_proto(annotation)435436assert isinstance(proto, FuriganaAnnotationProto)437assert proto.start_offset == 0438assert proto.end_offset == 1439assert proto.base_text == "神"440assert proto.reading == "かみ"441assert proto.is_standard_reading is True442assert proto.reading_source == "dictionary"443444def test_furigana_annotation_to_proto_non_standard(self) -> None:445"""Test converting non-standard FuriganaAnnotation to proto."""446annotation = FuriganaAnnotation(447start_offset=5,448end_offset=7,449base_text="聖書",450reading="みことば",451is_standard_reading=False,452reading_source="theological",453)454proto = furigana_annotation_to_proto(annotation)455456assert proto.is_standard_reading is False457assert proto.reading_source == "theological"458459460class TestAnnotatedWordConversion:461"""Tests for AnnotatedWord conversions."""462463def test_annotated_word_to_proto(self) -> None:464"""Test creating AnnotatedWordProto."""465proto = annotated_word_to_proto(466surface_form="神",467lemma="神",468part_of_speech="名詞",469reading="かみ",470start_offset=0,471end_offset=1,472alternative_readings=["しん", "じん"],473)474475assert isinstance(proto, AnnotatedWordProto)476assert proto.surface_form == "神"477assert proto.lemma == "神"478assert proto.part_of_speech == "名詞"479assert proto.reading == "かみ"480assert proto.start_offset == 0481assert proto.end_offset == 1482assert proto.alternative_readings == ["しん", "じん"]483484def test_annotated_word_to_proto_no_alternatives(self) -> None:485"""Test creating AnnotatedWordProto without alternatives."""486proto = annotated_word_to_proto(487surface_form="は",488lemma="は",489part_of_speech="助詞",490reading="は",491start_offset=1,492end_offset=2,493)494495assert proto.alternative_readings == []496497498class TestTranslationContextConversion:499"""Tests for TranslationContext conversions."""500501def test_translation_context_to_prompt_input_minimal(self) -> None:502"""Test converting minimal TranslationContext to proto."""503context = TranslationContext(504target_verse=Verse(505reference=VerseReference(book="john", chapter=1, verse=1),506greek_text="Ἐν ἀρχῇ ἦν ὁ λόγος",507),508)509proto = translation_context_to_prompt_input(510context,511system_prompt="You are a translator",512user_prompt="Translate this verse",513model="claude-3-opus",514prompt_version="v1.0",515)516517assert isinstance(proto, TranslationPromptInputProto)518assert proto.target_verse.reference.book == "john"519assert proto.system_prompt == "You are a translator"520assert proto.user_prompt == "Translate this verse"521assert proto.model == "claude-3-opus"522assert proto.prompt_version == "v1.0"523assert proto.preceding_verses == []524assert proto.following_verses == []525assert proto.chapter_summary is None526assert proto.book_metadata is None527528def test_translation_context_to_prompt_input_full(self) -> None:529"""Test converting full TranslationContext to proto."""530context = TranslationContext(531target_verse=Verse(532reference=VerseReference(book="john", chapter=1, verse=2),533greek_text="οὗτος ἦν ἐν ἀρχῇ πρὸς τὸν θεόν",534),535preceding_verses=[536Verse(537reference=VerseReference(book="john", chapter=1, verse=1),538greek_text="Ἐν ἀρχῇ ἦν ὁ λόγος",539),540],541following_verses=[542Verse(543reference=VerseReference(book="john", chapter=1, verse=3),544greek_text="πάντα δι' αὐτοῦ ἐγένετο",545),546],547chapter_summary=ChapterSummary(548book="john",549chapter=1,550summary="The prologue",551key_events=["Word became flesh"],552key_characters=["Jesus"],553key_themes=["Word"],554),555book_metadata=BookMetadata(556book="john",557name_japanese="ヨハネ",558name_english="John",559testament="NT",560author="John",561themes=["Word"],562description="Fourth Gospel",563chapter_count=21,564verse_count=879,565),566glossary_terms=[567EnhancedGlossaryTerm(568original="λόγος",569transliteration="logos",570japanese="言",571english="Word",572),573],574)575proto = translation_context_to_prompt_input(context)576577assert len(proto.preceding_verses) == 1578assert len(proto.following_verses) == 1579assert proto.chapter_summary is not None580assert proto.chapter_summary.book == "john"581assert proto.book_metadata is not None582assert proto.book_metadata.testament == BiblicalTestament.NEW_TESTAMENT583assert len(proto.glossary_terms) == 1584assert proto.glossary_terms[0].original == "λόγος"585586587class TestTranslationResultConversion:588"""Tests for TranslationResult conversions."""589590def test_translation_result_to_proto(self) -> None:591"""Test converting TranslationOutput to proto."""592output = TranslationOutput(593reference=VerseReference(book="john", chapter=1, verse=1),594text="初めに言葉があった",595model="claude-3-opus",596timestamp=datetime(2024, 1, 1, 12, 0, 0, tzinfo=timezone.utc),597prompt_version="v1.0",598sources_used=["greek", "kjv"],599glossary_terms_applied=["λόγος"],600)601602furigana_annotations = [603FuriganaAnnotation(0, 2, "初め", "はじめ"),604FuriganaAnnotation(3, 5, "言葉", "ことば"),605]606607proto = translation_result_to_proto(608output,609text_with_furigana="初め[はじめ]に言葉[ことば]があった",610furigana_annotations=furigana_annotations,611confidence=ConfidenceLevel.HIGH,612confidence_score=0.95,613input_hash="abc123",614)615616assert isinstance(proto, TranslationResultProto)617assert proto.reference.book == "john"618assert proto.text == "初めに言葉があった"619assert proto.text_with_inline_furigana == "初め[はじめ]に言葉[ことば]があった"620assert len(proto.furigana_annotations) == 2621assert proto.model == "claude-3-opus"622assert proto.confidence == ConfidenceLevel.HIGH623assert proto.confidence_score == 0.95624assert proto.input_hash == "abc123"625assert "greek" in proto.sources_used626assert "λόγος" in proto.glossary_terms_applied627628def test_translation_result_to_proto_minimal(self) -> None:629"""Test converting TranslationOutput with minimal data."""630output = TranslationOutput(631reference=VerseReference(book="john", chapter=1, verse=1),632text="初めに言葉があった",633model="claude-3-opus",634)635proto = translation_result_to_proto(output)636637assert proto.text_with_inline_furigana == "初めに言葉があった" # Falls back to text638assert proto.furigana_annotations == []639assert proto.confidence == ConfidenceLevel.CONFIDENCE_LEVEL_UNSPECIFIED640assert proto.confidence_score == 0.0641642def test_proto_to_translation_result(self) -> None:643"""Test converting proto to TranslationOutput."""644proto = TranslationResultProto(645reference=VerseReferenceProto(book="john", chapter=1, verse=1),646text="初めに言葉があった",647model="claude-3-opus",648timestamp=datetime(2024, 1, 1, 12, 0, 0, tzinfo=timezone.utc),649prompt_version="v1.0",650sources_used=["greek"],651glossary_terms_applied=["λόγος"],652)653output = proto_to_translation_result(proto)654655assert isinstance(output, TranslationOutput)656assert output.reference.book == "john"657assert output.text == "初めに言葉があった"658assert output.model == "claude-3-opus"659assert "greek" in output.sources_used660assert "λόγος" in output.glossary_terms_applied661662def test_translation_result_roundtrip(self) -> None:663"""Test roundtrip conversion preserves basic data."""664original = TranslationOutput(665reference=VerseReference(book="matthew", chapter=5, verse=3),666text="心の貧しい人々は幸いです",667model="claude-3-opus",668prompt_version="v2.0",669sources_used=["greek", "meiji"],670glossary_terms_applied=["μακάριοι"],671)672proto = translation_result_to_proto(original)673result = proto_to_translation_result(proto)674675assert result.reference.book == original.reference.book676assert result.text == original.text677assert result.model == original.model678assert result.prompt_version == original.prompt_version679assert list(result.sources_used) == list(original.sources_used)680681682class TestProtoEnums:683"""Tests for proto enum values."""684685def test_testament_enum_values(self) -> None:686"""Test BiblicalTestament enum has correct values."""687assert BiblicalTestament.TESTAMENT_UNSPECIFIED == 0688assert BiblicalTestament.OLD_TESTAMENT == 1689assert BiblicalTestament.NEW_TESTAMENT == 2690691def test_confidence_level_enum_values(self) -> None:692"""Test ConfidenceLevel enum has correct values."""693assert ConfidenceLevel.CONFIDENCE_LEVEL_UNSPECIFIED == 0694assert ConfidenceLevel.LOW == 1695assert ConfidenceLevel.MEDIUM == 2696assert ConfidenceLevel.HIGH == 3697assert ConfidenceLevel.VERY_HIGH == 4698699def test_match_type_enum_values(self) -> None:700"""Test MatchType enum has correct values."""701assert MatchTypeProto.MATCH_TYPE_UNSPECIFIED == 0702assert MatchTypeProto.EXACT == 1703assert MatchTypeProto.FUZZY == 2704assert MatchTypeProto.PHRASE == 3705706def test_segment_level_enum_values(self) -> None:707"""Test SegmentLevel enum has correct values."""708from src.protos.converters import SegmentLevel as SegmentLevelProto709710assert SegmentLevelProto.SEGMENT_LEVEL_UNSPECIFIED == 0711assert SegmentLevelProto.VERSE == 1712assert SegmentLevelProto.PHRASE == 2713714715class TestTMSegmentConversion:716"""Tests for TMSegment conversions."""717718def test_tm_segment_to_proto(self) -> None:719"""Test converting TMSegment to proto."""720from src.models.translation_memory import SegmentLevel, TMSegment721from src.protos.converters import TMSegmentProto, tm_segment_to_proto722723segment = TMSegment(724id="test-id-123",725source_text="Ἐν ἀρχῇ ἦν ὁ λόγος",726source_normalized="εν αρχη ην ο λογος",727target_text="初めに言葉があった",728reference=VerseReference(book="john", chapter=1, verse=1),729level=SegmentLevel.VERSE,730reuse_count=5,731metadata={"model": "claude-3-opus"},732)733proto = tm_segment_to_proto(segment)734735assert isinstance(proto, TMSegmentProto)736assert proto.id == "test-id-123"737assert proto.source_text == "Ἐν ἀρχῇ ἦν ὁ λόγος"738assert proto.source_normalized == "εν αρχη ην ο λογος"739assert proto.target_text == "初めに言葉があった"740assert proto.reference.book == "john"741assert proto.reference.chapter == 1742assert proto.reference.verse == 1743assert proto.reuse_count == 5744assert proto.metadata == {"model": "claude-3-opus"}745746def test_proto_to_tm_segment(self) -> None:747"""Test converting proto to TMSegment."""748from src.models.translation_memory import TMSegment749from src.protos.converters import (750SegmentLevel,751TMSegmentProto,752VerseReferenceProto,753proto_to_tm_segment,754)755756proto = TMSegmentProto(757id="proto-id-456",758source_text="Source text",759source_normalized="source text",760target_text="Target text",761reference=VerseReferenceProto(book="genesis", chapter=1, verse=1),762level=SegmentLevel.PHRASE,763reuse_count=3,764)765segment = proto_to_tm_segment(proto)766767assert isinstance(segment, TMSegment)768assert segment.id == "proto-id-456"769assert segment.source_text == "Source text"770assert segment.target_text == "Target text"771assert segment.reference.book == "genesis"772773def test_tm_segment_roundtrip(self) -> None:774"""Test roundtrip conversion preserves data."""775from src.models.translation_memory import SegmentLevel, TMSegment776from src.protos.converters import proto_to_tm_segment, tm_segment_to_proto777778original = TMSegment(779id="roundtrip-id",780source_text="Original source",781source_normalized="original source",782target_text="Original target",783reference=VerseReference(book="matthew", chapter=5, verse=3),784level=SegmentLevel.VERSE,785reuse_count=10,786metadata={"key": "value"},787)788proto = tm_segment_to_proto(original)789result = proto_to_tm_segment(proto)790791assert result.id == original.id792assert result.source_text == original.source_text793assert result.target_text == original.target_text794assert result.reference.book == original.reference.book795assert result.reuse_count == original.reuse_count796assert result.metadata == original.metadata797798799class TestTMMatchConversion:800"""Tests for TMMatch conversions."""801802def test_tm_match_to_proto(self) -> None:803"""Test converting TMMatch to proto."""804from src.models.translation_memory import MatchType, SegmentLevel, TMMatch, TMSegment805from src.protos.converters import TranslationMemoryMatchProto, tm_match_to_proto806807segment = TMSegment(808id="match-segment",809source_text="Source",810source_normalized="source",811target_text="Target",812reference=VerseReference(book="john", chapter=1, verse=1),813level=SegmentLevel.VERSE,814)815match = TMMatch(816segment=segment,817similarity=0.95,818match_type=MatchType.EXACT,819)820proto = tm_match_to_proto(match)821822assert isinstance(proto, TranslationMemoryMatchProto)823assert proto.segment.id == "match-segment"824assert proto.similarity == 0.95825assert proto.match_type == MatchTypeProto.EXACT826827def test_proto_to_tm_match(self) -> None:828"""Test converting proto to TMMatch."""829from src.models.translation_memory import TMMatch830from src.protos.converters import (831MatchType,832SegmentLevel,833TMSegmentProto,834TranslationMemoryMatchProto,835VerseReferenceProto,836proto_to_tm_match,837)838839segment_proto = TMSegmentProto(840id="proto-match-segment",841source_text="Proto source",842source_normalized="proto source",843target_text="Proto target",844reference=VerseReferenceProto(book="genesis", chapter=2, verse=1),845level=SegmentLevel.VERSE,846)847proto = TranslationMemoryMatchProto(848segment=segment_proto,849similarity=0.85,850match_type=MatchType.FUZZY,851)852match = proto_to_tm_match(proto)853854assert isinstance(match, TMMatch)855assert match.segment.id == "proto-match-segment"856assert match.similarity == 0.85857858def test_all_match_types(self) -> None:859"""Test all match type conversions."""860from src.models.translation_memory import (861MatchType as MatchTypeModel,862SegmentLevel,863TMMatch,864TMSegment,865)866from src.protos.converters import MatchType as MatchTypeProtoEnum867from src.protos.converters import tm_match_to_proto868869match_types = [870(MatchTypeModel.EXACT, MatchTypeProtoEnum.EXACT),871(MatchTypeModel.FUZZY, MatchTypeProtoEnum.FUZZY),872(MatchTypeModel.PHRASE, MatchTypeProtoEnum.PHRASE),873]874875for model_type, expected_proto in match_types:876segment = TMSegment(877id="test",878source_text="S",879source_normalized="s",880target_text="T",881reference=VerseReference(book="john", chapter=1, verse=1),882level=SegmentLevel.VERSE,883)884match = TMMatch(885segment=segment,886similarity=0.9,887match_type=model_type,888)889proto = tm_match_to_proto(match)890assert proto.match_type == expected_proto891892893class TestProvenanceConversion:894"""Tests for TranslationProvenance conversions."""895896def test_provenance_to_proto(self) -> None:897"""Test converting TranslationProvenance to proto."""898from src.orchestration.provenance import TranslationProvenance899from src.protos.converters import TranslationProvenanceProto, provenance_to_proto900901provenance = TranslationProvenance(902source_texts={"greek": "Greek text", "kjv": "KJV text"},903source_hashes={"greek": "hash1", "kjv": "hash2"},904preceding_verses=["john 1:1", "john 1:2"],905following_verses=["john 1:4"],906pericope_id="john-1-prologue",907style_config_hash="style-hash",908glossary_hash="glossary-hash",909prompt_version="v2.0",910model="claude-3-opus",911system_prompt="You are a translator",912user_prompt="Translate this",913tm_matches_used=[{"source": "s", "target": "t", "similarity": 0.9}],914attempt_number=2,915evaluation_score=85.5,916evaluation_flags=["needs_review"],917chapter_summary="Chapter about love",918book_metadata={"name": "John"},919)920proto = provenance_to_proto(provenance)921922assert isinstance(proto, TranslationProvenanceProto)923assert proto.source_texts == {"greek": "Greek text", "kjv": "KJV text"}924assert proto.source_hashes == {"greek": "hash1", "kjv": "hash2"}925assert proto.preceding_verses == ["john 1:1", "john 1:2"]926assert proto.following_verses == ["john 1:4"]927assert proto.pericope_id == "john-1-prologue"928assert proto.model == "claude-3-opus"929assert proto.prompt_version == "v2.0"930assert proto.attempt_number == 2931assert proto.evaluation_score == 85.5932assert proto.evaluation_flags == ["needs_review"]933934def test_proto_to_provenance(self) -> None:935"""Test converting proto to TranslationProvenance."""936from datetime import datetime, timezone937938from src.orchestration.provenance import TranslationProvenance939from src.protos.converters import TranslationProvenanceProto, proto_to_provenance940941started = datetime(2024, 1, 1, 12, 0, 0, tzinfo=timezone.utc)942completed = datetime(2024, 1, 1, 12, 0, 30, tzinfo=timezone.utc)943944proto = TranslationProvenanceProto(945source_texts={"greek": "Text"},946source_hashes={"greek": "hash"},947model="claude-3-haiku",948prompt_version="v1.0",949started_at=started,950completed_at=completed,951)952provenance = proto_to_provenance(proto)953954assert isinstance(provenance, TranslationProvenance)955assert provenance.source_texts == {"greek": "Text"}956assert provenance.model == "claude-3-haiku"957assert provenance.started_at == started958assert provenance.completed_at == completed959960def test_provenance_roundtrip(self) -> None:961"""Test roundtrip conversion preserves data."""962from datetime import datetime, timezone963964from src.orchestration.provenance import TranslationProvenance965from src.protos.converters import proto_to_provenance, provenance_to_proto966967original = TranslationProvenance(968source_texts={"greek": "Greek", "hebrew": "Hebrew"},969source_hashes={"greek": "h1", "hebrew": "h2"},970preceding_verses=["gen 1:1"],971following_verses=["gen 1:3"],972model="claude-opus",973prompt_version="v3.0",974system_prompt="System",975user_prompt="User",976attempt_number=1,977started_at=datetime(2024, 6, 15, 10, 0, 0, tzinfo=timezone.utc),978completed_at=datetime(2024, 6, 15, 10, 0, 45, tzinfo=timezone.utc),979evaluation_score=92.0,980evaluation_flags=["high_quality"],981)982proto = provenance_to_proto(original)983result = proto_to_provenance(proto)984985assert result.source_texts == original.source_texts986assert result.source_hashes == original.source_hashes987assert result.model == original.model988assert result.prompt_version == original.prompt_version989assert result.attempt_number == original.attempt_number990assert result.started_at == original.started_at991assert result.completed_at == original.completed_at992assert result.evaluation_score == original.evaluation_score993assert result.evaluation_flags == original.evaluation_flags994995996class TestTranslationPromptInputWithTMMatches:997"""Tests for TranslationPromptInputProto with TM matches."""998999def test_prompt_input_with_tm_matches(self) -> None:1000"""Test TranslationPromptInputProto includes tm_matches field."""1001from src.protos.converters import (1002MatchType,1003SegmentLevel,1004TMSegmentProto,1005TranslationMemoryMatchProto,1006TranslationPromptInputProto,1007VerseReferenceProto,1008)10091010segment = TMSegmentProto(1011id="tm-1",1012source_text="Source",1013source_normalized="source",1014target_text="Target",1015reference=VerseReferenceProto(book="john", chapter=1, verse=1),1016level=SegmentLevel.VERSE,1017)1018match = TranslationMemoryMatchProto(1019segment=segment,1020similarity=0.95,1021match_type=MatchType.EXACT,1022)10231024proto = TranslationPromptInputProto(1025system_prompt="System",1026user_prompt="User",1027model="claude-3-opus",1028tm_matches=[match],1029)10301031assert len(proto.tm_matches) == 11032assert proto.tm_matches[0].segment.id == "tm-1"1033assert proto.tm_matches[0].similarity == 0.95103410351036class TestTranslationResultWithProvenance:1037"""Tests for TranslationResultProto with provenance."""10381039def test_result_with_provenance(self) -> None:1040"""Test TranslationResultProto includes provenance field."""1041from src.protos.converters import (1042TranslationProvenanceProto,1043TranslationResultProto,1044VerseReferenceProto,1045)10461047provenance = TranslationProvenanceProto(1048model="claude-3-opus",1049prompt_version="v1.0",1050source_texts={"greek": "Greek text"},1051)10521053result = TranslationResultProto(1054reference=VerseReferenceProto(book="john", chapter=1, verse=1),1055text="Translation text",1056model="claude-3-opus",1057provenance=provenance,1058)10591060assert result.provenance is not None1061assert result.provenance.model == "claude-3-opus"1062assert result.provenance.source_texts == {"greek": "Greek text"}1063