feat: expand platform APIs, sources, and test coverage
Some checks failed
CI/CD Pipeline / Run Tests (pull_request) Successful in 1m53s
CI/CD Pipeline / Telegram Notify Success (push) Has been cancelled
CI/CD Pipeline / Run Tests (push) Has been cancelled
CI/CD Pipeline / Code Quality Checks (push) Has been cancelled
CI/CD Pipeline / Code Quality Checks (pull_request) Failing after 2m54s
CI/CD Pipeline / Telegram Notify Success (pull_request) Has been skipped
Some checks failed
CI/CD Pipeline / Run Tests (pull_request) Successful in 1m53s
CI/CD Pipeline / Telegram Notify Success (push) Has been cancelled
CI/CD Pipeline / Run Tests (push) Has been cancelled
CI/CD Pipeline / Code Quality Checks (push) Has been cancelled
CI/CD Pipeline / Code Quality Checks (pull_request) Failing after 2m54s
CI/CD Pipeline / Telegram Notify Success (pull_request) Has been skipped
This commit is contained in:
@@ -13,7 +13,12 @@ from apps.parsers.clients.base import (
|
||||
)
|
||||
from apps.parsers.clients.minpromtorg.industrial import IndustrialProductionClient
|
||||
from apps.parsers.clients.minpromtorg.manufactures import ManufacturesClient
|
||||
from apps.parsers.clients.minpromtorg.schemas import IndustrialCertificate, Manufacturer
|
||||
from apps.parsers.clients.minpromtorg.products import IndustrialProductsClient
|
||||
from apps.parsers.clients.minpromtorg.schemas import (
|
||||
IndustrialCertificate,
|
||||
IndustrialProduct,
|
||||
Manufacturer,
|
||||
)
|
||||
from apps.parsers.clients.proverki import ProverkiClient
|
||||
from apps.parsers.clients.proverki.schemas import Inspection
|
||||
from django.test import TestCase, tag
|
||||
@@ -23,6 +28,7 @@ from tests.utils import Response, TestHTTPServer
|
||||
from tests.utils.fixtures import (
|
||||
build_minpromtorg_certificates_excel,
|
||||
build_minpromtorg_manufacturers_excel,
|
||||
build_minpromtorg_products_excel,
|
||||
build_proverki_xml,
|
||||
fake,
|
||||
)
|
||||
@@ -43,6 +49,10 @@ def _proxy_address() -> str:
|
||||
return f"http://{fake.ipv4()}:{fake.port_number()}"
|
||||
|
||||
|
||||
def _digits(length: int) -> str:
|
||||
return "".join(str(fake.random_int(0, 9)) for _ in range(length))
|
||||
|
||||
|
||||
class _RaisingAdapter(BaseAdapter):
|
||||
def __init__(self, exc: Exception) -> None:
|
||||
super().__init__()
|
||||
@@ -440,6 +450,126 @@ class ManufacturesClientTest(TestCase):
|
||||
self.assertEqual(result.address, "")
|
||||
|
||||
|
||||
class IndustrialProductsClientTest(TestCase):
|
||||
"""Tests for IndustrialProductsClient."""
|
||||
|
||||
def test_client_initialization(self):
|
||||
client = IndustrialProductsClient()
|
||||
self.assertIsNone(client.proxies)
|
||||
self.assertEqual(client.host, "minpromtorg.gov.ru")
|
||||
|
||||
def test_fetch_products_success(self):
|
||||
excel_bytes, rows = build_minpromtorg_products_excel(count=5)
|
||||
date_str = fake.date_between(start_date="-30d", end_date="today").strftime(
|
||||
"%Y%m%d"
|
||||
)
|
||||
file_name = f"industrial_products_{date_str}.xlsx"
|
||||
|
||||
with TestHTTPServer() as server:
|
||||
server.add_json(
|
||||
"/api/kss-document-preview",
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"name": IndustrialProductsClient().query,
|
||||
"files": [
|
||||
{"name": file_name, "url": f"/files/{file_name}"}
|
||||
],
|
||||
}
|
||||
]
|
||||
},
|
||||
)
|
||||
server.add_bytes(
|
||||
f"/files/{file_name}",
|
||||
excel_bytes,
|
||||
content_type=(
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
||||
),
|
||||
)
|
||||
|
||||
client = IndustrialProductsClient(
|
||||
host=_host_from_base_url(server.base_url),
|
||||
scheme="http",
|
||||
http_adapter=server.adapter,
|
||||
)
|
||||
products = client.fetch_products()
|
||||
|
||||
self.assertEqual(len(products), len(rows))
|
||||
self.assertIsInstance(products[0], IndustrialProduct)
|
||||
self.assertSetEqual(
|
||||
{product.registry_number for product in products},
|
||||
{row.registry_number for row in rows},
|
||||
)
|
||||
|
||||
def test_fetch_products_no_files(self):
|
||||
with TestHTTPServer() as server:
|
||||
server.add_json("/api/kss-document-preview", {"data": []})
|
||||
client = IndustrialProductsClient(
|
||||
host=_host_from_base_url(server.base_url),
|
||||
scheme="http",
|
||||
http_adapter=server.adapter,
|
||||
)
|
||||
products = client.fetch_products()
|
||||
|
||||
self.assertEqual(products, [])
|
||||
|
||||
def test_get_latest_file_url_falls_back_to_excel_file(self):
|
||||
client = IndustrialProductsClient()
|
||||
files = [
|
||||
{"name": "readme.txt", "url": "/files/readme.txt"},
|
||||
{"name": "registry.xlsx", "url": "/files/registry.xlsx"},
|
||||
]
|
||||
|
||||
url = client._get_latest_file_url(files)
|
||||
self.assertEqual(url, "https://minpromtorg.gov.ru/files/registry.xlsx")
|
||||
|
||||
def test_parse_row_valid(self):
|
||||
client = IndustrialProductsClient()
|
||||
header_map = {
|
||||
"full_organisation_name": 0,
|
||||
"ogrn": 1,
|
||||
"inn": 2,
|
||||
"registry_number": 3,
|
||||
"product_name": 4,
|
||||
"product_model": 5,
|
||||
"okpd2_code": 6,
|
||||
"tnved_code": 7,
|
||||
"regulatory_document": 8,
|
||||
}
|
||||
row = (
|
||||
fake.company(),
|
||||
_digits(13),
|
||||
_digits(10),
|
||||
f"MPP-{_digits(8)}",
|
||||
fake.sentence(nb_words=4),
|
||||
fake.bothify(text="MODEL-###"),
|
||||
"25.11",
|
||||
_digits(10),
|
||||
fake.sentence(nb_words=5),
|
||||
)
|
||||
|
||||
result = client._parse_row(row, header_map)
|
||||
|
||||
self.assertIsInstance(result, IndustrialProduct)
|
||||
self.assertEqual(result.registry_number, row[3])
|
||||
self.assertEqual(result.product_name, row[4])
|
||||
|
||||
def test_parse_row_without_required_fields(self):
|
||||
client = IndustrialProductsClient()
|
||||
header_map = {
|
||||
"full_organisation_name": 0,
|
||||
"ogrn": 1,
|
||||
"inn": 2,
|
||||
"registry_number": 3,
|
||||
"product_name": 4,
|
||||
}
|
||||
|
||||
result = client._parse_row(
|
||||
(fake.company(), _digits(13), _digits(10), "", ""), header_map
|
||||
)
|
||||
self.assertIsNone(result)
|
||||
|
||||
|
||||
@tag("integration", "slow")
|
||||
class IndustrialProductionClientIntegrationTest(TestCase):
|
||||
"""Integration test using local HTTP server instead of external API."""
|
||||
@@ -516,6 +646,44 @@ class ManufacturesClientIntegrationTest(TestCase):
|
||||
self.assertEqual(len(manufacturers), len(rows))
|
||||
|
||||
|
||||
@tag("integration", "slow")
|
||||
class IndustrialProductsClientIntegrationTest(TestCase):
|
||||
"""Integration test using local HTTP server instead of external API."""
|
||||
|
||||
def test_fetch_products_local_server(self):
|
||||
excel_bytes, rows = build_minpromtorg_products_excel(count=3)
|
||||
date_str = fake.date_between(start_date="-30d", end_date="today").strftime(
|
||||
"%Y%m%d"
|
||||
)
|
||||
file_name = f"industrial_products_{date_str}.xlsx"
|
||||
|
||||
with TestHTTPServer() as server:
|
||||
server.add_json(
|
||||
"/api/kss-document-preview",
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"name": IndustrialProductsClient().query,
|
||||
"files": [
|
||||
{"name": file_name, "url": f"/files/{file_name}"}
|
||||
],
|
||||
}
|
||||
]
|
||||
},
|
||||
)
|
||||
server.add_bytes(f"/files/{file_name}", excel_bytes)
|
||||
|
||||
client = IndustrialProductsClient(
|
||||
host=_host_from_base_url(server.base_url),
|
||||
scheme="http",
|
||||
timeout=30,
|
||||
http_adapter=server.adapter,
|
||||
)
|
||||
products = client.fetch_products()
|
||||
|
||||
self.assertEqual(len(products), len(rows))
|
||||
|
||||
|
||||
class ProverkiClientTest(TestCase):
|
||||
"""Tests for ProverkiClient."""
|
||||
|
||||
@@ -574,7 +742,7 @@ class ProverkiClientTest(TestCase):
|
||||
self.assertEqual(inspections[0].control_authority, authority)
|
||||
|
||||
def test_parse_xml_record_with_attributes(self):
|
||||
from xml.etree import ElementTree as ET
|
||||
from defusedxml import ElementTree as ET
|
||||
|
||||
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))
|
||||
@@ -590,7 +758,7 @@ class ProverkiClientTest(TestCase):
|
||||
self.assertEqual(result.registration_number, reg_num)
|
||||
|
||||
def test_parse_xml_record_invalid(self):
|
||||
from xml.etree import ElementTree as ET
|
||||
from defusedxml import ElementTree as ET
|
||||
|
||||
element = ET.fromstring("<empty_record></empty_record>") # noqa: S314
|
||||
client = ProverkiClient()
|
||||
|
||||
Reference in New Issue
Block a user