test: prove a rejected call's reason reaches the telemetry table
Issue #10 Task 5, the acceptance claim. Before the fix this asserted against 'qwen_1 请求被拒: 400' and failed on the first substring - which is exactly what the downstream batch was left with. Uses the real body from the issue, and checks the trailing code too, since a head-only cut would drop the one field you quote when chasing the provider.
This commit is contained in:
@@ -11,7 +11,12 @@ import sqlite3
|
|||||||
import httpx
|
import httpx
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from polygateway import CircuitOpenError, GatewayClient, TransientError
|
from polygateway import (
|
||||||
|
CircuitOpenError,
|
||||||
|
GatewayClient,
|
||||||
|
RequestRejectedError,
|
||||||
|
TransientError,
|
||||||
|
)
|
||||||
from polygateway.backends.memory.breaker import InMemoryGate
|
from polygateway.backends.memory.breaker import InMemoryGate
|
||||||
from polygateway.backends.memory.cache import InMemoryCache
|
from polygateway.backends.memory.cache import InMemoryCache
|
||||||
from polygateway.backends.memory.limiter import InMemoryLimiter
|
from polygateway.backends.memory.limiter import InMemoryLimiter
|
||||||
@@ -173,6 +178,40 @@ class TestTelemetryAcrossPaths:
|
|||||||
assert len({cid for _, cid in rows}) == 2 # call_id 逐次独立
|
assert len({cid for _, cid in rows}) == 2 # call_id 逐次独立
|
||||||
|
|
||||||
|
|
||||||
|
class TestRejectionReasonIsQueryable:
|
||||||
|
"""issue #10 的验收主张: 400 之后,网关说的话必须能在遥测表里查到。
|
||||||
|
|
||||||
|
下游一轮 1050 张影像的批处理里,1 张在读表格时收到 400 被判确定性失败,
|
||||||
|
事后"这张图到底哪里不合规"无从查起——响应体在 transport 翻译层就没了。
|
||||||
|
"""
|
||||||
|
|
||||||
|
# issue #10 原文给出的真实响应体(一字不改)
|
||||||
|
_BODY = (
|
||||||
|
'{"error":{"message":"<400> ***.***.InvalidParameter: The image format is illegal '
|
||||||
|
'and cannot be opened","type":"invalid_request_error","param":"",'
|
||||||
|
'"code":"invalid_parameter_error"}}'
|
||||||
|
)
|
||||||
|
|
||||||
|
async def test_rejected_call_leaves_the_reason_in_telemetry(self, tmp_path):
|
||||||
|
recorder = SQLiteRecorder(tmp_path / "t.db")
|
||||||
|
client = _full_client(
|
||||||
|
lambda req: httpx.Response(400, content=self._BODY.encode()), telemetry=recorder
|
||||||
|
)
|
||||||
|
|
||||||
|
with pytest.raises(RequestRejectedError):
|
||||||
|
await client.chat([{"role": "user", "content": "hi"}])
|
||||||
|
|
||||||
|
recorder.close()
|
||||||
|
rows = sqlite3.connect(tmp_path / "t.db").execute("SELECT error FROM llm_calls").fetchall()
|
||||||
|
assert rows, "400 必须留下遥测行(遥测必录)"
|
||||||
|
errors = " ".join(r[0] or "" for r in rows)
|
||||||
|
# 修复前这里只有 "qwen_1 请求被拒: 400"——诊断信息一个字都不在
|
||||||
|
assert "InvalidParameter" in errors
|
||||||
|
assert "The image format is illegal" in errors
|
||||||
|
# 尾部的 code 才是向网关方追查的凭据,头部硬切正好会丢掉它
|
||||||
|
assert "invalid_parameter_error" in errors
|
||||||
|
|
||||||
|
|
||||||
class TestStructuredThroughStack:
|
class TestStructuredThroughStack:
|
||||||
async def test_feedback_reask_passes_through_governance(self):
|
async def test_feedback_reask_passes_through_governance(self):
|
||||||
"""重问经过内层治理: 第二次真实请求同样被限流/熔断记账。"""
|
"""重问经过内层治理: 第二次真实请求同样被限流/熔断记账。"""
|
||||||
|
|||||||
Reference in New Issue
Block a user