mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 17:30:23 +08:00
- 删除 aether-hub、aether-proxy 独立项目及其 Dockerfile/配置 - 删除 crates/aether-executor 和 crates/aether-gateway 全部模块 - 新增 apps/ 目录作为应用入口 - 将 hub 概念重构为 gateway tunnel transport - 将 executor 重构为 execution runtime - 新增 tunnel.rs 合约定义和 testkit tunnel/execution_runtime 模块 - 更新 Python 服务层和测试适配新架构命名
1326 lines
45 KiB
Python
1326 lines
45 KiB
Python
"""Gateway internal report/finalize/trace route tests."""
|
|
|
|
import asyncio
|
|
import base64
|
|
import json
|
|
import time
|
|
from types import SimpleNamespace
|
|
from typing import Any
|
|
from unittest.mock import AsyncMock, MagicMock
|
|
|
|
import pytest
|
|
from fastapi import BackgroundTasks, FastAPI
|
|
from fastapi.responses import JSONResponse, StreamingResponse
|
|
from fastapi.testclient import TestClient
|
|
from sqlalchemy import create_engine
|
|
from sqlalchemy.orm import sessionmaker
|
|
|
|
from src.api.internal.gateway import (
|
|
CONTROL_ACTION_HEADER,
|
|
CONTROL_ACTION_PROXY_PUBLIC,
|
|
CONTROL_EXECUTED_HEADER,
|
|
GatewayAuthContext,
|
|
GatewayExecutionDecisionResponse,
|
|
GatewayExecuteRequest,
|
|
GatewayExecutionPlanResponse,
|
|
GatewayResolveRequest,
|
|
GatewayStreamReportRequest,
|
|
GatewaySyncReportRequest,
|
|
_dispatch_gateway_sync_telemetry,
|
|
_build_gateway_sync_error_payload,
|
|
_build_gateway_sync_telemetry_writer,
|
|
_run_gateway_stream_report_background,
|
|
_run_gateway_sync_report_background,
|
|
_stream_executor_requires_python_rewrite,
|
|
_build_claude_chat_sync_decision,
|
|
_build_claude_chat_stream_decision,
|
|
_build_claude_cli_sync_decision,
|
|
_build_claude_cli_stream_decision,
|
|
_build_gemini_files_download_stream_decision,
|
|
_build_gemini_files_proxy_sync_decision,
|
|
_build_gemini_chat_sync_decision,
|
|
_build_gemini_chat_stream_decision,
|
|
_build_gemini_cli_sync_decision,
|
|
_build_gemini_cli_stream_decision,
|
|
_build_openai_chat_sync_decision,
|
|
_build_openai_chat_stream_decision,
|
|
_build_openai_cli_stream_decision,
|
|
_build_openai_video_content_stream_decision,
|
|
_extract_gateway_sync_error_message,
|
|
_record_gateway_direct_candidate_graph,
|
|
_resolve_gateway_sync_error_status_code,
|
|
_build_openai_chat_sync_plan,
|
|
_build_openai_cli_sync_decision,
|
|
_build_openai_cli_stream_plan,
|
|
_build_openai_cli_sync_plan,
|
|
_is_streaming_sync_payload,
|
|
_resolve_auth_context,
|
|
_resolve_gateway_sync_adapter,
|
|
classify_gateway_route,
|
|
router,
|
|
)
|
|
from src.database import get_db
|
|
from src.models.database import Base, RequestCandidate
|
|
from src.services.orchestration.candidate_resolver import CandidateResolver
|
|
from src.services.scheduling.schemas import PoolCandidate, ProviderCandidate
|
|
from src.services.request.execution_runtime_plan import ExecutionPlan, ExecutionPlanBody, PreparedExecutionPlan
|
|
|
|
|
|
def _wait_until(predicate: Any, *, timeout: float = 1.0, interval: float = 0.01) -> None:
|
|
deadline = time.time() + timeout
|
|
while time.time() < deadline:
|
|
if predicate():
|
|
return
|
|
time.sleep(interval)
|
|
assert predicate()
|
|
|
|
|
|
def _make_legacy_test_client(app: FastAPI) -> TestClient:
|
|
return TestClient(
|
|
app,
|
|
base_url="http://127.0.0.1",
|
|
headers={"x-aether-legacy-internal-gateway": "true"},
|
|
)
|
|
|
|
|
|
def test_finalize_sync_route_finalizes_openai_video_create_response(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
app = FastAPI()
|
|
app.include_router(router)
|
|
app.dependency_overrides[get_db] = lambda: object()
|
|
monkeypatch.setattr("src.api.internal.gateway.ensure_loopback", lambda request: None)
|
|
finalize_mock = AsyncMock(
|
|
return_value=JSONResponse(
|
|
content={
|
|
"id": "vid_local_123",
|
|
"object": "video",
|
|
"status": "submitted",
|
|
}
|
|
)
|
|
)
|
|
monkeypatch.setattr(
|
|
"src.api.internal.gateway._finalize_gateway_openai_video_create_sync",
|
|
finalize_mock,
|
|
)
|
|
|
|
client = _make_legacy_test_client(app)
|
|
response = client.post(
|
|
"/api/internal/gateway/finalize-sync",
|
|
json={
|
|
"trace_id": "trace-openai-video-finalize",
|
|
"report_kind": "openai_video_create_sync_finalize",
|
|
"report_context": {
|
|
"user_id": "user-123",
|
|
"api_key_id": "key-123",
|
|
},
|
|
"status_code": 200,
|
|
"headers": {"content-type": "application/json"},
|
|
"body_json": {"id": "ext-video-task-123", "status": "submitted"},
|
|
"telemetry": {"elapsed_ms": 42},
|
|
},
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
assert response.headers[CONTROL_EXECUTED_HEADER] == "true"
|
|
assert response.json() == {
|
|
"id": "vid_local_123",
|
|
"object": "video",
|
|
"status": "submitted",
|
|
}
|
|
finalize_mock.assert_awaited_once()
|
|
|
|
|
|
def test_finalize_sync_route_finalizes_openai_chat_response(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
app = FastAPI()
|
|
app.include_router(router)
|
|
app.dependency_overrides[get_db] = lambda: object()
|
|
monkeypatch.setattr("src.api.internal.gateway.ensure_loopback", lambda request: None)
|
|
finalize_mock = AsyncMock(
|
|
return_value=JSONResponse(
|
|
content={
|
|
"id": "chatcmpl-local-123",
|
|
"object": "chat.completion",
|
|
"choices": [],
|
|
}
|
|
)
|
|
)
|
|
monkeypatch.setattr(
|
|
"src.api.internal.gateway._finalize_gateway_chat_sync",
|
|
finalize_mock,
|
|
)
|
|
|
|
client = _make_legacy_test_client(app)
|
|
response = client.post(
|
|
"/api/internal/gateway/finalize-sync",
|
|
json={
|
|
"trace_id": "trace-openai-chat-finalize",
|
|
"report_kind": "openai_chat_sync_finalize",
|
|
"report_context": {
|
|
"user_id": "user-123",
|
|
"api_key_id": "key-123",
|
|
"client_api_format": "openai:chat",
|
|
},
|
|
"status_code": 200,
|
|
"headers": {"content-type": "application/json"},
|
|
"body_json": {"id": "upstream-123"},
|
|
"telemetry": {"elapsed_ms": 12},
|
|
},
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
assert response.headers[CONTROL_EXECUTED_HEADER] == "true"
|
|
assert response.json() == {
|
|
"id": "chatcmpl-local-123",
|
|
"object": "chat.completion",
|
|
"choices": [],
|
|
}
|
|
finalize_mock.assert_awaited_once()
|
|
|
|
|
|
def test_finalize_sync_route_finalizes_openai_cli_response(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
app = FastAPI()
|
|
app.include_router(router)
|
|
app.dependency_overrides[get_db] = lambda: object()
|
|
monkeypatch.setattr("src.api.internal.gateway.ensure_loopback", lambda request: None)
|
|
finalize_mock = AsyncMock(
|
|
return_value=JSONResponse(
|
|
content={
|
|
"id": "resp_local_123",
|
|
"object": "response",
|
|
"output": [],
|
|
}
|
|
)
|
|
)
|
|
monkeypatch.setattr(
|
|
"src.api.internal.gateway._finalize_gateway_cli_sync",
|
|
finalize_mock,
|
|
)
|
|
|
|
client = _make_legacy_test_client(app)
|
|
response = client.post(
|
|
"/api/internal/gateway/finalize-sync",
|
|
json={
|
|
"trace_id": "trace-openai-cli-finalize",
|
|
"report_kind": "openai_cli_sync_finalize",
|
|
"report_context": {
|
|
"user_id": "user-123",
|
|
"api_key_id": "key-123",
|
|
"client_api_format": "openai:cli",
|
|
},
|
|
"status_code": 200,
|
|
"headers": {"content-type": "application/json"},
|
|
"body_json": {"id": "upstream-resp-123"},
|
|
"telemetry": {"elapsed_ms": 23},
|
|
},
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
assert response.headers[CONTROL_EXECUTED_HEADER] == "true"
|
|
assert response.json() == {
|
|
"id": "resp_local_123",
|
|
"object": "response",
|
|
"output": [],
|
|
}
|
|
finalize_mock.assert_awaited_once()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_finalize_gateway_chat_sync_uses_dispatch_helper_for_success(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
from src.api.internal import gateway as gateway_module
|
|
|
|
class FakeQuery:
|
|
def __init__(self, obj: object) -> None:
|
|
self._obj = obj
|
|
|
|
def filter(self, *args: object, **kwargs: object) -> "FakeQuery":
|
|
return self
|
|
|
|
def first(self) -> object:
|
|
return self._obj
|
|
|
|
class FakeDB:
|
|
def __init__(self, mapping: dict[str, object]) -> None:
|
|
self._mapping = mapping
|
|
|
|
def query(self, model: object) -> FakeQuery:
|
|
return FakeQuery(self._mapping[getattr(model, "__name__", "")])
|
|
|
|
fake_handler = SimpleNamespace(
|
|
_prepare_provider_request=AsyncMock(
|
|
return_value=SimpleNamespace(
|
|
needs_conversion=False,
|
|
envelope=None,
|
|
upstream_is_stream=False,
|
|
)
|
|
),
|
|
_normalize_response=lambda body: body,
|
|
_extract_usage=lambda body: {"input_tokens": 1, "output_tokens": 2},
|
|
_extract_response_metadata=lambda body: {"finish_reason": "stop"},
|
|
telemetry=SimpleNamespace(
|
|
record_success=AsyncMock(return_value=None),
|
|
record_failure=AsyncMock(return_value=None),
|
|
),
|
|
)
|
|
dispatch_mock = AsyncMock(return_value=None)
|
|
fake_writer = SimpleNamespace(
|
|
supports_background_submission=lambda: True,
|
|
record_success=AsyncMock(return_value=None),
|
|
)
|
|
|
|
monkeypatch.setattr(
|
|
"src.api.handlers.openai.OpenAIChatAdapter._create_handler",
|
|
lambda self, **kwargs: fake_handler,
|
|
)
|
|
monkeypatch.setattr(gateway_module, "_dispatch_gateway_sync_telemetry", dispatch_mock)
|
|
monkeypatch.setattr(
|
|
gateway_module,
|
|
"_build_gateway_sync_telemetry_writer",
|
|
lambda **kwargs: fake_writer,
|
|
)
|
|
|
|
db = FakeDB(
|
|
{
|
|
"User": SimpleNamespace(id="user-chat-finalize-dispatch-123"),
|
|
"ApiKey": SimpleNamespace(id="api-key-chat-finalize-dispatch-123"),
|
|
"Provider": SimpleNamespace(
|
|
id="provider-chat-finalize-dispatch-123",
|
|
name="openai",
|
|
provider_type="openai",
|
|
),
|
|
"ProviderEndpoint": SimpleNamespace(id="endpoint-chat-finalize-dispatch-123"),
|
|
"ProviderAPIKey": SimpleNamespace(id="key-chat-finalize-dispatch-123"),
|
|
}
|
|
)
|
|
|
|
payload = GatewaySyncReportRequest(
|
|
trace_id="trace-chat-finalize-dispatch-123",
|
|
report_kind="openai_chat_sync_finalize",
|
|
report_context={
|
|
"user_id": "user-chat-finalize-dispatch-123",
|
|
"api_key_id": "api-key-chat-finalize-dispatch-123",
|
|
"provider_id": "provider-chat-finalize-dispatch-123",
|
|
"endpoint_id": "endpoint-chat-finalize-dispatch-123",
|
|
"key_id": "key-chat-finalize-dispatch-123",
|
|
"request_id": "req-chat-finalize-dispatch-123",
|
|
"model": "gpt-5",
|
|
"provider_name": "openai",
|
|
"provider_api_format": "openai:chat",
|
|
"client_api_format": "openai:chat",
|
|
"mapped_model": "gpt-5",
|
|
"original_headers": {"content-type": "application/json"},
|
|
"original_request_body": {"model": "gpt-5", "messages": []},
|
|
"provider_request_headers": {"content-type": "application/json"},
|
|
"provider_request_body": {"model": "gpt-5", "messages": []},
|
|
},
|
|
status_code=200,
|
|
headers={"content-type": "application/json"},
|
|
body_json={
|
|
"id": "chatcmpl-finalize-dispatch-123",
|
|
"object": "chat.completion",
|
|
"choices": [],
|
|
"usage": {
|
|
"prompt_tokens": 1,
|
|
"completion_tokens": 2,
|
|
"total_tokens": 3,
|
|
},
|
|
},
|
|
telemetry={"elapsed_ms": 11},
|
|
)
|
|
|
|
response = await gateway_module._finalize_gateway_chat_sync(payload, db=db)
|
|
|
|
assert response.status_code == 200
|
|
dispatch_mock.assert_awaited_once()
|
|
assert dispatch_mock.await_args.kwargs["telemetry_writer"] is fake_writer
|
|
assert dispatch_mock.await_args.kwargs["operation"] == "record_success"
|
|
fake_writer.record_success.assert_not_called()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_finalize_gateway_chat_sync_aggregates_upstream_stream_body_base64(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
from src.api.internal import gateway as gateway_module
|
|
|
|
class FakeQuery:
|
|
def __init__(self, obj: object) -> None:
|
|
self._obj = obj
|
|
|
|
def filter(self, *args: object, **kwargs: object) -> "FakeQuery":
|
|
return self
|
|
|
|
def first(self) -> object:
|
|
return self._obj
|
|
|
|
class FakeDB:
|
|
def __init__(self, mapping: dict[str, object]) -> None:
|
|
self._mapping = mapping
|
|
|
|
def query(self, model: object) -> FakeQuery:
|
|
return FakeQuery(self._mapping[getattr(model, "__name__", "")])
|
|
|
|
fake_handler = SimpleNamespace(
|
|
_prepare_provider_request=AsyncMock(
|
|
return_value=SimpleNamespace(
|
|
needs_conversion=False,
|
|
envelope=None,
|
|
upstream_is_stream=True,
|
|
provider_api_format="openai:chat",
|
|
mapped_model="gpt-5",
|
|
)
|
|
),
|
|
_normalize_response=lambda body: body,
|
|
_extract_usage=lambda body: {"input_tokens": 1, "output_tokens": 2},
|
|
telemetry=SimpleNamespace(
|
|
record_success=AsyncMock(return_value=None),
|
|
record_failure=AsyncMock(return_value=None),
|
|
),
|
|
)
|
|
seen_body: dict[str, bytes] = {}
|
|
|
|
class FakeChatSyncExecutor:
|
|
def __init__(self, handler: object) -> None:
|
|
self._ctx = SimpleNamespace(provider_response_json=None)
|
|
|
|
async def _finalize_rust_stream_sync_result(
|
|
self,
|
|
*,
|
|
prepared_plan: object,
|
|
provider: object,
|
|
model: str,
|
|
response_body_bytes: bytes,
|
|
) -> dict[str, object]:
|
|
seen_body["body"] = response_body_bytes
|
|
return {
|
|
"id": "chatcmpl-direct-stream-123",
|
|
"object": "chat.completion",
|
|
"choices": [],
|
|
"usage": {
|
|
"prompt_tokens": 1,
|
|
"completion_tokens": 2,
|
|
"total_tokens": 3,
|
|
},
|
|
}
|
|
|
|
monkeypatch.setattr(
|
|
"src.api.handlers.base.chat_sync_executor.ChatSyncExecutor",
|
|
FakeChatSyncExecutor,
|
|
)
|
|
monkeypatch.setattr(
|
|
"src.api.handlers.openai.OpenAIChatAdapter._create_handler",
|
|
lambda self, **kwargs: fake_handler,
|
|
)
|
|
|
|
db = FakeDB(
|
|
{
|
|
"User": SimpleNamespace(id="user-chat-stream-finalize-123"),
|
|
"ApiKey": SimpleNamespace(id="api-key-chat-stream-finalize-123"),
|
|
"Provider": SimpleNamespace(
|
|
id="provider-chat-stream-finalize-123",
|
|
name="openai",
|
|
provider_type="openai",
|
|
),
|
|
"ProviderEndpoint": SimpleNamespace(id="endpoint-chat-stream-finalize-123"),
|
|
"ProviderAPIKey": SimpleNamespace(id="key-chat-stream-finalize-123"),
|
|
}
|
|
)
|
|
|
|
payload = GatewaySyncReportRequest(
|
|
trace_id="trace-chat-stream-sync-finalize-123",
|
|
report_kind="openai_chat_sync_finalize",
|
|
report_context={
|
|
"user_id": "user-chat-stream-finalize-123",
|
|
"api_key_id": "api-key-chat-stream-finalize-123",
|
|
"provider_id": "provider-chat-stream-finalize-123",
|
|
"endpoint_id": "endpoint-chat-stream-finalize-123",
|
|
"key_id": "key-chat-stream-finalize-123",
|
|
"request_id": "req-chat-stream-sync-finalize-123",
|
|
"model": "gpt-5",
|
|
"provider_name": "openai",
|
|
"provider_api_format": "openai:chat",
|
|
"client_api_format": "openai:chat",
|
|
"mapped_model": "gpt-5",
|
|
"original_headers": {"content-type": "application/json"},
|
|
"original_request_body": {"model": "gpt-5", "messages": []},
|
|
"provider_request_headers": {"content-type": "application/json"},
|
|
"provider_request_body": {"model": "gpt-5", "messages": []},
|
|
},
|
|
status_code=200,
|
|
headers={"content-type": "text/event-stream"},
|
|
body_base64=base64.b64encode(b"data: stub\n\n").decode("ascii"),
|
|
telemetry={"elapsed_ms": 12},
|
|
)
|
|
|
|
response = await gateway_module._finalize_gateway_chat_sync(payload, db=db)
|
|
|
|
assert response.status_code == 200
|
|
assert json.loads(response.body) == {
|
|
"id": "chatcmpl-direct-stream-123",
|
|
"object": "chat.completion",
|
|
"choices": [],
|
|
"usage": {
|
|
"prompt_tokens": 1,
|
|
"completion_tokens": 2,
|
|
"total_tokens": 3,
|
|
},
|
|
}
|
|
assert seen_body["body"] == b"data: stub\n\n"
|
|
fake_handler.telemetry.record_success.assert_awaited_once()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_finalize_gateway_cli_sync_aggregates_upstream_stream_body_base64(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
from src.api.internal import gateway as gateway_module
|
|
from src.api.handlers.openai_cli import OpenAICliAdapter
|
|
|
|
class FakeQuery:
|
|
def __init__(self, obj: object) -> None:
|
|
self._obj = obj
|
|
|
|
def filter(self, *args: object, **kwargs: object) -> "FakeQuery":
|
|
return self
|
|
|
|
def first(self) -> object:
|
|
return self._obj
|
|
|
|
class FakeDB:
|
|
def __init__(self, mapping: dict[str, object]) -> None:
|
|
self._mapping = mapping
|
|
|
|
def query(self, model: object) -> FakeQuery:
|
|
return FakeQuery(self._mapping[getattr(model, "__name__", "")])
|
|
|
|
aggregate_mock = AsyncMock(
|
|
return_value={
|
|
"id": "resp-direct-stream-123",
|
|
"object": "response",
|
|
"output": [],
|
|
"usage": {"input_tokens": 1, "output_tokens": 2},
|
|
}
|
|
)
|
|
|
|
class FakeCliHandler:
|
|
def __init__(self, **kwargs: object) -> None:
|
|
self.parser = SimpleNamespace(
|
|
extract_usage_from_response=lambda body: {
|
|
"input_tokens": 1,
|
|
"output_tokens": 2,
|
|
}
|
|
)
|
|
self.telemetry = SimpleNamespace(
|
|
record_success=AsyncMock(return_value=None),
|
|
record_failure=AsyncMock(return_value=None),
|
|
)
|
|
|
|
async def _build_upstream_request(self, **kwargs: object) -> SimpleNamespace:
|
|
return SimpleNamespace(
|
|
upstream_is_stream=True,
|
|
envelope=None,
|
|
)
|
|
|
|
async def _aggregate_upstream_stream_sync_response(self, **kwargs: object) -> dict[str, object]:
|
|
return await aggregate_mock(**kwargs)
|
|
|
|
def _extract_response_metadata(self, body: dict[str, object]) -> dict[str, object]:
|
|
return {}
|
|
|
|
monkeypatch.setattr(
|
|
type(OpenAICliAdapter()),
|
|
"HANDLER_CLASS",
|
|
property(lambda self: FakeCliHandler),
|
|
)
|
|
|
|
db = FakeDB(
|
|
{
|
|
"User": SimpleNamespace(id="user-cli-stream-finalize-123"),
|
|
"ApiKey": SimpleNamespace(id="api-key-cli-stream-finalize-123"),
|
|
"Provider": SimpleNamespace(
|
|
id="provider-cli-stream-finalize-123",
|
|
name="openai",
|
|
provider_type="openai",
|
|
),
|
|
"ProviderEndpoint": SimpleNamespace(id="endpoint-cli-stream-finalize-123"),
|
|
"ProviderAPIKey": SimpleNamespace(id="key-cli-stream-finalize-123"),
|
|
}
|
|
)
|
|
|
|
payload = GatewaySyncReportRequest(
|
|
trace_id="trace-cli-stream-sync-finalize-123",
|
|
report_kind="openai_cli_sync_finalize",
|
|
report_context={
|
|
"user_id": "user-cli-stream-finalize-123",
|
|
"api_key_id": "api-key-cli-stream-finalize-123",
|
|
"provider_id": "provider-cli-stream-finalize-123",
|
|
"endpoint_id": "endpoint-cli-stream-finalize-123",
|
|
"key_id": "key-cli-stream-finalize-123",
|
|
"request_id": "req-cli-stream-sync-finalize-123",
|
|
"model": "gpt-5",
|
|
"provider_name": "openai",
|
|
"provider_api_format": "openai:cli",
|
|
"client_api_format": "openai:cli",
|
|
"mapped_model": "gpt-5",
|
|
"original_headers": {"content-type": "application/json"},
|
|
"original_request_body": {"model": "gpt-5", "input": "hello"},
|
|
"provider_request_headers": {"content-type": "application/json"},
|
|
"provider_request_body": {"model": "gpt-5", "input": "hello"},
|
|
},
|
|
status_code=200,
|
|
headers={"content-type": "text/event-stream"},
|
|
body_base64=base64.b64encode(b"data: cli\n\n").decode("ascii"),
|
|
telemetry={"elapsed_ms": 9},
|
|
)
|
|
|
|
response = await gateway_module._finalize_gateway_cli_sync(payload, db=db)
|
|
|
|
assert response.status_code == 200
|
|
assert json.loads(response.body) == {
|
|
"id": "resp-direct-stream-123",
|
|
"object": "response",
|
|
"output": [],
|
|
"usage": {"input_tokens": 1, "output_tokens": 2},
|
|
}
|
|
aggregate_mock.assert_awaited_once()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_finalize_gateway_chat_sync_fast_path_returns_before_db_lookup(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
from src.api.internal import gateway as gateway_module
|
|
|
|
aggregate_mock = AsyncMock(
|
|
return_value={
|
|
"id": "chatcmpl-fast-finalize-123",
|
|
"object": "chat.completion",
|
|
"choices": [],
|
|
"usage": {"prompt_tokens": 1, "completion_tokens": 2, "total_tokens": 3},
|
|
}
|
|
)
|
|
monkeypatch.setattr(
|
|
gateway_module,
|
|
"_aggregate_gateway_sync_response_bytes",
|
|
aggregate_mock,
|
|
)
|
|
|
|
class FailingDB:
|
|
def query(self, model: object) -> object:
|
|
raise AssertionError(f"db query should not happen for {getattr(model, '__name__', model)}")
|
|
|
|
payload = GatewaySyncReportRequest(
|
|
trace_id="trace-chat-fast-finalize-123",
|
|
report_kind="openai_chat_sync_finalize",
|
|
report_context={
|
|
"user_id": "user-chat-fast-finalize-123",
|
|
"api_key_id": "api-key-chat-fast-finalize-123",
|
|
"provider_id": "provider-chat-fast-finalize-123",
|
|
"endpoint_id": "endpoint-chat-fast-finalize-123",
|
|
"key_id": "key-chat-fast-finalize-123",
|
|
"request_id": "req-chat-fast-finalize-123",
|
|
"model": "gpt-5",
|
|
"provider_name": "openai",
|
|
"provider_api_format": "openai:chat",
|
|
"client_api_format": "openai:chat",
|
|
"mapped_model": "gpt-5",
|
|
"original_headers": {"content-type": "application/json"},
|
|
"original_request_body": {"model": "gpt-5", "messages": []},
|
|
"provider_request_headers": {"content-type": "application/json"},
|
|
"provider_request_body": {"model": "gpt-5", "messages": []},
|
|
"has_envelope": False,
|
|
"needs_conversion": False,
|
|
},
|
|
status_code=200,
|
|
headers={"content-type": "text/event-stream"},
|
|
body_base64=base64.b64encode(b"data: stub\n\n").decode("ascii"),
|
|
telemetry={"elapsed_ms": 7},
|
|
)
|
|
|
|
background_tasks = BackgroundTasks()
|
|
response = await gateway_module._finalize_gateway_chat_sync(
|
|
payload,
|
|
db=FailingDB(),
|
|
background_tasks=background_tasks,
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
assert json.loads(response.body) == {
|
|
"id": "chatcmpl-fast-finalize-123",
|
|
"object": "chat.completion",
|
|
"choices": [],
|
|
"usage": {"prompt_tokens": 1, "completion_tokens": 2, "total_tokens": 3},
|
|
}
|
|
aggregate_mock.assert_awaited_once()
|
|
assert len(background_tasks.tasks) == 1
|
|
assert background_tasks.tasks[0].func is gateway_module._run_gateway_chat_sync_finalize_background
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_finalize_gateway_cli_sync_fast_path_returns_before_db_lookup() -> None:
|
|
from src.api.internal import gateway as gateway_module
|
|
|
|
class FailingDB:
|
|
def query(self, model: object) -> object:
|
|
raise AssertionError(f"db query should not happen for {getattr(model, '__name__', model)}")
|
|
|
|
payload = GatewaySyncReportRequest(
|
|
trace_id="trace-cli-fast-finalize-123",
|
|
report_kind="openai_cli_sync_finalize",
|
|
report_context={
|
|
"user_id": "user-cli-fast-finalize-123",
|
|
"api_key_id": "api-key-cli-fast-finalize-123",
|
|
"provider_id": "provider-cli-fast-finalize-123",
|
|
"endpoint_id": "endpoint-cli-fast-finalize-123",
|
|
"key_id": "key-cli-fast-finalize-123",
|
|
"request_id": "req-cli-fast-finalize-123",
|
|
"model": "gpt-5",
|
|
"provider_name": "openai",
|
|
"provider_api_format": "openai:cli",
|
|
"client_api_format": "openai:cli",
|
|
"mapped_model": "gpt-5",
|
|
"original_headers": {"content-type": "application/json"},
|
|
"original_request_body": {"model": "gpt-5", "input": "hello"},
|
|
"provider_request_headers": {"content-type": "application/json"},
|
|
"provider_request_body": {"model": "gpt-5", "input": "hello"},
|
|
"has_envelope": False,
|
|
"needs_conversion": False,
|
|
},
|
|
status_code=200,
|
|
headers={"content-type": "application/json"},
|
|
body_json={
|
|
"id": "resp-fast-finalize-123",
|
|
"object": "response",
|
|
"output": [],
|
|
"usage": {"input_tokens": 1, "output_tokens": 2},
|
|
},
|
|
telemetry={"elapsed_ms": 5},
|
|
)
|
|
|
|
background_tasks = BackgroundTasks()
|
|
response = await gateway_module._finalize_gateway_cli_sync(
|
|
payload,
|
|
db=FailingDB(),
|
|
background_tasks=background_tasks,
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
assert json.loads(response.body) == {
|
|
"id": "resp-fast-finalize-123",
|
|
"object": "response",
|
|
"output": [],
|
|
"usage": {"input_tokens": 1, "output_tokens": 2},
|
|
}
|
|
assert len(background_tasks.tasks) == 1
|
|
assert background_tasks.tasks[0].func is gateway_module._run_gateway_cli_sync_finalize_background
|
|
|
|
|
|
def test_finalize_sync_route_uses_chat_fast_path_without_db_session(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
app = FastAPI()
|
|
app.include_router(router)
|
|
monkeypatch.setattr("src.api.internal.gateway.ensure_loopback", lambda request: None)
|
|
fast_mock = AsyncMock(
|
|
return_value=JSONResponse(
|
|
content={"id": "chat-fast-123", "object": "chat.completion", "choices": []}
|
|
)
|
|
)
|
|
background_mock = AsyncMock(return_value=None)
|
|
monkeypatch.setattr(
|
|
"src.api.internal.gateway._maybe_build_gateway_core_sync_fast_success_response",
|
|
fast_mock,
|
|
)
|
|
monkeypatch.setattr(
|
|
"src.api.internal.gateway._run_gateway_chat_sync_finalize_background_with_session",
|
|
background_mock,
|
|
)
|
|
monkeypatch.setattr(
|
|
"src.api.internal.gateway.create_session",
|
|
lambda: (_ for _ in ()).throw(AssertionError("create_session should not be called")),
|
|
)
|
|
|
|
client = _make_legacy_test_client(app)
|
|
response = client.post(
|
|
"/api/internal/gateway/finalize-sync",
|
|
json={
|
|
"trace_id": "trace-chat-fast-route-123",
|
|
"report_kind": "openai_chat_sync_finalize",
|
|
"report_context": {
|
|
"provider_api_format": "openai:chat",
|
|
"client_api_format": "openai:chat",
|
|
"has_envelope": False,
|
|
},
|
|
"status_code": 200,
|
|
"headers": {"content-type": "application/json"},
|
|
"body_json": {"id": "chat-fast-123", "object": "chat.completion", "choices": []},
|
|
},
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
assert response.headers[CONTROL_EXECUTED_HEADER] == "true"
|
|
assert response.json()["id"] == "chat-fast-123"
|
|
fast_mock.assert_awaited_once()
|
|
background_mock.assert_awaited_once()
|
|
|
|
|
|
def test_finalize_sync_route_uses_cli_fast_path_without_db_session(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
app = FastAPI()
|
|
app.include_router(router)
|
|
monkeypatch.setattr("src.api.internal.gateway.ensure_loopback", lambda request: None)
|
|
fast_mock = AsyncMock(
|
|
return_value=JSONResponse(
|
|
content={"id": "resp-fast-123", "object": "response", "output": []}
|
|
)
|
|
)
|
|
background_mock = AsyncMock(return_value=None)
|
|
monkeypatch.setattr(
|
|
"src.api.internal.gateway._maybe_build_gateway_core_sync_fast_success_response",
|
|
fast_mock,
|
|
)
|
|
monkeypatch.setattr(
|
|
"src.api.internal.gateway._run_gateway_cli_sync_finalize_background_with_session",
|
|
background_mock,
|
|
)
|
|
monkeypatch.setattr(
|
|
"src.api.internal.gateway.create_session",
|
|
lambda: (_ for _ in ()).throw(AssertionError("create_session should not be called")),
|
|
)
|
|
|
|
client = _make_legacy_test_client(app)
|
|
response = client.post(
|
|
"/api/internal/gateway/finalize-sync",
|
|
json={
|
|
"trace_id": "trace-cli-fast-route-123",
|
|
"report_kind": "openai_cli_sync_finalize",
|
|
"report_context": {
|
|
"provider_api_format": "openai:cli",
|
|
"client_api_format": "openai:cli",
|
|
"has_envelope": False,
|
|
},
|
|
"status_code": 200,
|
|
"headers": {"content-type": "application/json"},
|
|
"body_json": {"id": "resp-fast-123", "object": "response", "output": []},
|
|
},
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
assert response.headers[CONTROL_EXECUTED_HEADER] == "true"
|
|
assert response.json()["id"] == "resp-fast-123"
|
|
fast_mock.assert_awaited_once()
|
|
background_mock.assert_awaited_once()
|
|
|
|
|
|
def test_finalize_sync_route_finalizes_openai_video_remix_response(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
app = FastAPI()
|
|
app.include_router(router)
|
|
app.dependency_overrides[get_db] = lambda: object()
|
|
monkeypatch.setattr("src.api.internal.gateway.ensure_loopback", lambda request: None)
|
|
finalize_mock = AsyncMock(
|
|
return_value=JSONResponse(
|
|
content={
|
|
"id": "vid_local_remix_123",
|
|
"object": "video",
|
|
"status": "submitted",
|
|
}
|
|
)
|
|
)
|
|
monkeypatch.setattr(
|
|
"src.api.internal.gateway._finalize_gateway_openai_video_remix_sync",
|
|
finalize_mock,
|
|
)
|
|
|
|
client = _make_legacy_test_client(app)
|
|
response = client.post(
|
|
"/api/internal/gateway/finalize-sync",
|
|
json={
|
|
"trace_id": "trace-openai-video-remix-finalize",
|
|
"report_kind": "openai_video_remix_sync_finalize",
|
|
"report_context": {
|
|
"user_id": "user-123",
|
|
"api_key_id": "key-123",
|
|
"task_id": "task-123",
|
|
},
|
|
"status_code": 200,
|
|
"headers": {"content-type": "application/json"},
|
|
"body_json": {"id": "ext-remix-task-123", "status": "submitted"},
|
|
"telemetry": {"elapsed_ms": 55},
|
|
},
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
assert response.headers[CONTROL_EXECUTED_HEADER] == "true"
|
|
assert response.json() == {
|
|
"id": "vid_local_remix_123",
|
|
"object": "video",
|
|
"status": "submitted",
|
|
}
|
|
finalize_mock.assert_awaited_once()
|
|
|
|
|
|
def test_finalize_sync_route_finalizes_gemini_video_create_response(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
app = FastAPI()
|
|
app.include_router(router)
|
|
app.dependency_overrides[get_db] = lambda: object()
|
|
monkeypatch.setattr("src.api.internal.gateway.ensure_loopback", lambda request: None)
|
|
finalize_mock = AsyncMock(
|
|
return_value=JSONResponse(
|
|
content={
|
|
"name": "models/veo-3/operations/aev_123",
|
|
"done": False,
|
|
"metadata": {},
|
|
}
|
|
)
|
|
)
|
|
monkeypatch.setattr(
|
|
"src.api.internal.gateway._finalize_gateway_gemini_video_create_sync",
|
|
finalize_mock,
|
|
)
|
|
|
|
client = _make_legacy_test_client(app)
|
|
response = client.post(
|
|
"/api/internal/gateway/finalize-sync",
|
|
json={
|
|
"trace_id": "trace-gemini-video-create-finalize",
|
|
"report_kind": "gemini_video_create_sync_finalize",
|
|
"report_context": {
|
|
"user_id": "user-123",
|
|
"api_key_id": "key-123",
|
|
"model": "veo-3",
|
|
},
|
|
"status_code": 200,
|
|
"headers": {"content-type": "application/json"},
|
|
"body_json": {"name": "operations/ext-video-123"},
|
|
"telemetry": {"elapsed_ms": 48},
|
|
},
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
assert response.headers[CONTROL_EXECUTED_HEADER] == "true"
|
|
assert response.json() == {
|
|
"name": "models/veo-3/operations/aev_123",
|
|
"done": False,
|
|
"metadata": {},
|
|
}
|
|
finalize_mock.assert_awaited_once()
|
|
|
|
|
|
def test_finalize_sync_route_finalizes_openai_video_delete_response(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
app = FastAPI()
|
|
app.include_router(router)
|
|
app.dependency_overrides[get_db] = lambda: object()
|
|
monkeypatch.setattr("src.api.internal.gateway.ensure_loopback", lambda request: None)
|
|
finalize_mock = AsyncMock(
|
|
return_value=JSONResponse(
|
|
content={
|
|
"id": "task-123",
|
|
"object": "video",
|
|
"deleted": True,
|
|
}
|
|
)
|
|
)
|
|
monkeypatch.setattr(
|
|
"src.api.internal.gateway._finalize_gateway_openai_video_delete_sync",
|
|
finalize_mock,
|
|
)
|
|
|
|
client = _make_legacy_test_client(app)
|
|
response = client.post(
|
|
"/api/internal/gateway/finalize-sync",
|
|
json={
|
|
"trace_id": "trace-openai-video-delete-finalize",
|
|
"report_kind": "openai_video_delete_sync_finalize",
|
|
"report_context": {
|
|
"user_id": "user-123",
|
|
"api_key_id": "key-123",
|
|
"task_id": "task-123",
|
|
},
|
|
"status_code": 404,
|
|
"headers": {"content-type": "application/json"},
|
|
},
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
assert response.headers[CONTROL_EXECUTED_HEADER] == "true"
|
|
assert response.json() == {
|
|
"id": "task-123",
|
|
"object": "video",
|
|
"deleted": True,
|
|
}
|
|
finalize_mock.assert_awaited_once()
|
|
|
|
|
|
def test_finalize_sync_route_finalizes_openai_video_cancel_response(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
app = FastAPI()
|
|
app.include_router(router)
|
|
app.dependency_overrides[get_db] = lambda: object()
|
|
monkeypatch.setattr("src.api.internal.gateway.ensure_loopback", lambda request: None)
|
|
finalize_mock = AsyncMock(return_value=JSONResponse(content={}))
|
|
monkeypatch.setattr(
|
|
"src.api.internal.gateway._finalize_gateway_openai_video_cancel_sync",
|
|
finalize_mock,
|
|
)
|
|
|
|
client = _make_legacy_test_client(app)
|
|
response = client.post(
|
|
"/api/internal/gateway/finalize-sync",
|
|
json={
|
|
"trace_id": "trace-openai-video-cancel-finalize",
|
|
"report_kind": "openai_video_cancel_sync_finalize",
|
|
"report_context": {
|
|
"user_id": "user-123",
|
|
"api_key_id": "key-123",
|
|
"task_id": "task-123",
|
|
},
|
|
"status_code": 204,
|
|
"headers": {},
|
|
},
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
assert response.headers[CONTROL_EXECUTED_HEADER] == "true"
|
|
assert response.json() == {}
|
|
finalize_mock.assert_awaited_once()
|
|
|
|
|
|
def test_finalize_sync_route_finalizes_gemini_video_cancel_response(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
app = FastAPI()
|
|
app.include_router(router)
|
|
app.dependency_overrides[get_db] = lambda: object()
|
|
monkeypatch.setattr("src.api.internal.gateway.ensure_loopback", lambda request: None)
|
|
finalize_mock = AsyncMock(return_value=JSONResponse(content={}))
|
|
monkeypatch.setattr(
|
|
"src.api.internal.gateway._finalize_gateway_gemini_video_cancel_sync",
|
|
finalize_mock,
|
|
)
|
|
|
|
client = _make_legacy_test_client(app)
|
|
response = client.post(
|
|
"/api/internal/gateway/finalize-sync",
|
|
json={
|
|
"trace_id": "trace-gemini-video-cancel-finalize",
|
|
"report_kind": "gemini_video_cancel_sync_finalize",
|
|
"report_context": {
|
|
"user_id": "user-123",
|
|
"api_key_id": "key-123",
|
|
"task_id": "models/veo-3/operations/ext-123",
|
|
},
|
|
"status_code": 200,
|
|
"headers": {"content-type": "application/json"},
|
|
"body_json": {},
|
|
},
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
assert response.headers[CONTROL_EXECUTED_HEADER] == "true"
|
|
assert response.json() == {}
|
|
finalize_mock.assert_awaited_once()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_finalize_gateway_openai_video_create_sync_reuses_rust_owned_task(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
from src.api.internal import gateway as gateway_module
|
|
|
|
background_mock = AsyncMock(return_value=None)
|
|
begin_pending_usage = MagicMock()
|
|
|
|
class FakeQuery:
|
|
def __init__(self, value: Any) -> None:
|
|
self._value = value
|
|
|
|
def filter(self, *args: Any, **kwargs: Any) -> "FakeQuery":
|
|
return self
|
|
|
|
def first(self) -> Any:
|
|
return self._value
|
|
|
|
class FakeDB:
|
|
def __init__(self) -> None:
|
|
self._mapping = {
|
|
"User": SimpleNamespace(id="user-123"),
|
|
"ApiKey": SimpleNamespace(id="key-123"),
|
|
"VideoTask": SimpleNamespace(id="task-local-123"),
|
|
}
|
|
|
|
def query(self, model: Any) -> FakeQuery:
|
|
return FakeQuery(self._mapping.get(getattr(model, "__name__", "")))
|
|
|
|
def add(self, obj: Any) -> None:
|
|
raise AssertionError("legacy task creation should be skipped")
|
|
|
|
class FakeOpenAIVideoHandler:
|
|
FORMAT_ID = "openai:video"
|
|
|
|
def __init__(self, **kwargs: Any) -> None:
|
|
self._normalizer = SimpleNamespace(
|
|
video_task_from_internal=lambda task: {
|
|
"id": task["id"],
|
|
"object": "video",
|
|
"status": "submitted",
|
|
}
|
|
)
|
|
|
|
def _task_to_internal(self, task: Any) -> dict[str, Any]:
|
|
return {"id": task.id}
|
|
|
|
monkeypatch.setattr(
|
|
"src.api.handlers.openai.video_handler.OpenAIVideoHandler",
|
|
FakeOpenAIVideoHandler,
|
|
)
|
|
monkeypatch.setattr(
|
|
"src.api.internal.gateway._run_gateway_video_finalize_submitted_background",
|
|
background_mock,
|
|
)
|
|
monkeypatch.setattr(
|
|
"src.services.usage.service.UsageService.begin_pending_usage",
|
|
begin_pending_usage,
|
|
)
|
|
|
|
payload = GatewaySyncReportRequest(
|
|
trace_id="trace-openai-video-rust-owner-123",
|
|
report_kind="openai_video_create_sync_success",
|
|
report_context={
|
|
"user_id": "user-123",
|
|
"api_key_id": "key-123",
|
|
"provider_id": "provider-123",
|
|
"endpoint_id": "endpoint-123",
|
|
"key_id": "provider-key-123",
|
|
"request_id": "req-openai-video-rust-owner-123",
|
|
"local_task_id": "task-local-123",
|
|
"provider_name": "openai",
|
|
"provider_api_format": "openai:video",
|
|
"rust_video_task_persisted": True,
|
|
},
|
|
status_code=200,
|
|
headers={"content-type": "application/json"},
|
|
body_json={"id": "ext-video-123", "status": "submitted"},
|
|
telemetry={"elapsed_ms": 42},
|
|
)
|
|
|
|
response = await gateway_module._finalize_gateway_openai_video_create_sync(
|
|
payload,
|
|
db=FakeDB(),
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
assert json.loads(response.body) == {
|
|
"id": "task-local-123",
|
|
"object": "video",
|
|
"status": "submitted",
|
|
}
|
|
begin_pending_usage.assert_not_called()
|
|
background_mock.assert_awaited_once()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_finalize_gateway_openai_video_remix_sync_reuses_rust_owned_task(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
from src.api.internal import gateway as gateway_module
|
|
|
|
class FakeQuery:
|
|
def __init__(self, value: Any) -> None:
|
|
self._value = value
|
|
|
|
def filter(self, *args: Any, **kwargs: Any) -> "FakeQuery":
|
|
return self
|
|
|
|
def first(self) -> Any:
|
|
return self._value
|
|
|
|
class FakeDB:
|
|
def __init__(self) -> None:
|
|
self._mapping = {
|
|
"User": SimpleNamespace(id="user-123"),
|
|
"ApiKey": SimpleNamespace(id="key-123"),
|
|
"VideoTask": SimpleNamespace(id="task-local-remix-123"),
|
|
}
|
|
|
|
def query(self, model: Any) -> FakeQuery:
|
|
return FakeQuery(self._mapping.get(getattr(model, "__name__", "")))
|
|
|
|
def add(self, obj: Any) -> None:
|
|
raise AssertionError("legacy remix task creation should be skipped")
|
|
|
|
class FakeOpenAIVideoHandler:
|
|
def __init__(self, **kwargs: Any) -> None:
|
|
self._normalizer = SimpleNamespace(
|
|
video_task_from_internal=lambda task: {
|
|
"id": task["id"],
|
|
"object": "video",
|
|
"status": "submitted",
|
|
}
|
|
)
|
|
|
|
def _task_to_internal(self, task: Any) -> dict[str, Any]:
|
|
return {"id": task.id}
|
|
|
|
def _get_task(self, task_id: str) -> Any:
|
|
raise AssertionError("legacy remix source lookup should be skipped")
|
|
|
|
monkeypatch.setattr(
|
|
"src.api.handlers.openai.video_handler.OpenAIVideoHandler",
|
|
FakeOpenAIVideoHandler,
|
|
)
|
|
|
|
payload = GatewaySyncReportRequest(
|
|
trace_id="trace-openai-video-remix-rust-owner-123",
|
|
report_kind="openai_video_remix_sync_success",
|
|
report_context={
|
|
"user_id": "user-123",
|
|
"api_key_id": "key-123",
|
|
"task_id": "task-source-123",
|
|
"request_id": "req-openai-video-remix-rust-owner-123",
|
|
"local_task_id": "task-local-remix-123",
|
|
"rust_video_task_persisted": True,
|
|
},
|
|
status_code=200,
|
|
headers={"content-type": "application/json"},
|
|
body_json={"id": "ext-remix-task-123", "status": "submitted"},
|
|
)
|
|
|
|
response = await gateway_module._finalize_gateway_openai_video_remix_sync(
|
|
payload,
|
|
db=FakeDB(),
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
assert json.loads(response.body) == {
|
|
"id": "task-local-remix-123",
|
|
"object": "video",
|
|
"status": "submitted",
|
|
}
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_finalize_gateway_gemini_video_create_sync_reuses_rust_owned_task(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
from src.api.internal import gateway as gateway_module
|
|
|
|
background_mock = AsyncMock(return_value=None)
|
|
begin_pending_usage = MagicMock()
|
|
|
|
class FakeQuery:
|
|
def __init__(self, value: Any) -> None:
|
|
self._value = value
|
|
|
|
def filter(self, *args: Any, **kwargs: Any) -> "FakeQuery":
|
|
return self
|
|
|
|
def first(self) -> Any:
|
|
return self._value
|
|
|
|
class FakeDB:
|
|
def __init__(self) -> None:
|
|
self._mapping = {
|
|
"User": SimpleNamespace(id="user-123"),
|
|
"ApiKey": SimpleNamespace(id="key-123"),
|
|
"VideoTask": SimpleNamespace(short_id="short12345678"),
|
|
}
|
|
|
|
def query(self, model: Any) -> FakeQuery:
|
|
return FakeQuery(self._mapping.get(getattr(model, "__name__", "")))
|
|
|
|
def add(self, obj: Any) -> None:
|
|
raise AssertionError("legacy gemini task creation should be skipped")
|
|
|
|
class FakeGeminiVeoHandler:
|
|
FORMAT_ID = "gemini:video"
|
|
|
|
def __init__(self, **kwargs: Any) -> None:
|
|
self._normalizer = SimpleNamespace(
|
|
video_task_from_internal=lambda task: {
|
|
"name": f"models/veo-3/operations/{task['id']}",
|
|
"done": False,
|
|
"metadata": {},
|
|
}
|
|
)
|
|
|
|
def _task_to_internal(self, task: Any) -> dict[str, Any]:
|
|
return {"id": task.short_id}
|
|
|
|
monkeypatch.setattr(
|
|
"src.api.handlers.gemini.video_handler.GeminiVeoHandler",
|
|
FakeGeminiVeoHandler,
|
|
)
|
|
monkeypatch.setattr(
|
|
"src.api.internal.gateway._run_gateway_video_finalize_submitted_background",
|
|
background_mock,
|
|
)
|
|
monkeypatch.setattr(
|
|
"src.services.usage.service.UsageService.begin_pending_usage",
|
|
begin_pending_usage,
|
|
)
|
|
|
|
payload = GatewaySyncReportRequest(
|
|
trace_id="trace-gemini-video-rust-owner-123",
|
|
report_kind="gemini_video_create_sync_success",
|
|
report_context={
|
|
"user_id": "user-123",
|
|
"api_key_id": "key-123",
|
|
"provider_id": "provider-123",
|
|
"endpoint_id": "endpoint-123",
|
|
"key_id": "provider-key-123",
|
|
"request_id": "req-gemini-video-rust-owner-123",
|
|
"model": "veo-3",
|
|
"local_short_id": "short12345678",
|
|
"provider_name": "gemini",
|
|
"provider_api_format": "gemini:video",
|
|
"rust_video_task_persisted": True,
|
|
},
|
|
status_code=200,
|
|
headers={"content-type": "application/json"},
|
|
body_json={"name": "operations/ext-video-123"},
|
|
telemetry={"elapsed_ms": 48},
|
|
)
|
|
|
|
response = await gateway_module._finalize_gateway_gemini_video_create_sync(
|
|
payload,
|
|
db=FakeDB(),
|
|
)
|
|
|
|
assert response.status_code == 200
|
|
assert json.loads(response.body) == {
|
|
"name": "models/veo-3/operations/short12345678",
|
|
"done": False,
|
|
"metadata": {},
|
|
}
|
|
begin_pending_usage.assert_not_called()
|
|
background_mock.assert_awaited_once()
|