"""Tests for pericope auto-generation.""" import json import pytest from pathlib import Path from tempfile import TemporaryDirectory from src.context.pericope_generator import ( MAX_PERICOPE_SIZE, MIN_PERICOPE_SIZE, PARAGRAPH_MARKER, PericopeGenerator, merge_pericope_indexes, ) from src.models.pericope import Pericope, PericopeIndex class TestPericopeGenerator: """Tests for PericopeGenerator.""" @pytest.fixture def sample_greek_data(self): """Create sample Greek data with paragraph markers.""" return { "1:1": {"text": "¶ Ἐν ἀρχῇ ἦν ὁ λόγος"}, "1:2": {"text": "οὗτος ἦν ἐν ἀρχῇ"}, "1:3": {"text": "πάντα δι᾽ αὐτοῦ ἐγένετο"}, "1:4": {"text": "ἐν αὐτῷ ζωὴ ἦν"}, "1:5": {"text": "¶ καὶ τὸ φῶς ἐν τῇ σκοτίᾳ"}, "1:6": {"text": "ἐγένετο ἄνθρωπος"}, "1:7": {"text": "οὗτος ἦλθεν εἰς μαρτυρίαν"}, "1:8": {"text": "¶ οὐκ ἦν ἐκεῖνος τὸ φῶς"}, "1:9": {"text": "ἦν τὸ φῶς τὸ ἀληθινόν"}, "1:10": {"text": "ἐν τῷ κόσμῳ ἦν"}, } @pytest.fixture def temp_data_dir(self, sample_greek_data): """Create temporary data directory with Greek data.""" with TemporaryDirectory() as tmpdir: data_dir = Path(tmpdir) greek_dir = data_dir / "sources" / "greek" greek_dir.mkdir(parents=True) with (greek_dir / "john.json").open("w") as f: json.dump(sample_greek_data, f) yield data_dir def test_init(self, temp_data_dir): """Test generator initialization.""" generator = PericopeGenerator(temp_data_dir) assert generator._data_dir == temp_data_dir def test_find_paragraph_markers(self, temp_data_dir, sample_greek_data): """Test finding paragraph markers.""" generator = PericopeGenerator(temp_data_dir) markers = generator._find_paragraph_markers(sample_greek_data) assert "1:1" in markers # Has ¶ assert "1:5" in markers # Has ¶ assert "1:8" in markers # Has ¶ assert "1:2" not in markers # No ¶ def test_get_all_verses(self, temp_data_dir, sample_greek_data): """Test getting all verses in order.""" generator = PericopeGenerator(temp_data_dir) verses = generator._get_all_verses(sample_greek_data) assert verses == ["1:1", "1:2", "1:3", "1:4", "1:5", "1:6", "1:7", "1:8", "1:9", "1:10"] def test_ref_sort_key(self, temp_data_dir): """Test reference sort key.""" generator = PericopeGenerator(temp_data_dir) assert generator._ref_sort_key("1:1") == (1, 1) assert generator._ref_sort_key("2:15") == (2, 15) assert generator._ref_sort_key("10:5") == (10, 5) assert generator._ref_sort_key("invalid") == (0, 0) def test_split_large_pericope(self, temp_data_dir): """Test splitting large pericopes.""" generator = PericopeGenerator(temp_data_dir) # Small list should not be split small = ["1:1", "1:2", "1:3"] result = generator._split_large_pericope(small, max_size=10) assert len(result) == 1 assert result[0] == small # Large list should be split large = [f"1:{i}" for i in range(1, 31)] # 30 verses result = generator._split_large_pericope(large, max_size=10) assert len(result) == 3 assert all(len(part) <= 10 for part in result) def test_generate_pericopes(self, temp_data_dir): """Test generating pericopes from paragraph markers.""" generator = PericopeGenerator(temp_data_dir) index = generator.generate_pericopes("john", min_size=2) assert index.book == "john" assert len(index.pericopes) > 0 # All pericopes should be auto-generated for p in index.pericopes: assert p.auto_generated is True assert p.id.startswith("john-auto-") def test_generate_pericopes_respects_min_size(self, temp_data_dir): """Test that small pericopes are merged.""" generator = PericopeGenerator(temp_data_dir) index = generator.generate_pericopes("john", min_size=5) # Should have fewer pericopes due to merging for p in index.pericopes: assert len(p.verses) >= 5 or p == index.pericopes[-1] # Last may be smaller def test_generate_pericopes_empty_book(self, temp_data_dir): """Test generating pericopes for non-existent book.""" generator = PericopeGenerator(temp_data_dir) index = generator.generate_pericopes("nonexistent") assert index.book == "nonexistent" assert len(index.pericopes) == 0 def test_create_pericope(self, temp_data_dir): """Test creating a single pericope.""" generator = PericopeGenerator(temp_data_dir) verses = ["1:1", "1:2", "1:3"] pericope = generator._create_pericope("john", verses, 1) assert pericope.id == "john-auto-001" assert pericope.start_chapter == 1 assert pericope.start_verse == 1 assert pericope.end_chapter == 1 assert pericope.end_verse == 3 assert pericope.verses == verses assert pericope.auto_generated is True def test_generate_and_save(self, temp_data_dir): """Test generating and saving pericopes.""" generator = PericopeGenerator(temp_data_dir) index = generator.generate_and_save("john") # Check file was created output_path = temp_data_dir / "metadata" / "pericopes_generated.json" assert output_path.exists() # Check content with output_path.open() as f: data = json.load(f) assert "john" in data assert data["john"]["auto_generated"] is True def test_generate_all_books(self, temp_data_dir): """Test generating pericopes for multiple books.""" generator = PericopeGenerator(temp_data_dir) # Only john.json exists in our fixture result = generator.generate_all_books() assert "john" in result assert len(result["john"].pericopes) > 0 class TestMergePericopeIndexes: """Tests for merge_pericope_indexes function.""" def test_both_none(self): """Test merging two None values.""" result = merge_pericope_indexes(None, None) assert result is None def test_manual_only(self): """Test when only manual exists.""" manual = PericopeIndex( book="john", name="John", pericopes=[ Pericope( id="john-prologue", start_chapter=1, start_verse=1, end_chapter=1, end_verse=18, verses=["1:1", "1:2"], ) ], ) result = merge_pericope_indexes(manual, None) assert result == manual def test_generated_only(self): """Test when only generated exists.""" generated = PericopeIndex( book="john", name="John", pericopes=[ Pericope( id="john-auto-001", start_chapter=1, start_verse=1, end_chapter=1, end_verse=4, verses=["1:1", "1:2", "1:3", "1:4"], auto_generated=True, ) ], ) result = merge_pericope_indexes(None, generated) assert result == generated def test_manual_takes_precedence(self): """Test that manual pericopes take precedence.""" manual = PericopeIndex( book="john", name="John", pericopes=[ Pericope( id="john-prologue", start_chapter=1, start_verse=1, end_chapter=1, end_verse=18, verses=["1:1"], ) ], ) generated = PericopeIndex( book="john", name="John", pericopes=[ Pericope( id="john-auto-001", start_chapter=1, start_verse=1, end_chapter=1, end_verse=4, verses=["1:1", "1:2", "1:3", "1:4"], auto_generated=True, ) ], ) result = merge_pericope_indexes(manual, generated) assert result == manual def test_empty_manual_uses_generated(self): """Test that empty manual index falls back to generated.""" manual = PericopeIndex(book="john", name="John", pericopes=[]) generated = PericopeIndex( book="john", name="John", pericopes=[ Pericope( id="john-auto-001", start_chapter=1, start_verse=1, end_chapter=1, end_verse=4, verses=["1:1", "1:2", "1:3", "1:4"], auto_generated=True, ) ], ) result = merge_pericope_indexes(manual, generated) assert result == generated class TestConstants: """Tests for module constants.""" def test_paragraph_marker(self): """Test paragraph marker constant.""" assert PARAGRAPH_MARKER == "¶" def test_min_pericope_size(self): """Test minimum pericope size.""" assert MIN_PERICOPE_SIZE >= 1 def test_max_pericope_size(self): """Test maximum pericope size.""" assert MAX_PERICOPE_SIZE > MIN_PERICOPE_SIZE assert MAX_PERICOPE_SIZE <= 50 # Reasonable upper bound