179 lines
5.0 KiB
Python
179 lines
5.0 KiB
Python
|
import asyncio
|
|||
|
import tempfile
|
|||
|
from collections.abc import Generator
|
|||
|
from pathlib import Path
|
|||
|
from unittest.mock import AsyncMock, MagicMock
|
|||
|
|
|||
|
import pytest
|
|||
|
from openai.types.chat import ChatCompletion, ChatCompletionMessage
|
|||
|
from openai.types.chat.chat_completion import Choice
|
|||
|
|
|||
|
from src.models import AppConfig, Article, ArticleCreate, ProcessingStatus
|
|||
|
|
|||
|
|
|||
|
@pytest.fixture(scope="session")
|
|||
|
def event_loop() -> Generator[asyncio.AbstractEventLoop, None, None]:
|
|||
|
"""Создать event loop для всей сессии тестов."""
|
|||
|
loop = asyncio.new_event_loop()
|
|||
|
yield loop
|
|||
|
loop.close()
|
|||
|
|
|||
|
|
|||
|
@pytest.fixture
|
|||
|
def test_config() -> AppConfig:
|
|||
|
"""Тестовая конфигурация."""
|
|||
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|||
|
db_path = Path(temp_dir) / "test.db"
|
|||
|
return AppConfig(
|
|||
|
openai_api_key="test_key",
|
|||
|
openai_model="gpt-4o-mini",
|
|||
|
db_path=str(db_path),
|
|||
|
max_concurrent_llm=2,
|
|||
|
openai_rpm=10,
|
|||
|
max_concurrent_wiki=5,
|
|||
|
prompt_template_path="src/prompt.txt",
|
|||
|
log_level="DEBUG",
|
|||
|
)
|
|||
|
|
|||
|
|
|||
|
@pytest.fixture
|
|||
|
def sample_wiki_urls() -> list[str]:
|
|||
|
"""Список тестовых URL википедии."""
|
|||
|
return [
|
|||
|
"https://ru.wikipedia.org/wiki/Тест",
|
|||
|
"https://ru.wikipedia.org/wiki/Пример",
|
|||
|
"https://ru.wikipedia.org/wiki/Образец",
|
|||
|
]
|
|||
|
|
|||
|
|
|||
|
@pytest.fixture
|
|||
|
def invalid_urls() -> list[str]:
|
|||
|
"""Список невалидных URL."""
|
|||
|
return [
|
|||
|
"https://example.com/invalid",
|
|||
|
"https://en.wikipedia.org/wiki/English",
|
|||
|
"not_a_url",
|
|||
|
"",
|
|||
|
"https://ru.wikipedia.org/wiki/",
|
|||
|
]
|
|||
|
|
|||
|
|
|||
|
@pytest.fixture
|
|||
|
def sample_wikitext() -> str:
|
|||
|
return """'''Тест''' — это проверка чего-либо.
|
|||
|
|
|||
|
== Определение ==
|
|||
|
Тест может проводиться для различных целей:
|
|||
|
* Проверка знаний
|
|||
|
* Проверка работоспособности
|
|||
|
* Проверка качества
|
|||
|
|
|||
|
== История ==
|
|||
|
Тесты использовались с древних времён.
|
|||
|
|
|||
|
{{навигация|тема=Тестирование}}
|
|||
|
|
|||
|
[[Категория:Тестирование]]"""
|
|||
|
|
|||
|
|
|||
|
@pytest.fixture
|
|||
|
def simplified_text() -> str:
|
|||
|
return """'''Тест''' — это проверка чего-либо для школьников.
|
|||
|
|
|||
|
== Что такое тест ==
|
|||
|
Тест помогает проверить:
|
|||
|
* Знания учеников
|
|||
|
* Как работают устройства
|
|||
|
* Качество продуктов
|
|||
|
|
|||
|
== Когда появились тесты ==
|
|||
|
Люди проверяли друг друга очень давно.
|
|||
|
|
|||
|
###END###"""
|
|||
|
|
|||
|
|
|||
|
@pytest.fixture
|
|||
|
def sample_article_data() -> ArticleCreate:
|
|||
|
return ArticleCreate(
|
|||
|
url="https://ru.wikipedia.org/wiki/Тест",
|
|||
|
title="Тест",
|
|||
|
raw_text="Тестовый wiki-текст",
|
|||
|
)
|
|||
|
|
|||
|
|
|||
|
@pytest.fixture
|
|||
|
def sample_article(sample_article_data: ArticleCreate) -> Article:
|
|||
|
return Article(
|
|||
|
id=1,
|
|||
|
url=sample_article_data.url,
|
|||
|
title=sample_article_data.title,
|
|||
|
raw_text=sample_article_data.raw_text,
|
|||
|
status=ProcessingStatus.PENDING,
|
|||
|
)
|
|||
|
|
|||
|
|
|||
|
@pytest.fixture
|
|||
|
def completed_article(sample_article: Article, simplified_text: str) -> Article:
|
|||
|
article = sample_article.model_copy()
|
|||
|
article.mark_completed(
|
|||
|
simplified_text=simplified_text,
|
|||
|
token_count_raw=100,
|
|||
|
token_count_simplified=50,
|
|||
|
processing_time=2.5,
|
|||
|
)
|
|||
|
return article
|
|||
|
|
|||
|
|
|||
|
@pytest.fixture
|
|||
|
def mock_openai_response() -> ChatCompletion:
|
|||
|
return ChatCompletion(
|
|||
|
id="test_completion",
|
|||
|
object="chat.completion",
|
|||
|
created=1234567890,
|
|||
|
model="gpt-4o-mini",
|
|||
|
choices=[
|
|||
|
Choice(
|
|||
|
index=0,
|
|||
|
message=ChatCompletionMessage(
|
|||
|
role="assistant",
|
|||
|
content="Упрощённый текст для школьников.\n\n###END###",
|
|||
|
),
|
|||
|
finish_reason="stop",
|
|||
|
)
|
|||
|
],
|
|||
|
usage=None,
|
|||
|
)
|
|||
|
|
|||
|
|
|||
|
@pytest.fixture
|
|||
|
def temp_input_file(sample_wiki_urls: list[str]) -> Generator[str, None, None]:
|
|||
|
with tempfile.NamedTemporaryFile(mode="w", suffix=".txt", delete=False) as f:
|
|||
|
for url in sample_wiki_urls:
|
|||
|
f.write(f"{url}\n")
|
|||
|
f.write("# Комментарий\n")
|
|||
|
f.write("\n")
|
|||
|
f.write("https://ru.wikipedia.org/wiki/Дубликат\n")
|
|||
|
f.write("https://ru.wikipedia.org/wiki/Дубликат\n")
|
|||
|
temp_path = f.name
|
|||
|
|
|||
|
yield temp_path
|
|||
|
|
|||
|
Path(temp_path).unlink(missing_ok=True)
|
|||
|
|
|||
|
|
|||
|
@pytest.fixture
|
|||
|
async def mock_wiki_client() -> AsyncMock:
|
|||
|
mock_client = AsyncMock()
|
|||
|
mock_page = MagicMock()
|
|||
|
mock_page.exists = True
|
|||
|
mock_page.redirect = False
|
|||
|
mock_page.text.return_value = "Тестовый wiki-текст"
|
|||
|
mock_client.pages = {"Тест": mock_page}
|
|||
|
return mock_client
|
|||
|
|
|||
|
|
|||
|
@pytest.fixture
|
|||
|
async def mock_openai_client() -> AsyncMock:
|
|||
|
mock_client = AsyncMock()
|
|||
|
return mock_client
|