"""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
