Swap Redis limiters for in-memory in test fixture
CI / lint (pull_request) Failing after 3s
CI / typecheck (pull_request) Successful in 26s
CI / test (pull_request) Successful in 34s
CI / build-and-push (pull_request) Has been skipped

The conftest was setting rate_limit_redis_enabled=False but the
rate_limit module's _redis_client and the RedisSlidingWindow limiters
are constructed at module import. Flipping the setting inside the
fixture doesn't undo that, so the Redis client was still being
constructed and torn down at the end of the test event loop, raising
RuntimeError('Event loop is closed').

This swaps the limiters directly on the module in the fixture setup
and restores the originals in teardown. Local: 164 passed, 7
skipped.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Barcode Betty
2026-06-02 13:42:48 +00:00
parent ce23ee18b8
commit 69d7fe1508
+29 -1
View File
@@ -18,6 +18,7 @@ from sqlalchemy.types import CHAR
from cartsnitch_api.config import settings as cartsnitch_settings from cartsnitch_api.config import settings as cartsnitch_settings
from cartsnitch_api.database import get_db from cartsnitch_api.database import get_db
from cartsnitch_api.main import create_app from cartsnitch_api.main import create_app
from cartsnitch_api.middleware import rate_limit as _rate_limit_module
from cartsnitch_api.models import Base from cartsnitch_api.models import Base
@@ -140,12 +141,39 @@ TEST_DATABASE_URL = "sqlite+aiosqlite:///:memory:"
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
def disable_rate_limiting(): def disable_rate_limiting():
"""Disable rate limiting for all tests to prevent 429 interference.""" """Disable rate limiting for all tests to prevent 429 interference.
The rate_limit module creates its Redis client at import time when
``settings.rate_limit_redis_enabled`` is true. We can't undo that by
flipping the setting inside the fixture — the client and the
Redis-backed limiters are already constructed. So we swap them out
for the in-memory limiters directly on the module, which also
prevents "Event loop is closed" errors when the redis client tries
to disconnect after the test event loop ends.
"""
cartsnitch_settings.rate_limit_enabled = False cartsnitch_settings.rate_limit_enabled = False
cartsnitch_settings.rate_limit_redis_enabled = False cartsnitch_settings.rate_limit_redis_enabled = False
original_public = _rate_limit_module._public_limiter
original_auth = _rate_limit_module._auth_limiter
original_auth_strict = _rate_limit_module._auth_strict_limiter
_rate_limit_module._redis_client = None
_rate_limit_module._use_redis = False
_rate_limit_module._public_limiter = _rate_limit_module.InMemorySlidingWindow(
cartsnitch_settings.rate_limit_requests, cartsnitch_settings.rate_limit_window_seconds
)
_rate_limit_module._auth_limiter = _rate_limit_module.InMemorySlidingWindow(
cartsnitch_settings.rate_limit_requests * 5, cartsnitch_settings.rate_limit_window_seconds
)
_rate_limit_module._auth_strict_limiter = _rate_limit_module.InMemorySlidingWindow(
cartsnitch_settings.rate_limit_auth_requests,
cartsnitch_settings.rate_limit_auth_window_seconds,
)
yield yield
cartsnitch_settings.rate_limit_enabled = True cartsnitch_settings.rate_limit_enabled = True
cartsnitch_settings.rate_limit_redis_enabled = True cartsnitch_settings.rate_limit_redis_enabled = True
_rate_limit_module._public_limiter = original_public
_rate_limit_module._auth_limiter = original_auth
_rate_limit_module._auth_strict_limiter = original_auth_strict
@pytest.fixture @pytest.fixture