Compare commits

..
Author SHA1 Message Date
Shroominic 50eabafa57 remove performance tests due to unpredictable behaviour 2026-01-05 12:12:04 +01:00
9 changed files with 33 additions and 260 deletions
+1 -1
View File
@@ -59,7 +59,7 @@ class Settings(BaseSettings):
cors_origins: list[str] = Field(default_factory=lambda: ["*"], env="CORS_ORIGINS")
tor_proxy_url: str = Field(default="socks5://127.0.0.1:9050", env="TOR_PROXY_URL")
providers_refresh_interval_seconds: int = Field(
default=0, env="PROVIDERS_REFRESH_INTERVAL_SECONDS"
default=300, env="PROVIDERS_REFRESH_INTERVAL_SECONDS"
)
pricing_refresh_interval_seconds: int = Field(
default=120, env="PRICING_REFRESH_INTERVAL_SECONDS"
+1 -4
View File
@@ -6,7 +6,7 @@ from typing import Any
import httpx
import websockets
from fastapi import APIRouter, HTTPException
from fastapi import APIRouter
from .core.logging import get_logger
from .core.settings import settings
@@ -389,9 +389,6 @@ async def get_providers(
Return cached providers. If include_json, return provider+health; otherwise provider only.
Optional filter by pubkey.
"""
if settings.providers_refresh_interval_seconds == 0:
raise HTTPException(status_code=404, detail="Provider discovery is disabled")
cache = await get_cache()
if not cache:
await refresh_providers_cache(pubkey=pubkey)
-24
View File
@@ -10,7 +10,6 @@ from httpx import AsyncClient
from .utils import (
CashuTokenGenerator,
PerformanceValidator,
ResponseValidator,
)
@@ -159,30 +158,7 @@ async def test_error_handling(
assert response.status_code == 401
@pytest.mark.integration
@pytest.mark.asyncio
async def test_performance_requirements(integration_client: AsyncClient) -> None:
"""Test that endpoints meet performance requirements"""
validator = PerformanceValidator()
# Test info endpoint performance
for i in range(50):
start = validator.start_timing("info_endpoint")
response = await integration_client.get("/")
validator.end_timing("info_endpoint", start)
assert response.status_code == 200
# Validate 95th percentile is under 500ms
result = validator.validate_response_time(
"info_endpoint", max_duration=0.5, percentile=0.95
)
assert result["valid"], (
f"Performance requirement failed: "
f"95th percentile was {result['percentile_time']:.3f}s "
f"(required < {result['max_allowed']}s)"
)
@pytest.mark.integration
+28 -33
View File
@@ -9,7 +9,6 @@ import gc
import statistics
import time
from typing import Any, Dict, List
from unittest.mock import patch
import psutil
import pytest
@@ -106,46 +105,42 @@ class TestPerformanceBaseline:
("GET", "/v1/wallet/info", authenticated_client, None),
]
# Enable provider discovery for this test
with patch(
"routstr.core.settings.settings.providers_refresh_interval_seconds", 300
):
# Warm up
for _ in range(10):
await integration_client.get("/")
# Warm up
for _ in range(10):
await integration_client.get("/")
# Test each endpoint
for method, path, client, data in endpoints:
response_times = []
# Test each endpoint
for method, path, client, data in endpoints:
response_times = []
for i in range(100):
start = time.time()
for i in range(100):
start = time.time()
if method == "GET":
response = await client.get(path)
else:
response = await client.post(path, json=data)
if method == "GET":
response = await client.get(path)
else:
response = await client.post(path, json=data)
duration = time.time() - start
response_times.append(duration * 1000) # Convert to ms
duration = time.time() - start
response_times.append(duration * 1000) # Convert to ms
assert response.status_code in [200, 201]
assert response.status_code in [200, 201]
if i % 10 == 0:
metrics.record_system_metrics()
if i % 10 == 0:
metrics.record_system_metrics()
# Verify 95th percentile < 500ms
p95 = sorted(response_times)[int(len(response_times) * 0.95)]
assert p95 < 500, (
f"{method} {path} p95 response time {p95}ms exceeds 500ms limit"
)
# Verify 95th percentile < 500ms
p95 = sorted(response_times)[int(len(response_times) * 0.95)]
assert p95 < 500, (
f"{method} {path} p95 response time {p95}ms exceeds 500ms limit"
)
print(f"\n{method} {path}:")
print(f" Mean: {statistics.mean(response_times):.2f}ms")
print(f" P95: {p95:.2f}ms")
print(
f" P99: {sorted(response_times)[int(len(response_times) * 0.99)]:.2f}ms"
)
print(f"\n{method} {path}:")
print(f" Mean: {statistics.mean(response_times):.2f}ms")
print(f" P95: {p95:.2f}ms")
print(
f" P99: {sorted(response_times)[int(len(response_times) * 0.99)]:.2f}ms"
)
@pytest.mark.integration
+2 -51
View File
@@ -3,7 +3,7 @@ Integration tests for provider management functionality.
Tests GET /v1/providers/ endpoint for listing and managing providers.
"""
from typing import Any, Generator
from typing import Any
from unittest.mock import patch
import pytest
@@ -11,7 +11,7 @@ from httpx import AsyncClient
from routstr.discovery import _PROVIDERS_CACHE
from .utils import PerformanceValidator, ResponseValidator
from .utils import ResponseValidator
@pytest.fixture(autouse=True)
@@ -19,15 +19,6 @@ def _clear_providers_cache() -> None:
_PROVIDERS_CACHE.clear()
@pytest.fixture(autouse=True)
def _enable_provider_discovery() -> Generator[None, Any, Any]:
"""Enable provider discovery for all tests in this module"""
with patch(
"routstr.core.settings.settings.providers_refresh_interval_seconds", 300
):
yield
@pytest.mark.integration
@pytest.mark.asyncio
async def test_providers_endpoint_default_response(
@@ -527,46 +518,6 @@ async def test_providers_endpoint_response_format(
assert isinstance(data_json["providers"], list)
@pytest.mark.integration
@pytest.mark.asyncio
async def test_providers_endpoint_performance(integration_client: AsyncClient) -> None:
"""Test providers endpoint meets performance requirements"""
# Mock quick responses to avoid network delays
mock_events: list[dict[str, Any]] = [
{
"id": f"event{i}",
"content": f"Provider: http://provider{i}.onion",
"created_at": 1234567890 + i,
}
for i in range(5)
]
validator = PerformanceValidator()
with patch(
"routstr.discovery.query_nostr_relay_for_providers", return_value=mock_events
):
with patch("routstr.discovery.fetch_provider_health") as mock_fetch:
mock_fetch.return_value = {"status_code": 200, "json": {"status": "online"}}
# Test multiple requests
for i in range(10):
start = validator.start_timing("providers_endpoint")
response = await integration_client.get("/v1/providers/")
validator.end_timing("providers_endpoint", start)
assert response.status_code == 200
# Validate performance (should be fast with mocked dependencies)
perf_result = validator.validate_response_time(
"providers_endpoint",
max_duration=2.0, # Allow more time since it involves multiple operations
percentile=0.95,
)
assert perf_result["valid"], f"Performance requirement failed: {perf_result}"
@pytest.mark.integration
@pytest.mark.asyncio
async def test_providers_endpoint_concurrent_requests(
@@ -18,7 +18,6 @@ from routstr.core.db import ApiKey
from .utils import (
ConcurrencyTester,
PerformanceValidator,
)
@@ -551,39 +550,7 @@ async def test_proxy_get_concurrent_requests(
assert response.status_code == 200
@pytest.mark.integration
@pytest.mark.asyncio
async def test_proxy_get_performance_requirements(
integration_client: AsyncClient, authenticated_client: AsyncClient
) -> None:
"""Test that GET proxy requests meet performance requirements"""
validator = PerformanceValidator()
with patch("httpx.AsyncClient.request") as mock_request:
mock_response = AsyncMock()
mock_response.status_code = 200
mock_response.headers = {"content-type": "application/json"}
mock_response.json = MagicMock(return_value={"performance": "test"})
mock_response.text = '{"performance": "test"}'
mock_response.iter_bytes = AsyncMock(return_value=[b'{"performance": "test"}'])
mock_request.return_value = mock_response
# Test multiple requests for performance measurement
for i in range(20):
start = validator.start_timing("proxy_get")
response = await authenticated_client.get(f"/v1/perf-test-{i}")
validator.end_timing("proxy_get", start)
assert response.status_code == 200
# Validate performance requirements
perf_result = validator.validate_response_time(
"proxy_get",
max_duration=1.0, # Should complete within 1 second
percentile=0.95,
)
assert perf_result["valid"], f"Performance requirement failed: {perf_result}"
@pytest.mark.integration
@@ -15,7 +15,6 @@ from httpx import ASGITransport, AsyncClient
from .utils import (
ConcurrencyTester,
PerformanceValidator,
)
@@ -290,55 +289,7 @@ async def test_proxy_post_unauthorized_access(integration_client: AsyncClient) -
assert response.status_code in [400, 401]
@pytest.mark.integration
@pytest.mark.asyncio
async def test_proxy_post_performance(
integration_client: AsyncClient, authenticated_client: AsyncClient
) -> None:
"""Test POST endpoint performance requirements"""
test_payload = {
"model": "gpt-3.5-turbo",
"messages": [{"role": "user", "content": "Performance test"}],
}
validator = PerformanceValidator()
with patch("httpx.AsyncClient.send") as mock_send:
# Mock fast responses
async def mock_iter_bytes(*args: Any, **kwargs: Any) -> Any:
yield b'{"choices": [{"message": {"content": "Fast"}}], "usage": {"total_tokens": 5}}'
mock_response = AsyncMock()
mock_response.status_code = 200
mock_response.headers = {"content-type": "application/json"}
response_data = {
"choices": [{"message": {"content": "Fast"}}],
"usage": {"total_tokens": 5},
}
mock_response.text = json.dumps(response_data)
mock_response.json = AsyncMock(return_value=response_data)
mock_response.iter_bytes = mock_iter_bytes
mock_response.aiter_bytes = mock_iter_bytes
mock_send.return_value = mock_response
# Run multiple requests for performance measurement
for i in range(20):
start = validator.start_timing("proxy_post")
response = await authenticated_client.post(
"/v1/chat/completions", json=test_payload
)
validator.end_timing("proxy_post", start)
assert response.status_code == 200
# Validate performance
perf_result = validator.validate_response_time(
"proxy_post",
max_duration=1.5, # Allow slightly more time for POST
percentile=0.95,
)
assert perf_result["valid"], f"Performance requirement failed: {perf_result}"
@pytest.mark.integration
+1 -27
View File
@@ -3,7 +3,7 @@ Integration tests for wallet information retrieval endpoints.
Tests GET /v1/wallet/ and GET /v1/wallet/info endpoints with various scenarios.
"""
import time
from datetime import datetime, timedelta
from typing import Any
@@ -367,30 +367,4 @@ async def test_wallet_info_with_special_characters_in_headers(
# Note: Current implementation doesn't return refund_address in response
@pytest.mark.integration
@pytest.mark.asyncio
@pytest.mark.slow
async def test_wallet_endpoints_performance(authenticated_client: AsyncClient) -> None:
"""Test wallet endpoints meet performance requirements"""
# Warm up
await authenticated_client.get("/v1/wallet/")
# Measure response times
response_times = []
for _ in range(50):
start_time = time.time()
response = await authenticated_client.get("/v1/wallet/")
end_time = time.time()
assert response.status_code == 200
response_times.append(end_time - start_time)
# Calculate statistics
avg_time = sum(response_times) / len(response_times)
max_time = max(response_times)
# Performance assertions
assert avg_time < 0.1 # Average should be under 100ms
assert max_time < 0.5 # No request should take more than 500ms
-38
View File
@@ -537,42 +537,4 @@ async def test_refund_with_expired_key(
assert response.json()["recipient"] == "expired@ln.address"
@pytest.mark.integration
@pytest.mark.asyncio
@pytest.mark.slow
async def test_refund_performance(
integration_client: AsyncClient, testmint_wallet: Any
) -> None:
"""Test refund endpoint performance"""
import time
# Create multiple API keys
api_keys = []
for i in range(10):
token = await testmint_wallet.mint_tokens(100 + i)
# Use cashu token as Bearer auth to create API key
integration_client.headers["Authorization"] = f"Bearer {token}"
response = await integration_client.get("/v1/wallet/info")
assert response.status_code == 200
api_keys.append(response.json()["api_key"])
# Measure refund times
refund_times = []
for api_key in api_keys:
integration_client.headers["Authorization"] = f"Bearer {api_key}"
start_time = time.time()
response = await integration_client.post("/v1/wallet/refund")
end_time = time.time()
assert response.status_code == 200
refund_times.append(end_time - start_time)
# Performance assertions
avg_time = sum(refund_times) / len(refund_times)
max_time = max(refund_times)
assert avg_time < 0.5 # Average under 500ms
assert max_time < 1.0 # No refund takes more than 1 second