feat: implement production-ready features from improvement plan phase 2.5 & 2.6
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>
This commit is contained in:
89
tests/test_metrics.py
Normal file
89
tests/test_metrics.py
Normal file
@@ -0,0 +1,89 @@
|
||||
"""Tests for metrics functionality."""
|
||||
from wikijs.metrics import MetricsCollector, get_metrics
|
||||
|
||||
|
||||
def test_metrics_collector_init():
|
||||
"""Test metrics collector initialization."""
|
||||
collector = MetricsCollector()
|
||||
stats = collector.get_stats()
|
||||
|
||||
assert stats["total_requests"] == 0
|
||||
assert stats["total_errors"] == 0
|
||||
|
||||
|
||||
def test_record_request():
|
||||
"""Test recording requests."""
|
||||
collector = MetricsCollector()
|
||||
|
||||
# Record successful request
|
||||
collector.record_request("/api/test", "GET", 200, 100.0)
|
||||
|
||||
stats = collector.get_stats()
|
||||
assert stats["total_requests"] == 1
|
||||
assert stats["total_errors"] == 0
|
||||
|
||||
|
||||
def test_record_error():
|
||||
"""Test recording errors."""
|
||||
collector = MetricsCollector()
|
||||
|
||||
# Record error request
|
||||
collector.record_request("/api/test", "GET", 404, 50.0, error="Not found")
|
||||
|
||||
stats = collector.get_stats()
|
||||
assert stats["total_requests"] == 1
|
||||
assert stats["total_errors"] == 1
|
||||
|
||||
|
||||
def test_latency_stats():
|
||||
"""Test latency statistics."""
|
||||
collector = MetricsCollector()
|
||||
|
||||
# Record multiple requests
|
||||
collector.record_request("/api/test", "GET", 200, 100.0)
|
||||
collector.record_request("/api/test", "GET", 200, 200.0)
|
||||
collector.record_request("/api/test", "GET", 200, 150.0)
|
||||
|
||||
stats = collector.get_stats()
|
||||
assert "latency" in stats
|
||||
assert stats["latency"]["min"] == 100.0
|
||||
assert stats["latency"]["max"] == 200.0
|
||||
assert stats["latency"]["avg"] == 150.0
|
||||
|
||||
|
||||
def test_increment_counter():
|
||||
"""Test incrementing counters."""
|
||||
collector = MetricsCollector()
|
||||
|
||||
collector.increment("custom_counter", 5)
|
||||
collector.increment("custom_counter", 3)
|
||||
|
||||
stats = collector.get_stats()
|
||||
assert stats["counters"]["custom_counter"] == 8
|
||||
|
||||
|
||||
def test_set_gauge():
|
||||
"""Test setting gauges."""
|
||||
collector = MetricsCollector()
|
||||
|
||||
collector.set_gauge("memory_usage", 75.5)
|
||||
|
||||
stats = collector.get_stats()
|
||||
assert stats["gauges"]["memory_usage"] == 75.5
|
||||
|
||||
|
||||
def test_reset_metrics():
|
||||
"""Test resetting metrics."""
|
||||
collector = MetricsCollector()
|
||||
|
||||
collector.record_request("/api/test", "GET", 200, 100.0)
|
||||
collector.reset()
|
||||
|
||||
stats = collector.get_stats()
|
||||
assert stats["total_requests"] == 0
|
||||
|
||||
|
||||
def test_get_global_metrics():
|
||||
"""Test getting global metrics instance."""
|
||||
metrics = get_metrics()
|
||||
assert isinstance(metrics, MetricsCollector)
|
||||
Reference in New Issue
Block a user