"""章节封面:无 cover_asset_id 时用正文首张 asset:// 作列表封面。""" from unittest.mock import MagicMock from app.features.memoir.helpers import chapter_cover_to_dict def test_cover_falls_back_to_first_inline_asset_url() -> None: ch = MagicMock(spec=[]) ch.canonical_markdown = "正文\n\n![场景](asset://img-1)" ch.cover_asset_id = None ch.story_links = [] ch.images = [] m = chapter_cover_to_dict( ch, asset_url_map={"img-1": "https://cos.example.com/signed-1"}, markdown_for_response=None, ) assert m is not None assert m["url"] == "https://cos.example.com/signed-1" assert m["status"] == "completed" def test_cover_from_reading_segments_when_canonical_has_no_asset() -> None: """分段快照里有 asset://,章节 canonical 未带图时仍能出封面 URL。""" ch = MagicMock(spec=[]) ch.canonical_markdown = "只有文字没有图" * 20 ch.cover_asset_id = None ch.story_links = [] ch.images = [] ch.reading_segments_json = [ {"story_id": "s1", "body_markdown": "![景](asset://seg-1)"} ] m = chapter_cover_to_dict( ch, asset_url_map={"seg-1": "https://cos.example.com/seg"}, markdown_for_response=None, ) assert m is not None assert m["url"] == "https://cos.example.com/seg" def test_cover_prefers_cover_asset_id_over_inline() -> None: ch = MagicMock(spec=[]) ch.canonical_markdown = "![a](asset://inline-1)" ch.cover_asset_id = "cover-99" ch.story_links = [] ch.images = [] m = chapter_cover_to_dict( ch, asset_url_map={ "inline-1": "https://cos.example.com/inline", "cover-99": "https://cos.example.com/cover", }, markdown_for_response=None, ) assert m["url"] == "https://cos.example.com/cover"