Files
2026-07-13 13:08:41 +08:00

236 lines
7.3 KiB
Python

import asyncio
import uuid
from unittest.mock import AsyncMock, Mock
from models.sql.slide import SlideModel
from services.icon_finder_service import IconFinderService
from templates.presentation_layout import PresentationLayoutModel, SlideLayoutModel
from utils import process_slides
from utils.icon_weights import extract_icon_weight_from_settings, normalize_icon_weight
def test_icon_weight_settings_uses_only_icon_weight_and_fallback():
assert extract_icon_weight_from_settings({"icon_weight": "thin"}) == "thin"
assert extract_icon_weight_from_settings({"wrong_key": "thin"}) == "bold"
assert extract_icon_weight_from_settings({"icon_weight": "unknown"}) == "bold"
assert normalize_icon_weight(None) == "bold"
def test_presentation_layout_reads_template_icon_weight():
layout = PresentationLayoutModel(
name="general",
ordered=False,
icon_weight="duotone",
slides=[SlideLayoutModel(id="intro", json_schema={"title": "Intro"})],
)
assert layout.icon_weight == "duotone"
def test_icon_finder_builds_weighted_static_urls(monkeypatch):
service = IconFinderService()
monkeypatch.setattr(
"services.icon_finder_service.get_resource_path",
lambda path: f"/app/{path}",
)
monkeypatch.setattr(
"services.icon_finder_service.os.path.isfile",
lambda path: True,
)
regular_url = service._icon_url_for_weight(
"chart-line-up-bold||chart growth",
"regular",
)
thin_url = service._icon_url_for_weight("chart-line-up-bold", "thin")
assert regular_url.endswith("/static/icons/regular/chart-line-up.svg")
assert thin_url.endswith("/static/icons/thin/chart-line-up-thin.svg")
def test_process_slide_fetches_icons_with_template_weight(monkeypatch):
captured = {}
async def fake_search_icons(query, k=1, weight=None):
captured["query"] = query
captured["weight"] = weight
return [f"/static/icons/{weight}/checks-{weight}.svg"]
monkeypatch.setattr(
process_slides.ICON_FINDER_SERVICE,
"search_icons",
fake_search_icons,
)
slide = SlideModel(
presentation=uuid.uuid4(),
layout_group="general",
layout="layout-1",
index=0,
content={
"icon": {
"__icon_query__": "success check",
"__icon_url__": "/static/icons/placeholder.svg",
}
},
properties=None,
)
assets = asyncio.run(
process_slides.process_slide_and_fetch_assets(
image_generation_service=Mock(),
slide=slide,
icon_weight="thin",
)
)
assert assets == []
assert captured == {"query": "success check", "weight": "thin"}
assert slide.content["icon"]["__icon_url__"].endswith(
"/static/icons/thin/checks-thin.svg"
)
def test_process_slide_fetches_every_template_v2_image_and_icon(monkeypatch):
icon_queries = []
async def fake_search_icons(query, k=1, weight=None):
icon_queries.append(query)
return [f"/static/icons/{query.replace(' ', '-')}.svg"]
monkeypatch.setattr(
process_slides.ICON_FINDER_SERVICE,
"search_icons",
fake_search_icons,
)
image_generation_service = Mock()
image_generation_service.generate_image = AsyncMock(
side_effect=[
"/static/generated/market.png",
"/static/generated/team.png",
]
)
slide = SlideModel(
presentation=uuid.uuid4(),
layout_group="template-v2",
layout="layout-1",
index=0,
content={
"hero": {"image_prompt": "market dashboard"},
"cards": [
{"image_prompt": "collaborative team"},
{"icon_query": "growth chart"},
{"icon_query": "customer support"},
],
},
properties=None,
)
assets = asyncio.run(
process_slides.process_slide_and_fetch_assets(
image_generation_service=image_generation_service,
slide=slide,
icon_weight="regular",
)
)
assert assets == []
assert [
call.args[0].prompt
for call in image_generation_service.generate_image.await_args_list
] == ["market dashboard", "collaborative team"]
assert icon_queries == ["growth chart", "customer support"]
assert slide.content["hero"]["image_url"].endswith(
"/static/generated/market.png"
)
assert slide.content["cards"][0]["image_url"].endswith(
"/static/generated/team.png"
)
assert slide.content["cards"][1]["icon_url"].endswith(
"/static/icons/growth-chart.svg"
)
assert slide.content["cards"][2]["icon_url"].endswith(
"/static/icons/customer-support.svg"
)
assert "__image_url__" not in slide.content["hero"]
assert "__image_url__" not in slide.content["cards"][0]
assert "__icon_url__" not in slide.content["cards"][1]
assert "__icon_url__" not in slide.content["cards"][2]
def test_process_template_v2_edit_reuses_assets_with_clean_url_fields(monkeypatch):
search_icons = AsyncMock()
monkeypatch.setattr(
process_slides.ICON_FINDER_SERVICE,
"search_icons",
search_icons,
)
image_generation_service = Mock()
image_generation_service.generate_image = AsyncMock()
old_content = {
"hero": {
"image_prompt": "market dashboard",
"__image_url__": "/static/generated/market.png",
},
"badge": {
"icon_query": "growth chart",
"__icon_url__": "/static/icons/growth-chart.svg",
},
}
new_content = {
"hero": {"image_prompt": "market dashboard"},
"badge": {"icon_query": "growth chart"},
}
assets = asyncio.run(
process_slides.process_old_and_new_slides_and_fetch_assets(
image_generation_service=image_generation_service,
old_slide_content=old_content,
new_slide_content=new_content,
use_template_v2_asset_fields=True,
)
)
assert assets == []
image_generation_service.generate_image.assert_not_awaited()
search_icons.assert_not_awaited()
assert new_content["hero"] == {
"image_prompt": "market dashboard",
"image_url": "/static/generated/market.png",
}
assert new_content["badge"] == {
"icon_query": "growth chart",
"icon_url": "/static/icons/growth-chart.svg",
}
def test_template_v2_placeholders_use_clean_url_fields():
slide = SlideModel(
presentation=uuid.uuid4(),
layout_group="template-v2-template-id",
layout="layout-1",
index=0,
content={
"hero": {
"image_prompt": "market dashboard",
"__image_url__": "/old-image.png",
},
"badge": {
"icon_query": "growth chart",
"__icon_url__": "/old-icon.svg",
},
},
properties=None,
)
process_slides.process_slide_add_placeholder_assets(slide)
assert slide.content["hero"]["image_url"].endswith(
"/static/images/placeholder.jpg"
)
assert slide.content["badge"]["icon_url"].endswith(
"/static/icons/placeholder.svg"
)
assert "__image_url__" not in slide.content["hero"]
assert "__icon_url__" not in slide.content["badge"]