From 3f222a9141c7924bc4bdac807a0094d3a0b3f9c2 Mon Sep 17 00:00:00 2001 From: Aleksandr Meshchriakov Date: Mon, 2 Feb 2026 12:44:37 +0100 Subject: [PATCH] fix(lint): resolve ruff errors in tests and run_tests.py - Fix import sorting (I001) - Remove unused imports and variables (F401, F841) - Add noqa for test code (S106 hardcoded passwords, S314 XML parsing) - Auto-format with ruff format --- CHANGELOG.md | 6 +++++ run_tests.py | 35 +++++++++++------------- tests/apps/core/test_background_jobs.py | 4 +-- tests/apps/core/test_bulk_operations.py | 13 +++++++-- tests/apps/core/test_response.py | 4 +-- tests/apps/core/test_services.py | 4 +-- tests/apps/core/test_signals.py | 4 +-- tests/apps/parsers/factories.py | 12 ++++----- tests/apps/parsers/test_clients.py | 36 +++++++++++++++---------- tests/apps/parsers/test_models.py | 3 +-- tests/apps/parsers/test_services.py | 28 ++++++++++--------- tests/apps/user/factories.py | 8 +++--- 12 files changed, 87 insertions(+), 70 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0bf5a8b..f6d1a88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,12 @@ - `docker/build-push-action@v5` → чистые `docker build/push` команды - Тесты используют `config.settings.test` (SQLite in-memory) вместо PostgreSQL service +#### Code Quality +- Исправлены ошибки ruff lint: + - Сортировка импортов (I001) + - Удалены неиспользуемые импорты и переменные (F401, F841) + - Добавлены noqa для тестового кода (S106, S314) + --- ## [0.4.0] - 2026-01-28 diff --git a/run_tests.py b/run_tests.py index db61014..16d18ad 100644 --- a/run_tests.py +++ b/run_tests.py @@ -5,10 +5,9 @@ Поддерживает coverage и дополнительные опции """ +import argparse import os import sys -from io import StringIO -import argparse import django @@ -59,51 +58,47 @@ def run_tests_with_args(test_args, options): def parse_arguments(): """Парсинг аргументов командной строки""" - parser = argparse.ArgumentParser(description="Запуск Django тестов с дополнительными возможностями") + parser = argparse.ArgumentParser( + description="Запуск Django тестов с дополнительными возможностями" + ) parser.add_argument( "targets", nargs="*", help="Цели тестирования (по умолчанию: все тесты)", - default=["tests"] + default=["tests"], ) parser.add_argument( - "--coverage", "--cov", + "--coverage", + "--cov", action="store_true", - help="Запуск тестов с измерением покрытия кода" + help="Запуск тестов с измерением покрытия кода", ) parser.add_argument( "--fast", action="store_true", - help="Запуск только быстрых тестов (исключает медленные)" + help="Запуск только быстрых тестов (исключает медленные)", ) parser.add_argument( - "--failfast", - action="store_true", - help="Остановка при первой ошибке" + "--failfast", action="store_true", help="Остановка при первой ошибке" ) parser.add_argument( - "--verbose", "-v", - action="count", - default=2, - help="Уровень детализации вывода" + "--verbose", "-v", action="count", default=2, help="Уровень детализации вывода" ) parser.add_argument( - "--keepdb", - action="store_true", - help="Сохранить тестовую базу данных" + "--keepdb", action="store_true", help="Сохранить тестовую базу данных" ) parser.add_argument( "--parallel", type=int, metavar="N", - help="Запуск тестов в N параллельных процессах" + help="Запуск тестов в N параллельных процессах", ) args = parser.parse_args() @@ -186,6 +181,7 @@ def setup_coverage(): """Настройка coverage""" try: import coverage + cov = coverage.Coverage(config_file="pyproject.toml") cov.start() return cov @@ -245,7 +241,7 @@ def main(): print(f"\n❌ Тесты завершились с ошибками: {failures} неудачных тестов") sys.exit(1) else: - print(f"\n✅ Все тесты прошли успешно!") + print("\n✅ Все тесты прошли успешно!") if cov: print("📊 Отчет о покрытии сохранен") sys.exit(0) @@ -260,6 +256,7 @@ def main(): if cov: cov.stop() import traceback + traceback.print_exc() sys.exit(1) diff --git a/tests/apps/core/test_background_jobs.py b/tests/apps/core/test_background_jobs.py index b226195..756c4d9 100644 --- a/tests/apps/core/test_background_jobs.py +++ b/tests/apps/core/test_background_jobs.py @@ -187,7 +187,7 @@ class BackgroundJobServiceTest(TestCase): def test_get_user_jobs_with_status_filter(self): """Тест фильтрации по статусу.""" user_id = 456 - job1 = BackgroundJobService.create_job( + BackgroundJobService.create_job( task_id="task-pending", task_name="test.task", user_id=user_id, @@ -212,7 +212,7 @@ class BackgroundJobServiceTest(TestCase): def test_get_active_jobs(self): """Тест получения активных задач.""" # Создаём задачи с разными статусами - job_pending = BackgroundJobService.create_job( + BackgroundJobService.create_job( task_id="job-active-pending", task_name="test.task", ) diff --git a/tests/apps/core/test_bulk_operations.py b/tests/apps/core/test_bulk_operations.py index 5656d0e..5227ed4 100644 --- a/tests/apps/core/test_bulk_operations.py +++ b/tests/apps/core/test_bulk_operations.py @@ -71,6 +71,7 @@ class BulkOperationsIntegrationTest(TestCase): def test_bulk_create_chunked(self): """Тест массового создания чанками.""" + # Создаём тестовый сервис с BulkOperationsMixin class TestService(BulkOperationsMixin): model = BackgroundJob @@ -88,10 +89,13 @@ class BulkOperationsIntegrationTest(TestCase): self.assertEqual(count, 10) # Проверяем что все созданы - self.assertEqual(BackgroundJob.objects.filter(task_name="test.bulk.task").count(), 10) + self.assertEqual( + BackgroundJob.objects.filter(task_name="test.bulk.task").count(), 10 + ) def test_bulk_delete(self): """Тест массового удаления.""" + class TestService(BulkOperationsMixin): model = BackgroundJob @@ -109,10 +113,13 @@ class BulkOperationsIntegrationTest(TestCase): deleted = TestService.bulk_delete(ids_to_delete) self.assertEqual(deleted, 3) - self.assertEqual(BackgroundJob.objects.filter(task_name="test.delete.task").count(), 2) + self.assertEqual( + BackgroundJob.objects.filter(task_name="test.delete.task").count(), 2 + ) def test_bulk_update_fields(self): """Тест массового обновления полей.""" + class TestService(BulkOperationsMixin): model = BackgroundJob @@ -138,6 +145,7 @@ class BulkOperationsIntegrationTest(TestCase): def test_bulk_update_or_create_creates(self): """Тест upsert - создание новых.""" + class TestService(BulkOperationsMixin): model = BackgroundJob @@ -157,6 +165,7 @@ class BulkOperationsIntegrationTest(TestCase): def test_bulk_update_or_create_updates(self): """Тест upsert - обновление существующих.""" + class TestService(BulkOperationsMixin): model = BackgroundJob diff --git a/tests/apps/core/test_response.py b/tests/apps/core/test_response.py index 485d4f3..4368625 100644 --- a/tests/apps/core/test_response.py +++ b/tests/apps/core/test_response.py @@ -101,9 +101,7 @@ class APIPaginatedResponseTest(TestCase): def test_paginated_response(self): """Test paginated response with correct metadata""" data = [{"id": 1}, {"id": 2}] - response = api_paginated_response( - data, page=1, page_size=10, total_count=25 - ) + response = api_paginated_response(data, page=1, page_size=10, total_count=25) self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.data["data"], data) diff --git a/tests/apps/core/test_services.py b/tests/apps/core/test_services.py index f4fe1a6..e7b2c2e 100644 --- a/tests/apps/core/test_services.py +++ b/tests/apps/core/test_services.py @@ -21,7 +21,7 @@ class BaseServiceTest(TestCase): self.user = User.objects.create_user( username="testuser", email="test@example.com", - password="testpass123", + password="testpass123", # noqa: S106 ) def test_get_by_id_success(self): @@ -53,7 +53,7 @@ class BaseServiceTest(TestCase): User.objects.create_user( username="testuser2", email="test2@example.com", - password="testpass123", + password="testpass123", # noqa: S106 ) result = UserTestService.get_all() diff --git a/tests/apps/core/test_signals.py b/tests/apps/core/test_signals.py index af7f76f..4444401 100644 --- a/tests/apps/core/test_signals.py +++ b/tests/apps/core/test_signals.py @@ -88,7 +88,7 @@ class SignalDispatcherTest(TestCase): self.dispatcher.connect_all() # Create user to trigger signal - user = UserFactory.create_user() + UserFactory.create_user() self.assertTrue(handler_called["value"]) @@ -114,7 +114,7 @@ class SignalDispatcherTest(TestCase): # Create user - handler should not be called handler_called["value"] = False - user = UserFactory.create_user() + UserFactory.create_user() self.assertFalse(handler_called["value"]) diff --git a/tests/apps/parsers/factories.py b/tests/apps/parsers/factories.py index 4ff41ca..590403a 100644 --- a/tests/apps/parsers/factories.py +++ b/tests/apps/parsers/factories.py @@ -3,10 +3,7 @@ import random from datetime import timedelta -from django.utils import timezone - import factory - from apps.parsers.models import ( IndustrialCertificateRecord, InspectionRecord, @@ -14,6 +11,7 @@ from apps.parsers.models import ( ParserLoadLog, Proxy, ) +from django.utils import timezone # === Хелперы для генерации реалистичных данных === @@ -320,7 +318,9 @@ class InspectionRecordFactory(factory.django.DjangoModelFactory): lambda _: random.choice(["плановая", "внеплановая"]) ) inspection_form = factory.LazyAttribute( - lambda _: random.choice(["документарная", "выездная", "документарная и выездная"]) + lambda _: random.choice( + ["документарная", "выездная", "документарная и выездная"] + ) ) start_date = factory.LazyAttribute( lambda _: (timezone.now() - timedelta(days=random.randint(1, 180))).strftime( @@ -339,9 +339,7 @@ class InspectionRecordFactory(factory.django.DjangoModelFactory): lambda _: random.choice(["294-ФЗ", "248-ФЗ", "184-ФЗ"]) ) result = factory.LazyAttribute( - lambda _: random.choice( - ["нарушения не выявлены", "выявлены нарушения", ""] - ) + lambda _: random.choice(["нарушения не выявлены", "выявлены нарушения", ""]) if random.random() > 0.3 else "" ) diff --git a/tests/apps/parsers/test_clients.py b/tests/apps/parsers/test_clients.py index e16594c..30bc53e 100644 --- a/tests/apps/parsers/test_clients.py +++ b/tests/apps/parsers/test_clients.py @@ -3,17 +3,15 @@ from io import BytesIO from unittest.mock import patch -from django.test import TestCase, tag - -from faker import Faker -from openpyxl import Workbook - from apps.parsers.clients.base import BaseHTTPClient, HTTPClientError from apps.parsers.clients.minpromtorg.industrial import IndustrialProductionClient from apps.parsers.clients.minpromtorg.manufactures import ManufacturesClient from apps.parsers.clients.minpromtorg.schemas import IndustrialCertificate, Manufacturer from apps.parsers.clients.proverki import ProverkiClient from apps.parsers.clients.proverki.schemas import Inspection +from django.test import TestCase, tag +from faker import Faker +from openpyxl import Workbook fake = Faker("ru_RU") @@ -159,7 +157,10 @@ class IndustrialProductionClientTest(TestCase): { "name": "Заключения о подтверждении производства промышленной продукции на территории Российской Федерации", "files": [ - {"name": "data_resolutions_20240101.xlsx", "url": "/files/test.xlsx"}, + { + "name": "data_resolutions_20240101.xlsx", + "url": "/files/test.xlsx", + }, ], } ] @@ -193,9 +194,18 @@ class IndustrialProductionClientTest(TestCase): { "name": "Заключения о подтверждении производства промышленной продукции на территории Российской Федерации", "files": [ - {"name": "data_resolutions_20240101.xlsx", "url": "/files/old.xlsx"}, - {"name": "data_resolutions_20240315.xlsx", "url": "/files/new.xlsx"}, - {"name": "data_resolutions_20240201.xlsx", "url": "/files/mid.xlsx"}, + { + "name": "data_resolutions_20240101.xlsx", + "url": "/files/old.xlsx", + }, + { + "name": "data_resolutions_20240315.xlsx", + "url": "/files/new.xlsx", + }, + { + "name": "data_resolutions_20240201.xlsx", + "url": "/files/mid.xlsx", + }, ], } ] @@ -539,7 +549,7 @@ class ProverkiClientTest(TestCase): client = ProverkiClient() xml_str = '' - element = ET.fromstring(xml_str) + element = ET.fromstring(xml_str) # noqa: S314 result = client._parse_xml_record(element) @@ -553,7 +563,7 @@ class ProverkiClientTest(TestCase): client = ProverkiClient() xml_str = "" - element = ET.fromstring(xml_str) + element = ET.fromstring(xml_str) # noqa: S314 result = client._parse_xml_record(element) @@ -569,9 +579,7 @@ class ProverkiClientTest(TestCase): TEST001 Компания -""".encode( - "windows-1251" - ) +""".encode("windows-1251") inspections = client._parse_xml_content(xml_content, None) diff --git a/tests/apps/parsers/test_models.py b/tests/apps/parsers/test_models.py index 56c0e68..cc057c4 100644 --- a/tests/apps/parsers/test_models.py +++ b/tests/apps/parsers/test_models.py @@ -1,13 +1,12 @@ """Tests for parsers models.""" -from django.test import TestCase - from apps.parsers.models import ( IndustrialCertificateRecord, ManufacturerRecord, ParserLoadLog, Proxy, ) +from django.test import TestCase from .factories import ( IndustrialCertificateRecordFactory, diff --git a/tests/apps/parsers/test_services.py b/tests/apps/parsers/test_services.py index 0d41de9..a12d7a1 100644 --- a/tests/apps/parsers/test_services.py +++ b/tests/apps/parsers/test_services.py @@ -1,9 +1,5 @@ """Tests for parsers services.""" -from django.test import TestCase - -from faker import Faker - from apps.parsers.clients.minpromtorg.schemas import IndustrialCertificate, Manufacturer from apps.parsers.clients.proverki.schemas import Inspection from apps.parsers.models import ( @@ -20,6 +16,8 @@ from apps.parsers.services import ( ParserLoadLogService, ProxyService, ) +from django.test import TestCase +from faker import Faker from .factories import ( IndustrialCertificateRecordFactory, @@ -147,7 +145,9 @@ class ParserLoadLogServiceTest(TestCase): def test_get_next_batch_id_first(self): """Test getting first batch_id for new source.""" - batch_id = ParserLoadLogService.get_next_batch_id(ParserLoadLog.Source.INDUSTRIAL) + batch_id = ParserLoadLogService.get_next_batch_id( + ParserLoadLog.Source.INDUSTRIAL + ) self.assertEqual(batch_id, 1) def test_get_next_batch_id_increment(self): @@ -155,7 +155,9 @@ class ParserLoadLogServiceTest(TestCase): ParserLoadLogFactory(batch_id=5, source=ParserLoadLog.Source.INDUSTRIAL) ParserLoadLogFactory(batch_id=3, source=ParserLoadLog.Source.INDUSTRIAL) - batch_id = ParserLoadLogService.get_next_batch_id(ParserLoadLog.Source.INDUSTRIAL) + batch_id = ParserLoadLogService.get_next_batch_id( + ParserLoadLog.Source.INDUSTRIAL + ) self.assertEqual(batch_id, 6) def test_get_next_batch_id_per_source(self): @@ -272,7 +274,9 @@ class IndustrialCertificateServiceTest(TestCase): results = IndustrialCertificateService.find_by_inn("1111111111") self.assertEqual(results.count(), 2) - results_batch1 = IndustrialCertificateService.find_by_inn("1111111111", batch_id=1) + results_batch1 = IndustrialCertificateService.find_by_inn( + "1111111111", batch_id=1 + ) self.assertEqual(results_batch1.count(), 1) def test_find_by_certificate_number(self): @@ -313,7 +317,7 @@ class IndustrialCertificateServiceTest(TestCase): ogrn="9999999999999", ) ] - count2 = IndustrialCertificateService.save_certificates(duplicate, batch_id=2) + IndustrialCertificateService.save_certificates(duplicate, batch_id=2) # Should still be 1 record (duplicate skipped) self.assertEqual(IndustrialCertificateRecord.objects.count(), 1) @@ -420,7 +424,7 @@ class ManufacturerServiceTest(TestCase): address="New Address", ) ] - count2 = ManufacturerService.save_manufacturers(duplicate, batch_id=2) + ManufacturerService.save_manufacturers(duplicate, batch_id=2) # Should still be 1 record (duplicate skipped) self.assertEqual(ManufacturerRecord.objects.count(), 1) @@ -567,7 +571,7 @@ class InspectionServiceTest(TestCase): result="выявлены нарушения", ) ] - count2 = InspectionService.save_inspections(duplicate, batch_id=2) + InspectionService.save_inspections(duplicate, batch_id=2) # Should still be 1 record (duplicate skipped) self.assertEqual(InspectionRecord.objects.count(), 1) @@ -581,10 +585,9 @@ class InspectionServiceTest(TestCase): self.assertEqual(record.load_batch, 1) # Original batch -from django.test import tag - from apps.parsers.clients.base import HTTPClientError from apps.parsers.clients.minpromtorg.industrial import IndustrialProductionClient +from django.test import tag @tag("integration", "slow", "network", "e2e") @@ -674,4 +677,3 @@ class EndToEndIntegrationTest(TestCase): except HTTPClientError as e: self.skipTest(f"External API unavailable: {e}") - diff --git a/tests/apps/user/factories.py b/tests/apps/user/factories.py index 4fcd031..6a9af2b 100644 --- a/tests/apps/user/factories.py +++ b/tests/apps/user/factories.py @@ -16,9 +16,7 @@ class UserFactory(factory.django.DjangoModelFactory): email = factory.LazyAttribute(lambda _: fake.unique.email()) username = factory.LazyAttribute(lambda _: fake.unique.user_name()) - phone = factory.LazyAttribute( - lambda _: f"+7{fake.numerify('##########')}" - ) + phone = factory.LazyAttribute(lambda _: f"+7{fake.numerify('##########')}") is_verified = False is_staff = False is_superuser = False @@ -58,7 +56,9 @@ class ProfileFactory(factory.django.DjangoModelFactory): class Meta: model = Profile - django_get_or_create = ("user",) # Используем get_or_create для избежания дубликатов + django_get_or_create = ( + "user", + ) # Используем get_or_create для избежания дубликатов user = factory.SubFactory(UserFactory) first_name = factory.LazyAttribute(lambda _: fake.first_name())