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())