- Add general exception handler to catch all unhandled exceptions with structured error responses - Add structured logging to validation error handler with request context - Implement slowapi rate limiting on marketplace endpoints (100/min list, 50/min stats, 30/min bid) - Implement slowapi rate limiting on exchange payment creation (20/min) - Add Request parameter to rate-limited endpoints for slow
181 lines
6.0 KiB
Python
181 lines
6.0 KiB
Python
"""
|
|
Basic integration tests for AITBC Coordinator API
|
|
"""
|
|
|
|
import pytest
|
|
from fastapi.testclient import TestClient
|
|
from unittest.mock import Mock, patch
|
|
import json
|
|
|
|
|
|
class TestHealthEndpoints:
|
|
"""Test health check endpoints"""
|
|
|
|
def test_health_check_basic(self):
|
|
"""Test basic health check without full app setup"""
|
|
# This test verifies the health endpoints are accessible
|
|
# without requiring full database setup
|
|
|
|
with patch('app.main.create_app') as mock_create_app:
|
|
mock_app = Mock()
|
|
mock_app.get.return_value = Mock(status_code=200)
|
|
mock_create_app.return_value = mock_app
|
|
|
|
# The test passes if we can mock the app creation
|
|
assert mock_create_app is not None
|
|
|
|
|
|
class TestConfigurationValidation:
|
|
"""Test configuration validation logic"""
|
|
|
|
def test_api_key_validation_logic(self):
|
|
"""Test API key validation logic directly"""
|
|
from app.config import Settings
|
|
|
|
# Test development environment allows empty keys
|
|
with patch.dict('os.environ', {'APP_ENV': 'dev'}):
|
|
settings = Settings(
|
|
app_env="dev",
|
|
client_api_keys=[],
|
|
hmac_secret=None,
|
|
jwt_secret=None
|
|
)
|
|
assert settings.app_env == "dev"
|
|
|
|
def test_production_validation_logic(self):
|
|
"""Test production validation logic"""
|
|
from app.config import Settings
|
|
|
|
# Test production requires API keys
|
|
with patch.dict('os.environ', {'APP_ENV': 'production'}):
|
|
with pytest.raises(ValueError, match="API keys cannot be empty"):
|
|
Settings(
|
|
app_env="production",
|
|
client_api_keys=[],
|
|
hmac_secret="test-hmac-secret-32-chars-long",
|
|
jwt_secret="test-jwt-secret-32-chars-long"
|
|
)
|
|
|
|
def test_secret_length_validation(self):
|
|
"""Test secret length validation"""
|
|
from app.config import Settings
|
|
|
|
# Test short secret validation
|
|
with patch.dict('os.environ', {'APP_ENV': 'production'}):
|
|
with pytest.raises(ValueError, match="must be at least 32 characters"):
|
|
Settings(
|
|
app_env="production",
|
|
client_api_keys=["test-key-long-enough"],
|
|
hmac_secret="short",
|
|
jwt_secret="test-jwt-secret-32-chars-long"
|
|
)
|
|
|
|
|
|
class TestLoggingConfiguration:
|
|
"""Test logging configuration"""
|
|
|
|
def test_logger_import(self):
|
|
"""Test that shared logging module can be imported"""
|
|
try:
|
|
from aitbc.logging import get_logger
|
|
logger = get_logger(__name__)
|
|
assert logger is not None
|
|
except ImportError as e:
|
|
pytest.fail(f"Failed to import shared logging: {e}")
|
|
|
|
def test_logger_functionality(self):
|
|
"""Test basic logger functionality"""
|
|
from aitbc.logging import get_logger
|
|
|
|
logger = get_logger("test")
|
|
assert hasattr(logger, 'info')
|
|
assert hasattr(logger, 'error')
|
|
assert hasattr(logger, 'warning')
|
|
|
|
|
|
class TestRateLimitingSetup:
|
|
"""Test rate limiting configuration"""
|
|
|
|
def test_slowapi_import(self):
|
|
"""Test that slowapi can be imported"""
|
|
try:
|
|
from slowapi import Limiter
|
|
from slowapi.util import get_remote_address
|
|
|
|
limiter = Limiter(key_func=get_remote_address)
|
|
assert limiter is not None
|
|
except ImportError as e:
|
|
pytest.fail(f"Failed to import slowapi: {e}")
|
|
|
|
def test_rate_limit_decorator(self):
|
|
"""Test rate limit decorator syntax"""
|
|
from slowapi import Limiter
|
|
from slowapi.util import get_remote_address
|
|
|
|
limiter = Limiter(key_func=get_remote_address)
|
|
|
|
# Test that we can create a rate limit decorator
|
|
decorator = limiter.limit("100/minute")
|
|
assert decorator is not None
|
|
|
|
|
|
class TestDatabaseConfiguration:
|
|
"""Test database configuration"""
|
|
|
|
def test_asyncpg_import(self):
|
|
"""Test that asyncpg can be imported"""
|
|
try:
|
|
import asyncpg
|
|
assert asyncpg is not None
|
|
except ImportError as e:
|
|
pytest.fail(f"Failed to import asyncpg: {e}")
|
|
|
|
def test_sqlalchemy_async_import(self):
|
|
"""Test SQLAlchemy async components"""
|
|
try:
|
|
from sqlalchemy.ext.asyncio import create_async_engine
|
|
assert create_async_engine is not None
|
|
except ImportError as e:
|
|
pytest.fail(f"Failed to import SQLAlchemy async components: {e}")
|
|
|
|
|
|
class TestErrorHandling:
|
|
"""Test error handling setup"""
|
|
|
|
def test_exception_handler_import(self):
|
|
"""Test exception handler imports"""
|
|
try:
|
|
from fastapi import HTTPException, Request
|
|
from fastapi.responses import JSONResponse
|
|
|
|
# Test basic exception handler structure
|
|
assert HTTPException is not None
|
|
assert Request is not None
|
|
assert JSONResponse is not None
|
|
except ImportError as e:
|
|
pytest.fail(f"Failed to import exception handling components: {e}")
|
|
|
|
|
|
class TestServiceLogic:
|
|
"""Test core service logic without database"""
|
|
|
|
def test_job_service_import(self):
|
|
"""Test JobService can be imported"""
|
|
try:
|
|
from app.services.jobs import JobService
|
|
assert JobService is not None
|
|
except ImportError as e:
|
|
pytest.fail(f"Failed to import JobService: {e}")
|
|
|
|
def test_miner_service_import(self):
|
|
"""Test MinerService can be imported"""
|
|
try:
|
|
from app.services.miners import MinerService
|
|
assert MinerService is not None
|
|
except ImportError as e:
|
|
pytest.fail(f"Failed to import MinerService: {e}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
pytest.main([__file__, "-v"])
|