170 lines
5.4 KiB
Python
170 lines
5.4 KiB
Python
"""
|
||
Тесты API endpoints /similar и /submitted.
|
||
"""
|
||
|
||
from unittest.mock import AsyncMock, MagicMock
|
||
|
||
import pytest
|
||
from fastapi.testclient import TestClient
|
||
|
||
from app.main import app
|
||
|
||
|
||
@pytest.fixture
|
||
def mock_rag_service():
|
||
"""Mock RAGService для тестов API без загрузки модели."""
|
||
service = MagicMock()
|
||
service.is_model_loaded = True
|
||
service.vector_store.submitted_count = 0
|
||
return service
|
||
|
||
|
||
@pytest.fixture
|
||
def client(mock_rag_service):
|
||
"""TestClient с переопределённым RAG сервисом."""
|
||
from app.api.routes import get_service
|
||
|
||
def override_get_service():
|
||
return mock_rag_service
|
||
|
||
app.dependency_overrides[get_service] = override_get_service
|
||
# get_rag_service используется при создании сервиса - get_service вызывает get_rag_service
|
||
# Смотрю routes: get_service возвращает get_rag_service(). Значит override get_service достаточно.
|
||
with TestClient(app) as c:
|
||
yield c
|
||
app.dependency_overrides.clear()
|
||
|
||
|
||
def test_similar_endpoint(client, mock_rag_service):
|
||
"""POST /api/v1/similar возвращает похожие посты."""
|
||
mock_rag_service.find_similar_posts = AsyncMock(
|
||
return_value=[
|
||
{
|
||
"similarity": 0.95,
|
||
"created_at": 1706270000,
|
||
"post_id": 123,
|
||
"text": "Похожий пост",
|
||
"rag_score": 0.85,
|
||
}
|
||
]
|
||
)
|
||
|
||
response = client.post(
|
||
"/api/v1/similar",
|
||
json={"text": "Текст для поиска", "threshold": 0.9, "hours": 24},
|
||
)
|
||
|
||
assert response.status_code == 200
|
||
data = response.json()
|
||
assert data["similar_count"] == 1
|
||
assert len(data["similar_posts"]) == 1
|
||
assert data["similar_posts"][0]["similarity"] == 0.95
|
||
assert data["similar_posts"][0]["post_id"] == 123
|
||
assert data["similar_posts"][0]["text"] == "Похожий пост"
|
||
assert data["similar_posts"][0]["rag_score"] == 0.85
|
||
|
||
|
||
def test_similar_endpoint_empty(client, mock_rag_service):
|
||
"""POST /api/v1/similar с пустым результатом."""
|
||
mock_rag_service.find_similar_posts = AsyncMock(return_value=[])
|
||
|
||
response = client.post(
|
||
"/api/v1/similar",
|
||
json={"text": "Уникальный текст", "threshold": 0.99, "hours": 1},
|
||
)
|
||
|
||
assert response.status_code == 200
|
||
assert response.json()["similar_count"] == 0
|
||
assert response.json()["similar_posts"] == []
|
||
|
||
|
||
def test_similar_endpoint_default_params(client, mock_rag_service):
|
||
"""POST /api/v1/similar с дефолтными параметрами."""
|
||
mock_rag_service.find_similar_posts = AsyncMock(return_value=[])
|
||
|
||
response = client.post(
|
||
"/api/v1/similar",
|
||
json={"text": "Текст"},
|
||
)
|
||
|
||
assert response.status_code == 200
|
||
mock_rag_service.find_similar_posts.assert_called_once_with(
|
||
text="Текст",
|
||
threshold=0.9,
|
||
hours=24,
|
||
)
|
||
|
||
|
||
def test_submitted_endpoint_success(client, mock_rag_service):
|
||
"""POST /api/v1/submitted успешно добавляет пост."""
|
||
mock_rag_service.add_submitted_post = AsyncMock(return_value=True)
|
||
mock_rag_service.vector_store.submitted_count = 5
|
||
|
||
response = client.post(
|
||
"/api/v1/submitted",
|
||
json={"text": "Новый пост", "post_id": 42, "rag_score": 0.8},
|
||
)
|
||
|
||
assert response.status_code == 200
|
||
data = response.json()
|
||
assert data["success"] is True
|
||
assert "добавлен" in data["message"].lower()
|
||
assert data["submitted_count"] == 5
|
||
mock_rag_service.add_submitted_post.assert_called_once_with(
|
||
text="Новый пост",
|
||
post_id=42,
|
||
rag_score=0.8,
|
||
)
|
||
|
||
|
||
def test_submitted_endpoint_duplicate(client, mock_rag_service):
|
||
"""POST /api/v1/submitted при дубликате возвращает success=False."""
|
||
mock_rag_service.add_submitted_post = AsyncMock(return_value=False)
|
||
mock_rag_service.vector_store.submitted_count = 10
|
||
|
||
response = client.post(
|
||
"/api/v1/submitted",
|
||
json={"text": "Дубликат поста"},
|
||
)
|
||
|
||
assert response.status_code == 200
|
||
data = response.json()
|
||
assert data["success"] is False
|
||
assert data["submitted_count"] == 10
|
||
|
||
|
||
def test_submitted_endpoint_optional_fields(client, mock_rag_service):
|
||
"""POST /api/v1/submitted с только текстом (post_id, rag_score опциональны)."""
|
||
mock_rag_service.add_submitted_post = AsyncMock(return_value=True)
|
||
mock_rag_service.vector_store.submitted_count = 1
|
||
|
||
response = client.post(
|
||
"/api/v1/submitted",
|
||
json={"text": "Только текст"},
|
||
)
|
||
|
||
assert response.status_code == 200
|
||
mock_rag_service.add_submitted_post.assert_called_once_with(
|
||
text="Только текст",
|
||
post_id=None,
|
||
rag_score=None,
|
||
)
|
||
|
||
|
||
def test_similar_validation_empty_text(client):
|
||
"""POST /api/v1/similar с пустым текстом возвращает 422."""
|
||
response = client.post(
|
||
"/api/v1/similar",
|
||
json={"text": "", "threshold": 0.9, "hours": 24},
|
||
)
|
||
assert response.status_code == 422
|
||
|
||
|
||
def test_submitted_validation_empty_text(client):
|
||
"""POST /api/v1/submitted с пустым текстом возвращает 422."""
|
||
response = client.post(
|
||
"/api/v1/submitted",
|
||
json={"text": ""},
|
||
)
|
||
assert response.status_code == 422
|