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
+77
@@ -0,0 +1,77 @@
|
||||
"""Scene routing configuration (架构文档 §8.1: 改模型不改代码)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from packages.llm.errors import SceneNotRoutedError
|
||||
from packages.llm.routing import SceneRouter, build_scene_configs
|
||||
from packages.llm.types import Scene
|
||||
|
||||
|
||||
def test_defaults_put_the_primary_first_and_the_backup_second() -> None:
|
||||
configs = build_scene_configs(primary_provider="deepseek", backup_provider="zhipu")
|
||||
assert configs[Scene.AGENT_CHAT].providers == ("deepseek", "zhipu")
|
||||
|
||||
|
||||
def test_a_missing_backup_yields_a_single_provider_chain() -> None:
|
||||
configs = build_scene_configs(primary_provider="deepseek", backup_provider=None)
|
||||
assert configs[Scene.AGENT_CHAT].providers == ("deepseek",)
|
||||
|
||||
|
||||
def test_every_scene_is_routed_and_bound_to_a_prompt_version() -> None:
|
||||
configs = build_scene_configs(primary_provider="deepseek")
|
||||
assert set(configs) == set(Scene)
|
||||
for config in configs.values():
|
||||
assert config.prompt_id
|
||||
assert config.prompt_version
|
||||
|
||||
|
||||
def test_intent_classification_is_critical_so_budget_pressure_cannot_disable_it() -> None:
|
||||
configs = build_scene_configs(primary_provider="deepseek")
|
||||
assert configs[Scene.INTENT_CLASSIFY].critical is True
|
||||
assert configs[Scene.AGENT_CHAT].critical is False
|
||||
|
||||
|
||||
def test_agent_chat_output_budget_matches_the_token_plan() -> None:
|
||||
# 架构文档 §7.2: 输出 ≤ 800
|
||||
assert build_scene_configs(primary_provider="x")[Scene.AGENT_CHAT].max_tokens <= 800
|
||||
|
||||
|
||||
def test_overrides_change_routing_without_a_code_change() -> None:
|
||||
configs = build_scene_configs(
|
||||
primary_provider="deepseek",
|
||||
backup_provider="zhipu",
|
||||
overrides={
|
||||
"agent_chat": {
|
||||
"max_tokens": 1200,
|
||||
"prompt_version": "v2",
|
||||
"models": {"deepseek": "deepseek-v4-pro"},
|
||||
"providers": ["zhipu", "deepseek"],
|
||||
}
|
||||
},
|
||||
)
|
||||
config = configs[Scene.AGENT_CHAT]
|
||||
assert config.max_tokens == 1200
|
||||
assert config.prompt_version == "v2"
|
||||
assert config.providers == ("zhipu", "deepseek")
|
||||
assert config.model_for("deepseek", "fallback") == "deepseek-v4-pro"
|
||||
assert config.model_for("zhipu", "fallback") == "fallback"
|
||||
# Untouched scenes keep their defaults.
|
||||
assert configs[Scene.REPORT_COPY].max_tokens == 3000
|
||||
|
||||
|
||||
def test_structured_retry_budget_is_configurable() -> None:
|
||||
configs = build_scene_configs(primary_provider="x", structured_max_retries=5)
|
||||
assert configs[Scene.AGENT_CHAT].structured_max_retries == 5
|
||||
|
||||
|
||||
def test_router_raises_for_an_unconfigured_scene() -> None:
|
||||
router = SceneRouter({})
|
||||
with pytest.raises(SceneNotRoutedError):
|
||||
router.resolve(Scene.AGENT_CHAT)
|
||||
|
||||
|
||||
def test_router_lists_configured_scenes() -> None:
|
||||
router = SceneRouter(build_scene_configs(primary_provider="x"))
|
||||
assert set(router.scenes()) == set(Scene)
|
||||
Reference in New Issue
Block a user