"""登录 IP 限速(auth_api._login_rate_limited):窗口内超限拦截、过期滑出、独立 IP 隔离。""" from __future__ import annotations import pytest import app.auth_api as auth_api from app.auth_api import _LOGIN_MAX_PER_WINDOW, _LOGIN_WINDOW, _login_attempts, _login_rate_limited class _FakeClock: """替换 auth_api 命名空间里的 time(不影响全局 time 模块)。""" def __init__(self): self.now = 1000.0 def monotonic(self) -> float: return self.now @pytest.fixture() def clock(monkeypatch): c = _FakeClock() monkeypatch.setattr(auth_api, "time", c) _login_attempts.clear() yield c _login_attempts.clear() def test_under_limit_passes(clock): for _ in range(_LOGIN_MAX_PER_WINDOW): assert _login_rate_limited("1.2.3.4") is False assert _login_rate_limited("1.2.3.4") is True # 第 16 次被拦 def test_window_slides(clock): for _ in range(_LOGIN_MAX_PER_WINDOW): _login_rate_limited("1.2.3.4") assert _login_rate_limited("1.2.3.4") is True # 窗口滑过:最早的尝试过期出窗,重新放行 clock.now += _LOGIN_WINDOW + 0.1 assert _login_rate_limited("1.2.3.4") is False def test_ips_isolated(clock): for _ in range(_LOGIN_MAX_PER_WINDOW): _login_rate_limited("1.1.1.1") assert _login_rate_limited("1.1.1.1") is True assert _login_rate_limited("2.2.2.2") is False # 另一 IP 不受牵连