""" Tests for SQLAlchemy models Tests model creation, relationships, and constraints """ import pytest from datetime import datetime from sqlalchemy import select from app.models.user import User from app.models.conversation import Conversation from app.models.message import Message from app.models.token_usage import TokenUsage from app.models.session import Session from app.models.user_memory import UserMemory @pytest.mark.asyncio async def test_create_user(db_session): """Test creating a user""" user = User( azure_ad_id="test-azure-id-123", email="test@example.com", display_name="Test User", given_name="Test", surname="User", role="user", ) db_session.add(user) await db_session.commit() await db_session.refresh(user) assert user.id is not None assert user.email == "test@example.com" assert user.azure_ad_id == "test-azure-id-123" assert user.is_active is True assert user.role == "user" assert isinstance(user.created_at, datetime) @pytest.mark.asyncio async def test_user_conversation_relationship(db_session): """Test user-conversation relationship""" # Create user user = User( azure_ad_id="test-azure-id-456", email="user@example.com", display_name="User Test", ) db_session.add(user) await db_session.commit() await db_session.refresh(user) # Create conversation conversation = Conversation( user_id=user.id, title="Test Conversation", ) db_session.add(conversation) await db_session.commit() await db_session.refresh(conversation) # Verify relationship assert conversation.user_id == user.id assert conversation.title == "Test Conversation" assert isinstance(conversation.created_at, datetime) @pytest.mark.asyncio async def test_conversation_message_relationship(db_session): """Test conversation-message relationship""" # Create user and conversation user = User( azure_ad_id="test-azure-id-789", email="messages@example.com", display_name="Message User", ) db_session.add(user) await db_session.commit() await db_session.refresh(user) conversation = Conversation(user_id=user.id, title="Message Test") db_session.add(conversation) await db_session.commit() await db_session.refresh(conversation) # Create messages user_message = Message( conversation_id=conversation.id, role="user", content="Hello, bot!", token_count=3, ) assistant_message = Message( conversation_id=conversation.id, role="assistant", content="Hello! How can I help you?", token_count=7, ) db_session.add_all([user_message, assistant_message]) await db_session.commit() # Fetch conversation with messages result = await db_session.execute( select(Conversation).where(Conversation.id == conversation.id) ) conv = result.scalar_one() assert len(conv.messages) == 2 assert conv.messages[0].role == "user" assert conv.messages[1].role == "assistant" @pytest.mark.asyncio async def test_token_usage_tracking(db_session): """Test token usage model""" user = User( azure_ad_id="test-token-user", email="token@example.com", display_name="Token User", ) db_session.add(user) await db_session.commit() await db_session.refresh(user) # Create token usage record token_usage = TokenUsage( user_id=user.id, prompt_tokens=100, completion_tokens=50, total_tokens=150, model="gpt-5-nano-2025-08-07", cost_usd=0.015, operation_type="chat", ) db_session.add(token_usage) await db_session.commit() await db_session.refresh(token_usage) assert token_usage.prompt_tokens == 100 assert token_usage.completion_tokens == 50 assert token_usage.total_tokens == 150 assert token_usage.model == "gpt-5-nano-2025-08-07" assert float(token_usage.cost_usd) == 0.015 @pytest.mark.asyncio async def test_user_memory(db_session): """Test user memory model""" user = User( azure_ad_id="test-memory-user", email="memory@example.com", display_name="Memory User", ) db_session.add(user) await db_session.commit() await db_session.refresh(user) # Create memory memory = UserMemory( user_id=user.id, memory_key="office_location", memory_value="Sydney office", importance_score=0.8, ) db_session.add(memory) await db_session.commit() await db_session.refresh(memory) assert memory.memory_key == "office_location" assert memory.memory_value == "Sydney office" assert float(memory.importance_score) == 0.8 @pytest.mark.asyncio async def test_session_creation(db_session): """Test session model""" user = User( azure_ad_id="test-session-user", email="session@example.com", display_name="Session User", ) db_session.add(user) await db_session.commit() await db_session.refresh(user) # Create session from datetime import timedelta session = Session( user_id=user.id, access_token_hash="hashed_token_123", expires_at=datetime.utcnow() + timedelta(hours=1), ip_address="127.0.0.1", user_agent="Test Agent", ) db_session.add(session) await db_session.commit() await db_session.refresh(session) assert session.user_id == user.id assert session.access_token_hash == "hashed_token_123" assert session.is_active is True assert session.ip_address == "127.0.0.1" @pytest.mark.asyncio async def test_cascade_delete(db_session): """Test cascade delete functionality""" # Create user with conversation and messages user = User( azure_ad_id="test-cascade-user", email="cascade@example.com", display_name="Cascade User", ) db_session.add(user) await db_session.commit() await db_session.refresh(user) conversation = Conversation(user_id=user.id, title="Cascade Test") db_session.add(conversation) await db_session.commit() await db_session.refresh(conversation) message = Message( conversation_id=conversation.id, role="user", content="Test message", ) db_session.add(message) await db_session.commit() # Delete user - should cascade await db_session.delete(user) await db_session.commit() # Verify conversation is deleted result = await db_session.execute( select(Conversation).where(Conversation.id == conversation.id) ) assert result.scalar_one_or_none() is None # Verify message is deleted result = await db_session.execute( select(Message).where(Message.id == message.id) ) assert result.scalar_one_or_none() is None