- FastAPI backend with SQLModel, Alembic migrations, AgentScope agents - Next.js 15 frontend with React 19, Tailwind, Zustand, React Flow - Multi-provider AI system (DashScope, Kling, MiniMax, Volcengine, OpenAI, etc.) - All HTTP clients migrated from sync requests to async httpx - Admin-managed API keys via environment variables - SSRF vulnerability fixed in ensure_url()
46 lines
1.7 KiB
Python
46 lines
1.7 KiB
Python
import pytest
|
|
|
|
from src.middlewares.rate_limiter import RateLimiter
|
|
|
|
|
|
class TestRateLimiterPathNormalization:
|
|
def test_get_rate_limit_matches_versioned_api_prefix(self):
|
|
limiter = RateLimiter()
|
|
limit, window = limiter.get_rate_limit("/api/v1/generations/image")
|
|
assert (limit, window) == (10, 60)
|
|
|
|
def test_normalize_path_reduces_dynamic_segments(self):
|
|
limiter = RateLimiter()
|
|
assert limiter._normalize_path("/api/v1/projects/123") == "/projects/{id}"
|
|
assert (
|
|
limiter._normalize_path("/api/v1/tasks/550e8400-e29b-41d4-a716-446655440000")
|
|
== "/tasks/{id}"
|
|
)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
class TestRateLimiterLocalFallback:
|
|
async def test_local_fallback_limits_critical_endpoint_when_redis_unavailable(self):
|
|
limiter = RateLimiter()
|
|
limiter._connected = False
|
|
limiter.rate_limits["/generations/image"] = (2, 60)
|
|
|
|
limited_1, _, _, _ = await limiter.is_rate_limited("ip:test", "/api/v1/generations/image")
|
|
limited_2, _, _, _ = await limiter.is_rate_limited("ip:test", "/api/v1/generations/image")
|
|
limited_3, _, limit, _ = await limiter.is_rate_limited("ip:test", "/api/v1/generations/image")
|
|
|
|
assert limited_1 is False
|
|
assert limited_2 is False
|
|
assert limited_3 is True
|
|
assert limit == 2
|
|
|
|
async def test_local_fallback_not_used_for_non_critical_endpoint(self):
|
|
limiter = RateLimiter()
|
|
limiter._connected = False
|
|
|
|
limited, count, limit, reset = await limiter.is_rate_limited("ip:test", "/api/v1/health")
|
|
assert limited is False
|
|
assert count == 0
|
|
assert limit == 0
|
|
assert reset == 0
|