PlanOpticon

planopticon / tests / test_exporters.py
Source Blame History 157 lines
54d5d79… noreply 1 """Tests for PDF and PPTX exporters."""
54d5d79… noreply 2
54d5d79… noreply 3 import pytest
54d5d79… noreply 4
54d5d79… noreply 5 from video_processor.exporters.pdf_export import generate_pdf
54d5d79… noreply 6 from video_processor.exporters.pptx_export import generate_pptx
54d5d79… noreply 7
54d5d79… noreply 8
54d5d79… noreply 9 def _sample_kg():
54d5d79… noreply 10 """Return a sample knowledge graph dict for testing."""
54d5d79… noreply 11 return {
54d5d79… noreply 12 "nodes": [
54d5d79… noreply 13 {"name": "Python", "type": "technology", "descriptions": ["A programming language"]},
54d5d79… noreply 14 {"name": "Django", "type": "technology", "descriptions": ["A web framework"]},
54d5d79… noreply 15 {"name": "Alice", "type": "person", "descriptions": ["Software engineer"]},
54d5d79… noreply 16 {"name": "Bob", "type": "person", "descriptions": ["Product manager"]},
54d5d79… noreply 17 {"name": "Acme Corp", "type": "organization", "descriptions": ["A tech company"]},
54d5d79… noreply 18 ],
54d5d79… noreply 19 "relationships": [
54d5d79… noreply 20 {"source": "Alice", "target": "Python", "type": "uses"},
54d5d79… noreply 21 {"source": "Alice", "target": "Bob", "type": "works_with"},
54d5d79… noreply 22 {"source": "Django", "target": "Python", "type": "built_on"},
54d5d79… noreply 23 {"source": "Alice", "target": "Acme Corp", "type": "employed_by"},
54d5d79… noreply 24 ],
54d5d79… noreply 25 }
54d5d79… noreply 26
54d5d79… noreply 27
54d5d79… noreply 28 def _empty_kg():
54d5d79… noreply 29 return {"nodes": [], "relationships": []}
54d5d79… noreply 30
54d5d79… noreply 31
54d5d79… noreply 32 class TestPDFExport:
54d5d79… noreply 33 @pytest.fixture(autouse=True)
54d5d79… noreply 34 def _check_reportlab(self):
54d5d79… noreply 35 pytest.importorskip("reportlab")
54d5d79… noreply 36
54d5d79… noreply 37 def test_generate_pdf(self, tmp_path):
54d5d79… noreply 38 out = tmp_path / "report.pdf"
54d5d79… noreply 39 result = generate_pdf(_sample_kg(), out, title="Test Report")
54d5d79… noreply 40 assert result == out
54d5d79… noreply 41 assert out.exists()
54d5d79… noreply 42 assert out.stat().st_size > 0
54d5d79… noreply 43
54d5d79… noreply 44 def test_generate_pdf_empty_kg(self, tmp_path):
54d5d79… noreply 45 out = tmp_path / "empty.pdf"
54d5d79… noreply 46 result = generate_pdf(_empty_kg(), out)
54d5d79… noreply 47 assert result == out
54d5d79… noreply 48 assert out.exists()
54d5d79… noreply 49
54d5d79… noreply 50 def test_generate_pdf_creates_parent_dirs(self, tmp_path):
54d5d79… noreply 51 out = tmp_path / "sub" / "dir" / "report.pdf"
54d5d79… noreply 52 result = generate_pdf(_sample_kg(), out)
54d5d79… noreply 53 assert result == out
54d5d79… noreply 54 assert out.exists()
54d5d79… noreply 55
54d5d79… noreply 56 def test_generate_pdf_default_title(self, tmp_path):
54d5d79… noreply 57 out = tmp_path / "default.pdf"
54d5d79… noreply 58 generate_pdf(_sample_kg(), out)
54d5d79… noreply 59 assert out.exists()
54d5d79… noreply 60
54d5d79… noreply 61 def test_generate_pdf_with_diagrams_dir(self, tmp_path):
54d5d79… noreply 62 diag_dir = tmp_path / "diagrams"
54d5d79… noreply 63 diag_dir.mkdir()
54d5d79… noreply 64 out = tmp_path / "report.pdf"
54d5d79… noreply 65 # No PNGs in dir — should still work
54d5d79… noreply 66 result = generate_pdf(_sample_kg(), out, diagrams_dir=diag_dir)
54d5d79… noreply 67 assert result == out
54d5d79… noreply 68
54d5d79… noreply 69 def test_generate_pdf_no_reportlab(self, tmp_path, monkeypatch):
54d5d79… noreply 70 """Verify ImportError propagates when reportlab is missing."""
54d5d79… noreply 71 import builtins
54d5d79… noreply 72
54d5d79… noreply 73 real_import = builtins.__import__
54d5d79… noreply 74
54d5d79… noreply 75 def mock_import(name, *args, **kwargs):
54d5d79… noreply 76 if name.startswith("reportlab"):
54d5d79… noreply 77 raise ImportError("No module named 'reportlab'")
54d5d79… noreply 78 return real_import(name, *args, **kwargs)
54d5d79… noreply 79
54d5d79… noreply 80 monkeypatch.setattr(builtins, "__import__", mock_import)
54d5d79… noreply 81 with pytest.raises(ImportError):
54d5d79… noreply 82 generate_pdf(_sample_kg(), tmp_path / "fail.pdf")
54d5d79… noreply 83
54d5d79… noreply 84
54d5d79… noreply 85 class TestPPTXExport:
54d5d79… noreply 86 @pytest.fixture(autouse=True)
54d5d79… noreply 87 def _check_pptx(self):
54d5d79… noreply 88 pytest.importorskip("pptx")
54d5d79… noreply 89
54d5d79… noreply 90 def test_generate_pptx(self, tmp_path):
54d5d79… noreply 91 out = tmp_path / "slides.pptx"
54d5d79… noreply 92 result = generate_pptx(_sample_kg(), out, title="Test Deck")
54d5d79… noreply 93 assert result == out
54d5d79… noreply 94 assert out.exists()
54d5d79… noreply 95 assert out.stat().st_size > 0
54d5d79… noreply 96
54d5d79… noreply 97 def test_generate_pptx_empty_kg(self, tmp_path):
54d5d79… noreply 98 out = tmp_path / "empty.pptx"
54d5d79… noreply 99 result = generate_pptx(_empty_kg(), out)
54d5d79… noreply 100 assert result == out
54d5d79… noreply 101 assert out.exists()
54d5d79… noreply 102
54d5d79… noreply 103 def test_generate_pptx_creates_parent_dirs(self, tmp_path):
54d5d79… noreply 104 out = tmp_path / "sub" / "dir" / "slides.pptx"
54d5d79… noreply 105 result = generate_pptx(_sample_kg(), out)
54d5d79… noreply 106 assert result == out
54d5d79… noreply 107 assert out.exists()
54d5d79… noreply 108
54d5d79… noreply 109 def test_generate_pptx_with_diagrams_dir(self, tmp_path):
54d5d79… noreply 110 diag_dir = tmp_path / "diagrams"
54d5d79… noreply 111 diag_dir.mkdir()
54d5d79… noreply 112 out = tmp_path / "slides.pptx"
54d5d79… noreply 113 result = generate_pptx(_sample_kg(), out, diagrams_dir=diag_dir)
54d5d79… noreply 114 assert result == out
54d5d79… noreply 115
54d5d79… noreply 116 def test_pptx_slide_count(self, tmp_path):
54d5d79… noreply 117 """Verify expected number of slides are created."""
54d5d79… noreply 118 from pptx import Presentation
54d5d79… noreply 119
54d5d79… noreply 120 out = tmp_path / "count.pptx"
54d5d79… noreply 121 generate_pptx(_sample_kg(), out)
54d5d79… noreply 122 prs = Presentation(str(out))
54d5d79… noreply 123 # Title + Overview + Key Entities + Rel Types + 1 entity batch = 5
54d5d79… noreply 124 assert len(prs.slides) == 5
54d5d79… noreply 125
54d5d79… noreply 126 def test_pptx_many_entities_batched(self, tmp_path):
54d5d79… noreply 127 """Entities are batched into multiple slides when >12."""
54d5d79… noreply 128 from pptx import Presentation
54d5d79… noreply 129
54d5d79… noreply 130 kg = {
54d5d79… noreply 131 "nodes": [
54d5d79… noreply 132 {"name": f"Entity{i}", "type": "concept", "descriptions": [f"desc {i}"]}
54d5d79… noreply 133 for i in range(25)
54d5d79… noreply 134 ],
54d5d79… noreply 135 "relationships": [],
54d5d79… noreply 136 }
54d5d79… noreply 137 out = tmp_path / "many.pptx"
54d5d79… noreply 138 generate_pptx(kg, out)
54d5d79… noreply 139 prs = Presentation(str(out))
54d5d79… noreply 140 # Title + Overview + 3 entity batches (12 + 12 + 1) = 5
54d5d79… noreply 141 # No Key Entities or Rel Types slides (no relationships)
54d5d79… noreply 142 assert len(prs.slides) == 5
54d5d79… noreply 143
54d5d79… noreply 144 def test_generate_pptx_no_pptx(self, tmp_path, monkeypatch):
54d5d79… noreply 145 """Verify ImportError propagates when python-pptx is missing."""
54d5d79… noreply 146 import builtins
54d5d79… noreply 147
54d5d79… noreply 148 real_import = builtins.__import__
54d5d79… noreply 149
54d5d79… noreply 150 def mock_import(name, *args, **kwargs):
54d5d79… noreply 151 if name.startswith("pptx"):
54d5d79… noreply 152 raise ImportError("No module named 'pptx'")
54d5d79… noreply 153 return real_import(name, *args, **kwargs)
54d5d79… noreply 154
54d5d79… noreply 155 monkeypatch.setattr(builtins, "__import__", mock_import)
54d5d79… noreply 156 with pytest.raises(ImportError):
54d5d79… noreply 157 generate_pptx(_sample_kg(), tmp_path / "fail.pptx")

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button