TradingAgents/tests/unit/api/test_settings_model.py

1018 lines
35 KiB
Python

"""Unit tests for Settings model (Issue #5: DB-4).
Tests for Settings model fields including:
- RiskProfile enum (CONSERVATIVE, MODERATE, AGGRESSIVE)
- risk_score (0-10 validation)
- max_position_pct (0-100 validation)
- max_portfolio_risk_pct (0-100 validation)
- investment_horizon_years (>=0 validation)
- alert_preferences (JSON structure)
- One-to-one relationship with User
- Unique constraint on user_id
- Cascade delete behavior
- CheckConstraints for numeric bounds
Follows TDD principles with comprehensive coverage.
Tests written BEFORE implementation (RED phase).
"""
import pytest
import json
from decimal import Decimal
from sqlalchemy import select
from sqlalchemy.exc import IntegrityError
# Mark all tests in this module as asyncio
pytestmark = pytest.mark.asyncio
class TestSettingsBasicFields:
"""Tests for basic Settings model fields."""
@pytest.mark.asyncio
async def test_create_settings_with_required_fields(self, db_session, test_user):
"""Should create settings with only required fields (user_id)."""
try:
from tradingagents.api.models.settings import Settings, RiskProfile
settings = Settings(
user_id=test_user.id,
)
db_session.add(settings)
await db_session.commit()
await db_session.refresh(settings)
# Assert
assert settings.id is not None
assert settings.user_id == test_user.id
except ImportError:
pytest.skip("Settings model not yet implemented (TDD RED phase)")
@pytest.mark.asyncio
async def test_settings_defaults(self, db_session, test_user):
"""Should apply default values to optional fields."""
try:
from tradingagents.api.models.settings import Settings, RiskProfile
settings = Settings(
user_id=test_user.id,
)
db_session.add(settings)
await db_session.commit()
await db_session.refresh(settings)
# Check defaults
assert settings.risk_profile == RiskProfile.MODERATE
assert settings.risk_score == Decimal("5.0")
assert settings.max_position_pct == Decimal("10.0")
assert settings.max_portfolio_risk_pct == Decimal("2.0")
assert settings.investment_horizon_years == 5
assert settings.alert_preferences == {}
assert settings.created_at is not None
assert settings.updated_at is not None
except ImportError:
pytest.skip("Settings model not yet implemented (TDD RED phase)")
@pytest.mark.asyncio
async def test_settings_with_all_fields(self, db_session, test_user):
"""Should create settings with all fields specified."""
try:
from tradingagents.api.models.settings import Settings, RiskProfile
alert_prefs = {
"email": {
"enabled": True,
"address": "user@example.com",
"alert_types": ["price_alert", "portfolio_alert"]
},
"sms": {
"enabled": True,
"phone": "+1234567890",
"rate_limit": {"max_per_hour": 5}
}
}
settings = Settings(
user_id=test_user.id,
risk_profile=RiskProfile.AGGRESSIVE,
risk_score=Decimal("8.5"),
max_position_pct=Decimal("25.0"),
max_portfolio_risk_pct=Decimal("5.0"),
investment_horizon_years=10,
alert_preferences=alert_prefs,
)
db_session.add(settings)
await db_session.commit()
await db_session.refresh(settings)
# Assert all fields
assert settings.id is not None
assert settings.user_id == test_user.id
assert settings.risk_profile == RiskProfile.AGGRESSIVE
assert settings.risk_score == Decimal("8.5")
assert settings.max_position_pct == Decimal("25.0")
assert settings.max_portfolio_risk_pct == Decimal("5.0")
assert settings.investment_horizon_years == 10
assert settings.alert_preferences == alert_prefs
except ImportError:
pytest.skip("Settings model not yet implemented (TDD RED phase)")
@pytest.mark.asyncio
async def test_settings_timestamps_auto_populate(self, db_session, test_user):
"""Should auto-populate created_at and updated_at timestamps."""
try:
from tradingagents.api.models.settings import Settings
from datetime import datetime
settings = Settings(
user_id=test_user.id,
)
db_session.add(settings)
await db_session.commit()
await db_session.refresh(settings)
# Assert timestamps exist and are recent
assert settings.created_at is not None
assert settings.updated_at is not None
assert isinstance(settings.created_at, datetime)
assert isinstance(settings.updated_at, datetime)
except ImportError:
pytest.skip("Settings model not yet implemented (TDD RED phase)")
class TestRiskProfileEnum:
"""Tests for RiskProfile enum validation."""
@pytest.mark.asyncio
async def test_risk_profile_conservative(self, db_session, test_user):
"""Should create settings with CONSERVATIVE risk profile."""
try:
from tradingagents.api.models.settings import Settings, RiskProfile
settings = Settings(
user_id=test_user.id,
risk_profile=RiskProfile.CONSERVATIVE,
)
db_session.add(settings)
await db_session.commit()
await db_session.refresh(settings)
assert settings.risk_profile == RiskProfile.CONSERVATIVE
assert settings.risk_profile.value == "CONSERVATIVE"
except ImportError:
pytest.skip("Settings model not yet implemented (TDD RED phase)")
@pytest.mark.asyncio
async def test_risk_profile_moderate(self, db_session, test_user):
"""Should create settings with MODERATE risk profile."""
try:
from tradingagents.api.models.settings import Settings, RiskProfile
settings = Settings(
user_id=test_user.id,
risk_profile=RiskProfile.MODERATE,
)
db_session.add(settings)
await db_session.commit()
await db_session.refresh(settings)
assert settings.risk_profile == RiskProfile.MODERATE
assert settings.risk_profile.value == "MODERATE"
except ImportError:
pytest.skip("Settings model not yet implemented (TDD RED phase)")
@pytest.mark.asyncio
async def test_risk_profile_aggressive(self, db_session, test_user):
"""Should create settings with AGGRESSIVE risk profile."""
try:
from tradingagents.api.models.settings import Settings, RiskProfile
settings = Settings(
user_id=test_user.id,
risk_profile=RiskProfile.AGGRESSIVE,
)
db_session.add(settings)
await db_session.commit()
await db_session.refresh(settings)
assert settings.risk_profile == RiskProfile.AGGRESSIVE
assert settings.risk_profile.value == "AGGRESSIVE"
except ImportError:
pytest.skip("Settings model not yet implemented (TDD RED phase)")
@pytest.mark.asyncio
async def test_risk_profile_invalid_value(self, db_session, test_user):
"""Should reject invalid risk profile values."""
try:
from tradingagents.api.models.settings import Settings
# Attempting to use invalid value should raise ValueError
with pytest.raises((ValueError, AttributeError)):
settings = Settings(
user_id=test_user.id,
risk_profile="INVALID_RISK_PROFILE",
)
db_session.add(settings)
await db_session.commit()
except ImportError:
pytest.skip("Settings model not yet implemented (TDD RED phase)")
class TestRiskScoreValidation:
"""Tests for risk_score field validation (0-10 range)."""
@pytest.mark.asyncio
async def test_risk_score_minimum_valid(self, db_session, test_user):
"""Should accept risk_score of 0 (minimum valid)."""
try:
from tradingagents.api.models.settings import Settings
settings = Settings(
user_id=test_user.id,
risk_score=Decimal("0.0"),
)
db_session.add(settings)
await db_session.commit()
await db_session.refresh(settings)
assert settings.risk_score == Decimal("0.0")
except ImportError:
pytest.skip("Settings model not yet implemented (TDD RED phase)")
@pytest.mark.asyncio
async def test_risk_score_maximum_valid(self, db_session, test_user):
"""Should accept risk_score of 10 (maximum valid)."""
try:
from tradingagents.api.models.settings import Settings
settings = Settings(
user_id=test_user.id,
risk_score=Decimal("10.0"),
)
db_session.add(settings)
await db_session.commit()
await db_session.refresh(settings)
assert settings.risk_score == Decimal("10.0")
except ImportError:
pytest.skip("Settings model not yet implemented (TDD RED phase)")
@pytest.mark.asyncio
async def test_risk_score_mid_range(self, db_session, test_user):
"""Should accept mid-range risk_score values."""
try:
from tradingagents.api.models.settings import Settings
settings = Settings(
user_id=test_user.id,
risk_score=Decimal("5.5"),
)
db_session.add(settings)
await db_session.commit()
await db_session.refresh(settings)
assert settings.risk_score == Decimal("5.5")
except ImportError:
pytest.skip("Settings model not yet implemented (TDD RED phase)")
@pytest.mark.asyncio
async def test_risk_score_out_of_range(self, db_session, test_user):
"""Should reject risk_score outside 0-10 range."""
try:
from tradingagents.api.models.settings import Settings
from sqlalchemy.exc import IntegrityError
# Store user_id before async operations to avoid lazy load after rollback
user_id = test_user.id
# Test negative value
settings = Settings(
user_id=user_id,
risk_score=Decimal("-1.0"),
)
db_session.add(settings)
with pytest.raises(IntegrityError):
await db_session.commit()
await db_session.rollback()
# Test value > 10
settings2 = Settings(
user_id=user_id,
risk_score=Decimal("11.0"),
)
db_session.add(settings2)
with pytest.raises(IntegrityError):
await db_session.commit()
except ImportError:
pytest.skip("Settings model not yet implemented (TDD RED phase)")
class TestMaxPositionPctValidation:
"""Tests for max_position_pct field validation (0-100 range)."""
@pytest.mark.asyncio
async def test_max_position_pct_minimum_valid(self, db_session, test_user):
"""Should accept max_position_pct of 0."""
try:
from tradingagents.api.models.settings import Settings
settings = Settings(
user_id=test_user.id,
max_position_pct=Decimal("0.0"),
)
db_session.add(settings)
await db_session.commit()
await db_session.refresh(settings)
assert settings.max_position_pct == Decimal("0.0")
except ImportError:
pytest.skip("Settings model not yet implemented (TDD RED phase)")
@pytest.mark.asyncio
async def test_max_position_pct_maximum_valid(self, db_session, test_user):
"""Should accept max_position_pct of 100."""
try:
from tradingagents.api.models.settings import Settings
settings = Settings(
user_id=test_user.id,
max_position_pct=Decimal("100.0"),
)
db_session.add(settings)
await db_session.commit()
await db_session.refresh(settings)
assert settings.max_position_pct == Decimal("100.0")
except ImportError:
pytest.skip("Settings model not yet implemented (TDD RED phase)")
@pytest.mark.asyncio
async def test_max_position_pct_out_of_range(self, db_session, test_user):
"""Should reject max_position_pct outside 0-100 range."""
try:
from tradingagents.api.models.settings import Settings
from sqlalchemy.exc import IntegrityError
# Store user_id before async operations to avoid lazy load after rollback
user_id = test_user.id
# Test negative value
settings = Settings(
user_id=user_id,
max_position_pct=Decimal("-1.0"),
)
db_session.add(settings)
with pytest.raises(IntegrityError):
await db_session.commit()
await db_session.rollback()
# Test value > 100
settings2 = Settings(
user_id=user_id,
max_position_pct=Decimal("101.0"),
)
db_session.add(settings2)
with pytest.raises(IntegrityError):
await db_session.commit()
except ImportError:
pytest.skip("Settings model not yet implemented (TDD RED phase)")
class TestMaxPortfolioRiskPctValidation:
"""Tests for max_portfolio_risk_pct field validation (0-100 range)."""
@pytest.mark.asyncio
async def test_max_portfolio_risk_pct_minimum_valid(self, db_session, test_user):
"""Should accept max_portfolio_risk_pct of 0."""
try:
from tradingagents.api.models.settings import Settings
settings = Settings(
user_id=test_user.id,
max_portfolio_risk_pct=Decimal("0.0"),
)
db_session.add(settings)
await db_session.commit()
await db_session.refresh(settings)
assert settings.max_portfolio_risk_pct == Decimal("0.0")
except ImportError:
pytest.skip("Settings model not yet implemented (TDD RED phase)")
@pytest.mark.asyncio
async def test_max_portfolio_risk_pct_maximum_valid(self, db_session, test_user):
"""Should accept max_portfolio_risk_pct of 100."""
try:
from tradingagents.api.models.settings import Settings
settings = Settings(
user_id=test_user.id,
max_portfolio_risk_pct=Decimal("100.0"),
)
db_session.add(settings)
await db_session.commit()
await db_session.refresh(settings)
assert settings.max_portfolio_risk_pct == Decimal("100.0")
except ImportError:
pytest.skip("Settings model not yet implemented (TDD RED phase)")
@pytest.mark.asyncio
async def test_max_portfolio_risk_pct_out_of_range(self, db_session, test_user):
"""Should reject max_portfolio_risk_pct outside 0-100 range."""
try:
from tradingagents.api.models.settings import Settings
from sqlalchemy.exc import IntegrityError
# Store user_id before async operations to avoid lazy load after rollback
user_id = test_user.id
# Test negative value
settings = Settings(
user_id=user_id,
max_portfolio_risk_pct=Decimal("-1.0"),
)
db_session.add(settings)
with pytest.raises(IntegrityError):
await db_session.commit()
await db_session.rollback()
# Test value > 100
settings2 = Settings(
user_id=user_id,
max_portfolio_risk_pct=Decimal("101.0"),
)
db_session.add(settings2)
with pytest.raises(IntegrityError):
await db_session.commit()
except ImportError:
pytest.skip("Settings model not yet implemented (TDD RED phase)")
class TestInvestmentHorizonValidation:
"""Tests for investment_horizon_years field validation (>=0)."""
@pytest.mark.asyncio
async def test_investment_horizon_valid_positive(self, db_session, test_user):
"""Should accept positive investment horizon values."""
try:
from tradingagents.api.models.settings import Settings
settings = Settings(
user_id=test_user.id,
investment_horizon_years=15,
)
db_session.add(settings)
await db_session.commit()
await db_session.refresh(settings)
assert settings.investment_horizon_years == 15
except ImportError:
pytest.skip("Settings model not yet implemented (TDD RED phase)")
@pytest.mark.asyncio
async def test_investment_horizon_zero_valid(self, db_session, test_user):
"""Should accept investment horizon of 0."""
try:
from tradingagents.api.models.settings import Settings
settings = Settings(
user_id=test_user.id,
investment_horizon_years=0,
)
db_session.add(settings)
await db_session.commit()
await db_session.refresh(settings)
assert settings.investment_horizon_years == 0
except ImportError:
pytest.skip("Settings model not yet implemented (TDD RED phase)")
@pytest.mark.asyncio
async def test_investment_horizon_negative_invalid(self, db_session, test_user):
"""Should reject negative investment horizon values."""
try:
from tradingagents.api.models.settings import Settings
from sqlalchemy.exc import IntegrityError
settings = Settings(
user_id=test_user.id,
investment_horizon_years=-1,
)
db_session.add(settings)
with pytest.raises(IntegrityError):
await db_session.commit()
except ImportError:
pytest.skip("Settings model not yet implemented (TDD RED phase)")
class TestAlertPreferencesJSON:
"""Tests for alert_preferences JSON field."""
@pytest.mark.asyncio
async def test_alert_preferences_empty_dict(self, db_session, test_user):
"""Should accept empty dict as alert preferences."""
try:
from tradingagents.api.models.settings import Settings
settings = Settings(
user_id=test_user.id,
alert_preferences={},
)
db_session.add(settings)
await db_session.commit()
await db_session.refresh(settings)
assert settings.alert_preferences == {}
except ImportError:
pytest.skip("Settings model not yet implemented (TDD RED phase)")
@pytest.mark.asyncio
async def test_alert_preferences_email_config(self, db_session, test_user):
"""Should store email alert preferences."""
try:
from tradingagents.api.models.settings import Settings
email_prefs = {
"email": {
"enabled": True,
"address": "user@example.com",
"alert_types": ["price_alert", "portfolio_alert", "execution_alert"]
}
}
settings = Settings(
user_id=test_user.id,
alert_preferences=email_prefs,
)
db_session.add(settings)
await db_session.commit()
await db_session.refresh(settings)
assert settings.alert_preferences == email_prefs
assert settings.alert_preferences["email"]["enabled"] is True
assert settings.alert_preferences["email"]["address"] == "user@example.com"
except ImportError:
pytest.skip("Settings model not yet implemented (TDD RED phase)")
@pytest.mark.asyncio
async def test_alert_preferences_sms_config(self, db_session, test_user):
"""Should store SMS alert preferences."""
try:
from tradingagents.api.models.settings import Settings
sms_prefs = {
"sms": {
"enabled": True,
"phone": "+1234567890",
"alert_types": ["critical_alert"],
"rate_limit": {"max_per_hour": 5, "max_per_day": 20}
}
}
settings = Settings(
user_id=test_user.id,
alert_preferences=sms_prefs,
)
db_session.add(settings)
await db_session.commit()
await db_session.refresh(settings)
assert settings.alert_preferences == sms_prefs
assert settings.alert_preferences["sms"]["phone"] == "+1234567890"
assert settings.alert_preferences["sms"]["rate_limit"]["max_per_hour"] == 5
except ImportError:
pytest.skip("Settings model not yet implemented (TDD RED phase)")
@pytest.mark.asyncio
async def test_alert_preferences_multiple_channels(self, db_session, test_user):
"""Should store preferences for multiple alert channels."""
try:
from tradingagents.api.models.settings import Settings
multi_channel_prefs = {
"email": {
"enabled": True,
"address": "user@example.com",
"alert_types": ["price_alert", "portfolio_alert"]
},
"sms": {
"enabled": True,
"phone": "+1234567890",
"rate_limit": {"max_per_hour": 5}
},
"push": {
"enabled": False,
"device_tokens": []
}
}
settings = Settings(
user_id=test_user.id,
alert_preferences=multi_channel_prefs,
)
db_session.add(settings)
await db_session.commit()
await db_session.refresh(settings)
assert settings.alert_preferences == multi_channel_prefs
assert "email" in settings.alert_preferences
assert "sms" in settings.alert_preferences
assert "push" in settings.alert_preferences
except ImportError:
pytest.skip("Settings model not yet implemented (TDD RED phase)")
@pytest.mark.asyncio
async def test_alert_preferences_nested_structure(self, db_session, test_user):
"""Should handle deeply nested JSON structures."""
try:
from tradingagents.api.models.settings import Settings
nested_prefs = {
"email": {
"enabled": True,
"address": "user@example.com",
"filters": {
"price_alerts": {
"min_change_pct": 5.0,
"symbols": ["AAPL", "GOOGL", "MSFT"]
},
"portfolio_alerts": {
"thresholds": {
"daily_loss": -2.0,
"daily_gain": 5.0
}
}
}
}
}
settings = Settings(
user_id=test_user.id,
alert_preferences=nested_prefs,
)
db_session.add(settings)
await db_session.commit()
await db_session.refresh(settings)
assert settings.alert_preferences == nested_prefs
assert settings.alert_preferences["email"]["filters"]["price_alerts"]["min_change_pct"] == 5.0
except ImportError:
pytest.skip("Settings model not yet implemented (TDD RED phase)")
@pytest.mark.asyncio
async def test_alert_preferences_rate_limiting(self, db_session, test_user):
"""Should store rate limiting configuration."""
try:
from tradingagents.api.models.settings import Settings
rate_limit_prefs = {
"sms": {
"enabled": True,
"phone": "+1234567890",
"rate_limit": {
"max_per_hour": 5,
"max_per_day": 20,
"max_per_week": 100,
"cooldown_minutes": 15
}
}
}
settings = Settings(
user_id=test_user.id,
alert_preferences=rate_limit_prefs,
)
db_session.add(settings)
await db_session.commit()
await db_session.refresh(settings)
assert settings.alert_preferences["sms"]["rate_limit"]["max_per_hour"] == 5
assert settings.alert_preferences["sms"]["rate_limit"]["max_per_day"] == 20
assert settings.alert_preferences["sms"]["rate_limit"]["cooldown_minutes"] == 15
except ImportError:
pytest.skip("Settings model not yet implemented (TDD RED phase)")
@pytest.mark.asyncio
async def test_alert_preferences_update(self, db_session, test_user):
"""Should allow updating alert preferences."""
try:
from tradingagents.api.models.settings import Settings
settings = Settings(
user_id=test_user.id,
alert_preferences={"email": {"enabled": False}},
)
db_session.add(settings)
await db_session.commit()
await db_session.refresh(settings)
# Update preferences
settings.alert_preferences = {
"email": {"enabled": True, "address": "new@example.com"}
}
await db_session.commit()
await db_session.refresh(settings)
assert settings.alert_preferences["email"]["enabled"] is True
assert settings.alert_preferences["email"]["address"] == "new@example.com"
except ImportError:
pytest.skip("Settings model not yet implemented (TDD RED phase)")
@pytest.mark.asyncio
async def test_alert_preferences_null_allowed(self, db_session, test_user):
"""Should allow NULL alert preferences if column is nullable."""
try:
from tradingagents.api.models.settings import Settings
settings = Settings(
user_id=test_user.id,
alert_preferences=None,
)
db_session.add(settings)
await db_session.commit()
await db_session.refresh(settings)
# Should default to empty dict or be None
assert settings.alert_preferences is None or settings.alert_preferences == {}
except ImportError:
pytest.skip("Settings model not yet implemented (TDD RED phase)")
class TestUserRelationship:
"""Tests for Settings-User one-to-one relationship."""
@pytest.mark.asyncio
async def test_settings_belongs_to_user(self, db_session, test_user):
"""Should establish relationship between settings and user."""
try:
from tradingagents.api.models.settings import Settings
settings = Settings(
user_id=test_user.id,
)
db_session.add(settings)
await db_session.commit()
await db_session.refresh(settings)
# Verify relationship
assert settings.user_id == test_user.id
# If relationship is set up, we should be able to access user
# assert settings.user == test_user # Uncomment when relationship is implemented
except ImportError:
pytest.skip("Settings model not yet implemented (TDD RED phase)")
@pytest.mark.asyncio
async def test_one_settings_per_user_constraint(self, db_session, test_user):
"""Should enforce unique constraint - one settings per user."""
try:
from tradingagents.api.models.settings import Settings
from sqlalchemy.exc import IntegrityError
# Create first settings
settings1 = Settings(user_id=test_user.id)
db_session.add(settings1)
await db_session.commit()
# Try to create second settings for same user
settings2 = Settings(user_id=test_user.id)
db_session.add(settings2)
with pytest.raises(IntegrityError):
await db_session.commit()
except ImportError:
pytest.skip("Settings model not yet implemented (TDD RED phase)")
@pytest.mark.asyncio
async def test_settings_cascade_delete_with_user(self, db_session, test_user):
"""Should cascade delete settings when user is deleted."""
try:
from tradingagents.api.models.settings import Settings
from tradingagents.api.models import User
# Create settings
settings = Settings(user_id=test_user.id)
db_session.add(settings)
await db_session.commit()
settings_id = settings.id
# Delete user
await db_session.delete(test_user)
await db_session.commit()
# Verify settings was deleted
result = await db_session.execute(
select(Settings).where(Settings.id == settings_id)
)
deleted_settings = result.scalar_one_or_none()
assert deleted_settings is None
except ImportError:
pytest.skip("Settings model not yet implemented (TDD RED phase)")
@pytest.mark.asyncio
async def test_multiple_users_can_have_settings(self, db_session, test_user, second_user):
"""Should allow multiple users to have their own settings."""
try:
from tradingagents.api.models.settings import Settings, RiskProfile
# Create settings for first user
settings1 = Settings(
user_id=test_user.id,
risk_profile=RiskProfile.CONSERVATIVE,
)
db_session.add(settings1)
# Create settings for second user
settings2 = Settings(
user_id=second_user.id,
risk_profile=RiskProfile.AGGRESSIVE,
)
db_session.add(settings2)
await db_session.commit()
await db_session.refresh(settings1)
await db_session.refresh(settings2)
# Verify both settings exist with different configurations
assert settings1.user_id == test_user.id
assert settings1.risk_profile == RiskProfile.CONSERVATIVE
assert settings2.user_id == second_user.id
assert settings2.risk_profile == RiskProfile.AGGRESSIVE
except ImportError:
pytest.skip("Settings model not yet implemented (TDD RED phase)")
class TestSettingsConstraints:
"""Tests for database constraints and edge cases."""
@pytest.mark.asyncio
async def test_risk_score_boundary_values(self, db_session, test_user):
"""Should accept exact boundary values for risk_score."""
try:
from tradingagents.api.models.settings import Settings
# Test 0 exactly
settings1 = Settings(user_id=test_user.id, risk_score=Decimal("0"))
db_session.add(settings1)
await db_session.commit()
assert settings1.risk_score == Decimal("0")
await db_session.delete(settings1)
await db_session.commit()
# Test 10 exactly (with new user since one-to-one)
from tradingagents.api.models import User
from tradingagents.api.services.auth_service import hash_password
user2 = User(
username="testuser2",
email="test2@example.com",
hashed_password=hash_password("password123"),
)
db_session.add(user2)
await db_session.commit()
await db_session.refresh(user2)
settings2 = Settings(user_id=user2.id, risk_score=Decimal("10"))
db_session.add(settings2)
await db_session.commit()
assert settings2.risk_score == Decimal("10")
except ImportError:
pytest.skip("Settings model not yet implemented (TDD RED phase)")
@pytest.mark.asyncio
async def test_percentage_boundary_values(self, db_session, test_user):
"""Should accept exact boundary values for percentage fields."""
try:
from tradingagents.api.models.settings import Settings
settings = Settings(
user_id=test_user.id,
max_position_pct=Decimal("0"),
max_portfolio_risk_pct=Decimal("100"),
)
db_session.add(settings)
await db_session.commit()
await db_session.refresh(settings)
assert settings.max_position_pct == Decimal("0")
assert settings.max_portfolio_risk_pct == Decimal("100")
except ImportError:
pytest.skip("Settings model not yet implemented (TDD RED phase)")
@pytest.mark.asyncio
async def test_decimal_precision_preserved(self, db_session, test_user):
"""Should preserve decimal precision for numeric fields."""
try:
from tradingagents.api.models.settings import Settings
settings = Settings(
user_id=test_user.id,
risk_score=Decimal("7.5"),
max_position_pct=Decimal("15.25"),
max_portfolio_risk_pct=Decimal("3.75"),
)
db_session.add(settings)
await db_session.commit()
await db_session.refresh(settings)
assert settings.risk_score == Decimal("7.5")
assert settings.max_position_pct == Decimal("15.25")
assert settings.max_portfolio_risk_pct == Decimal("3.75")
except ImportError:
pytest.skip("Settings model not yet implemented (TDD RED phase)")
@pytest.mark.asyncio
async def test_required_user_id_constraint(self, db_session):
"""Should require user_id (NOT NULL constraint)."""
try:
from tradingagents.api.models.settings import Settings
from sqlalchemy.exc import IntegrityError
settings = Settings(
risk_profile="MODERATE",
# user_id intentionally missing
)
db_session.add(settings)
with pytest.raises(IntegrityError):
await db_session.commit()
except ImportError:
pytest.skip("Settings model not yet implemented (TDD RED phase)")