import pytest
from fastapi.testclient import TestClient
from httpx import Response
from unittest.mock import patch, MagicMock
import os
import requests
# Мокирование load_dotenv
patcher_dotenv_app = patch('dotenv.load_dotenv', return_value=True)
patcher_dotenv_app.start()
from backend.app import app, get_search_session
@pytest.fixture
def mock_search_session_fixture():
mock_session_instance = MagicMock(spec=requests.Session)
mock_response_search_ok = MagicMock(spec=Response)
mock_response_search_ok.status_code = 200
mock_response_search_ok.json.return_value = {
"hits": [
{"id": "test.txt", "_formatted": {"id": "test.txt", "content": "Это тест"}},
{"id": "another.pdf", "_formatted": {"id": "another.pdf", "content": "Еще один тестовый файл"}}
],
"estimatedTotalHits": 2
}
mock_response_search_ok.raise_for_status.return_value = None
mock_response_health_ok = MagicMock(spec=Response)
mock_response_health_ok.status_code = 200
mock_response_health_ok.json.return_value = {"status": "available"}
mock_response_health_ok.raise_for_status.return_value = None
def side_effect_post(*args, **kwargs):
url = args[0]
if 'search' in url:
query = kwargs.get('json', {}).get('q')
if query == "ошибка_сети":
raise requests.exceptions.RequestException("Simulated network error")
return mock_response_search_ok
return MagicMock(status_code=404)
def side_effect_get(*args, **kwargs):
url = args[0]
if 'health' in url:
return mock_response_health_ok
return MagicMock(status_code=404)
mock_session_instance.post.side_effect = side_effect_post
mock_session_instance.get.side_effect = side_effect_get
def override_get_search_session():
return mock_session_instance
yield override_get_search_session, mock_session_instance
patcher_dotenv_app.stop()
@pytest.fixture
def client(mock_search_session_fixture):
override_func, _ = mock_search_session_fixture
app.dependency_overrides[get_search_session] = override_func
yield TestClient(app)
app.dependency_overrides.clear()
def test_search_success(client, mock_search_session_fixture):
_, mock_session = mock_search_session_fixture
response = client.get("/search?q=тест")
assert response.status_code == 200
data = response.json()
assert "results" in data
assert len(data["results"]) == 2
mock_session.post.assert_called_once()
def test_search_empty_query(client):
response = client.get("/search?q=")
assert response.status_code == 200
data = response.json()
assert "results" in data
def test_search_meili_error(client, mock_search_session_fixture):
response = client.get("/search?q=ошибка_сети")
assert response.status_code == 503
assert "Сервис поиска временно недоступен" in response.json()["detail"]
def test_get_file_not_found(client):
with patch("backend.app.os.path.exists", return_value=False):
response = client.get("/files/nonexistent.txt")
assert response.status_code == 404
def test_get_file_invalid_name_slash(client):
response = client.get("/files/invalid/name.txt")
assert response.status_code == 404
def test_get_file_invalid_name_dotdot(client):
response = client.get("/files/../secret.txt")
assert response.status_code == 404
def test_health_check_meili_ok(client, mock_search_session_fixture):
override_func, mock_session = mock_search_session_fixture
mock_response = MagicMock(spec=Response)
mock_response.status_code = 200
mock_response.json.return_value = {"status": "available"}
mock_session.get.side_effect = lambda *args, **kwargs: mock_response if 'health' in args[0] else MagicMock(status_code=404)
app.dependency_overrides[get_search_session] = override_func
response = client.get("/health")
assert response.status_code == 200
assert response.json()["meilisearch_status"] == "доступен"
def test_health_check_meili_fail(client, mock_search_session_fixture):
override_func, mock_session = mock_search_session_fixture
mock_session.get.side_effect = requests.exceptions.RequestException("Error")
app.dependency_overrides[get_search_session] = override_func
response = client.get("/health")
assert response.status_code == 200
assert response.json()["meilisearch_status"] == "недоступен"