Phase 2.5: Fix Foundation (CRITICAL) - Fixed 4 failing tests by adding cache attribute to mock_client fixture - Created comprehensive cache tests for Pages endpoint (test_pages_cache.py) - Added missing dependencies: pydantic[email] and aiohttp to core requirements - Updated requirements.txt with proper dependency versions - Achieved 82.67% test coverage with 454 passing tests Phase 2.6: Production Essentials - Implemented structured logging (wikijs/logging.py) * JSON and text log formatters * Configurable log levels and output destinations * Integration with client operations - Implemented metrics and telemetry (wikijs/metrics.py) * Request tracking with duration, status codes, errors * Latency percentiles (min, max, avg, p50, p95, p99) * Error rate calculation * Thread-safe metrics collection - Implemented rate limiting (wikijs/ratelimit.py) * Token bucket algorithm for request throttling * Per-endpoint rate limiting support * Configurable timeout handling * Burst capacity management - Created SECURITY.md policy * Vulnerability reporting procedures * Security best practices * Response timelines * Supported versions Documentation - Added comprehensive logging guide (docs/logging.md) - Added metrics and telemetry guide (docs/metrics.md) - Added rate limiting guide (docs/rate_limiting.md) - Updated README.md with production features section - Updated IMPROVEMENT_PLAN_2.md with completed checkboxes Testing - Created test suite for logging (tests/test_logging.py) - Created test suite for metrics (tests/test_metrics.py) - Created test suite for rate limiting (tests/test_ratelimit.py) - All 454 tests passing - Test coverage: 82.67% Breaking Changes: None Dependencies Added: pydantic[email], email-validator, dnspython 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
74 lines
1.9 KiB
Python
74 lines
1.9 KiB
Python
"""Tests for rate limiting functionality."""
|
|
import time
|
|
import pytest
|
|
from wikijs.ratelimit import RateLimiter, PerEndpointRateLimiter
|
|
|
|
|
|
def test_rate_limiter_init():
|
|
"""Test rate limiter initialization."""
|
|
limiter = RateLimiter(requests_per_second=10.0)
|
|
|
|
assert limiter.rate == 10.0
|
|
assert limiter.burst == 10
|
|
|
|
|
|
def test_rate_limiter_acquire():
|
|
"""Test acquiring tokens."""
|
|
limiter = RateLimiter(requests_per_second=100.0)
|
|
|
|
# Should be able to acquire immediately
|
|
assert limiter.acquire(timeout=1.0) is True
|
|
|
|
|
|
def test_rate_limiter_burst():
|
|
"""Test burst behavior."""
|
|
limiter = RateLimiter(requests_per_second=10.0, burst=5)
|
|
|
|
# Should be able to acquire up to burst size
|
|
for _ in range(5):
|
|
assert limiter.acquire(timeout=0.1) is True
|
|
|
|
|
|
def test_rate_limiter_timeout():
|
|
"""Test timeout behavior."""
|
|
limiter = RateLimiter(requests_per_second=1.0)
|
|
|
|
# Exhaust tokens
|
|
assert limiter.acquire(timeout=1.0) is True
|
|
|
|
# Next acquire should timeout quickly
|
|
assert limiter.acquire(timeout=0.1) is False
|
|
|
|
|
|
def test_rate_limiter_reset():
|
|
"""Test rate limiter reset."""
|
|
limiter = RateLimiter(requests_per_second=1.0)
|
|
|
|
# Exhaust tokens
|
|
limiter.acquire()
|
|
|
|
# Reset
|
|
limiter.reset()
|
|
|
|
# Should be able to acquire again
|
|
assert limiter.acquire(timeout=0.1) is True
|
|
|
|
|
|
def test_per_endpoint_rate_limiter():
|
|
"""Test per-endpoint rate limiting."""
|
|
limiter = PerEndpointRateLimiter(default_rate=10.0)
|
|
|
|
# Set different rate for specific endpoint
|
|
limiter.set_limit("/api/special", 5.0)
|
|
|
|
# Should use endpoint-specific rate
|
|
assert limiter.acquire("/api/special", timeout=1.0) is True
|
|
|
|
|
|
def test_per_endpoint_default_rate():
|
|
"""Test default rate for endpoints."""
|
|
limiter = PerEndpointRateLimiter(default_rate=100.0)
|
|
|
|
# Should use default rate for unknown endpoint
|
|
assert limiter.acquire("/api/unknown", timeout=1.0) is True
|