"""Regression cases for shifted chapters, partial verses and mixed languages.""" import json import shutil from pathlib import Path import pytest from src.data.versification import DATA, load_map, spans_for, build_originals from src.output.originals import group_segments, build def words(count, replacement=None): result = [{'word': f'w{i}', 'morph': 'HNp'} for i in range(count)] if replacement: index, text = replacement result[index]['word'] = text return {'words': result} def test_cross_chapter_and_heading_maps(): mapping = load_map() assert mapping['joel', 4, 1] == [(3, 1, 0, None)] assert mapping['genesis', 32, 1] == [(31, 55, 0, None)] assert mapping['psalms', 3, 1] == [(3, 0, 0, None)] assert mapping['psalms', 51, 1] == [(51, 0, 0, None)] assert mapping['psalms', 51, 2] == [(51, 0, 0, None)] assert mapping['3john', 1, 15] == [(1, 14, 0, None)] assert mapping['revelation', 12, 18] == [(12, 17, 0, None)] @pytest.mark.parametrize('key,count,boundary,expected', [ (('1kings',18,34),16,(10,'ויאמר'),[(('1kings',18,33),0,10),(('1kings',18,34),10,16)]), (('1kings',20,3),15,(6,'כספך'),[(('1kings',20,2),0,6),(('1kings',20,3),6,15)]), (('1kings',22,21),12,(8,'ויאמר'),[(('1kings',22,21),0,8),(('1kings',22,22),8,12)]), (('1kings',22,44),9,None,[(('1kings',22,43),0,9)]), (('isaiah',63,19),16,(9,'לוא'),[(('isaiah',63,19),0,9),(('isaiah',64,1),9,16)]), (('psalms',13,6),11,(6,'אשירה'),[(('psalms',13,5),0,6),(('psalms',13,6),6,11)]), (('zechariah',2,15),18,(12,'וידעת'),[(('zechariah',2,11),0,12),(('zechariah',2,12),12,18)]), ]) def test_partial_boundaries(key,count,boundary,expected): assert spans_for(*key, words(count,boundary), load_map()) == expected def test_acts_closing_sentence_and_source_drift(): record=words(2) record['words'] += [{'word': w} for w in 'καὶ ταῦτα εἰπὼν ἀπέλυσεν τὴν ἐκκλησίαν'.split()] assert spans_for('acts',19,40,record,{}) == [(('acts',19,40),0,2),(('acts',19,41),2,8)] with pytest.raises(ValueError,match='closing sentence'): spans_for('acts',19,40,words(10),{}) with pytest.raises(ValueError,match='boundary'): spans_for('isaiah',63,19,words(16),load_map()) def test_invalid_spans_fail_closed(): key=('genesis',1,1) for spans in [[(1,1,0,0)],[(1,1,0,8)]]: with pytest.raises(ValueError,match='Invalid word span'): spans_for(*key,words(3),{key:spans}) with pytest.raises(ValueError,match='loss or duplication'): spans_for(*key,words(3),{key:[(1,1,0,2),(1,2,1,3)]}) def test_map_changes_rejected(tmp_path): original=(DATA/'versification/oshb-VerseMap.xml').read_text() p=tmp_path/'map.xml' p.write_text(original.replace('type="partial"','type="unsupported"',1)) with pytest.raises(ValueError,match='Unknown map type'):load_map(p) p.write_text(original.replace('type="partial"','type="full"',1)) with pytest.raises(ValueError,match='Partial verse map changed'):load_map(p) line='' p.write_text(original.replace(line,line+line)) with pytest.raises(ValueError,match='Duplicate'):load_map(p) def test_language_runs_and_word_conservation(tmp_path): (tmp_path/'versification').mkdir() shutil.copy(DATA/'versification/oshb-VerseMap.xml',tmp_path/'versification/oshb-VerseMap.xml') for language,book,record in [ ('hebrew','genesis',{'words':[{'word':'א/ב','morph':'HNp'},{'word':'גד','morph':'ANp'},{'word':'הו','morph':'HNp'}]}), ('greek','john',{'words':[{'word':'λόγος'},{'word':'θεός'}]}), ]: path=tmp_path/'sources'/language;path.mkdir(parents=True) (path/f'{book}.json').write_text(json.dumps({'1:1':record})) result,mapping=build_originals(tmp_path) assert sum(map(len,result.values()))==5 assert [w['language'] for w in result['genesis',1,1]]==['he','arc','he'] assert result['genesis',1,1][0]['text']=='אב' assert group_segments(result['john',1,1])==[{'language':'grc','source':'john:1:1','text':'λόγος θεός'}] assert len(mapping)==2 def test_export_never_overwrites_source_data(tmp_path): with pytest.raises(ValueError,match='outside translator data'): build(tmp_path/'output',data_dir=tmp_path) def test_unknown_language_tag_rejected(tmp_path): (tmp_path/'versification').mkdir() shutil.copy(DATA/'versification/oshb-VerseMap.xml',tmp_path/'versification/oshb-VerseMap.xml') (tmp_path/'sources/hebrew').mkdir(parents=True) (tmp_path/'sources/hebrew/genesis.json').write_text(json.dumps({'1:1':{'words':[{'word':'אב','morph':'?'}]}})) with pytest.raises(ValueError,match='Unknown word language'):build_originals(tmp_path) def test_export_includes_headings_omissions_hashes_and_language_runs(tmp_path,monkeypatch): from src.output import originals data=tmp_path/'input';(data/'versification').mkdir(parents=True) (data/'versification/oshb-VerseMap.xml').write_text('fixture') source=data/'sources/hebrew';source.mkdir(parents=True) (source/'psalms.json').write_text('{}') monkeypatch.setattr(originals,'load_site',lambda _: ({('psalms',3,1):'本文',('psalms',3,2):'本文'}, {'psalms':'ps'})) original_words={('psalms',3,0):[{'language':'he','text':'דוד','source':'psalms:3:1','word':0}],('psalms',3,1):[{'language':'he','text':'יהוה','source':'psalms:3:2','word':0}]} monkeypatch.setattr(originals,'build_originals',lambda _: (original_words,{('psalms',3,1):[(('psalms',3,0),0,1)],('psalms',3,2):[(('psalms',3,1),0,1)]})) out=tmp_path/'out';report=originals.build(out,data) payload=json.loads((out/'ps.json').read_text()) assert payload['chapters']['3']['heading'][0]['text']=='דוד' assert payload['chapters']['3']['verses']['2']==[] assert report['without_original']==['psalms:3:2'] assert report['words_by_language']=={'he':2} assert report['mapped_source_references']==2 assert len(report['files']['ps.json'])==64 original_words[('psalms',3,3)]=[] with pytest.raises(ValueError,match='outside site'):originals.build(out,data) def test_site_reference_validation(tmp_path): from src.output.originals import load_site,normalize_book assert normalize_book('III John')=='3john' assert normalize_book('II Corinthians')=='2corinthians' assert normalize_book('I Samuel')=='1samuel' assert normalize_book('Song of Solomon')=='song_of_solomon' refs,codes=load_site() assert len(refs)==31101 and len(codes)==66 (tmp_path/'src/lib').mkdir(parents=True) (tmp_path/'public').mkdir() (tmp_path/'src/lib/data.ts').write_text("'Genesis': { code: 'gen'") csvfile=tmp_path/'public/JapKougo.csv' csvfile.write_text('Book,Chapter,Verse,Text\nGenesis,1,1,本文\nGenesis,1,1,本文\n') with pytest.raises(ValueError,match='Invalid site reference'):load_site(tmp_path) csvfile.write_text('Book,Chapter,Verse,Text\nGenesis,1,1,本文\n') with pytest.raises(ValueError,match='complete'):load_site(tmp_path)