feat: add in-memory limiter and breaker satisfying backend contracts

This commit is contained in:
2026-07-20 06:54:07 -04:00
parent 454a8b5e0f
commit 4a176b6220
9 changed files with 645 additions and 0 deletions
+61
View File
@@ -0,0 +1,61 @@
"""契约测试共享 fixture: 后端参数化(M1 仅 memory,M2 增 redis 零改测试)。
FakeClock 仅对支持时钟注入的后端有效(memory);M2 接入 Redis 后端时,
依赖时钟推进的用例按后端能力跳过或改用真实等待。
"""
import pytest
from polygateway.backends.memory.breaker import InMemoryGate
from polygateway.backends.memory.limiter import InMemoryLimiter
from polygateway.types import BreakerConfig, GlobalLimits, SourceConfig
class FakeClock:
"""确定性单调时钟;契约测试推进时间验证租约/冷却语义。"""
def __init__(self, start: float = 1000.0) -> None:
self.t = start
def __call__(self) -> float:
return self.t
def advance(self, seconds: float) -> None:
self.t += seconds
def make_source(name: str = "s1", **overrides) -> SourceConfig:
base = dict(
name=name, provider="openai", base_url="https://gw.example/v1",
api_key="sk-test", model="m", timeout_s=10.0,
)
base.update(overrides)
return SourceConfig(**base)
@pytest.fixture
def clock() -> FakeClock:
return FakeClock()
@pytest.fixture(params=["memory"])
def limiter_factory(request, clock):
"""返回 (sources, global_limits, lease_ttl_s) -> RateLimiter 的工厂。"""
def make(sources: list[SourceConfig], global_limits: GlobalLimits, lease_ttl_s: float = 100.0):
return InMemoryLimiter(
scope="llm", sources={s.name: s for s in sources},
global_limits=global_limits, lease_ttl_s=lease_ttl_s, now=clock,
)
return make
@pytest.fixture(params=["memory"])
def gate_factory(request, clock):
"""返回 (BreakerConfig) -> ProviderGate 的工厂。"""
def make(config: BreakerConfig):
return InMemoryGate(config=config, now=clock)
return make