feat: обновления парсеров, тестов и миграций
Some checks failed
CI/CD Pipeline / Run Tests (push) Failing after 37s
CI/CD Pipeline / Code Quality Checks (push) Failing after 43s
CI/CD Pipeline / Build & Push Images (push) Has been skipped
CI/CD Pipeline / Deploy (dev) (push) Has been skipped
CI/CD Pipeline / Deploy (prod) (push) Has been skipped
CI/CD Pipeline / Code Quality Checks (pull_request) Failing after 0s
CI/CD Pipeline / Run Tests (pull_request) Failing after 0s
CI/CD Pipeline / Build & Push Images (pull_request) Has been skipped
CI/CD Pipeline / Deploy (dev) (pull_request) Has been skipped
CI/CD Pipeline / Deploy (prod) (pull_request) Has been skipped

- Обновлены клиенты парсеров (checko, fns, minpromtorg, proverki, zakupki)
- Добавлены новые миграции для моделей
- Расширено покрытие тестами
- Обновлены конфигурации и настройки проекта
- Добавлены утилиты для тестирования

Co-Authored-By: Warp <agent@warp.dev>
This commit is contained in:
2026-02-10 10:17:47 +01:00
parent 975d019ba5
commit ee95628a0a
59 changed files with 7292 additions and 2876 deletions

View File

