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,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",
|
||||
|
||||
Reference in New Issue
Block a user