Files
hair/tests/test_gateway_nullable_images.py
T
xsl 5b2b73f5d5 feat: 新增52号发际线接口并限制GPU worker来源IP
保留5号历史三档实现,新增中下庭高度定位、扶正裁切、矩形叠加与30案例对比页。

worker仅允许本机和配置白名单访问;网关不增加IP限制。空图片URL字段改写限定为52号,保持旧接口兼容。同步接口、裁切与部署文档。

验证:113项测试通过;1项历史数值基线失败,在未修改的a3fddbb版本复现。浏览器验证5/52及30案例对比页通过。
2026-11-04 10:20:29 +08:00

70 lines
3.2 KiB
Python

"""Nullable/deprecated worker images retain their documented gateway keys."""
import asyncio
import json
from types import SimpleNamespace
from unittest.mock import AsyncMock
import httpx
import pytest
from starlette.requests import Request
from gateway.forward import rewrite_base64_to_url
def test_nullable_image_fields_keep_external_names(tmp_path):
value = {"hairline_images": [{"image_high_base64": None, "image_low_base64": None,
"grown_image_base64": None}],
"high_hairline_center_point": None, "face_measure": None}
actual = rewrite_base64_to_url(value, "https://example.test", str(tmp_path), rewrite_null_images=True)
assert actual == {"hairline_images": [{"image_high_url": None, "image_low_url": None,
"grown_image_url": None}],
"high_hairline_center_point": None, "face_measure": None}
assert not list(tmp_path.iterdir())
def test_nonempty_images_still_rewrite_alongside_null_fields(tmp_path):
import base64
import io
from PIL import Image
buf = io.BytesIO()
Image.new("RGBA", (2, 2), (255, 255, 255, 0)).save(buf, format="PNG")
value = {"image_middle_base64": base64.b64encode(buf.getvalue()).decode(),
"image_high_base64": None}
actual = rewrite_base64_to_url(value, "https://example.test", str(tmp_path), rewrite_null_images=True)
assert actual["image_middle_url"].startswith("https://example.test/static/annotations/")
assert actual["image_high_url"] is None
assert len(list(tmp_path.iterdir())) == 1
def test_legacy_nullable_image_field_names_remain_unchanged(tmp_path):
value = {"hairline_images": [{"grown_image_base64": None}], "face_measure": None}
assert rewrite_base64_to_url(value, "https://example.test", str(tmp_path)) == value
assert not list(tmp_path.iterdir())
@pytest.mark.parametrize("path,expected_key", [
("/api/v1/hairline/generate", "grown_image_base64"),
("/api/v1/hairline/generate-52", "grown_image_url"),
])
def test_proxy_rewrites_null_images_only_for_interface52(tmp_path, monkeypatch, path, expected_key):
import gateway.config as config
import gateway.forward as forward
monkeypatch.setattr(config, "get_config", lambda: {
"dispatch": {"request_timeout_seconds": 10}, "shared_password": "test-token",
"public_base_url": "https://example.test", "static_dir": str(tmp_path),
})
monkeypatch.setattr(forward, "acquire_worker", AsyncMock(return_value=SimpleNamespace(url="http://worker")))
monkeypatch.setattr(forward, "release_worker", AsyncMock())
worker_response = httpx.Response(200, json={"code": 0, "data": {"hairline_images": [
{"grown_image_base64": None}]}})
monkeypatch.setattr(forward, "get_client", lambda: SimpleNamespace(request=AsyncMock(return_value=worker_response)))
async def receive():
return {"type": "http.request", "body": b"", "more_body": False}
request = Request({"type": "http", "method": "POST", "path": path, "headers": []}, receive)
response = asyncio.run(forward.proxy_request(request, path))
assert response.status_code == 200
assert json.loads(response.body)["data"]["hairline_images"] == [{expected_key: None}]