120 lines
4.1 KiB
Python
120 lines
4.1 KiB
Python
import pytest
|
|
|
|
from app.service.promotion_layout_planner import PromotionLayoutPlanner
|
|
|
|
|
|
def _draft() -> dict[str, object]:
|
|
return {
|
|
"style_code": "balanced_allocation",
|
|
"title": "测试材料",
|
|
"chapters": [
|
|
{"title": "产品基本信息", "body": "产品内容"},
|
|
{"title": "风险揭示", "body": "风险内容"},
|
|
],
|
|
}
|
|
|
|
|
|
def test_default_plan_uses_content_sensitive_layouts() -> None:
|
|
plan = PromotionLayoutPlanner.default_plan(_draft(), has_photo=True, has_chart=False)
|
|
|
|
assert plan["cover_layout"] == "cover_left_photo_right"
|
|
assert plan["page_plans"][1]["layout"] == "full_width_disclosure"
|
|
assert plan["design_profile"]["manager_layout"] == "profile_feature"
|
|
|
|
|
|
def test_strategy_heavy_material_uses_asymmetric_design_profile() -> None:
|
|
draft = {
|
|
**_draft(),
|
|
"chapters": [
|
|
{
|
|
"title": "投资范围、策略与限制",
|
|
"body": "投资策略说明" * 20,
|
|
}
|
|
],
|
|
}
|
|
|
|
plan = PromotionLayoutPlanner.default_plan(draft, has_photo=True, has_chart=True)
|
|
|
|
assert plan["design_profile"]["design_tone"] == "institutional_tech"
|
|
assert plan["design_profile"]["content_structure"] == "strategy_right_emphasis"
|
|
assert plan["design_profile"]["column_ratio"] == "40_60"
|
|
|
|
|
|
def test_low_density_chapter_uses_visual_focus_layout() -> None:
|
|
draft = {
|
|
**_draft(),
|
|
"chapters": [{"title": "投资观点", "body": "长期配置。"}],
|
|
}
|
|
|
|
plan = PromotionLayoutPlanner.default_plan(draft, has_photo=False, has_chart=False)
|
|
|
|
assert plan["page_plans"][0]["content_density"] == "low"
|
|
assert plan["page_plans"][0]["layout"] == "visual_focus"
|
|
|
|
|
|
def test_content_density_respects_existing_chart_signal() -> None:
|
|
chapter = {"title": "业绩", "body": "简要说明", "chart_index": 0}
|
|
|
|
assert PromotionLayoutPlanner.content_density(chapter) == "medium"
|
|
|
|
|
|
def test_validate_rejects_unknown_layout_and_keeps_fallback() -> None:
|
|
fallback = PromotionLayoutPlanner.default_plan(_draft(), has_photo=False, has_chart=False)
|
|
invalid = {
|
|
**fallback,
|
|
"page_plans": [
|
|
{"chapter_title": "产品基本信息", "layout": "free_coordinates"},
|
|
{"chapter_title": "风险揭示", "layout": "full_width_disclosure"},
|
|
],
|
|
}
|
|
|
|
result = PromotionLayoutPlanner._validate(invalid, _draft(), fallback)
|
|
|
|
assert result == fallback
|
|
|
|
|
|
def test_validate_preserves_chapter_titles_and_only_accepts_complete_plan() -> None:
|
|
fallback = PromotionLayoutPlanner.default_plan(_draft(), has_photo=False, has_chart=False)
|
|
value = {
|
|
"cover_layout": "cover_visual_right",
|
|
"poster_layout": "poster_balanced",
|
|
"background_theme": "geometric_grid",
|
|
"design_profile": {
|
|
"design_tone": "quiet_data",
|
|
"content_structure": "balanced_split",
|
|
"column_ratio": "50_50",
|
|
"manager_layout": "profile_compact",
|
|
"performance_layout": "metrics_first",
|
|
"background_role": "soft_paper",
|
|
},
|
|
"page_plans": [
|
|
{"chapter_title": "风险揭示", "layout": "full_width_disclosure"},
|
|
{"chapter_title": "产品基本信息", "layout": "two_columns"},
|
|
],
|
|
}
|
|
|
|
result = PromotionLayoutPlanner._validate(value, _draft(), fallback)
|
|
|
|
assert [item["chapter_title"] for item in result["page_plans"]] == [
|
|
"产品基本信息",
|
|
"风险揭示",
|
|
]
|
|
assert result["page_plans"][0]["layout"] == "two_columns"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_plan_falls_back_when_model_fails(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
async def fail_resolve(*, agent_type: str, task_type: str) -> list[object]:
|
|
raise RuntimeError("模型不可用")
|
|
|
|
monkeypatch.setattr(
|
|
"app.service.promotion_layout_planner.DatabaseModelEndpointResolver.resolve",
|
|
fail_resolve,
|
|
)
|
|
|
|
result = await PromotionLayoutPlanner().plan(_draft(), has_photo=False, has_chart=False)
|
|
|
|
assert result == PromotionLayoutPlanner.default_plan(
|
|
_draft(), has_photo=False, has_chart=False
|
|
)
|