Редактировать test_endpoints.py

This commit is contained in:
Markov Andrey
2026-06-30 22:38:15 +00:00
parent 3a237d90e3
commit 06bea77df6

View File

@@ -1,21 +1,293 @@
# tests/integration/test_endpoints.py
""" """
Интеграционные тесты для новых эндпоинтов RAG-сервера. Интеграционные тесты для новых эндпоинтов RAG-сервера.
Проверяются все эндпоинты, добавленные для команд бота:
- /rag/clear (личная, комнатная, глобальная)
- /rag/list
- /rag/delete_global
- /rag/template/* (save, list, delete)
- /rag/reset_history
- /rag/room/* (add, list)
- /rag/stats
- /rag/generate_document
Используется httpx.AsyncClient для отправки запросов к FastAPI приложению.
Все зависимости (оркестратор, БД, Qdrant) замоканы, чтобы тесты были изолированы.
""" """
import pytest import pytest
import httpx import httpx
from unittest.mock import AsyncMock, patch from unittest.mock import AsyncMock, MagicMock, patch
from fastapi import FastAPI
from rag.rag_server import app
from rag.config_models import AppConfig
from rag.services.postgres_service import PostgresService
from rag.services.kb_service import KBService
@pytest.fixture
def mock_orchestrator():
"""Создаёт мок для RAGOrchestrator с необходимыми методами."""
with patch("rag.rag_server.get_orchestrator") as mock_get:
orch = AsyncMock()
# Методы для очистки
orch.kb = AsyncMock()
orch.kb.clear_global_kb = AsyncMock(return_value=None)
orch.kb.clear_room_kb = AsyncMock(return_value=None)
orch.kb.clear_user_kb = AsyncMock(return_value=None)
orch.kb.get_sources_with_type = AsyncMock(return_value=[])
# Методы для шаблонов
orch.db = AsyncMock()
orch.db.save_template = AsyncMock(return_value=True)
orch.db.list_templates = AsyncMock(return_value=["template1", "template2"])
orch.db.delete_template = AsyncMock(return_value=True)
orch.db.add_room = AsyncMock(return_value=None)
orch.db.get_rooms = AsyncMock(return_value=["room1@conference.domain", "room2@conference.domain"])
orch.db.clear_room_history = AsyncMock(return_value=None)
orch.db.clear_user_history = AsyncMock(return_value=None)
orch.db.get_template = AsyncMock(return_value={"file_path": "/tmp/template.docx", "file_hash": "abc123"})
# Метод для генерации документа
orch.generate_document_from_template = AsyncMock(return_value="/tmp/generated.docx")
# Метод для статистики
orch.db.pool = AsyncMock()
orch.db.pool.acquire = AsyncMock()
conn = AsyncMock()
conn.fetchval = AsyncMock(return_value=42)
orch.db.pool.acquire.return_value.__aenter__.return_value = conn
mock_get.return_value = orch
yield orch
@pytest.fixture
def client():
"""Создаёт асинхронный HTTP-клиент для тестового приложения."""
return httpx.AsyncClient(app=app, base_url="http://test")
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_clear_endpoint(): async def test_clear_personal(client, mock_orchestrator):
async with httpx.AsyncClient() as client: """Тест очистки личной БЗ (room_jid=None, is_global=False)."""
with patch("rag.rag_server.get_orchestrator") as mock_orch: payload = {
mock_orch.return_value.clear_kb = AsyncMock(return_value={"status": "ok"}) "user_jid": "user@domain",
response = await client.post( "room_jid": None,
"http://localhost:8080/rag/clear", "is_global": False
json={"user_jid": "test@domain", "room_jid": None, "is_global": False}, }
headers={"X-API-Key": "testkey"} headers = {"X-API-Key": "testkey"}
) response = await client.post("/rag/clear", json=payload, headers=headers)
assert response.status_code == 200 assert response.status_code == 200
data = response.json() data = response.json()
assert data.get("status") == "ok" assert data.get("status") == "ok"
mock_orchestrator.kb.clear_user_kb.assert_called_once_with("user@domain")
@pytest.mark.asyncio
async def test_clear_room(client, mock_orchestrator):
"""Тест очистки комнатной БЗ (room_jid указан)."""
payload = {
"user_jid": "user@domain",
"room_jid": "room@conference.domain",
"is_global": False
}
headers = {"X-API-Key": "testkey"}
response = await client.post("/rag/clear", json=payload, headers=headers)
assert response.status_code == 200
data = response.json()
assert data.get("status") == "ok"
mock_orchestrator.kb.clear_room_kb.assert_called_once_with("room@conference.domain")
@pytest.mark.asyncio
async def test_clear_global(client, mock_orchestrator):
"""Тест очистки глобальной БЗ (is_global=True)."""
payload = {
"user_jid": "admin@domain",
"room_jid": None,
"is_global": True
}
headers = {"X-API-Key": "testkey"}
response = await client.post("/rag/clear", json=payload, headers=headers)
assert response.status_code == 200
data = response.json()
assert data.get("status") == "ok"
mock_orchestrator.kb.clear_global_kb.assert_called_once()
@pytest.mark.asyncio
async def test_list_documents(client, mock_orchestrator):
"""Тест получения списка документов."""
mock_orchestrator.kb.get_sources_with_type.return_value = [
{"source_name": "doc1.docx", "is_global": False},
{"source_name": "doc2.pdf", "is_global": True}
]
payload = {
"user_jid": "user@domain",
"room_jid": None
}
headers = {"X-API-Key": "testkey"}
response = await client.post("/rag/list", json=payload, headers=headers)
assert response.status_code == 200
data = response.json()
assert "documents" in data
assert len(data["documents"]) == 2
assert data["documents"][0]["source_name"] == "doc1.docx"
assert data["documents"][1]["source_name"] == "doc2.pdf"
mock_orchestrator.kb.get_sources_with_type.assert_called_once_with("user@domain", None)
@pytest.mark.asyncio
async def test_delete_global(client, mock_orchestrator):
"""Тест удаления глобального документа."""
# Мокаем поиск doc_id
mock_orchestrator.db.pool.acquire.return_value.__aenter__.return_value.fetch = AsyncMock(return_value=[
{"id": 123}
])
mock_orchestrator.qdrant = AsyncMock()
mock_orchestrator.qdrant.delete_by_doc_id = MagicMock(return_value=None)
mock_orchestrator.db.delete_documents_by_ids = AsyncMock(return_value=None)
payload = {"source_name": "ГОСТ Р 12345"}
headers = {"X-API-Key": "testkey"}
response = await client.post("/rag/delete_global", json=payload, headers=headers)
assert response.status_code == 200
data = response.json()
assert data.get("status") == "ok"
# Проверяем вызовы
mock_orchestrator.qdrant.delete_by_doc_id.assert_called_once_with(123)
mock_orchestrator.db.delete_documents_by_ids.assert_called_once_with([123])
@pytest.mark.asyncio
async def test_template_save(client, mock_orchestrator):
"""Тест сохранения шаблона."""
payload = {
"room_jid": "room@conference.domain",
"name": "Шаблон договора",
"file_path": "/tmp/template.docx",
"file_hash": "abc123"
}
headers = {"X-API-Key": "testkey"}
response = await client.post("/rag/template/save", json=payload, headers=headers)
assert response.status_code == 200
data = response.json()
assert data.get("status") == "ok"
mock_orchestrator.db.save_template.assert_called_once_with(
"room@conference.domain", "Шаблон договора", "/tmp/template.docx", "abc123"
)
@pytest.mark.asyncio
async def test_template_list(client, mock_orchestrator):
"""Тест получения списка шаблонов."""
payload = {"room_jid": "room@conference.domain"}
headers = {"X-API-Key": "testkey"}
response = await client.post("/rag/template/list", json=payload, headers=headers)
assert response.status_code == 200
data = response.json()
assert "templates" in data
assert data["templates"] == ["template1", "template2"]
mock_orchestrator.db.list_templates.assert_called_once_with("room@conference.domain")
@pytest.mark.asyncio
async def test_template_delete(client, mock_orchestrator):
"""Тест удаления шаблона."""
payload = {
"room_jid": "room@conference.domain",
"name": "Шаблон договора"
}
headers = {"X-API-Key": "testkey"}
response = await client.post("/rag/template/delete", json=payload, headers=headers)
assert response.status_code == 200
data = response.json()
assert data.get("status") == "ok"
mock_orchestrator.db.delete_template.assert_called_once_with("room@conference.domain", "Шаблон договора")
@pytest.mark.asyncio
async def test_reset_history_personal(client, mock_orchestrator):
"""Тест сброса личной истории."""
payload = {
"user_jid": "user@domain",
"room_jid": None
}
headers = {"X-API-Key": "testkey"}
response = await client.post("/rag/reset_history", json=payload, headers=headers)
assert response.status_code == 200
data = response.json()
assert data.get("status") == "ok"
mock_orchestrator.db.clear_user_history.assert_called_once_with("user@domain")
@pytest.mark.asyncio
async def test_reset_history_room(client, mock_orchestrator):
"""Тест сброса истории комнаты."""
payload = {
"user_jid": "user@domain",
"room_jid": "room@conference.domain"
}
headers = {"X-API-Key": "testkey"}
response = await client.post("/rag/reset_history", json=payload, headers=headers)
assert response.status_code == 200
data = response.json()
assert data.get("status") == "ok"
mock_orchestrator.db.clear_room_history.assert_called_once_with("room@conference.domain")
@pytest.mark.asyncio
async def test_add_room(client, mock_orchestrator):
"""Тест добавления комнаты."""
payload = {"room_jid": "newroom@conference.domain"}
headers = {"X-API-Key": "testkey"}
response = await client.post("/rag/room/add", json=payload, headers=headers)
assert response.status_code == 200
data = response.json()
assert data.get("status") == "ok"
mock_orchestrator.db.add_room.assert_called_once_with("newroom@conference.domain")
@pytest.mark.asyncio
async def test_get_rooms(client, mock_orchestrator):
"""Тест получения списка комнат."""
headers = {"X-API-Key": "testkey"}
response = await client.get("/rag/room/list", headers=headers)
assert response.status_code == 200
data = response.json()
assert "rooms" in data
assert data["rooms"] == ["room1@conference.domain", "room2@conference.domain"]
mock_orchestrator.db.get_rooms.assert_called_once()
@pytest.mark.asyncio
async def test_get_stats(client, mock_orchestrator):
"""Тест получения статистики."""
headers = {"X-API-Key": "testkey"}
response = await client.post("/rag/stats", json={}, headers=headers)
assert response.status_code == 200
data = response.json()
assert "documents" in data
assert data["documents"] == 42
# Также проверяем uptime (может быть 0 или другое)
assert "uptime" in data
@pytest.mark.asyncio
async def test_generate_document(client, mock_orchestrator):
"""Тест генерации документа по шаблону."""
payload = {
"template_name": "Шаблон договора",
"user_jid": "user@domain",
"room_jid": "room@conference.domain"
}
headers = {"X-API-Key": "testkey"}
response = await client.post("/rag/generate_document", json=payload, headers=headers)
assert response.status_code == 200
data = response.json()
assert "document" in data
assert data["document"] == "/tmp/generated.docx"
mock_orchestrator.db.get_template.assert_called_once_with("room@conference.domain", "Шаблон договора")
mock_orchestrator.generate_document_from_template.assert_called_once()