fix(i-e): BUG-097 geschlossen — auth-Suite 10/10 gruen
Root-Cause: Rate-Limiter-Zustand akkumulierte ueber Tests hinweg (alle Tests teilen dieselbe Client-IP): InMemoryRateLimiter (process-local) UND Redis rate:* Keys auf der App-DB (REDIS_URL=...db1). Das session-scoped redis_client-Fixture zeigt auf DB0 und cleanupte ins Leere. Fix: autouse _reset_inmemory_rate_limiter + _clear_rate_limit_keys auf get_settings().redis_url. Beweis: test_auth 10/10 in Kette (vorher 3 PasswordReset-Failures mit 429).
This commit is contained in:
@@ -469,6 +469,48 @@ def mock_imap_connections(monkeypatch):
|
||||
yield fake_client
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _reset_inmemory_rate_limiter():
|
||||
"""Clear the process-local rate limiter around every test (BUG-097).
|
||||
|
||||
The InMemoryRateLimiter is process-local and would otherwise accumulate
|
||||
password-reset/login attempts across tests, making later tests fail with
|
||||
429 depending purely on execution order.
|
||||
"""
|
||||
from app.core.resilience import get_inmemory_limiter
|
||||
|
||||
get_inmemory_limiter().clear_all()
|
||||
yield
|
||||
get_inmemory_limiter().clear_all()
|
||||
|
||||
|
||||
@pytest_asyncio.fixture(autouse=True)
|
||||
async def _clear_rate_limit_keys():
|
||||
"""Delete Redis rate-limit keys after every test (BUG-097).
|
||||
|
||||
check_rate_limit uses Redis INCR+EXPIRE keyed by client IP; all tests share
|
||||
the same test-client IP, so password-reset/login counters accumulate across
|
||||
tests and later tests hit 429 depending purely on execution order.
|
||||
|
||||
Uses the SAME Redis connection the app uses (app settings REDIS_URL, DB 1 in
|
||||
.env.test) — the session-scoped redis_client fixture points at a different
|
||||
logical DB than get_redis() and would clean up nothing.
|
||||
"""
|
||||
import redis.asyncio as aioredis_mod
|
||||
|
||||
from app.config import get_settings
|
||||
|
||||
url = get_settings().redis_url
|
||||
r = aioredis_mod.from_url(url, decode_responses=True)
|
||||
try:
|
||||
yield
|
||||
keys = await r.keys("rate:*")
|
||||
if keys:
|
||||
await r.delete(*keys)
|
||||
finally:
|
||||
await r.aclose()
|
||||
|
||||
|
||||
@pytest_asyncio.fixture(scope="session")
|
||||
async def engine() -> AsyncGenerator[AsyncEngine, None]:
|
||||
"""Async engine for the test database (session-scoped for speed)."""
|
||||
|
||||
Reference in New Issue
Block a user