Files
mostovik-backend/tests/apps/parsers/test_registry_procurement_tasks.py
Aleksandr Meshchriakov c344bef777
All checks were successful
CI/CD Pipeline / Quality Gate (push) Successful in 28s
CI/CD Pipeline / Build and Push Images (push) Successful in 16s
CI/CD Pipeline / Internal Notify (push) Successful in 0s
CI/CD Pipeline / Deploy Dev via Compose (push) Successful in 23s
feat: restore registry procurement collection
2026-07-19 17:46:15 +02:00

253 lines
9.2 KiB
Python

from __future__ import annotations
from contextlib import ExitStack
from types import SimpleNamespace
from unittest.mock import patch
from apps.parsers import tasks as parser_tasks
from apps.parsers.clients.common.schemas import GenericParserItem
from apps.parsers.models import CheckoCollectionAttempt, ParserLoadLog
from django.test import TestCase, override_settings
from tests.apps.parsers.organization_helpers import create_directory_organization
def _organization(index: int, *, membership: bool = True):
return create_directory_organization(
name=f"Организация {index}",
inn=f"7701000{index:03d}",
ogrn=f"1027700000{index:03d}",
opk_registry_membership=membership,
)
class RegistryProcurementTasksTest(TestCase):
def test_daily_orchestrator_queues_addressed_sources_with_same_batch_limit(self):
task_names = (
"parse_procurements_44fz",
"parse_procurements_223fz",
"parse_registry_contracts",
"parse_unfair_suppliers",
"parse_arbitration_cases",
"parse_fedresurs_bankruptcy",
"parse_registry_inspections",
"parse_trudvsem_vacancies",
)
with ExitStack() as stack:
mocks = [
stack.enter_context(
patch.object(
getattr(parser_tasks, task_name),
"delay",
return_value=SimpleNamespace(id=f"{task_name}-id"),
)
)
for task_name in task_names
]
result = parser_tasks.parse_registry_enrichment_sources(
limit=20,
proxies=[],
)
self.assertEqual(
set(result),
{
"procurements_44fz",
"procurements_223fz",
"contracts",
"unfair_suppliers",
"arbitration",
"bankruptcy",
"inspections",
"vacancies",
},
)
for task_mock in mocks[:5]:
task_mock.assert_called_once_with(
proxies=[],
requested_by_id=None,
limit=20,
)
mocks[5].assert_called_once_with(proxies=[], requested_by_id=None)
mocks[6].assert_called_once_with(proxies=[], requested_by_id=None, limit=20)
mocks[7].assert_called_once_with(proxies=[], requested_by_id=None)
def test_eis_monthly_claims_advance_to_the_next_batch(self):
organizations = [_organization(index) for index in range(1, 4)]
outside_registry = _organization(9, membership=False)
requested_inns: list[str] = []
class _Client:
def __init__(self, **_kwargs):
return
def fetch_for_customer(self, **kwargs):
requested_inns.append(kwargs["inn"])
return [
GenericParserItem(
source=ParserLoadLog.Source.PROCUREMENTS_44FZ,
external_id=f"notice-{kwargs['inn']}",
inn=kwargs["inn"],
organisation_name=kwargs["organization_name"],
)
]
with patch.object(parser_tasks, "EisRegistryProcurementClient", _Client):
first = parser_tasks._fetch_eis_registry_procurement_records(
source=ParserLoadLog.Source.PROCUREMENTS_44FZ,
law="44",
limit=2,
proxies=[],
)
second = parser_tasks._fetch_eis_registry_procurement_records(
source=ParserLoadLog.Source.PROCUREMENTS_44FZ,
law="44",
limit=2,
proxies=[],
)
self.assertEqual(len(first), 2)
self.assertEqual(len(second), 1)
self.assertEqual(requested_inns, [item.inn for item in organizations])
self.assertNotIn(outside_registry.inn, requested_inns)
self.assertEqual(
CheckoCollectionAttempt.objects.filter(
source=CheckoCollectionAttempt.Source.PROCUREMENTS_44FZ,
status=CheckoCollectionAttempt.Status.SUCCESS,
).count(),
3,
)
def test_eis_request_error_creates_failed_monthly_claim(self):
organization = _organization(1)
class _Client:
def __init__(self, **_kwargs):
return
def fetch_for_customer(self, **_kwargs):
raise parser_tasks.HTTPClientError("EIS unavailable")
with (
patch.object(parser_tasks, "EisRegistryProcurementClient", _Client),
self.assertRaises(parser_tasks.ParserSourceSkipped),
):
parser_tasks._fetch_eis_registry_procurement_records(
source=ParserLoadLog.Source.PROCUREMENTS_223FZ,
law="223",
limit=1,
proxies=[],
)
attempt = CheckoCollectionAttempt.objects.get(
organization=organization,
source=CheckoCollectionAttempt.Source.PROCUREMENTS_223FZ,
)
self.assertEqual(attempt.status, CheckoCollectionAttempt.Status.FAILED)
def test_explicit_organization_ids_do_not_fall_through_to_next_batch(self):
selected = _organization(1)
_organization(2)
calls: list[str] = []
class _Client:
def __init__(self, **_kwargs):
return
def fetch_for_customer(self, **kwargs):
calls.append(kwargs["inn"])
return []
kwargs = {
"source": ParserLoadLog.Source.PROCUREMENTS_44FZ,
"law": "44",
"limit": 20,
"proxies": [],
"organization_ids": [str(selected.uid)],
}
with patch.object(parser_tasks, "EisRegistryProcurementClient", _Client):
self.assertEqual(
parser_tasks._fetch_eis_registry_procurement_records(**kwargs),
[],
)
with self.assertRaises(parser_tasks.ParserSourceSkipped):
parser_tasks._fetch_eis_registry_procurement_records(**kwargs)
self.assertEqual(calls, [selected.inn])
@override_settings(CHECKO_API_KEY="test-key")
def test_checko_rnp_is_supplier_bound_and_not_requested_twice(self):
organization = _organization(1)
calls: list[str] = []
class _Client:
def __init__(self, **_kwargs):
return
def get_company(self, request):
calls.append(request.inn)
return SimpleNamespace(
data=SimpleNamespace(
inn=organization.inn,
ogrn=organization.ogrn,
unfair_supplier=(
SimpleNamespace(
registry_number="RNP-1",
publish_date="2026-07-01",
approval_date="2026-06-30",
customer_short_name="Заказчик",
customer_full_name="Заказчик полный",
customer_inn="7702000002",
customer_kpp="770201001",
purchase_number="PURCHASE-1",
purchase_description="Поставка оборудования",
contract_price=1500000,
),
),
)
)
with patch.object(parser_tasks, "CheckoClient", _Client):
records = parser_tasks._fetch_checko_unfair_supplier_records(
limit=1,
proxies=[],
)
with self.assertRaises(parser_tasks.ParserSourceSkipped):
parser_tasks._fetch_checko_unfair_supplier_records(
limit=1,
proxies=[],
)
self.assertEqual(calls, [organization.inn])
self.assertEqual(len(records), 1)
self.assertEqual(records[0].inn, organization.inn)
self.assertEqual(records[0].payload["supplier"]["inn"], organization.inn)
self.assertEqual(records[0].payload["customer"]["inn"], "7702000002")
self.assertEqual(records[0].amount, 1500000)
@override_settings(CHECKO_API_KEY="test-key")
def test_checko_rnp_api_error_creates_failed_monthly_claim(self):
organization = _organization(1)
class _Client:
def __init__(self, **_kwargs):
return
def get_company(self, _request):
raise parser_tasks.CheckoError("Checko unavailable")
with (
patch.object(parser_tasks, "CheckoClient", _Client),
self.assertRaises(parser_tasks.ParserSourceSkipped),
):
parser_tasks._fetch_checko_unfair_supplier_records(
limit=1,
proxies=[],
)
attempt = CheckoCollectionAttempt.objects.get(
organization=organization,
source=CheckoCollectionAttempt.Source.UNFAIR_SUPPLIERS,
)
self.assertEqual(attempt.status, CheckoCollectionAttempt.Status.FAILED)