feat(gateway): 接入接口22与账号鉴权测试页
This commit is contained in:
@@ -14,7 +14,7 @@ LOGIN = "/api/v1/auth/login"
|
||||
REFRESH = "/api/v1/auth/refresh"
|
||||
FEATURES_42 = "/api/v1/face/features-42"
|
||||
CHECK = "/api/v1/auth/check"
|
||||
PROTECTED = [FEATURES_42, "/api/v1/hairline/generate-52", "/api/v1/face/b-end-points"]
|
||||
PROTECTED = [FEATURES_42, "/api/v1/hairline/generate-52", "/api/v1/face/b-end-points", "/api/v1/hair/grow-22"]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -33,7 +33,7 @@ def login(client):
|
||||
|
||||
|
||||
@pytest.mark.parametrize("path", PROTECTED)
|
||||
def test_all_three_routes_reject_missing_static_and_refresh_tokens(client, monkeypatch, path):
|
||||
def test_protected_routes_reject_missing_static_and_refresh_tokens(client, monkeypatch, path):
|
||||
proxy = AsyncMock()
|
||||
features = AsyncMock()
|
||||
monkeypatch.setattr(gateway, "_proxy", proxy)
|
||||
@@ -99,7 +99,7 @@ def test_features42_combines_processed_photo_with_main_features(client, monkeypa
|
||||
analyzer.assert_called_once_with(b"processed", None, accept_age_alias=True)
|
||||
|
||||
|
||||
def test_bearer_requirement_is_scoped_to_three_business_routes_and_check(client):
|
||||
def test_bearer_requirement_is_scoped_to_protected_business_routes_and_check(client):
|
||||
schema = client.get("/openapi.json").json()
|
||||
protected = {
|
||||
path
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
"""Gateway-only interface 52 integration: raw forwarding, token and image URLs."""
|
||||
"""Gateway integration for interfaces 52 and 22: raw forwarding, token and image URLs."""
|
||||
import asyncio
|
||||
import base64
|
||||
import importlib
|
||||
import io
|
||||
from contextlib import closing
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import AsyncMock, Mock
|
||||
|
||||
@@ -15,6 +16,9 @@ from gateway import config, forward
|
||||
|
||||
gateway = importlib.import_module("gateway.app")
|
||||
PATH = "/api/v1/hairline/generate-52"
|
||||
GROW22 = "/api/v1/hair/grow-22"
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client_headers(monkeypatch):
|
||||
monkeypatch.setenv("HAIR_API_TOKEN_SECRET", "gateway-test-signing-secret-at-least-32")
|
||||
@@ -167,3 +171,66 @@ def test_public_interface52_page_has_no_internal_password():
|
||||
assert client.get("/static/hairline_reference_box.js").status_code == 200
|
||||
finally:
|
||||
client.close()
|
||||
|
||||
|
||||
def test_interface22_forwards_repeated_density_fields_and_serves_image_urls(
|
||||
proxy_setup, monkeypatch, client_headers,
|
||||
):
|
||||
cfg, acquire, release, release_worker, unhealthy = proxy_setup
|
||||
png, jpg = image_bytes("PNG"), image_bytes("JPEG")
|
||||
calls = []
|
||||
|
||||
def worker(request):
|
||||
calls.append(request)
|
||||
return httpx.Response(200, json={"code": 0, "data": {"results": [
|
||||
{"order": 1, "hairline_type": "ellipse", "hair_density": "sparse",
|
||||
"image_base64": base64.b64encode(png).decode(),
|
||||
"grown_image_base64": base64.b64encode(jpg).decode()},
|
||||
{"order": 1, "hairline_type": "ellipse", "hair_density": "dense",
|
||||
"image_base64": base64.b64encode(png).decode(), "grown_image_base64": None},
|
||||
]}})
|
||||
|
||||
upstream = httpx.AsyncClient(transport=httpx.MockTransport(worker))
|
||||
monkeypatch.setattr(forward, "get_client", lambda: upstream)
|
||||
source = httpx.Request(
|
||||
"POST", "http://testserver" + GROW22,
|
||||
files={"image_file": ("face.png", png, "image/png")},
|
||||
data={"gender": "female", "hair_style": "1", "hairline_height": "-0.5",
|
||||
"hair_density": ["sparse", "dense"]},
|
||||
)
|
||||
raw = source.read()
|
||||
assert raw.count(b'name="hair_density"') == 2
|
||||
try:
|
||||
with closing(TestClient(gateway.app)) as client:
|
||||
response = client.post(GROW22, content=raw, headers={
|
||||
**client_headers, "Content-Type": source.headers["content-type"],
|
||||
})
|
||||
assert response.status_code == 200
|
||||
sparse, dense = response.json()["data"]["results"]
|
||||
assert [sparse["hair_density"], dense["hair_density"]] == ["sparse", "dense"]
|
||||
assert client.get(sparse["image_url"]).content == png
|
||||
assert client.get(sparse["grown_image_url"]).content == jpg
|
||||
assert dense["grown_image_base64"] is None
|
||||
finally:
|
||||
asyncio.run(upstream.aclose())
|
||||
assert len(calls) == 1
|
||||
assert str(calls[0].url) == "http://worker" + GROW22
|
||||
assert calls[0].content == raw
|
||||
assert calls[0].headers["x-internal-token"] == cfg["shared_password"]
|
||||
assert "authorization" not in calls[0].headers
|
||||
acquire.assert_awaited_once_with(cfg, 0)
|
||||
release.assert_called_once()
|
||||
release_worker.assert_awaited_once()
|
||||
unhealthy.assert_not_awaited()
|
||||
|
||||
|
||||
def test_interface22_page_uses_gateway_login_and_authenticated_requests():
|
||||
with closing(TestClient(gateway.app)) as client:
|
||||
page = client.get("/static/test_interface22.html")
|
||||
assert page.status_code == 200
|
||||
assert 'id="client-auth"' in page.text
|
||||
for path in (GROW22, PATH):
|
||||
assert f"window.hairClientFetch('{path}'" in page.text
|
||||
assert "X-Internal-Token" not in page.text
|
||||
assert "localhost:8187" not in page.text
|
||||
assert client.get("/static/client_auth.js").status_code == 200
|
||||
|
||||
Reference in New Issue
Block a user