docs/tests/test_chat.py
markov 2bab3cf60a Добавлены E2E тесты для Team Board API
- Полный набор тестов для всех модулей API
- test_auth.py: аутентификация и JWT токены
- test_members.py: CRUD участников, агенты, токены
- test_projects.py: CRUD проектов, участники проектов
- test_tasks.py: CRUD задач, этапы, назначения, зависимости
- test_chat.py: сообщения, комментарии, mentions
- test_files.py: upload/download файлов проектов
- test_labels.py: CRUD лейблов, привязка к задачам
- test_websocket.py: WebSocket подключения и события
- test_streaming.py: агентный стриминг через WebSocket
- conftest.py: фикстуры для подключения к API
- requirements.txt: зависимости pytest, httpx, websockets
- pytest.ini: настройки asyncio для pytest
2026-03-13 22:47:19 +01:00

315 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Тесты работы с чатами и сообщениями - отправка, 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 == 201
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 == 201
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 == 201
message = response.json()
assert len(message["mentions"]) == 1
assert message["mentions"][0]["id"] == test_user["id"]
assert message["mentions"][0]["slug"] == test_user["slug"]
assert message["mentions"][0]["name"] == test_user["name"]
@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 == 201
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 == 201
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 == 201
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 == 201
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 == 404
@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 == 404
@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 == 201
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 == 201
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