125 lines
4.5 KiB
Python
125 lines
4.5 KiB
Python
from __future__ import annotations
|
|
|
|
from datetime import date
|
|
from decimal import Decimal
|
|
from urllib.parse import parse_qs, urlsplit
|
|
|
|
from apps.parsers.clients.base import HTTPClientError
|
|
from apps.parsers.clients.common.eis_registry import EisRegistryProcurementClient
|
|
from apps.parsers.clients.common.schemas import GenericParserItem
|
|
from django.test import SimpleTestCase
|
|
|
|
|
|
def _record(*, external_id: str, inn: str, amount: str = "1") -> GenericParserItem:
|
|
return GenericParserItem(
|
|
source="procurements_44fz",
|
|
external_id=external_id,
|
|
inn=inn,
|
|
organisation_name="Наименование из ЕИС",
|
|
title="Закупка",
|
|
amount=Decimal(amount),
|
|
payload={"raw": external_id},
|
|
)
|
|
|
|
|
|
class _FakeHttpClient:
|
|
def __init__(self, pages: dict[int, bytes], *, error_page: int | None = None):
|
|
self.pages = pages
|
|
self.error_page = error_page
|
|
self.urls: list[str] = []
|
|
|
|
def download_file(self, url: str, **_kwargs) -> bytes:
|
|
self.urls.append(url)
|
|
page = int(parse_qs(urlsplit(url).query)["pageNumber"][0])
|
|
if page == self.error_page:
|
|
raise HTTPClientError("page failed")
|
|
return self.pages[page]
|
|
|
|
|
|
class _FakeStructuredClient:
|
|
def __init__(self, pages: dict[int, bytes], records: dict[bytes, list]):
|
|
self.http_client = _FakeHttpClient(pages)
|
|
self.records = records
|
|
|
|
def fetch_records(self, *, content: bytes, file_name: str):
|
|
assert file_name == "results.html"
|
|
return self.records[content]
|
|
|
|
|
|
class EisRegistryProcurementClientTest(SimpleTestCase):
|
|
def test_fetches_all_pages_filters_inn_and_deduplicates_external_id(self):
|
|
page_1 = b'<a data-pagenumber="2"></a>'
|
|
page_2 = b'<a data-pagenumber="2"></a>'
|
|
structured = _FakeStructuredClient(
|
|
{1: page_1, 2: page_2},
|
|
{
|
|
page_1: [
|
|
_record(external_id="same", inn="7701000001", amount="1"),
|
|
_record(external_id="foreign", inn="7701000099"),
|
|
],
|
|
page_2: [
|
|
_record(external_id="same", inn="7701000001", amount="2"),
|
|
_record(external_id="other", inn="7701000001"),
|
|
],
|
|
},
|
|
)
|
|
client = EisRegistryProcurementClient(
|
|
source="procurements_44fz",
|
|
law="44",
|
|
_structured_client=structured,
|
|
)
|
|
|
|
records = client.fetch_for_customer(
|
|
inn="7701000001",
|
|
organization_name="ОПК Заказчик",
|
|
organization_id="org-1",
|
|
today=date(2026, 7, 19),
|
|
)
|
|
|
|
self.assertEqual([row.external_id for row in records], ["same", "other"])
|
|
self.assertEqual(records[0].amount, Decimal("2"))
|
|
self.assertTrue(all(row.inn == "7701000001" for row in records))
|
|
self.assertEqual(records[0].payload["organization_id"], "org-1")
|
|
query = parse_qs(urlsplit(structured.http_client.urls[0]).query)
|
|
self.assertEqual(query["fz44"], ["on"])
|
|
self.assertEqual(query["strictEqual"], ["true"])
|
|
self.assertEqual(query["publishDateFrom"], ["19.07.2025"])
|
|
self.assertEqual(query["publishDateTo"], ["19.07.2026"])
|
|
|
|
def test_builds_223fz_query_and_accepts_empty_page(self):
|
|
empty_page = b"<html></html>"
|
|
structured = _FakeStructuredClient({1: empty_page}, {empty_page: []})
|
|
client = EisRegistryProcurementClient(
|
|
source="procurements_223fz",
|
|
law="223",
|
|
_structured_client=structured,
|
|
)
|
|
|
|
records = client.fetch_for_customer(
|
|
inn="7701000001",
|
|
organization_name="ОПК Заказчик",
|
|
organization_id="org-1",
|
|
)
|
|
|
|
self.assertEqual(records, [])
|
|
query = parse_qs(urlsplit(structured.http_client.urls[0]).query)
|
|
self.assertEqual(query["fz223"], ["on"])
|
|
self.assertNotIn("fz44", query)
|
|
|
|
def test_page_error_is_not_silenced(self):
|
|
page_1 = b'<a data-pagenumber="2"></a>'
|
|
structured = _FakeStructuredClient({1: page_1}, {page_1: []})
|
|
structured.http_client.error_page = 2
|
|
client = EisRegistryProcurementClient(
|
|
source="procurements_44fz",
|
|
law="44",
|
|
_structured_client=structured,
|
|
)
|
|
|
|
with self.assertRaises(HTTPClientError):
|
|
client.fetch_for_customer(
|
|
inn="7701000001",
|
|
organization_name="ОПК Заказчик",
|
|
organization_id="org-1",
|
|
)
|