82 lines
3.8 KiB
Python
82 lines
3.8 KiB
Python
"""单张编辑的新参数必须进入同一次生图任务,且主图顺序与尺寸准确。"""
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
import unittest
|
|
from pathlib import Path
|
|
from types import SimpleNamespace
|
|
from unittest.mock import AsyncMock, patch
|
|
from fastapi import HTTPException
|
|
|
|
SERVER_DIR = Path(__file__).resolve().parents[1]
|
|
if str(SERVER_DIR) not in sys.path:
|
|
sys.path.insert(0, str(SERVER_DIR))
|
|
|
|
from api.suite import suite_image_edit, _with_dimensions
|
|
from schemas.suite import ImageEditSingleRequest, ProductDimensions
|
|
from services.generator import _image_size
|
|
from services.prompts.common import build_context
|
|
from services.prompts.single_edit import build_single_edit_prompt
|
|
|
|
|
|
class SingleEditRequestTests(unittest.IsolatedAsyncioTestCase):
|
|
async def test_main_and_two_auxiliary_images_keep_order_in_one_item(self) -> None:
|
|
request = ImageEditSingleRequest(
|
|
main_image_url="main", reference_image_urls=["detail", "scene"],
|
|
prompt="替换背景", model="gpt-image-2@pcore", translate_text=True,
|
|
target_language="ar", background="reference", ratio="4:5",
|
|
)
|
|
job = AsyncMock(return_value=SimpleNamespace(id="task-id"))
|
|
with patch("api.suite.create_image_job", job):
|
|
response = await suite_image_edit(request)
|
|
self.assertEqual(response.suite_id, "task-id")
|
|
payload = job.await_args.kwargs
|
|
self.assertEqual(payload["ratio"], "4:5")
|
|
self.assertEqual(payload["lang"], "ar")
|
|
self.assertEqual(payload["provider"], "pcore")
|
|
self.assertEqual(payload["model"], "gpt-image-2")
|
|
self.assertEqual(len(payload["item_payloads"]), 1)
|
|
self.assertEqual([image["url"] for image in payload["request_payload"]["ref_images"]], ["main", "detail", "scene"])
|
|
self.assertTrue(payload["request_payload"]["single_edit"]["translate_text"])
|
|
|
|
async def test_structured_edit_rejects_other_provider(self) -> None:
|
|
with self.assertRaises(HTTPException) as raised:
|
|
await suite_image_edit(ImageEditSingleRequest(
|
|
main_image_url="main", model="nano-banana-2",
|
|
))
|
|
self.assertEqual(raised.exception.status_code, 400)
|
|
|
|
|
|
class SingleEditPromptTests(unittest.TestCase):
|
|
def test_translation_background_and_partial_dimensions_share_one_prompt(self) -> None:
|
|
context = build_context(_with_dimensions(
|
|
{"title": "AI 精修"}, ProductDimensions(width=8, unit="cm"), required=True,
|
|
))
|
|
prompt = build_single_edit_prompt(
|
|
requirements="保留商品颜色", config={"translate_text": True, "target_language": "ar", "background": "scene", "background_description": "warm kitchen"},
|
|
context=context, style_set=2, style_prompt=None, ratio="3:4", is_size_chart=True, reference_count=2,
|
|
)
|
|
self.assertIn("Image 1 is the fixed main product image", prompt)
|
|
self.assertIn("Images 2 through 3 are auxiliary", prompt)
|
|
self.assertIn("Arabic", prompt)
|
|
self.assertIn("right-to-left", prompt)
|
|
self.assertIn("warm kitchen", prompt)
|
|
self.assertIn("width: 8.0 cm", prompt)
|
|
self.assertIn("Do not draw, label, or show an arrow for: length, height", prompt)
|
|
|
|
def test_four_to_five_size_does_not_fall_back_to_square(self) -> None:
|
|
for provider, is_wan, model in (
|
|
("pcore", True, "gpt-image-2"),
|
|
("rightapi", True, "gpt-image-2"),
|
|
("doubao", True, "seedream"),
|
|
("tongyi", True, "wan2.6-image"),
|
|
("tongyi", False, "qwen-image-3.0-pro"),
|
|
):
|
|
with self.subTest(provider=provider, model=model):
|
|
width, height = map(int, _image_size(provider, "4:5", is_wan, model).replace("*", "x").split("x"))
|
|
self.assertEqual(width * 5, height * 4)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|