"""CircuitBreaker 单元测试。 验证内存级熔断器的核心行为:阈值触发、冷却半开、成功重置、强制熔断。 """ from __future__ import annotations import pytest from adapters.breaker import CircuitBreaker @pytest.fixture() def breaker() -> CircuitBreaker: """创建 threshold=3, cooldown=10s 的熔断器。""" return CircuitBreaker(fail_threshold=3, cooldown_s=10.0) class TestCircuitBreaker: """CircuitBreaker 行为验证。""" def test_closed_by_default(self, breaker: CircuitBreaker) -> None: """初始状态为关闭(放行)。""" assert breaker.is_open("llm", now=0.0) is False def test_opens_after_threshold_failures(self, breaker: CircuitBreaker) -> None: """连续失败未达阈值不开路;达到阈值后开路。""" # 2 次失败 < threshold=3,仍关闭 breaker.record_failure("llm", now=1.0) breaker.record_failure("llm", now=2.0) assert breaker.is_open("llm", now=2.5) is False # 第 3 次失败 >= threshold=3,开路 breaker.record_failure("llm", now=3.0) assert breaker.is_open("llm", now=3.5) is True def test_half_open_after_cooldown(self, breaker: CircuitBreaker) -> None: """冷却期过后自动半开(放行试探)。""" for t in range(3): breaker.record_failure("llm", now=float(t)) # 开路期间 assert breaker.is_open("llm", now=5.0) is True # cooldown=10s,第 3 次失败 now=2.0,open_until=12.0 # now=12.0 时冷却到期 → 半开 assert breaker.is_open("llm", now=12.0) is False def test_success_resets(self, breaker: CircuitBreaker) -> None: """record_success 重置计数并关闭熔断器。""" for t in range(3): breaker.record_failure("llm", now=float(t)) assert breaker.is_open("llm", now=3.0) is True breaker.record_success("llm") assert breaker.is_open("llm", now=3.0) is False def test_force_open_immediate(self, breaker: CircuitBreaker) -> None: """force_open 无需积累失败即可直接熔断。""" breaker.force_open("llm", now=5.0) assert breaker.is_open("llm", now=5.5) is True def test_force_open_probe_failure_reopens(self, breaker: CircuitBreaker) -> None: """半开探针失败后重新熔断(因 fails 已置为 threshold)。""" breaker.force_open("llm", now=0.0) # cooldown=10s → open_until=10.0,now=10.0 半开 assert breaker.is_open("llm", now=10.0) is False # 半开探针失败:一次 record_failure 即达阈值(fails 已=3),重新熔断 breaker.record_failure("llm", now=10.0) assert breaker.is_open("llm", now=10.5) is True