ruwiki-test/tests/test_integration.py

312 lines
11 KiB
Python

import asyncio
import tempfile
from pathlib import Path
from unittest.mock import AsyncMock, patch
import pytest
from src.dependency_injection import DependencyContainer
from src.models import ProcessingStatus
from src.sources import FileSource
class TestFileSourceIntegration:
@pytest.mark.asyncio
async def test_read_urls_from_file(self, temp_input_file):
source = FileSource(temp_input_file)
commands = []
async for command in source.read_urls():
commands.append(command)
assert len(commands) >= 3
for command in commands:
assert command.url.startswith("https://ru.wikipedia.org/wiki/")
assert command.force_reprocess is False
@pytest.mark.asyncio
async def test_count_urls(self, temp_input_file):
source = FileSource(temp_input_file)
count = await source.count_urls()
assert count >= 3
@pytest.mark.asyncio
async def test_file_not_found(self):
source = FileSource("nonexistent.txt")
with pytest.raises(FileNotFoundError):
async for _ in source.read_urls():
pass
class TestDatabaseIntegration:
@pytest.mark.asyncio
async def test_full_article_lifecycle(self, test_config, sample_article_data):
container = DependencyContainer(test_config)
try:
await container.initialize()
repository = container.get_repository()
article = await repository.create_article(sample_article_data)
assert article.id is not None
assert article.status == ProcessingStatus.PENDING
found_article = await repository.get_by_url(sample_article_data.url)
assert found_article is not None
assert found_article.id == article.id
article.mark_processing()
updated_article = await repository.update_article(article)
assert updated_article.status == ProcessingStatus.PROCESSING
article.mark_completed(
simplified_text="Упрощённый текст",
token_count_raw=100,
token_count_simplified=50,
processing_time=2.5,
)
final_article = await repository.update_article(article)
assert final_article.status == ProcessingStatus.COMPLETED
assert final_article.simplified_text == "Упрощённый текст"
completed_count = await repository.count_by_status(ProcessingStatus.COMPLETED)
assert completed_count == 1
finally:
await container.cleanup()
@pytest.mark.asyncio
async def test_write_queue_integration(self, test_config, sample_article_data):
container = DependencyContainer(test_config)
try:
await container.initialize()
repository = container.get_repository()
write_queue = container.get_write_queue()
article = await repository.create_article(sample_article_data)
from src.models import ProcessingResult
result = ProcessingResult.success_result(
url=article.url,
title=article.title,
raw_text=article.raw_text,
simplified_text="Упрощённый текст",
token_count_raw=100,
token_count_simplified=50,
processing_time_seconds=2.0,
)
updated_article = await write_queue.update_from_result(result)
assert updated_article.status == ProcessingStatus.COMPLETED
assert updated_article.simplified_text == "Упрощённый текст"
finally:
await container.cleanup()
class TestSystemIntegration:
@pytest.mark.asyncio
async def test_dependency_container_initialization(self, test_config):
container = DependencyContainer(test_config)
try:
await container.initialize()
db_service = container.get_database_service()
repository = container.get_repository()
write_queue = container.get_write_queue()
ruwiki_adapter = container.get_ruwiki_adapter()
llm_adapter = container.get_llm_adapter()
simplify_service = container.get_simplify_service()
assert db_service is not None
assert repository is not None
assert write_queue is not None
assert ruwiki_adapter is not None
assert llm_adapter is not None
assert simplify_service is not None
checks = await container.health_check()
assert "database" in checks
assert "write_queue" in checks
finally:
await container.cleanup()
@pytest.mark.asyncio
async def test_runner_with_mocked_adapters(self, test_config, temp_input_file):
container = DependencyContainer(test_config)
try:
await container.initialize()
with (
patch.object(container, "get_ruwiki_adapter") as mock_ruwiki,
patch.object(container, "get_llm_adapter") as mock_llm,
):
mock_ruwiki_instance = AsyncMock()
mock_ruwiki.return_value = mock_ruwiki_instance
from src.adapters.ruwiki import WikiPageInfo
mock_ruwiki_instance.fetch_page_cleaned.return_value = WikiPageInfo(
title="Тест",
content="Тестовый контент",
)
mock_llm_instance = AsyncMock()
mock_llm.return_value = mock_llm_instance
mock_llm_instance.simplify_text.return_value = ("Упрощённый текст", 100, 50)
mock_llm_instance.count_tokens.return_value = 100
with tempfile.NamedTemporaryFile(mode="w", suffix=".txt", delete=False) as f:
f.write("### role: user\n{wiki_source_text}")
test_config.prompt_template_path = f.name
try:
runner = container.create_runner(max_workers=2)
stats = await runner.run_from_file(
input_file=temp_input_file,
max_articles=2,
)
assert stats.total_processed >= 1
assert stats.successful >= 0
finally:
Path(test_config.prompt_template_path).unlink(missing_ok=True)
finally:
await container.cleanup()
@pytest.mark.asyncio
async def test_error_handling_in_runner(self, test_config, temp_input_file):
container = DependencyContainer(test_config)
try:
await container.initialize()
with patch.object(container, "get_ruwiki_adapter") as mock_ruwiki:
mock_ruwiki_instance = AsyncMock()
mock_ruwiki.return_value = mock_ruwiki_instance
from src.adapters import WikiPageNotFoundError
mock_ruwiki_instance.fetch_page_cleaned.side_effect = WikiPageNotFoundError(
"Страница не найдена"
)
runner = container.create_runner(max_workers=1)
stats = await runner.run_from_file(
input_file=temp_input_file,
max_articles=1,
)
assert stats.total_processed >= 1
assert stats.failed >= 1
assert stats.success_rate < 100.0
finally:
await container.cleanup()
@pytest.mark.asyncio
async def test_concurrent_processing(self, test_config, temp_input_file):
container = DependencyContainer(test_config)
try:
await container.initialize()
with (
patch.object(container, "get_ruwiki_adapter") as mock_ruwiki,
patch.object(container, "get_llm_adapter") as mock_llm,
):
async def delayed_fetch(*args, **kwargs):
await asyncio.sleep(0.1)
from src.adapters.ruwiki import WikiPageInfo
return WikiPageInfo(title="Тест", content="Контент")
async def delayed_simplify(*args, **kwargs):
await asyncio.sleep(0.1)
return ("Упрощённый", 100, 50)
mock_ruwiki_instance = AsyncMock()
mock_ruwiki.return_value = mock_ruwiki_instance
mock_ruwiki_instance.fetch_page_cleaned.side_effect = delayed_fetch
mock_llm_instance = AsyncMock()
mock_llm.return_value = mock_llm_instance
mock_llm_instance.simplify_text.side_effect = delayed_simplify
mock_llm_instance.count_tokens.return_value = 100
with tempfile.NamedTemporaryFile(mode="w", suffix=".txt", delete=False) as f:
f.write("### role: user\n{wiki_source_text}")
test_config.prompt_template_path = f.name
try:
import time
start_time = time.time()
runner = container.create_runner(max_workers=3)
stats = await runner.run_from_file(
input_file=temp_input_file,
max_articles=3,
)
elapsed_time = time.time() - start_time
assert elapsed_time < 1.0
assert stats.total_processed >= 1
finally:
Path(test_config.prompt_template_path).unlink(missing_ok=True)
finally:
await container.cleanup()
@pytest.mark.asyncio
async def test_health_check_integration(self, test_config):
container = DependencyContainer(test_config)
try:
await container.initialize()
with (
patch.object(container, "get_ruwiki_adapter") as mock_ruwiki,
patch.object(container, "get_llm_adapter") as mock_llm,
):
mock_ruwiki_instance = AsyncMock()
mock_ruwiki.return_value = mock_ruwiki_instance
mock_ruwiki_instance.health_check.return_value = True
mock_llm_instance = AsyncMock()
mock_llm.return_value = mock_llm_instance
mock_llm_instance.health_check.return_value = True
checks = await container.health_check()
assert checks["database"] is True
assert checks["write_queue"] is True
assert checks["ruwiki"] is True
assert checks["llm"] is True
finally:
await container.cleanup()