"""Tests for UsageStats model (T09). T09: Creare model UsageStats (SQLAlchemy) """ import pytest from datetime import datetime, date from decimal import Decimal from sqlalchemy import create_engine, inspect from sqlalchemy.orm import sessionmaker from sqlalchemy.exc import IntegrityError # Import models to register them with Base from openrouter_monitor.models import User, ApiKey, UsageStats, ApiToken from openrouter_monitor.database import Base @pytest.mark.unit class TestUsageStatsModelBasics: """Test UsageStats model basic attributes and creation.""" def test_usage_stats_model_exists(self): """Test that UsageStats model can be imported.""" # Assert assert UsageStats is not None assert hasattr(UsageStats, '__tablename__') assert UsageStats.__tablename__ == 'usage_stats' def test_usage_stats_has_required_fields(self): """Test that UsageStats model has all required fields.""" # Assert assert hasattr(UsageStats, 'id') assert hasattr(UsageStats, 'api_key_id') assert hasattr(UsageStats, 'date') assert hasattr(UsageStats, 'model') assert hasattr(UsageStats, 'requests_count') assert hasattr(UsageStats, 'tokens_input') assert hasattr(UsageStats, 'tokens_output') assert hasattr(UsageStats, 'cost') assert hasattr(UsageStats, 'created_at') def test_usage_stats_create_with_valid_data(self, tmp_path): """Test creating UsageStats with valid data.""" # Arrange engine = create_engine(f'sqlite:///{tmp_path}/test_usage.db') Session = sessionmaker(bind=engine) Base.metadata.create_all(bind=engine) session = Session() # Act stats = UsageStats( api_key_id=1, date=date.today(), model="anthropic/claude-3-opus" ) session.add(stats) session.flush() # Assert assert stats.api_key_id == 1 assert stats.model == "anthropic/claude-3-opus" assert stats.requests_count == 0 assert stats.tokens_input == 0 assert stats.tokens_output == 0 assert stats.cost == 0.0 session.close() def test_usage_stats_defaults_are_zero(self, tmp_path): """Test that numeric fields default to zero.""" # Arrange engine = create_engine(f'sqlite:///{tmp_path}/test_defaults.db') Session = sessionmaker(bind=engine) Base.metadata.create_all(bind=engine) session = Session() # Act stats = UsageStats( api_key_id=1, date=date.today(), model="gpt-4" ) session.add(stats) session.flush() # Assert assert stats.requests_count == 0 assert stats.tokens_input == 0 assert stats.tokens_output == 0 assert float(stats.cost) == 0.0 session.close() @pytest.mark.unit class TestUsageStatsConstraints: """Test UsageStats model constraints.""" def test_usage_stats_unique_constraint(self): """Test that unique constraint on (api_key_id, date, model) exists.""" # Assert assert hasattr(UsageStats, '__table_args__') def test_usage_stats_api_key_id_index_exists(self): """Test that api_key_id has an index.""" # Act inspector = inspect(UsageStats.__table__) indexes = inspector.indexes # Assert index_names = [idx.name for idx in indexes] assert any('api_key' in name for name in index_names) def test_usage_stats_date_index_exists(self): """Test that date has an index.""" # Act inspector = inspect(UsageStats.__table__) indexes = inspector.indexes # Assert index_names = [idx.name for idx in indexes] assert any('date' in name for name in index_names) def test_usage_stats_model_index_exists(self): """Test that model has an index.""" # Act inspector = inspect(UsageStats.__table__) indexes = inspector.indexes # Assert index_names = [idx.name for idx in indexes] assert any('model' in name for name in index_names) @pytest.mark.unit class TestUsageStatsRelationships: """Test UsageStats model relationships.""" def test_usage_stats_has_api_key_relationship(self): """Test that UsageStats has api_key relationship.""" # Assert assert hasattr(UsageStats, 'api_key') @pytest.mark.integration class TestUsageStatsDatabaseIntegration: """Integration tests for UsageStats model with database.""" def test_usage_stats_persist_and_retrieve(self, tmp_path): """Test persisting and retrieving usage stats from database.""" # Arrange engine = create_engine(f'sqlite:///{tmp_path}/test_stats_persist.db') Session = sessionmaker(bind=engine) Base.metadata.create_all(bind=engine) session = Session() today = date.today() # Act stats = UsageStats( api_key_id=1, date=today, model="anthropic/claude-3-opus", requests_count=100, tokens_input=50000, tokens_output=20000, cost=Decimal("15.50") ) session.add(stats) session.commit() # Retrieve retrieved = session.query(UsageStats).first() # Assert assert retrieved is not None assert retrieved.requests_count == 100 assert retrieved.tokens_input == 50000 assert retrieved.tokens_output == 20000 assert float(retrieved.cost) == 15.50 session.close() def test_usage_stats_unique_violation(self, tmp_path): """Test that duplicate (api_key_id, date, model) raises error.""" # Arrange engine = create_engine(f'sqlite:///{tmp_path}/test_unique.db') Session = sessionmaker(bind=engine) Base.metadata.create_all(bind=engine) session = Session() today = date.today() # Create first record stats1 = UsageStats( api_key_id=1, date=today, model="gpt-4", requests_count=10 ) session.add(stats1) session.commit() # Try to create duplicate stats2 = UsageStats( api_key_id=1, date=today, model="gpt-4", requests_count=20 ) session.add(stats2) # Assert - Should raise IntegrityError with pytest.raises(IntegrityError): session.commit() session.close() def test_usage_stats_numeric_precision(self, tmp_path): """Test that cost field stores numeric values correctly.""" # Arrange engine = create_engine(f'sqlite:///{tmp_path}/test_numeric.db') Session = sessionmaker(bind=engine) Base.metadata.create_all(bind=engine) session = Session() # Act stats = UsageStats( api_key_id=1, date=date.today(), model="test-model", cost=Decimal("123.456789") ) session.add(stats) session.commit() # Retrieve retrieved = session.query(UsageStats).first() # Assert assert retrieved is not None assert float(retrieved.cost) > 0 session.close()