feat(test,export,headers): 模型测试复用统一运行时、实时进度展示、导出增强与请求头大小写保留

- 模型测试 failover 从手动 FailoverEngine 改为 TaskService.execute_sync_candidates 统一运行时
- 前端新增实时 trace 轮询进度展示(候选状态、测试账号、进度条)
- 用户导出/导入支持明文 Key 优先(版本升至 1.2),新增 email_verified 字段
- SENSITIVE_CREDENTIAL_FIELDS 统一到 provider_ops/types.py,补充 refresh_token
- 请求头大小写保留机制(resolve_header_name_case + HeaderBuilder.add 语义修改)
- Codex envelope 移除合成头部,保留客户端原始请求头
- endpoint_checker 支持自定义超时透传
- 新增 x-forwarded-scheme 到上游丢弃头部列表
This commit is contained in:
fawney19
2026-03-06 21:06:45 +08:00
parent 7950ba7dc5
commit 90760da499
28 changed files with 1485 additions and 429 deletions

View File

@@ -65,7 +65,8 @@ class TestHeaderBuilder:
builder.add("authorization", "b")
built = builder.build()
assert len(built) == 1
assert list(built.values()) == ["b"]
assert built["Authorization"] == "b"
assert "authorization" not in built
def test_add_protected_does_not_override(self) -> None:
builder = HeaderBuilder()
@@ -135,7 +136,8 @@ class TestBuildUpstreamHeaders:
extra_headers={"User-Agent": "b"},
)
assert len([k for k in result if k.lower() == "user-agent"]) == 1
assert result["User-Agent"] == "b"
assert result["user-agent"] == "b"
assert "User-Agent" not in result
def test_default_content_type(self) -> None:
result = build_upstream_headers_for_endpoint({}, "openai:chat", "provider")
@@ -169,3 +171,35 @@ class TestCapabilityResolverHeaderParsing:
request_headers={"x-require-capability": "context_1m"}
)
assert reqs == {"context_1m": True}
class TestAuthHeaderCasePreservation:
def test_build_upstream_headers_preserves_lowercase_authorization_key(self) -> None:
result = build_upstream_headers_for_endpoint(
{"authorization": "Bearer client-token", "X-Test": "1"},
"openai:chat",
"provider",
)
assert "authorization" in result
assert "Authorization" not in result
assert result["authorization"] == "Bearer provider"
def test_passthrough_request_builder_preserves_lowercase_authorization_key(self) -> None:
from types import SimpleNamespace
from src.api.handlers.base.request_builder import PassthroughRequestBuilder
builder = PassthroughRequestBuilder()
endpoint = SimpleNamespace(api_family="openai", endpoint_kind="cli", header_rules=None)
key = SimpleNamespace(api_key="unused")
headers = builder.build_headers(
original_headers={"authorization": "Bearer client-token"},
endpoint=endpoint,
key=key,
pre_computed_auth=("Authorization", "Bearer provider-token"),
)
assert "authorization" in headers
assert "Authorization" not in headers
assert headers["authorization"] == "Bearer provider-token"