fix pre-commit
Some checks failed
CI/CD Pipeline / Telegram Notify Success (push) Has been cancelled
CI/CD Pipeline / Run Tests (push) Has been cancelled
CI/CD Pipeline / Code Quality Checks (push) Has been cancelled
CI/CD Pipeline / Code Quality Checks (pull_request) Successful in 1m42s
CI/CD Pipeline / Run Tests (pull_request) Successful in 2m25s
CI/CD Pipeline / Telegram Notify Success (pull_request) Successful in 1m34s

This commit is contained in:
2026-03-17 13:55:34 +01:00
parent 3d298ce352
commit 25176f31b4
31 changed files with 653 additions and 553 deletions

View File

@@ -69,8 +69,12 @@ class RegistryOrganizationResolverTest(TestCase):
self.assertEqual(lookup.by_ogrn, {})
def test_resolve_organization_id_by_unique_inn_and_ogrn(self):
org_by_inn = OrganizationFactory(mn_inn=7_701_001_001, mn_ogrn=10_277_001_000_001)
org_by_ogrn = OrganizationFactory(mn_inn=7_701_001_002, mn_ogrn=10_277_001_000_002)
org_by_inn = OrganizationFactory(
mn_inn=7_701_001_001, mn_ogrn=10_277_001_000_001
)
org_by_ogrn = OrganizationFactory(
mn_inn=7_701_001_002, mn_ogrn=10_277_001_000_002
)
lookup = RegistryOrganizationResolver.build_lookup(
[
(org_by_inn.mn_inn, None),
@@ -103,37 +107,39 @@ class ParserLoadLogServiceRetryTest(TestCase):
batch_id=2,
)
with patch.object(ParserLoadLogService, "get_next_batch_id", side_effect=[1, 2]):
with patch.object(
ParserLoadLogService,
"create_load_log",
side_effect=[IntegrityError("duplicate"), log],
) as create_mock:
created_log, batch_id = (
ParserLoadLogService.create_load_log_with_next_batch_id(
source=ParserLoadLog.Source.INDUSTRIAL
)
)
with patch.object(
ParserLoadLogService, "get_next_batch_id", side_effect=[1, 2]
), patch.object(
ParserLoadLogService,
"create_load_log",
side_effect=[IntegrityError("duplicate"), log],
) as create_mock:
(
created_log,
batch_id,
) = ParserLoadLogService.create_load_log_with_next_batch_id(
source=ParserLoadLog.Source.INDUSTRIAL
)
self.assertEqual(created_log, log)
self.assertEqual(batch_id, 2)
self.assertEqual(create_mock.call_count, 2)
def test_create_load_log_with_next_batch_id_raises_after_max_retries(self):
with patch.object(ParserLoadLogService, "get_next_batch_id", return_value=1):
with patch.object(
ParserLoadLogService,
"create_load_log",
side_effect=IntegrityError("duplicate"),
):
with self.assertRaisesMessage(
RuntimeError,
"Failed to allocate unique batch_id",
):
ParserLoadLogService.create_load_log_with_next_batch_id(
source=ParserLoadLog.Source.INDUSTRIAL,
max_retries=2,
)
with patch.object(
ParserLoadLogService, "get_next_batch_id", return_value=1
), patch.object(
ParserLoadLogService,
"create_load_log",
side_effect=IntegrityError("duplicate"),
), self.assertRaisesMessage(
RuntimeError,
"Failed to allocate unique batch_id",
):
ParserLoadLogService.create_load_log_with_next_batch_id(
source=ParserLoadLog.Source.INDUSTRIAL,
max_retries=2,
)
class SmallParserServiceQueryTest(TestCase):
@@ -177,7 +183,9 @@ class SmallParserServiceQueryTest(TestCase):
load_batch=12,
)
self.assertEqual(ProcurementService.find_by_customer_name("Тестовый").count(), 2)
self.assertEqual(
ProcurementService.find_by_customer_name("Тестовый").count(), 2
)
self.assertEqual(
ProcurementService.find_by_customer_name("Тестовый", batch_id=11).count(),
1,

View File

@@ -4,7 +4,6 @@ from types import SimpleNamespace
from unittest.mock import MagicMock, patch
from apps.parsers.source_cards import (
RefreshParamDefinition,
SourceCardDefinition,
SourceCardService,
SourceItemDefinition,
@@ -66,8 +65,14 @@ class SourceCardServiceUnitTest(SimpleTestCase):
@patch(
"apps.parsers.source_cards.SourceCardService._enqueue_task",
side_effect=[
{"task_id": "task-1", "task_name": "apps.parsers.tasks.parse_industrial_production"},
{"task_id": "task-2", "task_name": "apps.parsers.tasks.parse_industrial_products"},
{
"task_id": "task-1",
"task_name": "apps.parsers.tasks.parse_industrial_production",
},
{
"task_id": "task-2",
"task_name": "apps.parsers.tasks.parse_industrial_products",
},
{"task_id": "task-3", "task_name": "apps.parsers.tasks.parse_manufactures"},
],
)
@@ -78,14 +83,22 @@ class SourceCardServiceUnitTest(SimpleTestCase):
)
self.assertEqual(result["source_card"], "manufacturers-and-products")
self.assertEqual([item["task_id"] for item in result["tasks"]], ["task-1", "task-2", "task-3"])
self.assertEqual(
[item["task_id"] for item in result["tasks"]],
["task-1", "task-2", "task-3"],
)
self.assertEqual(enqueue_mock.call_count, 3)
@patch(
"apps.parsers.source_cards.SourceCardService._enqueue_task",
return_value={"task_id": "task-1", "task_name": "apps.parsers.tasks.sync_inspections"},
return_value={
"task_id": "task-1",
"task_name": "apps.parsers.tasks.sync_inspections",
},
)
def test_launch_refresh_for_inspections_passes_supported_kwargs_only(self, enqueue_mock):
def test_launch_refresh_for_inspections_passes_supported_kwargs_only(
self, enqueue_mock
):
definition = SourceCardService.get_definition("planned-inspections")
result = SourceCardService._launch_refresh(
@@ -99,7 +112,10 @@ class SourceCardServiceUnitTest(SimpleTestCase):
},
)
self.assertEqual(result, [{"task_id": "task-1", "task_name": "apps.parsers.tasks.sync_inspections"}])
self.assertEqual(
result,
[{"task_id": "task-1", "task_name": "apps.parsers.tasks.sync_inspections"}],
)
self.assertEqual(
enqueue_mock.call_args.kwargs["kwargs"],
{
@@ -112,7 +128,10 @@ class SourceCardServiceUnitTest(SimpleTestCase):
@patch(
"apps.parsers.source_cards.SourceCardService._enqueue_task",
return_value={"task_id": "task-9", "task_name": "apps.parsers.tasks.sync_procurements"},
return_value={
"task_id": "task-9",
"task_name": "apps.parsers.tasks.sync_procurements",
},
)
def test_refresh_card_for_procurements_uses_default_law_type(self, enqueue_mock):
result = SourceCardService.refresh_card(
@@ -156,34 +175,37 @@ class SourceCardServiceUnitTest(SimpleTestCase):
params={},
)
self.assertIn("Обновление для карточки не поддерживается", str(error.exception.detail))
self.assertIn(
"Обновление для карточки не поддерживается", str(error.exception.detail)
)
def test_enqueue_task_deletes_background_job_on_async_error(self):
task = MagicMock()
task.apply_async.side_effect = RuntimeError("broker down")
queryset = MagicMock()
with patch("apps.parsers.source_cards.uuid.uuid4", return_value="task-id-1"):
with patch("apps.parsers.source_cards.BackgroundJobService.create_job"):
with patch(
"apps.parsers.source_cards.BackgroundJobService.get_queryset",
return_value=queryset,
):
with self.assertRaisesMessage(RuntimeError, "broker down"):
SourceCardService._enqueue_task(
task=task,
task_name="apps.parsers.tasks.sync_procurements",
requested_by_id=5,
meta={"source_card": "public-procurements"},
kwargs={"region_code": "77"},
)
with patch(
"apps.parsers.source_cards.uuid.uuid4", return_value="task-id-1"
), patch("apps.parsers.source_cards.BackgroundJobService.create_job"), patch(
"apps.parsers.source_cards.BackgroundJobService.get_queryset",
return_value=queryset,
), self.assertRaisesMessage(RuntimeError, "broker down"):
SourceCardService._enqueue_task(
task=task,
task_name="apps.parsers.tasks.sync_procurements",
requested_by_id=5,
meta={"source_card": "public-procurements"},
kwargs={"region_code": "77"},
)
queryset.filter.assert_called_once_with(task_id="task-id-1")
queryset.filter.return_value.delete.assert_called_once_with()
def test_helper_methods_cover_unknown_codes_and_status_variants(self):
self.assertEqual(SourceCardService._get_source_records_count("unknown"), 0)
self.assertEqual(SourceCardService._get_source_organizations_count("unknown"), 0)
self.assertEqual(
SourceCardService._get_source_organizations_count("unknown"), 0
)
self.assertIsNone(SourceCardService._get_source_data_timestamp("unknown"))
self.assertIsNone(SourceCardService._get_latest_load_by_source(None))
self.assertEqual(SourceCardService._get_status_label("custom"), "custom")

View File

@@ -2,6 +2,9 @@
from __future__ import annotations
from pathlib import Path
from tempfile import TemporaryDirectory
from apps.core.models import BackgroundJob, JobStatus
from apps.parsers.models import FinancialReport, FinancialReportLine, ParserLoadLog
from django.test import override_settings
@@ -161,21 +164,21 @@ class SourceCardsApiTestCase(APITestCase):
self.assertIn("status_label", row)
self.assertIn("active_tasks", row)
@override_settings(
FNS_WATCH_DIRECTORY="/tmp/mostovik-test-fns/watch",
FNS_PROCESSED_DIRECTORY="/tmp/mostovik-test-fns/processed",
FNS_FAILED_DIRECTORY="/tmp/mostovik-test-fns/failed",
)
def test_refresh_creates_background_job_and_returns_task(self):
self.client.force_authenticate(self.admin)
response = self.client.post(
reverse(
"api_v1:sources:source-cards-refresh",
kwargs={"slug": "financial-indicators"},
),
{},
format="json",
)
with TemporaryDirectory() as tmp_dir, override_settings(
FNS_WATCH_DIRECTORY=str(Path(tmp_dir) / "watch"),
FNS_PROCESSED_DIRECTORY=str(Path(tmp_dir) / "processed"),
FNS_FAILED_DIRECTORY=str(Path(tmp_dir) / "failed"),
):
response = self.client.post(
reverse(
"api_v1:sources:source-cards-refresh",
kwargs={"slug": "financial-indicators"},
),
{},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED)
self.assertTrue(response.data["success"])

View File

@@ -111,7 +111,9 @@ class SourcesApiE2ETest(APITestCase):
self.assertEqual(cards["planned-inspections"]["status"], "in_progress")
self.assertEqual(cards["planned-inspections"]["progress"], 55)
self.assertEqual(cards["public-procurements"]["status"], "error")
self.assertEqual(cards["public-procurements"]["error_message"], "download error")
self.assertEqual(
cards["public-procurements"]["error_message"], "download error"
)
minprom_card = detail_response.data["data"]
self.assertEqual(minprom_card["records_count"], 3)
@@ -169,27 +171,24 @@ class SourcesApiE2ETest(APITestCase):
"request-procurements",
"task-procurements",
],
), patch(
"apps.parsers.tasks.parse_industrial_production.apply_async",
return_value=SimpleNamespace(id="task-industrial"),
), patch(
"apps.parsers.tasks.parse_industrial_products.apply_async",
return_value=SimpleNamespace(id="task-products"),
), patch(
"apps.parsers.tasks.parse_manufactures.apply_async",
return_value=SimpleNamespace(id="task-manufactures"),
):
with patch(
"apps.parsers.tasks.parse_industrial_production.apply_async",
return_value=SimpleNamespace(id="task-industrial"),
):
with patch(
"apps.parsers.tasks.parse_industrial_products.apply_async",
return_value=SimpleNamespace(id="task-products"),
):
with patch(
"apps.parsers.tasks.parse_manufactures.apply_async",
return_value=SimpleNamespace(id="task-manufactures"),
):
minprom_response = self.client.post(
reverse(
"api_v1:sources:source-cards-refresh",
kwargs={"slug": "manufacturers-and-products"},
),
{},
format="json",
)
minprom_response = self.client.post(
reverse(
"api_v1:sources:source-cards-refresh",
kwargs={"slug": "manufacturers-and-products"},
),
{},
format="json",
)
with patch(
"apps.parsers.tasks.sync_procurements.apply_async",