Files
goodbuddy/resources/skills/product-presentation/tests/test_build_pptx.py
T

148 lines
5.4 KiB
Python

import copy
import importlib.util
import io
import json
import tempfile
import unittest
from pathlib import Path
from PIL import Image
from pptx import Presentation
SKILL_DIR = Path(__file__).resolve().parents[1]
SCRIPT = SKILL_DIR / "scripts" / "build_pptx.py"
SPEC = importlib.util.spec_from_file_location("build_pptx", SCRIPT)
build_pptx = importlib.util.module_from_spec(SPEC)
SPEC.loader.exec_module(build_pptx)
class BuildPptxTests(unittest.TestCase):
@classmethod
def setUpClass(cls):
template = SKILL_DIR / "templates" / "deck.example.json"
cls.deck = json.loads(template.read_text(encoding="utf-8"))
def test_builds_example_deck(self):
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
input_path = root / "deck.json"
input_path.write_text(
json.dumps(self.deck, ensure_ascii=False),
encoding="utf-8",
)
loaded_path, deck = build_pptx.load_deck(input_path)
output = build_pptx.build_presentation(
loaded_path,
deck,
root / "deck.pptx",
)
presentation = Presentation(output)
self.assertEqual(len(presentation.slides), len(self.deck["slides"]))
all_text = "\n".join(
shape.text
for slide in presentation.slides
for shape in slide.shapes
if hasattr(shape, "text")
)
self.assertIn("目标场景与问题", all_text)
self.assertIn("下一步", all_text)
for slide, item in zip(presentation.slides, self.deck["slides"]):
self.assertEqual(
slide.notes_slide.notes_text_frame.text.strip(),
item["notes"].strip(),
)
def test_requires_notes_outside_section_slides(self):
deck = copy.deepcopy(self.deck)
deck["slides"][1].pop("notes")
with tempfile.TemporaryDirectory() as tmp:
path = Path(tmp) / "deck.json"
path.write_text(json.dumps(deck, ensure_ascii=False), encoding="utf-8")
with self.assertRaisesRegex(ValueError, r"slides\[1\]\.notes"):
build_pptx.load_deck(path)
def test_downsamples_large_image_slide(self):
deck = copy.deepcopy(self.deck)
deck["slides"].insert(
3,
{
"type": "image",
"title": "参考架构",
"image": "architecture.png",
"caption": "示例架构图",
"notes": "说明组件边界。",
},
)
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
Image.new("RGB", (5000, 3000), "white").save(root / "architecture.png")
path = root / "deck.json"
path.write_text(json.dumps(deck, ensure_ascii=False), encoding="utf-8")
loaded_path, loaded = build_pptx.load_deck(path)
output = build_pptx.build_presentation(
loaded_path, loaded, root / "deck.pptx"
)
presentation = Presentation(output)
self.assertEqual(len(presentation.slides), len(deck["slides"]))
pictures = [
shape
for slide in presentation.slides
for shape in slide.shapes
if shape.shape_type == 13
]
self.assertEqual(len(pictures), 1)
with Image.open(io.BytesIO(pictures[0].image.blob)) as embedded:
self.assertLess(embedded.width, 5000)
def test_rejects_image_outside_deck_directory(self):
deck = copy.deepcopy(self.deck)
deck["slides"].insert(
1,
{
"type": "image",
"title": "外部图片",
"image": "../outside.png",
"notes": "越界图片。",
},
)
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp) / "deck"
root.mkdir()
Image.new("RGB", (10, 10), "white").save(Path(tmp) / "outside.png")
path = root / "deck.json"
path.write_text(json.dumps(deck, ensure_ascii=False), encoding="utf-8")
loaded_path, loaded = build_pptx.load_deck(path)
with self.assertRaisesRegex(ValueError, "必须位于"):
build_pptx.build_presentation(
loaded_path, loaded, root / "deck.pptx"
)
def test_rejects_too_many_bullets(self):
deck = copy.deepcopy(self.deck)
deck["slides"][1]["bullets"] = [f"项目 {index}" for index in range(7)]
with tempfile.TemporaryDirectory() as tmp:
path = Path(tmp) / "deck.json"
path.write_text(
json.dumps(deck, ensure_ascii=False),
encoding="utf-8",
)
with self.assertRaisesRegex(ValueError, "最多 6 项"):
build_pptx.load_deck(path)
def test_rejects_unknown_slide_type(self):
deck = copy.deepcopy(self.deck)
deck["slides"][0]["type"] = "unknown"
with tempfile.TemporaryDirectory() as tmp:
path = Path(tmp) / "deck.json"
path.write_text(
json.dumps(deck, ensure_ascii=False),
encoding="utf-8",
)
with self.assertRaisesRegex(ValueError, "未知"):
build_pptx.load_deck(path)
if __name__ == "__main__":
unittest.main()