@@ -1,5 +1,6 @@
"""Tests for parsers services."""
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.models import (
@@ -16,8 +17,10 @@ from apps.parsers.services import (
ParserLoadLogService,
ProxyService,
)
from django.test import TestCase
from faker import Faker
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,
@@ -27,8 +30,13 @@ from .factories import (
ProxyFactory,
)
fake = Faker("ru_RU")
def _digits(length: int) -> str:
return "".join(str(fake.random_int(0, 9)) for _ in range(length))
def _proxy_address() -> str:
return f"http://{fake.ipv4()}:{fake.port_number()}"
class ProxyServiceTest(TestCase):
"""Tests for ProxyService."""
@@ -66,7 +74,7 @@ class ProxyServiceTest(TestCase):
def test_mark_used(self):
"""Test marking proxy as used updates timestamp."""
proxy = ProxyFactory()
proxy = ProxyFactory(last_used_at=None)
self.assertIsNone(proxy.last_used_at)
ProxyService.mark_used(proxy.address)
@@ -94,8 +102,8 @@ class ProxyServiceTest(TestCase):
def test_add_proxy(self):
"""Test adding new proxy."""
address = "http://new-proxy:8080"
description = "Test proxy"
address = _proxy_address()
description = fake.sentence(nb_words=3)
proxy = ProxyService.add_proxy(address, description)
@@ -105,21 +113,19 @@ class ProxyServiceTest(TestCase):
def test_add_proxy_idempotent(self):
"""Test adding existing proxy returns existing record."""
address = "http://existing:8080"
existing = ProxyFactory(address=address, description="Original")
address = _proxy_address()
existing_description = fake.sentence(nb_words=3)
existing = ProxyFactory(address=address, description=existing_description)
proxy = ProxyService.add_proxy(address, "New description")
new_description = fake.sentence(nb_words=3)
proxy = ProxyService.add_proxy(address, new_description)
self.assertEqual(proxy.id, existing.id)
self.assertEqual(proxy.description, "Original") # Not updated
self.assertEqual(proxy.description, existing_description) # Not updated
def test_add_proxies(self):
"""Test bulk adding proxies."""
addresses = [
"http://proxy1:8080",
"http://proxy2:8080",
"http://proxy3:8080",
]
addresses = [_proxy_address() for _ in range(3)]
created = ProxyService.add_proxies(addresses)
@@ -128,10 +134,14 @@ class ProxyServiceTest(TestCase):
def test_add_proxies_skips_existing(self):
"""Test bulk add skips existing proxies."""
ProxyFactory(address="http://existing:8080")
existing_address = _proxy_address()
new_address = _proxy_address()
while new_address == existing_address:
new_address = _proxy_address()
ProxyFactory(address=existing_address)
addresses = [
"http://existing:8080", # Already exists
"http://new:8080",
existing_address, # Already exists
new_address,
]
created = ProxyService.add_proxies(addresses)
@@ -194,11 +204,12 @@ class ParserLoadLogServiceTest(TestCase):
"""Test marking log as failed."""
log = ParserLoadLogFactory(status="success")
ParserLoadLogService.mark_failed(log, "Connection error")
error_message = fake.sentence(nb_words=4)
ParserLoadLogService.mark_failed(log, error_message)
log.refresh_from_db()
self.assertEqual(log.status, "failed")
self.assertEqual(log.error_message, "Connection error")
self.assertEqual(log.error_message, error_message)
def test_update_records_count(self):
"""Test updating records count."""
@@ -222,13 +233,13 @@ class IndustrialCertificateServiceTest(TestCase):
"""Test saving certificates from dataclass."""
certificates = [
IndustrialCertificate(
issue_date="2024-01-01",
certificate_number=f"CERT-{i}",
expiry_date="2025-01-01",
certificate_file_url=f"https://example.com/cert{i}.pdf",
organisation_name=f"Company {i}",
inn=f"123456789{i}",
ogrn=f"123456789012{i}",
issue_date=str(fake.date()),
certificate_number=fake.bothify(text="??-####-#####"),
expiry_date=str(fake.date()),
certificate_file_url=fake.url(),
organisation_name=fake.company(),
inn=_digits(10),
ogrn=_digits(13),
)
for i in range(5)
]
@@ -242,13 +253,13 @@ class IndustrialCertificateServiceTest(TestCase):
"""Test saving certificates in chunks."""
certificates = [
IndustrialCertificate(
issue_date="2024-01-01",
certificate_number=f"CERT-{i}",
expiry_date="2025-01-01",
certificate_file_url=f"https://example.com/cert{i}.pdf",
organisation_name=f"Company {i}",
inn=f"12345678{i:02d}",
ogrn=f"1234567890{i:03d}",
issue_date=str(fake.date()),
certificate_number=fake.bothify(text="??-####-#####"),
expiry_date=str(fake.date()),
certificate_file_url=fake.url(),
organisation_name=fake.company(),
inn=_digits(10),
ogrn=_digits(13),
)
for i in range(10)
]
@@ -261,44 +272,48 @@ class IndustrialCertificateServiceTest(TestCase):
def test_find_by_inn(self):
"""Test finding certificates by INN."""
inn_a = _digits(10)
inn_b = _digits(10)
IndustrialCertificateRecordFactory(
inn="1111111111", certificate_number="CERT-A1", load_batch=1
inn=inn_a, certificate_number=fake.bothify(text="CERT-####"), load_batch=1
)
IndustrialCertificateRecordFactory(
inn="1111111111", certificate_number="CERT-A2", load_batch=2
inn=inn_a, certificate_number=fake.bothify(text="CERT-####"), load_batch=2
)
IndustrialCertificateRecordFactory(
inn="2222222222", certificate_number="CERT-B1", load_batch=1
inn=inn_b, certificate_number=fake.bothify(text="CERT-####"), load_batch=1
)
results = IndustrialCertificateService.find_by_inn("1111111111")
results = IndustrialCertificateService.find_by_inn(inn_a)
self.assertEqual(results.count(), 2)
results_batch1 = IndustrialCertificateService.find_by_inn(
"1111111111", batch_id=1
inn_a, batch_id=1
)
self.assertEqual(results_batch1.count(), 1)
def test_find_by_certificate_number(self):
"""Test finding certificate by number."""
IndustrialCertificateRecordFactory(certificate_number="CERT-UNIQUE")
IndustrialCertificateRecordFactory(certificate_number="CERT-OTHER")
unique_number = fake.bothify(text="CERT-#####")
IndustrialCertificateRecordFactory(certificate_number=unique_number)
IndustrialCertificateRecordFactory(certificate_number=fake.bothify(text="CERT-#####"))
results = IndustrialCertificateService.find_by_certificate_number("CERT-UNIQUE")
results = IndustrialCertificateService.find_by_certificate_number(unique_number)
self.assertEqual(results.count(), 1)
def test_save_certificates_deduplication(self):
"""Test saving certificates skips duplicates by certificate_number."""
# Create initial certificate
cert_number = fake.bothify(text="CERT-DEDUP-#####")
initial = [
IndustrialCertificate(
issue_date="2024-01-01",
certificate_number="CERT-DEDUP-001",
expiry_date="2025-01-01",
certificate_file_url="https://example.com/old.pdf",
organisation_name="Old Company Name",
inn="1234567890",
ogrn="1234567890123",
issue_date=str(fake.date()),
certificate_number=cert_number,
expiry_date=str(fake.date()),
certificate_file_url=fake.url(),
organisation_name=fake.company(),
inn=_digits(10),
ogrn=_digits(13),
)
]
count1 = IndustrialCertificateService.save_certificates(initial, batch_id=1)
@@ -308,13 +323,13 @@ class IndustrialCertificateServiceTest(TestCase):
# Try to save with same certificate_number - should be skipped
duplicate = [
IndustrialCertificate(
issue_date="2024-06-01",
certificate_number="CERT-DEDUP-001", # Same number - will be skipped
expiry_date="2026-01-01",
certificate_file_url="https://example.com/new.pdf",
organisation_name="New Company Name",
inn="9999999999",
ogrn="9999999999999",
issue_date=str(fake.date()),
certificate_number=cert_number, # Same number - will be skipped
expiry_date=str(fake.date()),
certificate_file_url=fake.url(),
organisation_name=fake.company(),
inn=_digits(10),
ogrn=_digits(13),
)
]
IndustrialCertificateService.save_certificates(duplicate, batch_id=2)
@@ -324,8 +339,8 @@ class IndustrialCertificateServiceTest(TestCase):
# Verify original data preserved
record = IndustrialCertificateRecord.objects.first()
self.assertEqual(record.organisation_name, "Old Company Name")
self.assertEqual(record.inn, "1234567890")
self.assertEqual(record.organisation_name, initial[0].organisation_name)
self.assertEqual(record.inn, initial[0].inn)
self.assertEqual(record.load_batch, 1) # Original batch
@@ -341,10 +356,10 @@ class ManufacturerServiceTest(TestCase):
"""Test saving manufacturers from dataclass."""
manufacturers = [
Manufacturer(
full_legal_name=f"Company {i} LLC",
inn=f"123456789{i}",
ogrn=f"123456789012{i}",
address=f"Address {i}",
full_legal_name=fake.company(),
inn=_digits(10),
ogrn=_digits(13),
address=fake.address().replace("\n", ", "),
)
for i in range(5)
]
@@ -358,10 +373,10 @@ class ManufacturerServiceTest(TestCase):
"""Test saving manufacturers in chunks."""
manufacturers = [
Manufacturer(
full_legal_name=f"Company {i}",
inn=f"12345678{i:02d}",
ogrn=f"1234567890{i:03d}",
address=f"Address {i}",
full_legal_name=fake.company(),
inn=_digits(10),
ogrn=_digits(13),
address=fake.address().replace("\n", ", "),
)
for i in range(10)
]
@@ -374,41 +389,50 @@ class ManufacturerServiceTest(TestCase):
def test_find_by_inn(self):
"""Test finding manufacturers by INN."""
ManufacturerRecordFactory(inn="1111111111", load_batch=1)
ManufacturerRecordFactory(inn="2222222222", load_batch=1)
ManufacturerRecordFactory(inn="3333333333", load_batch=2)
inn_target = _digits(10)
inn_other = _digits(10)
inn_third = _digits(10)
ManufacturerRecordFactory(inn=inn_target, load_batch=1)
ManufacturerRecordFactory(inn=inn_other, load_batch=1)
ManufacturerRecordFactory(inn=inn_third, load_batch=2)
results = ManufacturerService.find_by_inn("1111111111")
results = ManufacturerService.find_by_inn(inn_target)
self.assertEqual(results.count(), 1)
def test_find_by_inn_with_batch_filter(self):
"""Test finding manufacturers by INN with batch filter."""
ManufacturerRecordFactory(inn="4444444444", load_batch=1)
ManufacturerRecordFactory(inn="5555555555", load_batch=2)
inn_value = _digits(10)
ManufacturerRecordFactory(inn=inn_value, load_batch=1)
ManufacturerRecordFactory(inn=_digits(10), load_batch=2)
results_batch1 = ManufacturerService.find_by_inn("4444444444", batch_id=1)
results_batch1 = ManufacturerService.find_by_inn(inn_value, batch_id=1)
self.assertEqual(results_batch1.count(), 1)
results_batch2 = ManufacturerService.find_by_inn("4444444444", batch_id=2)
results_batch2 = ManufacturerService.find_by_inn(inn_value, batch_id=2)
self.assertEqual(results_batch2.count(), 0)
def test_find_by_ogrn(self):
"""Test finding manufacturers by OGRN."""
ManufacturerRecordFactory(ogrn="1234567890123")
ManufacturerRecordFactory(ogrn="9999999999999")
ogrn_target = _digits(13)
ManufacturerRecordFactory(ogrn=ogrn_target)
ManufacturerRecordFactory(ogrn=_digits(13))
results = ManufacturerService.find_by_ogrn("1234567890123")
results = ManufacturerService.find_by_ogrn(ogrn_target)
self.assertEqual(results.count(), 1)
def test_save_manufacturers_deduplication(self):
"""Test saving manufacturers skips duplicates by INN."""
# Create initial manufacturer
inn_value = _digits(10)
ogrn_value = _digits(13)
address_value = fake.address().replace("\n", ", ")
company_name = fake.company()
initial = [
Manufacturer(
full_legal_name="Old Company Name LLC",
inn="7777777777",
ogrn="1234567890123",
address="Old Address",
full_legal_name=company_name,
inn=inn_value,
ogrn=ogrn_value,
address=address_value,
)
]
count1 = ManufacturerService.save_manufacturers(initial, batch_id=1)
@@ -418,10 +442,10 @@ class ManufacturerServiceTest(TestCase):
# Try to save with same INN - should be skipped
duplicate = [
Manufacturer(
full_legal_name="New Company Name LLC",
inn="7777777777", # Same INN - will be skipped
ogrn="9999999999999",
address="New Address",
full_legal_name=fake.company(),
inn=inn_value, # Same INN - will be skipped
ogrn=_digits(13),
address=fake.address().replace("\n", ", "),
)
]
ManufacturerService.save_manufacturers(duplicate, batch_id=2)
@@ -431,9 +455,9 @@ class ManufacturerServiceTest(TestCase):
# Verify original data preserved
record = ManufacturerRecord.objects.first()
self.assertEqual(record.full_legal_name, "Old Company Name LLC")
self.assertEqual(record.ogrn, "1234567890123")
self.assertEqual(record.address, "Old Address")
self.assertEqual(record.full_legal_name, company_name)
self.assertEqual(record.ogrn, ogrn_value)
self.assertEqual(record.address, address_value)
self.assertEqual(record.load_batch, 1) # Original batch
@@ -449,18 +473,18 @@ class InspectionServiceTest(TestCase):
"""Test saving inspections from dataclass."""
inspections = [
Inspection(
registration_number=f"77202400000{i}",
inn=f"770{i}234567",
ogrn=f"102770000000{i}",
organisation_name=f"Компания {i}",
control_authority="Роспотребнадзор",
inspection_type="плановая",
inspection_form="документарная",
start_date="2024-01-15",
end_date="2024-01-30",
status="завершена",
legal_basis="294-ФЗ",
result="нарушения не выявлены",
registration_number=_digits(12),
inn=_digits(10),
ogrn=_digits(13),
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),
)
for i in range(5)
]
@@ -474,17 +498,17 @@ class InspectionServiceTest(TestCase):
"""Test saving inspections in chunks."""
inspections = [
Inspection(
registration_number=f"7720240000{i:02d}",
inn=f"770{i:02d}34567",
ogrn=f"10277000000{i:02d}",
organisation_name=f"Компания {i}",
control_authority="Ростехнадзор",
inspection_type="внеплановая",
inspection_form="выездная",
start_date="2024-02-01",
end_date="2024-02-15",
status="завершена",
legal_basis="248-ФЗ",
registration_number=_digits(12),
inn=_digits(10),
ogrn=_digits(13),
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),
)
for i in range(10)
]
@@ -497,57 +521,74 @@ class InspectionServiceTest(TestCase):
def test_find_by_inn(self):
"""Test finding inspections by INN."""
InspectionRecordFactory(inn="1111111111", load_batch=1)
InspectionRecordFactory(inn="1111111111", load_batch=2)
InspectionRecordFactory(inn="2222222222", load_batch=1)
inn_value = _digits(10)
InspectionRecordFactory(inn=inn_value, load_batch=1)
InspectionRecordFactory(inn=inn_value, load_batch=2)
InspectionRecordFactory(inn=_digits(10), load_batch=1)
results = InspectionService.find_by_inn("1111111111")
results = InspectionService.find_by_inn(inn_value)
self.assertEqual(results.count(), 2)
results_batch1 = InspectionService.find_by_inn("1111111111", batch_id=1)
results_batch1 = InspectionService.find_by_inn(inn_value, batch_id=1)
self.assertEqual(results_batch1.count(), 1)
def test_find_by_registration_number(self):
"""Test finding inspection by registration number."""
InspectionRecordFactory(registration_number="772024000001")
InspectionRecordFactory(registration_number="772024000002")
target_number = _digits(12)
other_number = _digits(12)
InspectionRecordFactory(registration_number=target_number)
InspectionRecordFactory(registration_number=other_number)
results = InspectionService.find_by_registration_number("772024000001")
results = InspectionService.find_by_registration_number(target_number)
self.assertEqual(results.count(), 1)
def test_find_by_control_authority(self):
"""Test finding inspections by control authority."""
InspectionRecordFactory(control_authority="Роспотребнадзор", load_batch=1)
InspectionRecordFactory(
control_authority="Управление Роспотребнадзора по г. Москве", load_batch=1
)
InspectionRecordFactory(control_authority="Ростехнадзор", load_batch=1)
authority_key = fake.word()
authority_match_1 = f"{fake.company()} {authority_key}"
authority_match_2 = f"{authority_key} {fake.company()}"
authority_other = fake.company()
InspectionRecordFactory(control_authority=authority_match_1, load_batch=1)
InspectionRecordFactory(control_authority=authority_match_2, load_batch=1)
InspectionRecordFactory(control_authority=authority_other, load_batch=1)
results = InspectionService.find_by_control_authority("Роспотребнадзор")
results = InspectionService.find_by_control_authority(authority_key)
self.assertEqual(results.count(), 2)
results_batch1 = InspectionService.find_by_control_authority(
"Роспотребнадзор", batch_id=1
authority_key, batch_id=1
)
self.assertEqual(results_batch1.count(), 2)
def test_save_inspections_deduplication(self):
"""Test saving inspections skips duplicates by registration_number."""
# Create initial inspection
reg_number = _digits(12)
inn_value = _digits(10)
ogrn_value = _digits(13)
org_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_text = fake.sentence(nb_words=3)
initial = [
Inspection(
registration_number="DEDUP-REG-001",
inn="1234567890",
ogrn="1234567890123",
organisation_name="Old Organisation",
control_authority="Роспотребнадзор",
inspection_type="плановая",
inspection_form="документарная",
start_date="2024-01-01",
end_date="2024-01-15",
status="завершена",
legal_basis="294-ФЗ",
result="нарушения не выявлены",
registration_number=reg_number,
inn=inn_value,
ogrn=ogrn_value,
organisation_name=org_name,
control_authority=control_authority,
inspection_type=inspection_type,
inspection_form=inspection_form,
start_date=start_date,
end_date=end_date,
status=status,
legal_basis=legal_basis,
result=result_text,
)
]
count1 = InspectionService.save_inspections(initial, batch_id=1)
@@ -557,18 +598,18 @@ class InspectionServiceTest(TestCase):
# Try to save with same registration_number - should be skipped
duplicate = [
Inspection(
registration_number="DEDUP-REG-001", # Same number - will be skipped
inn="9999999999",
ogrn="9999999999999",
organisation_name="New Organisation",
control_authority="Ростехнадзор",
inspection_type="внеплановая",
inspection_form="выездная",
start_date="2024-06-01",
end_date="2024-06-30",
status="в процессе",
legal_basis="248-ФЗ",
result="выявлены нарушения",
registration_number=reg_number, # Same number - will be skipped
inn=_digits(10),
ogrn=_digits(13),
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),
)
]
InspectionService.save_inspections(duplicate, batch_id=2)
@@ -578,19 +619,14 @@ class InspectionServiceTest(TestCase):
# Verify original data preserved
record = InspectionRecord.objects.first()
self.assertEqual(record.organisation_name, "Old Organisation")
self.assertEqual(record.inn, "1234567890")
self.assertEqual(record.control_authority, "Роспотребнадзор")
self.assertEqual(record.status, "завершена")
self.assertEqual(record.organisation_name, org_name)
self.assertEqual(record.inn, inn_value)
self.assertEqual(record.control_authority, control_authority)
self.assertEqual(record.status, status)
self.assertEqual(record.load_batch, 1) # Original batch
from apps.parsers.clients.base import HTTPClientError
from apps.parsers.clients.minpromtorg.industrial import IndustrialProductionClient
from django.test import tag
@tag("integration", "slow", "network", "e2e")
@tag("integration", "slow", "e2e")
class EndToEndIntegrationTest(TestCase):
"""
End-to-end интеграционные тесты полного flow.
@@ -608,72 +644,96 @@ class EndToEndIntegrationTest(TestCase):
3. Сохраняем первые N записей в БД
4. Проверяем что данные корректно сохранились
"""
try:
# 1. Загружаем данные с API
print("\n[E2E] Step 1: Fetching certificates from API...")
with IndustrialProductionClient(timeout=120) as client:
# 1. Загружаем данные через локальный HTTP сервер (без внешнего API)
print("\n[E2E] Step 1: Fetching certificates from local API...")
excel_bytes, rows = build_minpromtorg_certificates_excel(count=5)
date_str = fake.date_between(start_date="-30d", end_date="today").strftime(
"%Y%m%d"
)
file_name = f"data_resolutions_{date_str}.xlsx"
with TestHTTPServer() as server:
server.add_json(
"/api/kss-document-preview",
{
"data": [
{
"name": IndustrialProductionClient().query,
"files": [
{"name": file_name, "url": f"/files/{file_name}"}
],
}
]
},
)
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
)
with IndustrialProductionClient(
host=client_host,
scheme="http",
timeout=30,
http_adapter=server.adapter,
) as client:
all_certificates = client.fetch_certificates()
if not all_certificates:
self.skipTest("No certificates returned from API")
self.assertEqual(len(all_certificates), len(rows))
print(f"[E2E] Loaded {len(all_certificates)} certificates from local API")
print(f"[E2E] Loaded {len(all_certificates)} certificates from API")
# Берём все для теста
certificates = all_certificates
# Берём только первые 100 для теста
certificates = all_certificates[:100]
# 2. Создаём batch_id и лог
print("[E2E] Step 2: Creating load log...")
batch_id = ParserLoadLogService.get_next_batch_id(
ParserLoadLog.Source.INDUSTRIAL
)
log = ParserLoadLogService.create_load_log(
source=ParserLoadLog.Source.INDUSTRIAL,
batch_id=batch_id,
records_count=0,
)
print(f"[E2E] Created batch_id={batch_id}")
# 2. Создаём batch_id и лог
print("[E2E] Step 2: Creating load log...")
batch_id = ParserLoadLogService.get_next_batch_id(
ParserLoadLog.Source.INDUSTRIAL
)
log = ParserLoadLogService.create_load_log(
source=ParserLoadLog.Source.INDUSTRIAL,
batch_id=batch_id,
records_count=0,
)
print(f"[E2E] Created batch_id={batch_id}")
# 3. Сохраняем в БД
print("[E2E] Step 3: Saving certificates to database...")
saved_count = IndustrialCertificateService.save_certificates(
certificates, batch_id=batch_id
)
ParserLoadLogService.update_records_count(log, saved_count)
# 3. Сохраняем в БД
print("[E2E] Step 3: Saving certificates to database...")
saved_count = IndustrialCertificateService.save_certificates(
certificates, batch_id=batch_id
)
ParserLoadLogService.update_records_count(log, saved_count)
print(f"[E2E] Saved {saved_count} certificates")
print(f"[E2E] Saved {saved_count} certificates")
# 4. Проверяем результат
print("[E2E] Step 4: Verifying saved data...")
# 4. Проверяем результат
print("[E2E] Step 4: Verifying saved data...")
# Проверяем количество
db_count = IndustrialCertificateRecord.objects.filter(
load_batch=batch_id
).count()
self.assertEqual(db_count, saved_count)
self.assertEqual(db_count, len(certificates))
# Проверяем количество
db_count = IndustrialCertificateRecord.objects.filter(
load_batch=batch_id
).count()
self.assertEqual(db_count, saved_count)
self.assertEqual(db_count, len(certificates))
# Проверяем первую запись
first_cert = certificates[0]
db_record = IndustrialCertificateRecord.objects.filter(
load_batch=batch_id,
certificate_number=first_cert.certificate_number,
).first()
# Проверяем первую запись
first_cert = certificates[0]
db_record = IndustrialCertificateRecord.objects.filter(
load_batch=batch_id,
certificate_number=first_cert.certificate_number,
).first()
self.assertIsNotNone(db_record)
self.assertEqual(db_record.inn, first_cert.inn)
self.assertEqual(db_record.ogrn, first_cert.ogrn)
self.assertEqual(db_record.organisation_name, first_cert.organisation_name)
self.assertIsNotNone(db_record)
self.assertEqual(db_record.inn, first_cert.inn)
self.assertEqual(db_record.ogrn, first_cert.ogrn)
self.assertEqual(db_record.organisation_name, first_cert.organisation_name)
# Проверяем лог
log.refresh_from_db()
self.assertEqual(log.records_count, saved_count)
self.assertEqual(log.status, "success")
# Проверяем лог
log.refresh_from_db()
self.assertEqual(log.records_count, saved_count)
self.assertEqual(log.status, "success")
print("[E2E] ✅ All checks passed!")
print(f"[E2E] Sample record: {db_record.certificate_number}")
print(f"[E2E] Organisation: {db_record.organisation_name}")
print(f"[E2E] INN: {db_record.inn}, OGRN: {db_record.ogrn}")
except HTTPClientError as e:
self.skipTest(f"External API unavailable: {e}")
print("[E2E] ✅ All checks passed!")
print(f"[E2E] Sample record: {db_record.certificate_number}")
print(f"[E2E] Organisation: {db_record.organisation_name}")
print(f"[E2E] INN: {db_record.inn}, OGRN: {db_record.ogrn}")