TradingAgents/tests/api/test_config.py

477 lines
16 KiB
Python

"""
Test suite for API configuration and settings.
This module tests Issue #48 configuration features:
1. Settings loading from environment variables
2. JWT configuration (secret key, algorithm, expiration)
3. Database URL configuration
4. CORS configuration
5. Environment-specific settings (dev/prod)
6. Configuration validation
Tests follow TDD - written before implementation.
"""
import pytest
import os
from typing import Dict, Any
from unittest.mock import patch
# ============================================================================
# Unit Tests: Settings Loading
# ============================================================================
class TestSettingsLoading:
"""Test configuration settings loading."""
def test_load_settings_from_environment(self):
"""Test that settings are loaded from environment variables."""
# Arrange
try:
with patch.dict(os.environ, {
"DATABASE_URL": "sqlite+aiosqlite:///test.db",
"JWT_SECRET_KEY": "test-secret-key",
"JWT_ALGORITHM": "HS256",
"JWT_EXPIRATION_MINUTES": "30",
}):
# Act
from tradingagents.api.config import Settings
settings = Settings()
# Assert
assert settings.DATABASE_URL == "sqlite+aiosqlite:///test.db"
assert settings.JWT_SECRET_KEY == "test-secret-key"
assert settings.JWT_ALGORITHM == "HS256"
assert settings.JWT_EXPIRATION_MINUTES == 30
except ImportError:
pytest.skip("Config not implemented yet")
def test_settings_default_values(self):
"""Test that settings have sensible defaults."""
# Arrange
try:
with patch.dict(os.environ, {}, clear=True):
# Act
from tradingagents.api.config import Settings
settings = Settings()
# Assert: Should have defaults
assert hasattr(settings, "JWT_ALGORITHM")
if settings.JWT_ALGORITHM:
assert settings.JWT_ALGORITHM == "HS256"
except ImportError:
pytest.skip("Config not implemented yet")
def test_settings_required_fields_validation(self):
"""Test that required settings raise error if missing."""
# Arrange
try:
with patch.dict(os.environ, {}, clear=True):
# Act & Assert
from tradingagents.api.config import Settings
# May raise ValidationError if required fields missing
# Or may use defaults - depends on implementation
settings = Settings()
assert settings is not None
except ImportError:
pytest.skip("Config not implemented yet")
except Exception as e:
# Expected if required fields are missing
assert "JWT_SECRET_KEY" in str(e) or True
# ============================================================================
# Unit Tests: JWT Configuration
# ============================================================================
class TestJWTConfiguration:
"""Test JWT-specific configuration."""
def test_jwt_secret_key_from_env(self):
"""Test JWT secret key is loaded from environment."""
# Arrange
try:
with patch.dict(os.environ, {
"JWT_SECRET_KEY": "my-super-secret-key-123",
}):
# Act
from tradingagents.api.config import Settings
settings = Settings()
# Assert
assert settings.JWT_SECRET_KEY == "my-super-secret-key-123"
except ImportError:
pytest.skip("Config not implemented yet")
def test_jwt_algorithm_configuration(self):
"""Test JWT algorithm can be configured."""
# Arrange
try:
with patch.dict(os.environ, {
"JWT_SECRET_KEY": "test-key",
"JWT_ALGORITHM": "HS512",
}):
# Act
from tradingagents.api.config import Settings
settings = Settings()
# Assert
assert settings.JWT_ALGORITHM == "HS512"
except ImportError:
pytest.skip("Config not implemented yet")
def test_jwt_expiration_minutes(self):
"""Test JWT expiration time configuration."""
# Arrange
try:
with patch.dict(os.environ, {
"JWT_SECRET_KEY": "test-key",
"JWT_EXPIRATION_MINUTES": "60",
}):
# Act
from tradingagents.api.config import Settings
settings = Settings()
# Assert
assert settings.JWT_EXPIRATION_MINUTES == 60
except ImportError:
pytest.skip("Config not implemented yet")
def test_jwt_secret_key_min_length(self):
"""Test that JWT secret key has minimum length requirement."""
# Arrange
try:
with patch.dict(os.environ, {
"JWT_SECRET_KEY": "short", # Too short
}):
# Act
from tradingagents.api.config import Settings
# May raise ValidationError for weak secret
# Or may accept it (validation in code)
settings = Settings()
# Assert: If no validation, at least warn
assert len(settings.JWT_SECRET_KEY) >= 5
except ImportError:
pytest.skip("Config not implemented yet")
except Exception:
# Expected if validation is strict
pass
# ============================================================================
# Unit Tests: Database Configuration
# ============================================================================
class TestDatabaseConfiguration:
"""Test database URL configuration."""
def test_database_url_from_env(self):
"""Test database URL is loaded from environment."""
# Arrange
try:
with patch.dict(os.environ, {
"DATABASE_URL": "postgresql+asyncpg://user:pass@localhost/db",
}):
# Act
from tradingagents.api.config import Settings
settings = Settings()
# Assert
assert settings.DATABASE_URL == "postgresql+asyncpg://user:pass@localhost/db"
except ImportError:
pytest.skip("Config not implemented yet")
def test_database_url_sqlite_default(self):
"""Test that SQLite is used if no DATABASE_URL provided."""
# Arrange
try:
with patch.dict(os.environ, {}, clear=True):
# Act
from tradingagents.api.config import Settings
settings = Settings()
# Assert: Should have some default
if hasattr(settings, "DATABASE_URL") and settings.DATABASE_URL:
assert "sqlite" in settings.DATABASE_URL.lower()
except ImportError:
pytest.skip("Config not implemented yet")
def test_database_url_validation(self):
"""Test that invalid database URLs are rejected."""
# Arrange
try:
with patch.dict(os.environ, {
"DATABASE_URL": "invalid-url",
}):
# Act
from tradingagents.api.config import Settings
# May raise ValidationError or accept it
settings = Settings()
# Assert: At least it's set
assert settings.DATABASE_URL is not None
except ImportError:
pytest.skip("Config not implemented yet")
# ============================================================================
# Unit Tests: CORS Configuration
# ============================================================================
class TestCORSConfiguration:
"""Test CORS (Cross-Origin Resource Sharing) configuration."""
def test_cors_origins_from_env(self):
"""Test CORS allowed origins configuration."""
# Arrange
try:
with patch.dict(os.environ, {
"CORS_ORIGINS": "http://localhost:3000,https://app.example.com",
}):
# Act
from tradingagents.api.config import Settings
settings = Settings()
# Assert
if hasattr(settings, "CORS_ORIGINS"):
assert "localhost:3000" in settings.CORS_ORIGINS
except ImportError:
pytest.skip("Config not implemented yet")
def test_cors_allow_credentials(self):
"""Test CORS allow credentials setting."""
# Arrange
try:
with patch.dict(os.environ, {
"CORS_ALLOW_CREDENTIALS": "true",
}):
# Act
from tradingagents.api.config import Settings
settings = Settings()
# Assert
if hasattr(settings, "CORS_ALLOW_CREDENTIALS"):
assert settings.CORS_ALLOW_CREDENTIALS is True
except ImportError:
pytest.skip("Config not implemented yet")
def test_cors_wildcard_origin(self):
"""Test CORS wildcard origin (*) configuration."""
# Arrange
try:
with patch.dict(os.environ, {
"CORS_ORIGINS": "*",
}):
# Act
from tradingagents.api.config import Settings
settings = Settings()
# Assert
if hasattr(settings, "CORS_ORIGINS"):
assert "*" in settings.CORS_ORIGINS or settings.CORS_ORIGINS == "*"
except ImportError:
pytest.skip("Config not implemented yet")
# ============================================================================
# Unit Tests: Environment-Specific Settings
# ============================================================================
class TestEnvironmentSettings:
"""Test environment-specific configuration (dev/staging/prod)."""
def test_debug_mode_in_development(self):
"""Test debug mode enabled in development."""
# Arrange
try:
with patch.dict(os.environ, {
"ENVIRONMENT": "development",
}):
# Act
from tradingagents.api.config import Settings
settings = Settings()
# Assert
if hasattr(settings, "DEBUG"):
assert settings.DEBUG is True
except ImportError:
pytest.skip("Config not implemented yet")
def test_debug_mode_in_production(self):
"""Test debug mode disabled in production."""
# Arrange
try:
with patch.dict(os.environ, {
"ENVIRONMENT": "production",
}):
# Act
from tradingagents.api.config import Settings
settings = Settings()
# Assert
if hasattr(settings, "DEBUG"):
assert settings.DEBUG is False
except ImportError:
pytest.skip("Config not implemented yet")
def test_log_level_configuration(self):
"""Test log level can be configured."""
# Arrange
try:
with patch.dict(os.environ, {
"LOG_LEVEL": "DEBUG",
}):
# Act
from tradingagents.api.config import Settings
settings = Settings()
# Assert
if hasattr(settings, "LOG_LEVEL"):
assert settings.LOG_LEVEL == "DEBUG"
except ImportError:
pytest.skip("Config not implemented yet")
# ============================================================================
# Integration Tests: Settings in Application
# ============================================================================
class TestSettingsIntegration:
"""Test that settings are used correctly in application."""
def test_settings_singleton_pattern(self):
"""Test that settings use singleton or cached instance."""
# Arrange
try:
from tradingagents.api.config import Settings
# Act
settings1 = Settings()
settings2 = Settings()
# Assert: May be same instance (singleton) or different but equal
assert settings1.JWT_ALGORITHM == settings2.JWT_ALGORITHM
except ImportError:
pytest.skip("Config not implemented yet")
def test_settings_in_dependency_injection(self):
"""Test that settings can be used in FastAPI dependencies."""
# This would test get_settings() dependency
try:
from tradingagents.api.dependencies import get_settings
# Act
settings = get_settings()
# Assert
assert settings is not None
assert hasattr(settings, "JWT_SECRET_KEY")
except ImportError:
pytest.skip("Dependencies not implemented yet")
# ============================================================================
# Edge Cases: Configuration
# ============================================================================
class TestConfigurationEdgeCases:
"""Test edge cases in configuration."""
def test_empty_jwt_secret_key(self):
"""Test handling of empty JWT secret key."""
# Arrange
try:
with patch.dict(os.environ, {
"JWT_SECRET_KEY": "",
}):
# Act & Assert
from tradingagents.api.config import Settings
# Should either raise error or use default
settings = Settings()
assert settings.JWT_SECRET_KEY != "" # Should have fallback
except ImportError:
pytest.skip("Config not implemented yet")
except Exception:
# Expected if validation is strict
pass
def test_negative_jwt_expiration(self):
"""Test handling of negative JWT expiration time."""
# Arrange
try:
with patch.dict(os.environ, {
"JWT_SECRET_KEY": "test-key",
"JWT_EXPIRATION_MINUTES": "-30",
}):
# Act
from tradingagents.api.config import Settings
# Should either raise error or use default
settings = Settings()
assert settings.JWT_EXPIRATION_MINUTES > 0
except ImportError:
pytest.skip("Config not implemented yet")
except Exception:
# Expected if validation rejects negative values
pass
def test_very_large_jwt_expiration(self):
"""Test handling of very large JWT expiration time."""
# Arrange
try:
with patch.dict(os.environ, {
"JWT_SECRET_KEY": "test-key",
"JWT_EXPIRATION_MINUTES": "525600", # 1 year
}):
# Act
from tradingagents.api.config import Settings
settings = Settings()
# Assert: Should accept or cap at reasonable max
assert settings.JWT_EXPIRATION_MINUTES <= 525600
except ImportError:
pytest.skip("Config not implemented yet")
def test_malformed_database_url(self):
"""Test handling of malformed database URL."""
# Arrange
malformed_urls = [
"not-a-url",
"postgresql://", # Incomplete
"sqlite://", # Missing path
]
try:
from tradingagents.api.config import Settings
for url in malformed_urls:
with patch.dict(os.environ, {"DATABASE_URL": url}):
# Act: Should either accept or reject
settings = Settings()
# No assertion - just check it doesn't crash
except ImportError:
pytest.skip("Config not implemented yet")
def test_unicode_in_config_values(self):
"""Test Unicode characters in configuration values."""
# Arrange
try:
with patch.dict(os.environ, {
"APP_NAME": "交易代理 🚀",
}):
# Act
from tradingagents.api.config import Settings
settings = Settings()
# Assert
if hasattr(settings, "APP_NAME"):
assert "🚀" in settings.APP_NAME
except ImportError:
pytest.skip("Config not implemented yet")