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