reddit-media-collector/tests/test_rate_limit.py
Richard Nixon 34c2b16e19 test: cover routers, auth, rate limit, config_manager, imgur extractor
- tests/test_auth.py: enable/disable via env, valid/invalid creds
- tests/test_rate_limit.py: bucket math + window expiry
- tests/test_router_media.py: path traversal (file/thumb), paginated subreddits/authors, blacklist preview shape, DELETE flow, cleanup-by-type 400 paths
- tests/test_router_stats.py: cache TTL, .json exclusion, missing dir
- tests/test_router_favorites.py: paginated favorites/authors, no N+1 (query count), favorites_only filter
- tests/test_router_config.py: CRUD subreddits/users/blacklist
- tests/test_config_manager.py: case-insensitive dedupe, blacklist removes from collection, domain normalization
- tests/test_extractors_imgur.py: i.imgur direct, gifv->mp4, albums/galleries -> None

src/database.py: resolve RMC_DB_PATH lazily in Database() so tests can monkeypatch the env per case (no more module-import capture)

Result: 135 passed (was 77). Coverage 55% global; 100% on auth/imgur, 96% rate_limit, 84% stats router, 80% config_manager.
2026-05-17 11:54:40 +01:00

61 lines
1.6 KiB
Python

"""Tests for the in-memory rate limiter."""
from __future__ import annotations
import asyncio
import time
import pytest
from fastapi import FastAPI
from fastapi.testclient import TestClient
from src.web.rate_limit import _reset_buckets, rate_limit
@pytest.fixture(autouse=True)
def reset_buckets():
_reset_buckets()
yield
_reset_buckets()
def _make_app(max_calls: int, window: float) -> FastAPI:
app = FastAPI()
@app.get("/limited", dependencies=[pytest.importorskip("fastapi").Depends(rate_limit(max_calls, window))])
async def _route():
return {"ok": True}
return app
def test_allows_within_window():
app = _make_app(3, 60.0)
client = TestClient(app)
for _ in range(3):
assert client.get("/limited").status_code == 200
def test_blocks_when_exceeded():
app = _make_app(2, 60.0)
client = TestClient(app)
assert client.get("/limited").status_code == 200
assert client.get("/limited").status_code == 200
response = client.get("/limited")
assert response.status_code == 429
assert response.headers.get("retry-after") is not None
def test_resets_after_window():
app = _make_app(1, 0.1)
client = TestClient(app)
assert client.get("/limited").status_code == 200
assert client.get("/limited").status_code == 429
time.sleep(0.15)
assert client.get("/limited").status_code == 200
def test_returns_dependency_callable():
"""Direct unit test on the closure (no FastAPI dependency injection)."""
dep = rate_limit(2, 60.0)
assert asyncio.iscoroutinefunction(dep)