From fef5023d74debb51da3e367355911fe5db7d065c Mon Sep 17 00:00:00 2001 From: itqop Date: Sat, 12 Jul 2025 11:47:31 +0300 Subject: [PATCH] Format tests --- tests/conftest.py | 6 ++- tests/test_adapters.py | 28 +++++++++--- tests/test_integration.py | 13 +++--- tests/test_models.py | 4 +- tests/test_services.py | 93 ++++++++++++++++++++++----------------- 5 files changed, 86 insertions(+), 58 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index b39b2f8..2bc2fac 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -24,17 +24,19 @@ def level_to_int(logger, method_name, event_dict): pass return event_dict + @pytest.fixture(autouse=True, scope="session") def configure_structlog(): import tenacity + logging.basicConfig(level=logging.DEBUG) structlog.configure( processors=[ level_to_int, structlog.processors.TimeStamper(fmt="iso"), - structlog.dev.ConsoleRenderer() + structlog.dev.ConsoleRenderer(), ], - wrapper_class=structlog.make_filtering_bound_logger(logging.DEBUG) + wrapper_class=structlog.make_filtering_bound_logger(logging.DEBUG), ) tenacity.logger = structlog.get_logger("tenacity") diff --git a/tests/test_adapters.py b/tests/test_adapters.py index dee9ef3..5650f04 100644 --- a/tests/test_adapters.py +++ b/tests/test_adapters.py @@ -185,7 +185,11 @@ class TestLLMProviderAdapter: long_text = "word " * 2000 with patch.object(adapter, "_check_rpm_limit"): with patch.object(adapter, "count_tokens", return_value=50000): - with patch.object(adapter, "_make_completion_request", side_effect=LLMTokenLimitError("Token limit exceeded")): + with patch.object( + adapter, + "_make_completion_request", + side_effect=LLMTokenLimitError("Token limit exceeded"), + ): with pytest.raises(LLMTokenLimitError): await adapter.simplify_text("Test", long_text, "template") @@ -193,7 +197,9 @@ class TestLLMProviderAdapter: async def test_simplify_text_success(self, test_config, mock_openai_response): adapter = LLMProviderAdapter(test_config) - with patch.object(adapter.client.chat.completions, "create", new_callable=AsyncMock) as mock_create: + with patch.object( + adapter.client.chat.completions, "create", new_callable=AsyncMock + ) as mock_create: mock_create.return_value = mock_openai_response with patch.object(adapter, "_check_rpm_limit"): @@ -215,7 +221,7 @@ class TestLLMProviderAdapter: from tenacity import AsyncRetrying, before_sleep_log import structlog import logging - + adapter = LLMProviderAdapter(test_config) good_logger = structlog.get_logger("tenacity") @@ -230,9 +236,13 @@ class TestLLMProviderAdapter: **{**kwargs, "before_sleep": fixed_before_sleep_log(good_logger, logging.WARNING)} ) - with patch.object(adapter.client.chat.completions, "create", new_callable=AsyncMock) as mock_create: + with patch.object( + adapter.client.chat.completions, "create", new_callable=AsyncMock + ) as mock_create: mock_response = MagicMock() - mock_create.side_effect = RateLimitError("Rate limit exceeded", response=mock_response, body=None) + mock_create.side_effect = RateLimitError( + "Rate limit exceeded", response=mock_response, body=None + ) with patch.object(adapter, "_check_rpm_limit"): with pytest.raises(LLMRateLimitError): await adapter.simplify_text( @@ -272,7 +282,9 @@ class TestLLMProviderAdapter: async def test_health_check_success(self, test_config, mock_openai_response): adapter = LLMProviderAdapter(test_config) - with patch.object(adapter.client.chat.completions, "create", new_callable=AsyncMock) as mock_create: + with patch.object( + adapter.client.chat.completions, "create", new_callable=AsyncMock + ) as mock_create: mock_create.return_value = mock_openai_response result = await adapter.health_check() @@ -281,7 +293,9 @@ class TestLLMProviderAdapter: @pytest.mark.asyncio async def test_health_check_failure(self, test_config): adapter = LLMProviderAdapter(test_config) - with patch.object(adapter.client.chat.completions, "create", new_callable=AsyncMock) as mock_create: + with patch.object( + adapter.client.chat.completions, "create", new_callable=AsyncMock + ) as mock_create: mock_request = MagicMock() mock_create.side_effect = APIError("API Error", body=None, request=mock_request) result = await adapter.health_check() diff --git a/tests/test_integration.py b/tests/test_integration.py index 0e27c65..83e3531 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -113,7 +113,7 @@ class TestRepositoryIntegration: article2 = await repository.create_article( url="https://ru.ruwiki.ru/wiki/Test2", - title="Test 2", + title="Test 2", raw_text="Content 2", ) @@ -217,13 +217,12 @@ class TestAsyncOperations: async def test_concurrent_read_operations(self, multiple_articles_in_db): articles = multiple_articles_in_db - repository = articles[0].__class__.__module__ + repository = articles[0].__class__.__module__ async def read_article(article_id: int): pass - class TestSystemIntegration: @pytest.mark.asyncio @@ -281,7 +280,9 @@ class TestSystemIntegration: 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, encoding="utf-8") as f: + with tempfile.NamedTemporaryFile( + mode="w", suffix=".txt", delete=False, encoding="utf-8" + ) as f: f.write("### role: user\n{wiki_source_text}") test_config.prompt_template_path = f.name @@ -364,7 +365,9 @@ class TestSystemIntegration: 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, encoding="utf-8") as f: + with tempfile.NamedTemporaryFile( + mode="w", suffix=".txt", delete=False, encoding="utf-8" + ) as f: f.write("### role: user\n{wiki_source_text}") test_config.prompt_template_path = f.name diff --git a/tests/test_models.py b/tests/test_models.py index 28830f4..302e014 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -75,15 +75,13 @@ class TestAppConfig: def test_app_config_defaults(self): from pathlib import Path - import os from unittest.mock import patch - + with patch.dict(os.environ, {}, clear=True): config = AppConfig(openai_api_key="test-key") - assert isinstance(config.db_path, str) assert Path(config.db_path).suffix == ".db" assert isinstance(config.openai_model, str) diff --git a/tests/test_services.py b/tests/test_services.py index 47a9aff..d13841f 100644 --- a/tests/test_services.py +++ b/tests/test_services.py @@ -29,7 +29,7 @@ class TestDatabaseService: async def test_health_check_failure(self, test_config): test_config.db_path = "/invalid/path/database.db" service = DatabaseService(test_config) - + result = await service.health_check() assert result is False @@ -52,14 +52,13 @@ class TestArticleRepository: async def test_create_duplicate_article(self, repository: ArticleRepository): url = "https://ru.ruwiki.ru/wiki/Test" - + await repository.create_article( url=url, title="Test Article", raw_text="Test content", ) - with pytest.raises(ValueError, match="уже существует"): await repository.create_article( url=url, @@ -69,7 +68,7 @@ class TestArticleRepository: async def test_get_by_id(self, repository: ArticleRepository, sample_article_in_db: ArticleDTO): article = sample_article_in_db - + retrieved = await repository.get_by_id(article.id) assert retrieved is not None assert retrieved.id == article.id @@ -79,9 +78,11 @@ class TestArticleRepository: result = await repository.get_by_id(99999) assert result is None - async def test_get_by_url(self, repository: ArticleRepository, sample_article_in_db: ArticleDTO): + async def test_get_by_url( + self, repository: ArticleRepository, sample_article_in_db: ArticleDTO + ): article = sample_article_in_db - + retrieved = await repository.get_by_url(article.url) assert retrieved is not None assert retrieved.id == article.id @@ -91,14 +92,16 @@ class TestArticleRepository: result = await repository.get_by_url("https://ru.ruwiki.ru/wiki/NonExistent") assert result is None - async def test_update_article(self, repository: ArticleRepository, sample_article_in_db: ArticleDTO): + async def test_update_article( + self, repository: ArticleRepository, sample_article_in_db: ArticleDTO + ): article = sample_article_in_db - + article.simplified_text = "Simplified content" article.status = ArticleStatus.SIMPLIFIED - + updated = await repository.update_article(article) - + assert updated.simplified_text == "Simplified content" assert updated.status == ArticleStatus.SIMPLIFIED assert updated.updated_at is not None @@ -112,7 +115,7 @@ class TestArticleRepository: status=ArticleStatus.PENDING, created_at=datetime.now(timezone.utc), ) - + with pytest.raises(ValueError, match="не найдена"): await repository.update_article(article) @@ -122,20 +125,20 @@ class TestArticleRepository: title="Test 1", raw_text="Content 1", ) - + article2 = await repository.create_article( url="https://ru.ruwiki.ru/wiki/Test2", title="Test 2", raw_text="Content 2", ) - + article2.status = ArticleStatus.SIMPLIFIED await repository.update_article(article2) - + pending = await repository.get_articles_by_status(ArticleStatus.PENDING) assert len(pending) == 1 assert pending[0].id == article1.id - + simplified = await repository.get_articles_by_status(ArticleStatus.SIMPLIFIED) assert len(simplified) == 1 assert simplified[0].id == article2.id @@ -154,16 +157,18 @@ class TestArticleRepository: title="Test 2", raw_text="Content 2", ) - + count = await repository.count_by_status(ArticleStatus.PENDING) assert count == 2 - async def test_delete_article(self, repository: ArticleRepository, sample_article_in_db: ArticleDTO): + async def test_delete_article( + self, repository: ArticleRepository, sample_article_in_db: ArticleDTO + ): article = sample_article_in_db - + deleted = await repository.delete_article(article.id) assert deleted is True - + retrieved = await repository.get_by_id(article.id) assert retrieved is None @@ -191,20 +196,24 @@ class TestAsyncWriteQueue: await queue.stop() assert queue._worker_task.done() - async def test_update_article_operation(self, write_queue: AsyncWriteQueue, sample_article_in_db: ArticleDTO): + async def test_update_article_operation( + self, write_queue: AsyncWriteQueue, sample_article_in_db: ArticleDTO + ): article = sample_article_in_db article.simplified_text = "Updated content" - + await write_queue.update_article(article) - + await asyncio.sleep(0.2) - + retrieved = await write_queue.repository.get_by_id(article.id) assert retrieved.simplified_text == "Updated content" - async def test_update_from_result_success(self, write_queue: AsyncWriteQueue, sample_article_in_db: ArticleDTO): + async def test_update_from_result_success( + self, write_queue: AsyncWriteQueue, sample_article_in_db: ArticleDTO + ): article = sample_article_in_db - + result = ProcessingResult.success_result( url=article.url, title=article.title, @@ -214,27 +223,29 @@ class TestAsyncWriteQueue: token_count_simplified=50, processing_time_seconds=1.0, ) - + updated_article = await write_queue.update_from_result(result) - + assert updated_article.simplified_text == "Processed content" assert updated_article.status == ArticleStatus.SIMPLIFIED - async def test_update_from_result_failure(self, write_queue: AsyncWriteQueue, sample_article_in_db: ArticleDTO): + async def test_update_from_result_failure( + self, write_queue: AsyncWriteQueue, sample_article_in_db: ArticleDTO + ): article = sample_article_in_db - + result = ProcessingResult.failure_result( url=article.url, error_message="Processing failed", ) - + updated_article = await write_queue.update_from_result(result) - + assert updated_article.status == ArticleStatus.FAILED async def test_queue_stats(self, write_queue: AsyncWriteQueue): stats = write_queue.stats - + assert "total_operations" in stats assert "failed_operations" in stats assert "success_rate" in stats @@ -247,7 +258,7 @@ class TestSimplifyService: ruwiki_adapter = AsyncMock() llm_adapter = AsyncMock() write_queue = AsyncMock() - + service = SimplifyService( config=test_config, ruwiki_adapter=ruwiki_adapter, @@ -255,7 +266,7 @@ class TestSimplifyService: repository=repository, write_queue=write_queue, ) - + return service async def test_get_prompt_template(self, simplify_service: SimplifyService): @@ -274,28 +285,28 @@ class TestSimplifyService: status=ArticleStatus.SIMPLIFIED, created_at=datetime.now(timezone.utc), ) - + simplify_service.repository.get_by_url = AsyncMock(return_value=existing_article) - + result = await simplify_service._check_existing_article("https://ru.ruwiki.ru/wiki/Test") - + assert result is not None assert result.success is True assert result.simplified_text == "Simplified" async def test_check_existing_article_not_found(self, simplify_service: SimplifyService): simplify_service.repository.get_by_url = AsyncMock(return_value=None) - + result = await simplify_service._check_existing_article("https://ru.ruwiki.ru/wiki/Test") - + assert result is None async def test_health_check(self, simplify_service: SimplifyService): simplify_service.ruwiki_adapter.health_check = AsyncMock() simplify_service.llm_adapter.health_check = AsyncMock() - + checks = await simplify_service.health_check() - + assert "ruwiki" in checks assert "llm" in checks assert "prompt_template" in checks