"""实盘限流 / 退避单测。""" from __future__ import annotations import time from app.live.rate_limit import ( LiveRetryGate, RateLimitError, TradeThrottle, get_throttle, is_rate_limit_error, ) def test_is_rate_limit_error() -> None: assert is_rate_limit_error("HTTP 429 too many") assert is_rate_limit_error("binance eapi cooldown 12s") assert is_rate_limit_error(RateLimitError("x", retry_after=5)) assert not is_rate_limit_error("保证金不足") def test_trade_throttle_cooldown() -> None: t = TradeThrottle("ut_throttle", min_interval_sec=0.01, cooldown_429_sec=0.3) t.before_request() t.mark_http(429) try: t.before_request() assert False, "expected RateLimitError" except RateLimitError as e: assert e.retry_after > 0 time.sleep(0.35) t.before_request() # 冷却结束后可继续 def test_get_throttle_singleton() -> None: a = get_throttle("ut_shared_x", min_interval_sec=0.01) b = get_throttle("ut_shared_x") assert a is b def test_live_retry_gate_backoff() -> None: g = LiveRetryGate(base_sec=0.05, max_sec=0.2, rate_limit_min_sec=0.1, trip_after=100) assert g.allow("k")[0] is True d1 = g.fail("k") assert d1 >= 0.05 ok, left = g.allow("k") assert ok is False assert left > 0 time.sleep(d1 + 0.02) assert g.allow("k")[0] is True g.success("k") assert g.fails("k") == 0 def test_live_retry_gate_rate_limited_longer() -> None: g = LiveRetryGate(base_sec=0.01, rate_limit_min_sec=0.2) d = g.fail("rl", rate_limited=True) assert d >= 0.2