feat: 试用过程中的细节优化
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
"""单图导出响应头回归测试。"""
|
||||
from __future__ import annotations
|
||||
|
||||
import io
|
||||
import sys
|
||||
import unittest
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
@@ -10,7 +12,7 @@ SERVER_DIR = Path(__file__).resolve().parents[1]
|
||||
if str(SERVER_DIR) not in sys.path:
|
||||
sys.path.insert(0, str(SERVER_DIR))
|
||||
|
||||
from api.export import ExportImageItem, export_image
|
||||
from api.export import ExportImageItem, ExportImagesRequest, export_image, export_images
|
||||
|
||||
|
||||
class ExportImageTests(unittest.IsolatedAsyncioTestCase):
|
||||
@@ -27,6 +29,24 @@ class ExportImageTests(unittest.IsolatedAsyncioTestCase):
|
||||
self.assertIn('filename="_____-__.png"', disposition)
|
||||
self.assertIn("filename*=UTF-8''%E5%B0%BA%E5%AF%B8%E6%A0%87%E6%B3%A8%E5%9B%BE-%E8%93%9D%E8%89%B2.png", disposition)
|
||||
|
||||
async def test_flat_export_places_generated_images_in_product_root(self) -> None:
|
||||
request = ExportImagesRequest(
|
||||
title="商品",
|
||||
flat=True,
|
||||
images=[
|
||||
ExportImageItem(url="https://example.com/single.png", groupName="单张生图", key="single"),
|
||||
ExportImageItem(url="https://example.com/suite.png", groupName="套图结果", key="suite"),
|
||||
],
|
||||
)
|
||||
with patch("api.export._download", new=AsyncMock(return_value=(b"image", "image/png"))):
|
||||
response = await export_images(request)
|
||||
payload = b"".join([chunk async for chunk in response.body_iterator])
|
||||
|
||||
self.assertEqual(
|
||||
sorted(zipfile.ZipFile(io.BytesIO(payload)).namelist()),
|
||||
["商品/single.png", "商品/suite.png"],
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
"""平台目标比例到供应商尺寸参数的映射。"""
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
SERVER_DIR = Path(__file__).resolve().parents[1]
|
||||
if str(SERVER_DIR) not in sys.path:
|
||||
sys.path.insert(0, str(SERVER_DIR))
|
||||
|
||||
from services.generator import _image_size
|
||||
|
||||
|
||||
class ImageSizeTests(unittest.TestCase):
|
||||
def test_pcore_ozon_size_is_exactly_three_to_four(self) -> None:
|
||||
width, height = map(int, _image_size("pcore", "3:4").split("x"))
|
||||
self.assertEqual(width * 4, height * 3)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,70 @@
|
||||
"""仅扩图的像素保护与单张 AI 编辑提示词回归测试。"""
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import base64
|
||||
import io
|
||||
import sys
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
from PIL import Image
|
||||
|
||||
SERVER_DIR = Path(__file__).resolve().parents[1]
|
||||
if str(SERVER_DIR) not in sys.path:
|
||||
sys.path.insert(0, str(SERVER_DIR))
|
||||
|
||||
from services.generator import prepare_outpaint_canvas
|
||||
from services.prompts import build_prompt
|
||||
|
||||
|
||||
def _data_url(image: Image.Image) -> str:
|
||||
buffer = io.BytesIO()
|
||||
image.save(buffer, format="PNG")
|
||||
return "data:image/png;base64," + base64.b64encode(buffer.getvalue()).decode()
|
||||
|
||||
|
||||
class OutpaintTests(unittest.TestCase):
|
||||
def test_outpaint_restores_original_region_after_provider_response(self) -> None:
|
||||
original = Image.new("RGB", (400, 400), (220, 20, 60))
|
||||
canvas = asyncio.run(prepare_outpaint_canvas(_data_url(original), "1152x1536"))
|
||||
|
||||
self.assertEqual(canvas.size, (1152, 1536))
|
||||
self.assertEqual(canvas.box, (0, 192, 1152, 1344))
|
||||
|
||||
# 模拟供应商把整张图都重绘成蓝色;服务端合成后中心原图必须仍为红色。
|
||||
generated = Image.new("RGB", canvas.size, (0, 0, 255))
|
||||
restored = Image.open(io.BytesIO(canvas.restore_original(_png_bytes(generated)))).convert("RGB")
|
||||
self.assertEqual(restored.getpixel((576, 768)), (220, 20, 60))
|
||||
self.assertEqual(restored.getpixel((576, 80)), (0, 0, 255))
|
||||
|
||||
def test_expanded_margin_is_transparent_in_mask(self) -> None:
|
||||
original = Image.new("RGB", (400, 400), (1, 2, 3))
|
||||
canvas = asyncio.run(prepare_outpaint_canvas(_data_url(original), "1152x1536"))
|
||||
mask_bytes = base64.b64decode(canvas.mask_data_url.split(",", 1)[1])
|
||||
mask = Image.open(io.BytesIO(mask_bytes)).convert("RGBA")
|
||||
self.assertEqual(mask.getpixel((576, 80))[3], 0)
|
||||
self.assertEqual(mask.getpixel((576, 768))[3], 255)
|
||||
|
||||
|
||||
def _png_bytes(image: Image.Image) -> bytes:
|
||||
buffer = io.BytesIO()
|
||||
image.save(buffer, format="PNG")
|
||||
return buffer.getvalue()
|
||||
|
||||
|
||||
class ExclusiveEditPromptTests(unittest.TestCase):
|
||||
def test_user_edit_requirements_do_not_inherit_marketing_or_light_studio_prompt(self) -> None:
|
||||
prompt = build_prompt(
|
||||
"pcore", "gpt-image-2", "custom", {"title": "product"}, 5, "ru",
|
||||
requirements="背景必须是纯黑色,其他不要改变",
|
||||
requirements_exclusive=True,
|
||||
)
|
||||
self.assertIn("背景必须是纯黑色", prompt)
|
||||
self.assertNotIn("e-commerce marketing image", prompt)
|
||||
self.assertNotIn("plain light background", prompt)
|
||||
self.assertNotIn("Render concise Russian marketing text", prompt)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,44 @@
|
||||
"""部分尺寸的尺寸标注图:仅传入已知方向,绝不补造缺失方向。"""
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
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 _with_dimensions
|
||||
from schemas.suite import ProductDimensions
|
||||
from services.prompts.common import build_context
|
||||
from services.prompts.gpt import _task_size_chart
|
||||
|
||||
|
||||
class PartialDimensionsTests(unittest.TestCase):
|
||||
def test_only_known_direction_is_passed_to_prompt_context(self) -> None:
|
||||
raw = _with_dimensions(
|
||||
{"title": "测试商品", "params": [
|
||||
{"key": "产品尺寸", "value": "12.5×8×4 cm"},
|
||||
{"key": "材质", "value": "棉"},
|
||||
]},
|
||||
ProductDimensions(width=8, height=4, unit="cm"),
|
||||
required=True,
|
||||
)
|
||||
|
||||
self.assertEqual(raw["dimensions"], {"width": 8, "height": 4, "unit": "cm"})
|
||||
self.assertEqual(raw["params"], [{"key": "材质", "value": "棉"}])
|
||||
context = build_context(raw)
|
||||
prompt = _task_size_chart(context, "en")
|
||||
self.assertIn("width: 8.0 cm; height: 4.0 cm", prompt)
|
||||
self.assertIn("render exactly 2 measurement arrows and labels, only for: width, height", prompt)
|
||||
self.assertIn("Do not draw, label, or show an arrow for: length", prompt)
|
||||
self.assertNotIn("12.5×8×4", prompt)
|
||||
|
||||
def test_size_chart_still_requires_at_least_one_dimension(self) -> None:
|
||||
with self.assertRaisesRegex(Exception, "至少需要填写一项"):
|
||||
_with_dimensions({"title": "测试商品"}, ProductDimensions(unit="mm"), required=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user