test: unit suite for the gateway invariants plus an integration slice
Covers routing, failover, the circuit breaker, structured-output retries, the cache-prefix validator, cost computation including cached-token pricing, the daily budget breaker, the quota protocol, the task state machine and log redaction. HTTP is mocked -- no unit test spends money. The integration test runs the real slice against PostgreSQL and Redis with a faked provider.
This commit is contained in:
Executable
+102
@@ -0,0 +1,102 @@
|
||||
"""End-to-end vertical slice against real PostgreSQL + Redis.
|
||||
|
||||
Run with ``make up && make migrate`` first, or let ``scripts/smoke.sh`` do it.
|
||||
Skipped automatically when the infrastructure is not reachable, so ``make test``
|
||||
stays fast and hermetic.
|
||||
|
||||
The LLM provider is faked here — ``scripts/smoke.sh`` is the one that proves the
|
||||
real DeepSeek path.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import uuid
|
||||
from decimal import Decimal
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
|
||||
pytestmark = pytest.mark.integration
|
||||
|
||||
|
||||
def _infra_available() -> bool:
|
||||
return bool(os.getenv("MEIDA_RUN_INTEGRATION"))
|
||||
|
||||
|
||||
pytestmark = [
|
||||
pytest.mark.integration,
|
||||
pytest.mark.skipif(
|
||||
not _infra_available(),
|
||||
reason="set MEIDA_RUN_INTEGRATION=1 with PostgreSQL and Redis running",
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def client() -> Any:
|
||||
import httpx
|
||||
|
||||
from apps.api.main import create_app
|
||||
from packages.llm.types import ProviderResponse, Usage
|
||||
from packages.shared.config import get_settings
|
||||
|
||||
app = create_app(get_settings())
|
||||
|
||||
class FakeProvider:
|
||||
name = "deepseek"
|
||||
default_model = "deepseek-v4-flash"
|
||||
|
||||
async def complete(self, request: Any) -> ProviderResponse:
|
||||
return ProviderResponse(
|
||||
text="建议白衬衫配西裤。",
|
||||
model="deepseek-v4-flash",
|
||||
usage=Usage(tokens_in=800, tokens_out=120, tokens_cached=600),
|
||||
)
|
||||
|
||||
async def aclose(self) -> None:
|
||||
return None
|
||||
|
||||
transport = httpx.ASGITransport(app=app)
|
||||
async with (
|
||||
httpx.AsyncClient(transport=transport, base_url="http://test") as http,
|
||||
app.router.lifespan_context(app),
|
||||
):
|
||||
app.state.llm_gateway._providers["deepseek"] = FakeProvider()
|
||||
yield http
|
||||
|
||||
|
||||
async def test_chat_slice_reserves_commits_and_records_cost(client: Any) -> None:
|
||||
token_resp = await client.post(
|
||||
"/api/v1/auth/dev-token", json={"device_key": f"it-{uuid.uuid4().hex[:8]}"}
|
||||
)
|
||||
assert token_resp.json()["code"] == 0
|
||||
headers = {"Authorization": f"Bearer {token_resp.json()['data']['access_token']}"}
|
||||
|
||||
before = (await client.get("/api/v1/quota", headers=headers)).json()["data"]
|
||||
|
||||
chat = await client.post("/api/v1/agent/chat", json={"message": "面试穿什么"}, headers=headers)
|
||||
body = chat.json()
|
||||
assert body["code"] == 0
|
||||
assert body["data"]["reply"]
|
||||
assert body["data"]["prompt_version"] == "v1"
|
||||
assert Decimal(body["data"]["usage"]["cost_cents"]) > 0
|
||||
|
||||
after = (await client.get("/api/v1/quota", headers=headers)).json()["data"]
|
||||
assert after["used"] == before["used"] + 1
|
||||
|
||||
ledger = (await client.get("/api/v1/quota/ledger", headers=headers)).json()["data"]
|
||||
assert ledger[0]["tx_type"] == "consume"
|
||||
assert ledger[0]["status"] == "committed"
|
||||
|
||||
|
||||
async def test_alias_path_v1_matches_api_v1(client: Any) -> None:
|
||||
assert (await client.get("/healthz")).status_code == 200
|
||||
token = await client.post("/v1/auth/dev-token", json={"device_key": "alias-check"})
|
||||
assert token.json()["code"] == 0
|
||||
|
||||
|
||||
async def test_unauthenticated_requests_get_the_unified_envelope(client: Any) -> None:
|
||||
resp = await client.post("/api/v1/agent/chat", json={"message": "hi"})
|
||||
assert resp.status_code == 401
|
||||
assert resp.json()["code"] == 40100
|
||||
Reference in New Issue
Block a user