feat(registry): add new endpoints for registers, exchange, and backups; update routing and configurations
Some checks failed
CI/CD Pipeline / Code Quality Checks (push) Failing after 3m10s
CI/CD Pipeline / Run Tests (push) Successful in 3m35s
CI/CD Pipeline / Telegram Notify Success (push) Has been skipped
CI/CD Pipeline / Code Quality Checks (pull_request) Failing after 2m26s
CI/CD Pipeline / Run Tests (pull_request) Successful in 2m46s
CI/CD Pipeline / Telegram Notify Success (pull_request) Has been skipped
Some checks failed
CI/CD Pipeline / Code Quality Checks (push) Failing after 3m10s
CI/CD Pipeline / Run Tests (push) Successful in 3m35s
CI/CD Pipeline / Telegram Notify Success (push) Has been skipped
CI/CD Pipeline / Code Quality Checks (pull_request) Failing after 2m26s
CI/CD Pipeline / Run Tests (pull_request) Successful in 2m46s
CI/CD Pipeline / Telegram Notify Success (pull_request) Has been skipped
This commit is contained in:
@@ -1,9 +1,5 @@
|
||||
"""Tests for parsers admin configurations."""
|
||||
|
||||
from django.contrib.admin.sites import AdminSite
|
||||
from django.contrib.messages.storage.fallback import FallbackStorage
|
||||
from django.test import RequestFactory, TestCase
|
||||
|
||||
from apps.parsers.admin import (
|
||||
FinancialReportAdmin,
|
||||
HasCertificateNumberFilter,
|
||||
@@ -24,6 +20,10 @@ from apps.parsers.models import (
|
||||
ProcurementRecord,
|
||||
Proxy,
|
||||
)
|
||||
from django.contrib.admin.sites import AdminSite
|
||||
from django.contrib.messages.storage.fallback import FallbackStorage
|
||||
from django.test import RequestFactory, TestCase
|
||||
|
||||
from tests.apps.parsers.factories import (
|
||||
IndustrialCertificateRecordFactory,
|
||||
InspectionRecordFactory,
|
||||
@@ -34,7 +34,6 @@ from tests.apps.parsers.factories import (
|
||||
from tests.apps.user.factories import UserFactory
|
||||
from tests.utils.fixtures import fake
|
||||
|
||||
|
||||
_CYRILLIC_FINISHED = "\u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d"
|
||||
_CYRILLIC_PUBLISHED = "\u043e\u043f\u0443\u0431\u043b\u0438\u043a"
|
||||
|
||||
@@ -121,7 +120,9 @@ class ParsersAdminTest(TestCase):
|
||||
filter_none = HasCertificateNumberFilter(
|
||||
request, {}, IndustrialCertificateRecord, admin
|
||||
)
|
||||
qs_none = filter_none.queryset(request, IndustrialCertificateRecord.objects.all())
|
||||
qs_none = filter_none.queryset(
|
||||
request, IndustrialCertificateRecord.objects.all()
|
||||
)
|
||||
self.assertIn(record_good, qs_none)
|
||||
|
||||
def test_manufacturer_admin_helpers(self):
|
||||
@@ -141,13 +142,17 @@ class ParsersAdminTest(TestCase):
|
||||
record = InspectionRecordFactory(
|
||||
organisation_name="Org" * 30,
|
||||
control_authority="Auth" * 20,
|
||||
status=f"{_CYRILLIC_FINISHED}"
|
||||
status=f"{_CYRILLIC_FINISHED}",
|
||||
)
|
||||
self.assertTrue(admin.organisation_name_short(record).endswith("..."))
|
||||
self.assertTrue(admin.control_authority_short(record).endswith("..."))
|
||||
self.assertIn("span", str(admin.status_badge(record)))
|
||||
record_progress = InspectionRecordFactory(status="\u0432 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435")
|
||||
record_cancel = InspectionRecordFactory(status="\u043e\u0442\u043c\u0435\u043d\u0435\u043d\u0430")
|
||||
record_progress = InspectionRecordFactory(
|
||||
status="\u0432 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435"
|
||||
)
|
||||
record_cancel = InspectionRecordFactory(
|
||||
status="\u043e\u0442\u043c\u0435\u043d\u0435\u043d\u0430"
|
||||
)
|
||||
record_other = InspectionRecordFactory(status="unknown")
|
||||
self.assertIn("span", str(admin.status_badge(record_progress)))
|
||||
self.assertIn("span", str(admin.status_badge(record_cancel)))
|
||||
|
||||
@@ -3,49 +3,47 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import requests
|
||||
from urllib.parse import parse_qs
|
||||
|
||||
from django.test import SimpleTestCase
|
||||
|
||||
from requests.adapters import BaseAdapter
|
||||
|
||||
import requests
|
||||
from apps.parsers.clients.checko import (
|
||||
CheckoClient,
|
||||
BankRequest,
|
||||
CaseRole,
|
||||
CheckoAPIError,
|
||||
CheckoClient,
|
||||
CheckoConnectionError,
|
||||
CheckoNotFoundError,
|
||||
CheckoRateLimitError,
|
||||
CheckoValidationError,
|
||||
CheckoConnectionError,
|
||||
BankRequest,
|
||||
CompanyRequest,
|
||||
ContractLaw,
|
||||
ContractRole,
|
||||
ContractsRequest,
|
||||
EntrepreneurRequest,
|
||||
EnforcementsRequest,
|
||||
EntrepreneurRequest,
|
||||
FinancesRequest,
|
||||
InspectionsRequest,
|
||||
LegalCasesRequest,
|
||||
ObjectType,
|
||||
PersonRequest,
|
||||
SearchRequest,
|
||||
CaseRole,
|
||||
ContractRole,
|
||||
SearchType,
|
||||
ObjectType,
|
||||
ContractLaw,
|
||||
SortOrder,
|
||||
)
|
||||
from apps.parsers.clients.checko.datasets import (
|
||||
OKVED2,
|
||||
OKFS,
|
||||
OKOPF,
|
||||
OKPD,
|
||||
OKPD2,
|
||||
OKVED2,
|
||||
AccountCodes,
|
||||
CompanyStatuses,
|
||||
EntrepreneurStatuses,
|
||||
)
|
||||
from django.test import SimpleTestCase
|
||||
from requests.adapters import BaseAdapter
|
||||
|
||||
from tests.utils import TestHTTPServer, Response
|
||||
from tests.utils import Response, TestHTTPServer
|
||||
from tests.utils.fixtures import fake
|
||||
|
||||
|
||||
@@ -125,7 +123,9 @@ class CheckoClientValidationTest(SimpleTestCase):
|
||||
def test_search_request_min_query_length(self):
|
||||
with self.assertRaises(CheckoValidationError) as context:
|
||||
self.client.search(
|
||||
SearchRequest(by=SearchType.NAME, obj=ObjectType.ORGANIZATION, query="abc")
|
||||
SearchRequest(
|
||||
by=SearchType.NAME, obj=ObjectType.ORGANIZATION, query="abc"
|
||||
)
|
||||
)
|
||||
self.assertIn("4", str(context.exception))
|
||||
|
||||
@@ -241,6 +241,7 @@ class CheckoRequestParamsTest(SimpleTestCase):
|
||||
bank = BankRequest(bic=_digits(9))
|
||||
self.assertEqual(bank.to_params()["bic"], bank.bic)
|
||||
|
||||
|
||||
class CheckoClientApiTest(SimpleTestCase):
|
||||
def test_get_company_success(self):
|
||||
inn = "".join(str(fake.random_int(0, 9)) for _ in range(10))
|
||||
@@ -279,7 +280,11 @@ class CheckoClientApiTest(SimpleTestCase):
|
||||
)
|
||||
client = _client_for(server)
|
||||
with self.assertRaises(CheckoNotFoundError):
|
||||
client.get_company(CompanyRequest(inn="".join(str(fake.random_int(0, 9)) for _ in range(10))))
|
||||
client.get_company(
|
||||
CompanyRequest(
|
||||
inn="".join(str(fake.random_int(0, 9)) for _ in range(10))
|
||||
)
|
||||
)
|
||||
|
||||
def test_get_entrepreneur_success(self):
|
||||
ogrnip = "".join(str(fake.random_int(0, 9)) for _ in range(15))
|
||||
@@ -468,7 +473,11 @@ class CheckoClientApiTest(SimpleTestCase):
|
||||
)
|
||||
client = _client_for(server)
|
||||
with self.assertRaises(CheckoAPIError):
|
||||
client.get_company(CompanyRequest(inn="".join(str(fake.random_int(0, 9)) for _ in range(10))))
|
||||
client.get_company(
|
||||
CompanyRequest(
|
||||
inn="".join(str(fake.random_int(0, 9)) for _ in range(10))
|
||||
)
|
||||
)
|
||||
|
||||
def test_rate_limit_error_handling(self):
|
||||
with TestHTTPServer() as server:
|
||||
@@ -478,7 +487,11 @@ class CheckoClientApiTest(SimpleTestCase):
|
||||
)
|
||||
client = _client_for(server)
|
||||
with self.assertRaises(CheckoRateLimitError):
|
||||
client.get_company(CompanyRequest(inn="".join(str(fake.random_int(0, 9)) for _ in range(10))))
|
||||
client.get_company(
|
||||
CompanyRequest(
|
||||
inn="".join(str(fake.random_int(0, 9)) for _ in range(10))
|
||||
)
|
||||
)
|
||||
|
||||
def test_not_found_error_handling(self):
|
||||
with TestHTTPServer() as server:
|
||||
@@ -488,7 +501,11 @@ class CheckoClientApiTest(SimpleTestCase):
|
||||
)
|
||||
client = _client_for(server)
|
||||
with self.assertRaises(CheckoNotFoundError):
|
||||
client.get_company(CompanyRequest(inn="".join(str(fake.random_int(0, 9)) for _ in range(10))))
|
||||
client.get_company(
|
||||
CompanyRequest(
|
||||
inn="".join(str(fake.random_int(0, 9)) for _ in range(10))
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class CheckoClientExtraEndpointsTest(SimpleTestCase):
|
||||
@@ -564,9 +581,7 @@ class CheckoClientExtraEndpointsTest(SimpleTestCase):
|
||||
with TestHTTPServer() as server:
|
||||
server.add_route("GET", "/v2/inspections", inspections_handler)
|
||||
client = _client_for(server)
|
||||
inspections = list(
|
||||
client.iter_inspections(InspectionsRequest(inn=inn))
|
||||
)
|
||||
inspections = list(client.iter_inspections(InspectionsRequest(inn=inn)))
|
||||
|
||||
self.assertTrue(inspections)
|
||||
|
||||
@@ -599,9 +614,7 @@ class CheckoClientExtraEndpointsTest(SimpleTestCase):
|
||||
with TestHTTPServer() as server:
|
||||
server.add_route("GET", "/v2/inspections", inspections_handler)
|
||||
client = _client_for(server)
|
||||
inspections = list(
|
||||
client.iter_inspections(InspectionsRequest(inn=inn))
|
||||
)
|
||||
inspections = list(client.iter_inspections(InspectionsRequest(inn=inn)))
|
||||
|
||||
self.assertEqual(len(inspections), 1)
|
||||
|
||||
@@ -642,9 +655,7 @@ class CheckoClientExtraEndpointsTest(SimpleTestCase):
|
||||
page = int(params.get("page", ["1"])[0])
|
||||
payload = {
|
||||
"data": {
|
||||
"enforcements": [
|
||||
{"number": fake.bothify(text="##-####")}
|
||||
],
|
||||
"enforcements": [{"number": fake.bothify(text="##-####")}],
|
||||
"pagination": {
|
||||
"total_records": 2,
|
||||
"total_pages": 2,
|
||||
@@ -664,9 +675,7 @@ class CheckoClientExtraEndpointsTest(SimpleTestCase):
|
||||
with TestHTTPServer() as server:
|
||||
server.add_route("GET", "/v2/enforcements", enforcements_handler)
|
||||
client = _client_for(server)
|
||||
enforcements = list(
|
||||
client.iter_enforcements(EnforcementsRequest(inn=inn))
|
||||
)
|
||||
enforcements = list(client.iter_enforcements(EnforcementsRequest(inn=inn)))
|
||||
|
||||
self.assertTrue(enforcements)
|
||||
|
||||
@@ -676,9 +685,7 @@ class CheckoClientExtraEndpointsTest(SimpleTestCase):
|
||||
def enforcements_handler(_req, _body):
|
||||
payload = {
|
||||
"data": {
|
||||
"enforcements": [
|
||||
{"number": fake.bothify(text="##-####")}
|
||||
],
|
||||
"enforcements": [{"number": fake.bothify(text="##-####")}],
|
||||
"pagination": {
|
||||
"total_records": 1,
|
||||
"total_pages": 1,
|
||||
@@ -696,9 +703,7 @@ class CheckoClientExtraEndpointsTest(SimpleTestCase):
|
||||
with TestHTTPServer() as server:
|
||||
server.add_route("GET", "/v2/enforcements", enforcements_handler)
|
||||
client = _client_for(server)
|
||||
enforcements = list(
|
||||
client.iter_enforcements(EnforcementsRequest(inn=inn))
|
||||
)
|
||||
enforcements = list(client.iter_enforcements(EnforcementsRequest(inn=inn)))
|
||||
|
||||
self.assertEqual(len(enforcements), 1)
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from apps.parsers.clients.checko.client import CheckoClient, _map_ru_keys
|
||||
from django.test import SimpleTestCase
|
||||
|
||||
from apps.parsers.clients.checko.client import CheckoClient, _map_ru_keys
|
||||
from tests.utils.fixtures import fake
|
||||
|
||||
|
||||
@@ -32,7 +32,10 @@ class CheckoClientParsingTest(SimpleTestCase):
|
||||
self.assertEqual(mapped["inn"], data["\u0418\u041d\u041d"])
|
||||
self.assertIn("legal_address", mapped)
|
||||
self.assertIn("records", mapped)
|
||||
self.assertEqual(mapped["records"][0]["ogrn"], data["\u0417\u0430\u043f\u0438\u0441\u0438"][0]["\u041e\u0413\u0420\u041d"])
|
||||
self.assertEqual(
|
||||
mapped["records"][0]["ogrn"],
|
||||
data["\u0417\u0430\u043f\u0438\u0441\u0438"][0]["\u041e\u0413\u0420\u041d"],
|
||||
)
|
||||
|
||||
def test_parse_okved_info_with_additional(self):
|
||||
info = self.client._parse_okved_info(
|
||||
@@ -96,7 +99,11 @@ class CheckoClientParsingTest(SimpleTestCase):
|
||||
"reg_date": str(fake.date()),
|
||||
"short_name": fake.company(),
|
||||
"full_name": fake.company(),
|
||||
"status": {"code": "100", "name": "Active", "record_date": str(fake.date())},
|
||||
"status": {
|
||||
"code": "100",
|
||||
"name": "Active",
|
||||
"record_date": str(fake.date()),
|
||||
},
|
||||
"legal_address": {
|
||||
"full_address": fake.address(),
|
||||
"region": {"code": "77", "name": "Moscow"},
|
||||
@@ -168,7 +175,9 @@ class CheckoClientParsingTest(SimpleTestCase):
|
||||
"full_name": fake.company(),
|
||||
}
|
||||
],
|
||||
"branches": {"branch": [{"full_name": fake.company(), "address": fake.address()}]},
|
||||
"branches": {
|
||||
"branch": [{"full_name": fake.company(), "address": fake.address()}]
|
||||
},
|
||||
"licenses": [{"number": "L-1", "activities": [fake.word()]}],
|
||||
"trademarks": [{"id": 1, "url": fake.url()}],
|
||||
"tax_debt": {"total": 123, "date": str(fake.date())},
|
||||
@@ -239,9 +248,7 @@ class CheckoClientParsingTest(SimpleTestCase):
|
||||
"companies_as_founder": [
|
||||
{"ogrn": _digits(13), "short_name": fake.company()}
|
||||
],
|
||||
"entrepreneurs": [
|
||||
{"ogrnip": _digits(15), "full_name": fake.name()}
|
||||
],
|
||||
"entrepreneurs": [{"ogrnip": _digits(15), "full_name": fake.name()}],
|
||||
}
|
||||
)
|
||||
self.assertEqual(len(person.disqualifications), 1)
|
||||
|
||||
@@ -5,8 +5,6 @@ from __future__ import annotations
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import requests
|
||||
from requests.adapters import BaseAdapter
|
||||
|
||||
from apps.parsers.clients.base import (
|
||||
BaseHTTPClient,
|
||||
ConnectionError,
|
||||
@@ -19,6 +17,7 @@ from apps.parsers.clients.minpromtorg.schemas import IndustrialCertificate, Manu
|
||||
from apps.parsers.clients.proverki import ProverkiClient
|
||||
from apps.parsers.clients.proverki.schemas import Inspection
|
||||
from django.test import TestCase, tag
|
||||
from requests.adapters import BaseAdapter
|
||||
|
||||
from tests.utils import Response, TestHTTPServer
|
||||
from tests.utils.fixtures import (
|
||||
@@ -110,7 +109,9 @@ class BaseHTTPClientTest(TestCase):
|
||||
|
||||
with TestHTTPServer() as server:
|
||||
server.add_route("POST", "/echo", echo_handler)
|
||||
server.add_route("GET", "/missing", lambda _req, _body: Response(status=404))
|
||||
server.add_route(
|
||||
"GET", "/missing", lambda _req, _body: Response(status=404)
|
||||
)
|
||||
client = BaseHTTPClient(base_url=server.base_url, adapter=server.adapter)
|
||||
result = client.post("/echo", data=b"ping")
|
||||
self.assertEqual(result, b"ping")
|
||||
@@ -123,7 +124,9 @@ class BaseHTTPClientTest(TestCase):
|
||||
|
||||
def test_download_file_error(self):
|
||||
with TestHTTPServer() as server:
|
||||
server.add_route("GET", "/missing.bin", lambda _req, _body: Response(status=404))
|
||||
server.add_route(
|
||||
"GET", "/missing.bin", lambda _req, _body: Response(status=404)
|
||||
)
|
||||
client = BaseHTTPClient(base_url=server.base_url, adapter=server.adapter)
|
||||
with self.assertRaises(HTTPError):
|
||||
client.download_file("/missing.bin")
|
||||
@@ -136,7 +139,9 @@ class BaseHTTPClientTest(TestCase):
|
||||
def test_context_manager_closes_session(self):
|
||||
with TestHTTPServer() as server:
|
||||
server.add_json("/ping", {"ok": True})
|
||||
with BaseHTTPClient(base_url=server.base_url, adapter=server.adapter) as client:
|
||||
with BaseHTTPClient(
|
||||
base_url=server.base_url, adapter=server.adapter
|
||||
) as client:
|
||||
client.get_json("/ping")
|
||||
self.assertIsNotNone(client._session)
|
||||
self.assertIsNone(client._session)
|
||||
@@ -280,10 +285,7 @@ class IndustrialProductionClientTest(TestCase):
|
||||
def test_get_latest_file_url_selects_newest(self):
|
||||
client = IndustrialProductionClient()
|
||||
dates = sorted(
|
||||
{
|
||||
fake.date_between(start_date="-90d", end_date="today")
|
||||
for _ in range(3)
|
||||
}
|
||||
{fake.date_between(start_date="-90d", end_date="today") for _ in range(3)}
|
||||
)
|
||||
files = []
|
||||
for date in dates:
|
||||
@@ -397,10 +399,7 @@ class ManufacturesClientTest(TestCase):
|
||||
def test_get_latest_file_url_selects_newest(self):
|
||||
client = ManufacturesClient()
|
||||
dates = sorted(
|
||||
{
|
||||
fake.date_between(start_date="-90d", end_date="today")
|
||||
for _ in range(3)
|
||||
}
|
||||
{fake.date_between(start_date="-90d", end_date="today") for _ in range(3)}
|
||||
)
|
||||
files = []
|
||||
for date in dates:
|
||||
@@ -565,7 +564,7 @@ class ProverkiClientTest(TestCase):
|
||||
f"<КонтрольныйОрган>{authority}</КонтрольныйОрган>"
|
||||
"</КНМ>"
|
||||
"</Проверки>"
|
||||
).encode("utf-8")
|
||||
).encode()
|
||||
|
||||
inspections = client._parse_xml_content(xml_content, None)
|
||||
|
||||
@@ -580,7 +579,7 @@ class ProverkiClientTest(TestCase):
|
||||
row_inn = "".join(str(fake.random_int(0, 9)) for _ in range(10))
|
||||
reg_num = "".join(str(fake.random_int(0, 9)) for _ in range(12))
|
||||
element = ET.fromstring(
|
||||
f"<inspection inn=\"{row_inn}\" registration_number=\"{reg_num}\" />"
|
||||
f'<inspection inn="{row_inn}" registration_number="{reg_num}" />'
|
||||
) # noqa: S314
|
||||
|
||||
client = ProverkiClient()
|
||||
@@ -602,7 +601,7 @@ class ProverkiClientTest(TestCase):
|
||||
inn = "".join(str(fake.random_int(0, 9)) for _ in range(10))
|
||||
reg_num = "".join(str(fake.random_int(0, 9)) for _ in range(12))
|
||||
xml_content = (
|
||||
"<?xml version=\"1.0\" encoding=\"windows-1251\"?>"
|
||||
'<?xml version="1.0" encoding="windows-1251"?>'
|
||||
"<inspections>"
|
||||
"<inspection>"
|
||||
f"<inn>{inn}</inn>"
|
||||
|
||||
@@ -27,7 +27,9 @@ def _build_fns_excel_bytes() -> bytes:
|
||||
year = fake.random_int(min=2020, max=2025)
|
||||
ws.append(["Форма №1", None, year, None])
|
||||
ws.append([None, "Код", "Начало", "Конец"])
|
||||
ws.append([fake.word(), _digits(4), fake.random_int(10, 999), fake.random_int(10, 999)])
|
||||
ws.append(
|
||||
[fake.word(), _digits(4), fake.random_int(10, 999), fake.random_int(10, 999)]
|
||||
)
|
||||
buf = io.BytesIO()
|
||||
wb.save(buf)
|
||||
wb.close()
|
||||
@@ -80,9 +82,7 @@ class FNSUploadIntegrationTest(APITestCase):
|
||||
report = FinancialReport.objects.first()
|
||||
self.assertEqual(report.external_id, external_id)
|
||||
self.assertEqual(report.ogrn, ogrn)
|
||||
self.assertTrue(
|
||||
FinancialReportLine.objects.filter(report=report).exists()
|
||||
)
|
||||
self.assertTrue(FinancialReportLine.objects.filter(report=report).exists())
|
||||
|
||||
processed_path = os.path.join(processed_dir, filename)
|
||||
self.assertTrue(os.path.exists(processed_path))
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
"""Tests for parsers services."""
|
||||
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from apps.parsers.clients.minpromtorg.industrial import IndustrialProductionClient
|
||||
from apps.parsers.clients.minpromtorg.schemas import IndustrialCertificate, Manufacturer
|
||||
from apps.parsers.clients.proverki.schemas import Inspection
|
||||
from apps.parsers.clients.zakupki.schemas import Procurement
|
||||
from apps.parsers.models import (
|
||||
IndustrialCertificateRecord,
|
||||
InspectionRecord,
|
||||
ManufacturerRecord,
|
||||
ParserLoadLog,
|
||||
ProcurementRecord,
|
||||
Proxy,
|
||||
)
|
||||
from apps.parsers.services import (
|
||||
@@ -15,12 +19,14 @@ from apps.parsers.services import (
|
||||
InspectionService,
|
||||
ManufacturerService,
|
||||
ParserLoadLogService,
|
||||
ProcurementService,
|
||||
ProxyService,
|
||||
)
|
||||
from apps.registers.models import Organization
|
||||
from django.test import TestCase, tag
|
||||
|
||||
from tests.utils import TestHTTPServer
|
||||
from tests.utils.fixtures import build_minpromtorg_certificates_excel, fake
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from .factories import (
|
||||
IndustrialCertificateRecordFactory,
|
||||
@@ -38,6 +44,17 @@ def _digits(length: int) -> str:
|
||||
def _proxy_address() -> str:
|
||||
return f"http://{fake.ipv4()}:{fake.port_number()}"
|
||||
|
||||
|
||||
def _create_registry_organization(*, inn: str, ogrn: str) -> Organization:
|
||||
return Organization.objects.create(
|
||||
pn_name=fake.company(),
|
||||
mn_ogrn=int(ogrn),
|
||||
mn_inn=int(inn),
|
||||
in_kpp=int(_digits(9)),
|
||||
mn_okpo=_digits(8),
|
||||
)
|
||||
|
||||
|
||||
class ProxyServiceTest(TestCase):
|
||||
"""Tests for ProxyService."""
|
||||
|
||||
@@ -248,6 +265,59 @@ class IndustrialCertificateServiceTest(TestCase):
|
||||
|
||||
self.assertEqual(count, 5)
|
||||
self.assertEqual(IndustrialCertificateRecord.objects.count(), 5)
|
||||
record = IndustrialCertificateRecord.objects.first()
|
||||
self.assertIsNotNone(record.issue_date_normalized)
|
||||
self.assertIsNotNone(record.expiry_date_normalized)
|
||||
|
||||
def test_save_certificates_links_registry_organization_when_exists(self):
|
||||
"""Test linking to registers organization is created when identifiers match."""
|
||||
inn = _digits(10)
|
||||
ogrn = _digits(13)
|
||||
organization = _create_registry_organization(inn=inn, ogrn=ogrn)
|
||||
certificate_number = fake.bothify(text="??-####-#####")
|
||||
|
||||
certificates = [
|
||||
IndustrialCertificate(
|
||||
issue_date=str(fake.date()),
|
||||
certificate_number=certificate_number,
|
||||
expiry_date=str(fake.date()),
|
||||
certificate_file_url=fake.url(),
|
||||
organisation_name=fake.company(),
|
||||
inn=inn,
|
||||
ogrn=ogrn,
|
||||
)
|
||||
]
|
||||
|
||||
saved = IndustrialCertificateService.save_certificates(certificates, batch_id=1)
|
||||
|
||||
self.assertEqual(saved, 1)
|
||||
record = IndustrialCertificateRecord.objects.get(
|
||||
certificate_number=certificate_number
|
||||
)
|
||||
self.assertEqual(record.registry_organization_id, organization.id)
|
||||
|
||||
def test_save_certificates_keeps_null_registry_organization_when_not_found(self):
|
||||
"""Test parser save does not fail and keeps null when organization is absent."""
|
||||
certificate_number = fake.bothify(text="??-####-#####")
|
||||
certificates = [
|
||||
IndustrialCertificate(
|
||||
issue_date=str(fake.date()),
|
||||
certificate_number=certificate_number,
|
||||
expiry_date=str(fake.date()),
|
||||
certificate_file_url=fake.url(),
|
||||
organisation_name=fake.company(),
|
||||
inn=_digits(10),
|
||||
ogrn=_digits(13),
|
||||
)
|
||||
]
|
||||
|
||||
saved = IndustrialCertificateService.save_certificates(certificates, batch_id=1)
|
||||
|
||||
self.assertEqual(saved, 1)
|
||||
record = IndustrialCertificateRecord.objects.get(
|
||||
certificate_number=certificate_number
|
||||
)
|
||||
self.assertIsNone(record.registry_organization_id)
|
||||
|
||||
def test_save_certificates_with_chunk_size(self):
|
||||
"""Test saving certificates in chunks."""
|
||||
@@ -287,16 +357,16 @@ class IndustrialCertificateServiceTest(TestCase):
|
||||
results = IndustrialCertificateService.find_by_inn(inn_a)
|
||||
self.assertEqual(results.count(), 2)
|
||||
|
||||
results_batch1 = IndustrialCertificateService.find_by_inn(
|
||||
inn_a, batch_id=1
|
||||
)
|
||||
results_batch1 = IndustrialCertificateService.find_by_inn(inn_a, batch_id=1)
|
||||
self.assertEqual(results_batch1.count(), 1)
|
||||
|
||||
def test_find_by_certificate_number(self):
|
||||
"""Test finding certificate by number."""
|
||||
unique_number = fake.bothify(text="CERT-#####")
|
||||
IndustrialCertificateRecordFactory(certificate_number=unique_number)
|
||||
IndustrialCertificateRecordFactory(certificate_number=fake.bothify(text="CERT-#####"))
|
||||
IndustrialCertificateRecordFactory(
|
||||
certificate_number=fake.bothify(text="CERT-#####")
|
||||
)
|
||||
|
||||
results = IndustrialCertificateService.find_by_certificate_number(unique_number)
|
||||
self.assertEqual(results.count(), 1)
|
||||
@@ -332,9 +402,10 @@ class IndustrialCertificateServiceTest(TestCase):
|
||||
ogrn=_digits(13),
|
||||
)
|
||||
]
|
||||
IndustrialCertificateService.save_certificates(duplicate, batch_id=2)
|
||||
count2 = IndustrialCertificateService.save_certificates(duplicate, batch_id=2)
|
||||
|
||||
# Should still be 1 record (duplicate skipped)
|
||||
self.assertEqual(count2, 0)
|
||||
self.assertEqual(IndustrialCertificateRecord.objects.count(), 1)
|
||||
|
||||
# Verify original data preserved
|
||||
@@ -369,6 +440,27 @@ class ManufacturerServiceTest(TestCase):
|
||||
self.assertEqual(count, 5)
|
||||
self.assertEqual(ManufacturerRecord.objects.count(), 5)
|
||||
|
||||
def test_save_manufacturers_links_registry_organization_when_exists(self):
|
||||
"""Test linking manufacturer to registers organization by INN/ОГРН."""
|
||||
inn = _digits(10)
|
||||
ogrn = _digits(13)
|
||||
organization = _create_registry_organization(inn=inn, ogrn=ogrn)
|
||||
|
||||
manufacturers = [
|
||||
Manufacturer(
|
||||
full_legal_name=fake.company(),
|
||||
inn=inn,
|
||||
ogrn=ogrn,
|
||||
address=fake.address().replace("\n", ", "),
|
||||
)
|
||||
]
|
||||
|
||||
saved = ManufacturerService.save_manufacturers(manufacturers, batch_id=1)
|
||||
|
||||
self.assertEqual(saved, 1)
|
||||
record = ManufacturerRecord.objects.get(inn=inn)
|
||||
self.assertEqual(record.registry_organization_id, organization.id)
|
||||
|
||||
def test_save_manufacturers_with_chunk_size(self):
|
||||
"""Test saving manufacturers in chunks."""
|
||||
manufacturers = [
|
||||
@@ -448,9 +540,10 @@ class ManufacturerServiceTest(TestCase):
|
||||
address=fake.address().replace("\n", ", "),
|
||||
)
|
||||
]
|
||||
ManufacturerService.save_manufacturers(duplicate, batch_id=2)
|
||||
count2 = ManufacturerService.save_manufacturers(duplicate, batch_id=2)
|
||||
|
||||
# Should still be 1 record (duplicate skipped)
|
||||
self.assertEqual(count2, 0)
|
||||
self.assertEqual(ManufacturerRecord.objects.count(), 1)
|
||||
|
||||
# Verify original data preserved
|
||||
@@ -493,6 +586,39 @@ class InspectionServiceTest(TestCase):
|
||||
|
||||
self.assertEqual(count, 5)
|
||||
self.assertEqual(InspectionRecord.objects.count(), 5)
|
||||
record = InspectionRecord.objects.first()
|
||||
self.assertIsNotNone(record.start_date_normalized)
|
||||
self.assertIsNotNone(record.end_date_normalized)
|
||||
|
||||
def test_save_inspections_links_registry_organization_when_exists(self):
|
||||
"""Test linking inspection to registers organization by INN/ОГРН."""
|
||||
inn = _digits(10)
|
||||
ogrn = _digits(13)
|
||||
organization = _create_registry_organization(inn=inn, ogrn=ogrn)
|
||||
registration_number = _digits(12)
|
||||
|
||||
inspections = [
|
||||
Inspection(
|
||||
registration_number=registration_number,
|
||||
inn=inn,
|
||||
ogrn=ogrn,
|
||||
organisation_name=fake.company(),
|
||||
control_authority=fake.company(),
|
||||
inspection_type=fake.word(),
|
||||
inspection_form=fake.word(),
|
||||
start_date=str(fake.date()),
|
||||
end_date=str(fake.date()),
|
||||
status=fake.word(),
|
||||
legal_basis=fake.sentence(nb_words=3),
|
||||
result=fake.sentence(nb_words=3),
|
||||
)
|
||||
]
|
||||
|
||||
saved = InspectionService.save_inspections(inspections, batch_id=1)
|
||||
|
||||
self.assertEqual(saved, 1)
|
||||
record = InspectionRecord.objects.get(registration_number=registration_number)
|
||||
self.assertEqual(record.registry_organization_id, organization.id)
|
||||
|
||||
def test_save_inspections_with_chunk_size(self):
|
||||
"""Test saving inspections in chunks."""
|
||||
@@ -612,9 +738,10 @@ class InspectionServiceTest(TestCase):
|
||||
result=fake.sentence(nb_words=3),
|
||||
)
|
||||
]
|
||||
InspectionService.save_inspections(duplicate, batch_id=2)
|
||||
count2 = InspectionService.save_inspections(duplicate, batch_id=2)
|
||||
|
||||
# Should still be 1 record (duplicate skipped)
|
||||
self.assertEqual(count2, 0)
|
||||
self.assertEqual(InspectionRecord.objects.count(), 1)
|
||||
|
||||
# Verify original data preserved
|
||||
@@ -626,6 +753,57 @@ class InspectionServiceTest(TestCase):
|
||||
self.assertEqual(record.load_batch, 1) # Original batch
|
||||
|
||||
|
||||
class ProcurementServiceTest(TestCase):
|
||||
"""Tests for ProcurementService."""
|
||||
|
||||
def _build_procurement(self, **overrides) -> Procurement:
|
||||
data = {
|
||||
"purchase_number": _digits(19),
|
||||
"purchase_name": fake.sentence(nb_words=4),
|
||||
"customer_inn": _digits(10),
|
||||
"customer_kpp": _digits(9),
|
||||
"customer_ogrn": _digits(13),
|
||||
"customer_name": fake.company(),
|
||||
"max_price": "1 234 567,89",
|
||||
"currency_code": "RUB",
|
||||
"placement_method": fake.word(),
|
||||
"publish_date": "01.03.2026",
|
||||
"end_date": "2026-03-15",
|
||||
"status": fake.word(),
|
||||
"law_type": "44-FZ",
|
||||
"purchase_object_info": fake.sentence(nb_words=4),
|
||||
"href": fake.url(),
|
||||
}
|
||||
data.update(overrides)
|
||||
return Procurement(**data)
|
||||
|
||||
def test_save_procurements_sets_normalized_fields(self):
|
||||
procurement = self._build_procurement()
|
||||
|
||||
saved = ProcurementService.save_procurements([procurement], batch_id=1)
|
||||
|
||||
self.assertEqual(saved, 1)
|
||||
record = ProcurementRecord.objects.get(purchase_number=procurement.purchase_number)
|
||||
self.assertEqual(str(record.max_price_amount), "1234567.89")
|
||||
self.assertEqual(str(record.publish_date_normalized), "2026-03-01")
|
||||
self.assertEqual(str(record.end_date_normalized), "2026-03-15")
|
||||
|
||||
def test_save_procurements_duplicate_returns_zero(self):
|
||||
purchase_number = _digits(19)
|
||||
first = self._build_procurement(purchase_number=purchase_number)
|
||||
duplicate = self._build_procurement(
|
||||
purchase_number=purchase_number,
|
||||
customer_name=fake.company(),
|
||||
)
|
||||
|
||||
saved_first = ProcurementService.save_procurements([first], batch_id=1)
|
||||
saved_second = ProcurementService.save_procurements([duplicate], batch_id=2)
|
||||
|
||||
self.assertEqual(saved_first, 1)
|
||||
self.assertEqual(saved_second, 0)
|
||||
self.assertEqual(ProcurementRecord.objects.count(), 1)
|
||||
|
||||
|
||||
@tag("integration", "slow", "e2e")
|
||||
class EndToEndIntegrationTest(TestCase):
|
||||
"""
|
||||
@@ -668,9 +846,7 @@ class EndToEndIntegrationTest(TestCase):
|
||||
)
|
||||
server.add_bytes(f"/files/{file_name}", excel_bytes)
|
||||
host = urlparse(server.base_url)
|
||||
client_host = (
|
||||
f"{host.hostname}:{host.port}" if host.port else host.hostname
|
||||
)
|
||||
client_host = f"{host.hostname}:{host.port}" if host.port else host.hostname
|
||||
with IndustrialProductionClient(
|
||||
host=client_host,
|
||||
scheme="http",
|
||||
|
||||
@@ -6,13 +6,13 @@ import io
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
from apps.parsers.models import FinancialReport, FinancialReportLine, ProcurementRecord
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
from django.urls import reverse
|
||||
from openpyxl import Workbook
|
||||
from rest_framework import status
|
||||
from rest_framework.test import APITestCase
|
||||
|
||||
from apps.parsers.models import FinancialReport, FinancialReportLine, ProcurementRecord
|
||||
from tests.apps.parsers.factories import (
|
||||
IndustrialCertificateRecordFactory,
|
||||
InspectionRecordFactory,
|
||||
@@ -34,7 +34,9 @@ def _build_fns_excel_bytes() -> bytes:
|
||||
year = fake.random_int(min=2020, max=2025)
|
||||
ws.append(["Form", None, year, None])
|
||||
ws.append([None, "Code", "Start", "End"])
|
||||
ws.append([fake.word(), _digits(4), fake.random_int(10, 999), fake.random_int(10, 999)])
|
||||
ws.append(
|
||||
[fake.word(), _digits(4), fake.random_int(10, 999), fake.random_int(10, 999)]
|
||||
)
|
||||
buf = io.BytesIO()
|
||||
wb.save(buf)
|
||||
wb.close()
|
||||
@@ -130,6 +132,7 @@ class ParsersViewSetTest(APITestCase):
|
||||
url = reverse("api_v1:fns:fns-reports-list")
|
||||
response = self.client.get(url)
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data["data"][0]["lines_count"], 1)
|
||||
detail = self.client.get(
|
||||
reverse("api_v1:fns:fns-reports-detail", args=[report.id])
|
||||
)
|
||||
@@ -184,5 +187,7 @@ class ParsersViewSetTest(APITestCase):
|
||||
FNS_FAILED_DIRECTORY=failed_dir,
|
||||
):
|
||||
url = reverse("api_v1:fns:fns-upload")
|
||||
response = self.client.post(url, {"files": [upload]}, format="multipart")
|
||||
response = self.client.post(
|
||||
url, {"files": [upload]}, format="multipart"
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
Reference in New Issue
Block a user