import os import unittest from unittest.mock import Mock, patch from api.routers.chapters import _chapter_to_dict class ChaptersRouterImagesTest(unittest.TestCase): @patch("api.routers.chapters.TencentCosStorageService") @patch.dict( os.environ, { "TENCENT_COS_BUCKET": "life-echo-dev-1319381411", "TENCENT_COS_REGION": "ap-shanghai", "TENCENT_COS_BASE_URL": "https://life-echo-dev-1319381411.cos.ap-shanghai.myqcloud.com", }, clear=False, ) def test_chapter_to_dict_returns_signed_image_urls_for_response(self, storage_cls): storage = Mock() storage.get_download_url.return_value = "https://signed.example.com/memoirs/u1/c1/0-demo.png?sig=123" storage_cls.from_env.return_value = storage chapter = type( "ChapterStub", (), { "id": "chapter-1", "title": "童年的夏天", "content": "{{IMAGE:南方小镇的青石板路}}", "order_index": 0, "status": "completed", "category": "childhood", "images": [ { "index": 0, "placeholder": "{{IMAGE:南方小镇的青石板路}}", "description": "南方小镇的青石板路", "status": "completed", "prompt": "A serene southern China town", "url": "https://life-echo-dev-1319381411.cos.ap-shanghai.myqcloud.com/memoirs/u1/c1/0-demo.png", "storage_key": "memoirs/u1/c1/0-demo.png", } ], "updated_at": None, "is_new": False, "source_segments": [], }, )() payload = _chapter_to_dict(chapter) self.assertEqual( payload["images"][0]["url"], "https://signed.example.com/memoirs/u1/c1/0-demo.png?sig=123", ) self.assertEqual(payload["images"][0]["prompt"], "A serene southern China town") self.assertNotIn("storage_key", payload["images"][0]) @patch("api.routers.chapters.TencentCosStorageService") @patch.dict( os.environ, { "TENCENT_COS_BUCKET": "life-echo-dev-1319381411", "TENCENT_COS_REGION": "ap-shanghai", "TENCENT_COS_BASE_URL": "https://life-echo-dev-1319381411.cos.ap-shanghai.myqcloud.com", }, clear=False, ) def test_chapter_to_dict_preserves_completed_asset_when_signing_fails(self, storage_cls): storage = Mock() storage.get_download_url.side_effect = RuntimeError("cos unavailable") storage_cls.from_env.return_value = storage chapter = type( "ChapterStub", (), { "id": "chapter-1", "title": "童年的夏天", "content": "{{IMAGE:南方小镇的青石板路}}", "order_index": 0, "status": "completed", "category": "childhood", "images": [ { "index": 0, "placeholder": "{{IMAGE:南方小镇的青石板路}}", "description": "南方小镇的青石板路", "status": "completed", "prompt": "A serene southern China town", "url": "https://life-echo-dev-1319381411.cos.ap-shanghai.myqcloud.com/memoirs/u1/c1/0-demo.png", "storage_key": "memoirs/u1/c1/0-demo.png", } ], "updated_at": None, "is_new": False, "source_segments": [], }, )() payload = _chapter_to_dict(chapter) self.assertEqual(payload["images"][0]["status"], "completed") self.assertEqual( payload["images"][0]["url"], "https://life-echo-dev-1319381411.cos.ap-shanghai.myqcloud.com/memoirs/u1/c1/0-demo.png", ) self.assertEqual(payload["images"][0]["prompt"], "A serene southern China town") self.assertNotIn("storage_key", payload["images"][0]) @patch("api.routers.chapters.TencentCosStorageService") def test_chapter_to_dict_drops_malformed_image_assets(self, storage_cls): storage_cls.from_env.return_value = Mock() chapter = type( "ChapterStub", (), { "id": "chapter-1", "title": "童年的夏天", "content": "{{IMAGE:南方小镇的青石板路}}", "order_index": 0, "status": "completed", "category": "childhood", "images": [ { "index": 0, "status": "completed", } ], "updated_at": None, "is_new": False, "source_segments": [], }, )() payload = _chapter_to_dict(chapter) self.assertEqual(payload["images"], []) @patch("api.routers.chapters.TencentCosStorageService") @patch.dict(os.environ, {"MEMOIR_IMAGE_ENABLED": "false"}, clear=False) def test_chapter_to_dict_hides_non_completed_assets_when_feature_disabled(self, storage_cls): storage = Mock() storage.get_download_url.return_value = "https://signed.example.com/0.png?sig=123" storage_cls.from_env.return_value = storage chapter = type( "ChapterStub", (), { "id": "chapter-1", "title": "童年的夏天", "content": "{{IMAGE:南方小镇的青石板路}}", "order_index": 0, "status": "completed", "category": "childhood", "images": [ { "index": 0, "placeholder": "{{IMAGE:南方小镇的青石板路}}", "description": "南方小镇的青石板路", "status": "pending", "url": None, }, { "index": 1, "placeholder": "{{IMAGE:奶奶坐在院子里的藤椅上}}", "description": "奶奶坐在院子里的藤椅上", "status": "completed", "url": "https://life-echo-dev-1319381411.cos.ap-shanghai.myqcloud.com/memoirs/u1/c1/1-demo.png", "storage_key": "memoirs/u1/c1/1-demo.png", }, ], "updated_at": None, "is_new": False, "source_segments": [], }, )() payload = _chapter_to_dict(chapter) self.assertEqual(len(payload["images"]), 1) self.assertEqual(payload["images"][0]["status"], "completed")