126 lines
3.9 KiB
Python
126 lines
3.9 KiB
Python
from pathlib import Path
|
|
import sys
|
|
|
|
import pytest
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "api"))
|
|
pytest.importorskip("pptx")
|
|
|
|
from insurance.ppt.template_asset_service import (
|
|
parse_template_pptx,
|
|
resolve_template_asset,
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("asset_id", "expected_slides"),
|
|
[
|
|
("builtin://single_savings.pptx", 20),
|
|
("builtin://multi_savings_comparison.pptx", 17),
|
|
("builtin://savings_iul_comprehensive.pptx", 18),
|
|
],
|
|
)
|
|
def test_builtin_ppt_templates_are_parseable(asset_id, expected_slides):
|
|
path = resolve_template_asset(asset_id)
|
|
|
|
assert path
|
|
assert Path(path).is_file()
|
|
parsed = parse_template_pptx(path)
|
|
assert parsed["slideCount"] == expected_slides
|
|
assert parsed["slidesConfig"][0]["pageType"] == "cover"
|
|
assert parsed["slidesConfig"][-1]["pageType"] == "closing"
|
|
|
|
|
|
def test_template_asset_resolver_blocks_parent_traversal():
|
|
with pytest.raises(ValueError, match="路径非法"):
|
|
resolve_template_asset("builtin://../outside.pptx")
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("asset_id", "expect_picture"),
|
|
[
|
|
("builtin://single_savings.pptx", False),
|
|
("builtin://multi_savings_comparison.pptx", False),
|
|
("builtin://savings_iul_comprehensive.pptx", True),
|
|
],
|
|
)
|
|
def test_renderer_uses_builtin_template_frames_without_sample_copy(
|
|
tmp_path, asset_id, expect_picture
|
|
):
|
|
from insurance.ppt.scripts.fast_pptx_renderer import render_deck
|
|
from pptx import Presentation
|
|
|
|
source_path = resolve_template_asset(asset_id)
|
|
output_path = tmp_path / f"{Path(source_path).stem}-rendered.pptx"
|
|
result = render_deck({
|
|
"products": [{
|
|
"kind": "savings",
|
|
"productName": "测试储蓄计划",
|
|
"insured": {"age": 35},
|
|
"policy": {
|
|
"currency": "USD",
|
|
"annualPremium": 50000,
|
|
"payYears": 5,
|
|
},
|
|
"benefitRows": [],
|
|
"withdrawalRows": [],
|
|
}],
|
|
"company": {},
|
|
"scenarioSlides": [
|
|
{"pageType": "cover", "title": "测试模板"},
|
|
{"pageType": "closing", "title": "下一步"},
|
|
],
|
|
"templateConfig": {
|
|
"sourceTemplatePath": source_path,
|
|
"slidesConfig": [
|
|
{"pageType": "cover", "sourceSlide": 1},
|
|
{"pageType": "closing", "sourceSlide": 2},
|
|
],
|
|
},
|
|
}, str(output_path), "deepblue")
|
|
|
|
assert result["ok"] is True
|
|
assert result["slides"] == 2
|
|
assert result["rendererMode"] == "clone-edit-v2"
|
|
assert result["templateFrameMap"] == [1, 2]
|
|
assert output_path.is_file()
|
|
|
|
presentation = Presentation(str(output_path))
|
|
all_text = "\n".join(
|
|
shape.text
|
|
for slide in presentation.slides
|
|
for shape in slide.shapes
|
|
if getattr(shape, "has_text_frame", False)
|
|
)
|
|
assert "测试模板" in all_text
|
|
assert "US$500,000" not in all_text
|
|
pictures = sum(
|
|
1
|
|
for slide in presentation.slides
|
|
for shape in slide.shapes
|
|
if shape.shape_type == 13
|
|
)
|
|
assert (pictures > 0) is expect_picture
|
|
|
|
|
|
def test_renderer_rejects_uploaded_template_with_too_few_frames(tmp_path):
|
|
from insurance.ppt.scripts.fast_pptx_renderer import render_deck
|
|
|
|
source_path = resolve_template_asset("builtin://single_savings.pptx")
|
|
output_path = tmp_path / "too-many-slides.pptx"
|
|
scenario_slides = [
|
|
{"pageType": "guidance", "title": f"第 {index} 页"}
|
|
for index in range(21)
|
|
]
|
|
|
|
with pytest.raises(ValueError, match="只有 20 页"):
|
|
render_deck({
|
|
"products": [{"kind": "savings"}],
|
|
"company": {},
|
|
"scenarioSlides": scenario_slides,
|
|
"templateConfig": {
|
|
"sourceTemplatePath": source_path,
|
|
"slidesConfig": [{"pageType": "guidance"}] * 20,
|
|
},
|
|
}, str(output_path), "deepblue")
|