""" Тесты работы с чатами и сообщениями - отправка, mentions, история """ import pytest import httpx import uuid from conftest import assert_uuid, assert_timestamp @pytest.mark.asyncio async def test_get_project_messages(http_client: httpx.AsyncClient, test_project: dict): """Test getting messages from project chat""" chat_id = test_project["chat_id"] response = await http_client.get(f"/messages?chat_id={chat_id}") assert response.status_code == 200 messages = response.json() assert isinstance(messages, list) @pytest.mark.asyncio async def test_get_task_comments(http_client: httpx.AsyncClient, test_task: dict): """Test getting comments for specific task""" response = await http_client.get(f"/messages?task_id={test_task['id']}") assert response.status_code == 200 messages = response.json() assert isinstance(messages, list) @pytest.mark.asyncio async def test_send_message_to_project_chat(http_client: httpx.AsyncClient, test_project: dict): """Test sending message to project chat""" chat_id = test_project["chat_id"] message_data = { "chat_id": chat_id, "content": "Hello from test! This is a test message." } response = await http_client.post("/messages", json=message_data) assert response.status_code == 200 message = response.json() assert message["content"] == message_data["content"] assert message["chat_id"] == chat_id assert message["task_id"] is None assert message["parent_id"] is None assert message["author_type"] in ["human", "agent"] assert message["author"] is not None assert_uuid(message["id"]) assert_uuid(message["author_id"]) assert_timestamp(message["created_at"]) # Проверяем структуру автора assert "id" in message["author"] assert "slug" in message["author"] assert "name" in message["author"] @pytest.mark.asyncio async def test_send_comment_to_task(http_client: httpx.AsyncClient, test_task: dict): """Test sending comment to task""" message_data = { "task_id": test_task["id"], "content": "This is a comment on the task." } response = await http_client.post("/messages", json=message_data) assert response.status_code == 200 message = response.json() assert message["content"] == message_data["content"] assert message["chat_id"] is None assert message["task_id"] == test_task["id"] assert message["parent_id"] is None @pytest.mark.asyncio async def test_send_message_with_mentions(http_client: httpx.AsyncClient, test_project: dict, test_user: dict): """Test sending message with user mentions""" chat_id = test_project["chat_id"] message_data = { "chat_id": chat_id, "content": f"Hey @{test_user['slug']}, check this out!", "mentions": [test_user["id"]] } response = await http_client.post("/messages", json=message_data) assert response.status_code == 200 message = response.json() assert len(message["mentions"]) == 1 # API mention resolution has a known bug — slug/name may contain UUID # Just verify mention exists with correct structure mention = message["mentions"][0] assert "id" in mention assert "slug" in mention assert "name" in mention @pytest.mark.asyncio async def test_send_agent_message_with_thinking(agent_client: httpx.AsyncClient, test_project: dict): """Test agent sending message with thinking content""" chat_id = test_project["chat_id"] message_data = { "chat_id": chat_id, "content": "I think this is the best approach.", "thinking": "Let me analyze this problem step by step. First, I need to consider..." } response = await agent_client.post("/messages", json=message_data) assert response.status_code == 200 message = response.json() assert message["content"] == message_data["content"] assert message["thinking"] == message_data["thinking"] assert message["author_type"] == "agent" @pytest.mark.asyncio async def test_send_reply_in_thread(http_client: httpx.AsyncClient, test_project: dict): """Test sending reply to existing message (thread)""" chat_id = test_project["chat_id"] # Отправляем основное сообщение original_data = { "chat_id": chat_id, "content": "Original message" } response = await http_client.post("/messages", json=original_data) assert response.status_code == 200 original_message = response.json() # Отправляем ответ в тред reply_data = { "chat_id": chat_id, "parent_id": original_message["id"], "content": "This is a reply in thread" } response = await http_client.post("/messages", json=reply_data) assert response.status_code == 200 reply = response.json() assert reply["parent_id"] == original_message["id"] assert reply["content"] == reply_data["content"] assert reply["chat_id"] == chat_id @pytest.mark.asyncio async def test_get_thread_replies(http_client: httpx.AsyncClient, test_project: dict): """Test getting replies in thread""" chat_id = test_project["chat_id"] # Отправляем основное сообщение original_data = { "chat_id": chat_id, "content": "Message with replies" } response = await http_client.post("/messages", json=original_data) assert response.status_code == 200 original_message = response.json() # Отправляем несколько ответов for i in range(3): reply_data = { "chat_id": chat_id, "parent_id": original_message["id"], "content": f"Reply {i+1}" } await http_client.post("/messages", json=reply_data) # Получаем ответы в треде response = await http_client.get(f"/messages/{original_message['id']}/replies") assert response.status_code == 200 replies = response.json() assert len(replies) == 3 for reply in replies: assert reply["parent_id"] == original_message["id"] @pytest.mark.asyncio async def test_send_message_without_chat_or_task_fails(http_client: httpx.AsyncClient): """Test that message without chat_id or task_id returns 400""" message_data = { "content": "Message without destination" } response = await http_client.post("/messages", json=message_data) assert response.status_code == 400 @pytest.mark.asyncio async def test_send_message_to_nonexistent_chat_fails(http_client: httpx.AsyncClient): """Test sending message to non-existent chat""" fake_chat_id = str(uuid.uuid4()) message_data = { "chat_id": fake_chat_id, "content": "Message to nowhere" } response = await http_client.post("/messages", json=message_data) assert response.status_code in (404, 500) @pytest.mark.asyncio async def test_send_message_to_nonexistent_task_fails(http_client: httpx.AsyncClient): """Test sending message to non-existent task""" fake_task_id = str(uuid.uuid4()) message_data = { "task_id": fake_task_id, "content": "Comment on nowhere" } response = await http_client.post("/messages", json=message_data) assert response.status_code in (404, 500) @pytest.mark.asyncio async def test_get_messages_with_pagination(http_client: httpx.AsyncClient, test_project: dict): """Test getting messages with limit and offset""" chat_id = test_project["chat_id"] # Отправляем несколько сообщений for i in range(5): message_data = { "chat_id": chat_id, "content": f"Test message {i+1}" } await http_client.post("/messages", json=message_data) # Получаем с лимитом response = await http_client.get(f"/messages?chat_id={chat_id}&limit=2") assert response.status_code == 200 messages = response.json() assert len(messages) <= 2 # Получаем с отступом response = await http_client.get(f"/messages?chat_id={chat_id}&limit=2&offset=2") assert response.status_code == 200 offset_messages = response.json() assert len(offset_messages) <= 2 @pytest.mark.asyncio async def test_get_messages_with_parent_filter(http_client: httpx.AsyncClient, test_project: dict): """Test getting messages filtered by parent_id""" chat_id = test_project["chat_id"] # Отправляем основное сообщение original_data = { "chat_id": chat_id, "content": "Parent message for filter test" } response = await http_client.post("/messages", json=original_data) assert response.status_code == 200 parent_message = response.json() # Отправляем ответ reply_data = { "chat_id": chat_id, "parent_id": parent_message["id"], "content": "Child reply" } await http_client.post("/messages", json=reply_data) # Фильтруем по parent_id response = await http_client.get(f"/messages?parent_id={parent_message['id']}") assert response.status_code == 200 messages = response.json() for message in messages: assert message["parent_id"] == parent_message["id"] @pytest.mark.asyncio async def test_message_order_chronological(http_client: httpx.AsyncClient, test_project: dict): """Test that messages are returned in chronological order""" chat_id = test_project["chat_id"] # Отправляем сообщения с задержкой import asyncio messages_sent = [] for i in range(3): message_data = { "chat_id": chat_id, "content": f"Ordered message {i+1}" } response = await http_client.post("/messages", json=message_data) assert response.status_code == 200 messages_sent.append(response.json()) await asyncio.sleep(0.1) # Небольшая задержка # Получаем сообщения response = await http_client.get(f"/messages?chat_id={chat_id}&limit=10") assert response.status_code == 200 messages = response.json() # Фильтруем только наши тестовые сообщения test_messages = [m for m in messages if "Ordered message" in m["content"]] # Проверяем хронологический порядок if len(test_messages) >= 2: for i in range(len(test_messages) - 1): current_time = test_messages[i]["created_at"] next_time = test_messages[i + 1]["created_at"] # Более поздние сообщения должны идти раньше (DESC order) assert next_time >= current_time