""" Tests for conversations domain. Tests conversation CRUD, context building, and API endpoints. """ import pytest from uuid import uuid4 from src.domains.conversations.models import Conversation, Message from src.domains.conversations.schemas import ( CreateConversationRequest, AddMessageRequest, ConversationResponse, MessageResponse, ) class TestConversationModels: """Tests for conversation database models.""" def test_conversation_creation(self): """Test Conversation model creation with explicit values.""" conv = Conversation( user_id="test-user", agent_type="explore", working_dir=".", total_tokens=0, ) assert conv.user_id == "test-user" assert conv.agent_type == "explore" assert conv.working_dir == "." assert conv.total_tokens == 0 def test_conversation_with_values(self): """Test Conversation with explicit values.""" conv = Conversation( user_id="test-user", agent_type="plan", working_dir="/tmp/project", title="Test Conversation", ) assert conv.agent_type == "plan" assert conv.working_dir == "/tmp/project" assert conv.title == "Test Conversation" def test_message_creation(self): """Test Message model creation with explicit values.""" msg = Message( conversation_id=uuid4(), role="user", content="Hello", token_count=0, is_summary=False, ) assert msg.role == "user" assert msg.content == "Hello" assert msg.token_count == 0 assert msg.is_summary is False def test_message_repr(self): """Test Message string representation.""" msg = Message( conversation_id=uuid4(), role="user", content="This is a test message", ) repr_str = repr(msg) assert "user" in repr_str assert "This is a test" in repr_str class TestConversationSchemas: """Tests for Pydantic schemas.""" def test_create_request_defaults(self): """Test CreateConversationRequest defaults.""" request = CreateConversationRequest() assert request.agent_type == "explore" assert request.working_dir == "." assert request.title is None def test_create_request_custom(self): """Test CreateConversationRequest with values.""" request = CreateConversationRequest( agent_type="task", working_dir="/home/user/project", title="My Task", ) assert request.agent_type == "task" assert request.working_dir == "/home/user/project" assert request.title == "My Task" def test_add_message_request_valid(self): """Test AddMessageRequest validation.""" request = AddMessageRequest(content="Hello, world!") assert request.content == "Hello, world!" def test_add_message_request_empty_fails(self): """Test that empty content fails validation.""" with pytest.raises(ValueError): AddMessageRequest(content="") class TestConversationAPI: """Tests for conversation API endpoints.""" @pytest.mark.anyio async def test_create_conversation(self, auth_client): """Test creating a conversation.""" response = await auth_client.post( "/conversations/", json={"agent_type": "explore", "working_dir": "."} ) assert response.status_code == 201 data = response.json() assert "id" in data assert data["agent_type"] == "explore" assert data["total_tokens"] == 0 @pytest.mark.anyio async def test_create_conversation_with_title(self, auth_client): """Test creating a conversation with title.""" response = await auth_client.post( "/conversations/", json={ "agent_type": "plan", "working_dir": "/tmp", "title": "Planning Session" } ) assert response.status_code == 201 data = response.json() assert data["title"] == "Planning Session" assert data["agent_type"] == "plan" @pytest.mark.anyio async def test_list_conversations_empty(self, auth_client): """Test listing conversations when empty.""" response = await auth_client.get("/conversations/") assert response.status_code == 200 data = response.json() assert "conversations" in data assert "total" in data @pytest.mark.anyio async def test_get_conversation_not_found(self, auth_client): """Test getting non-existent conversation.""" fake_id = uuid4() response = await auth_client.get(f"/conversations/{fake_id}") assert response.status_code == 404 @pytest.mark.anyio async def test_delete_conversation_not_found(self, auth_client): """Test deleting non-existent conversation.""" fake_id = uuid4() response = await auth_client.delete(f"/conversations/{fake_id}") assert response.status_code == 404 @pytest.mark.anyio async def test_add_message_not_found(self, auth_client): """Test adding message to non-existent conversation.""" fake_id = uuid4() response = await auth_client.post( f"/conversations/{fake_id}/messages", json={"content": "Hello"} ) assert response.status_code == 404 class TestConversationService: """Tests for ConversationService business logic.""" @pytest.mark.anyio async def test_context_prompt_no_history(self): """Test building context prompt with no history.""" from src.domains.conversations.service import ConversationService from unittest.mock import MagicMock # Create mock session mock_session = MagicMock() service = ConversationService(mock_session) prompt = service.build_context_prompt([], "What files are here?") assert "" in prompt assert "What files are here?" in prompt assert "" not in prompt assert "" not in prompt @pytest.mark.anyio async def test_context_prompt_with_history(self): """Test building context prompt with message history.""" from src.domains.conversations.service import ConversationService from src.domains.conversations.models import Message from unittest.mock import MagicMock mock_session = MagicMock() service = ConversationService(mock_session) messages = [ Message( conversation_id=uuid4(), role="user", content="Find Python files", ), Message( conversation_id=uuid4(), role="assistant", content="Found 10 Python files.", ), ] prompt = service.build_context_prompt(messages, "Show the largest") assert "" in prompt assert "USER: Find Python files" in prompt assert "ASSISTANT: Found 10 Python files" in prompt assert "" in prompt assert "Show the largest" in prompt @pytest.mark.anyio async def test_context_prompt_with_summary(self): """Test building context prompt with summary message.""" from src.domains.conversations.service import ConversationService from src.domains.conversations.models import Message from unittest.mock import MagicMock mock_session = MagicMock() service = ConversationService(mock_session) messages = [ Message( conversation_id=uuid4(), role="summary", content="Previously discussed: project setup", is_summary=True, ), Message( conversation_id=uuid4(), role="user", content="Now what?", ), ] prompt = service.build_context_prompt(messages, "Continue") assert "" in prompt assert "Previously discussed: project setup" in prompt class TestSummarization: """Tests for conversation summarization.""" def test_format_messages_for_summary(self): """Test formatting messages for summarization.""" from src.domains.conversations.summarize import format_messages_for_summary from src.domains.conversations.models import Message messages = [ Message( conversation_id=uuid4(), role="user", content="Hello", ), Message( conversation_id=uuid4(), role="assistant", content="Hi there!", ), ] formatted = format_messages_for_summary(messages) assert "USER: Hello" in formatted assert "ASSISTANT: Hi there!" in formatted def test_format_messages_with_summary(self): """Test formatting messages that include a summary.""" from src.domains.conversations.summarize import format_messages_for_summary from src.domains.conversations.models import Message messages = [ Message( conversation_id=uuid4(), role="summary", content="Previous context summary", is_summary=True, ), Message( conversation_id=uuid4(), role="user", content="Continue", ), ] formatted = format_messages_for_summary(messages) assert "[Previous Summary]" in formatted assert "Previous context summary" in formatted