Files
mostovik-backend/tests/apps/parsers/test_service_helpers.py
Aleksandr Meshchriakov 25176f31b4
Some checks failed
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) Successful in 1m42s
CI/CD Pipeline / Run Tests (pull_request) Successful in 2m25s
CI/CD Pipeline / Telegram Notify Success (pull_request) Successful in 1m34s
fix pre-commit
2026-03-17 13:55:34 +01:00

223 lines
7.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Focused unit tests for parser service helpers and small query methods."""
from __future__ import annotations
from datetime import date
from decimal import Decimal
from unittest.mock import patch
from apps.parsers.models import ParserLoadLog
from apps.parsers.services import (
FNSReportService,
IndustrialProductService,
InspectionService,
ParserLoadLogService,
ProcurementService,
RegistryOrganizationResolver,
normalize_to_date,
normalize_to_decimal,
)
from django.db import IntegrityError
from django.test import TestCase
from tests.apps.parsers.factories import (
IndustrialProductRecordFactory,
InspectionRecordFactory,
ParserLoadLogFactory,
ProcurementRecordFactory,
)
from tests.apps.registers.factories import OrganizationFactory
class NormalizeHelpersTest(TestCase):
def test_normalize_to_date_handles_direct_and_embedded_formats(self):
self.assertIsNone(normalize_to_date(None))
self.assertIsNone(normalize_to_date(" "))
self.assertEqual(
normalize_to_date("2026-03-17T10:15:30+03:00"),
date(2026, 3, 17),
)
self.assertEqual(
normalize_to_date("report created at 2026-03-17 10:15"),
date(2026, 3, 17),
)
self.assertEqual(
normalize_to_date("актуально на 17.03.2026 10:15"),
date(2026, 3, 17),
)
self.assertIsNone(normalize_to_date("not a date"))
def test_normalize_to_decimal_handles_common_formats_and_invalid_values(self):
self.assertIsNone(normalize_to_decimal(None))
self.assertIsNone(normalize_to_decimal(" "))
self.assertEqual(normalize_to_decimal("1.234,56 руб."), Decimal("1234.56"))
self.assertEqual(normalize_to_decimal("1,234.56"), Decimal("1234.56"))
self.assertIsNone(normalize_to_decimal("руб."))
self.assertIsNone(normalize_to_decimal("--1"))
class RegistryOrganizationResolverTest(TestCase):
def test_normalize_identifier_rejects_non_digits(self):
self.assertIsNone(RegistryOrganizationResolver.normalize_identifier(""))
self.assertIsNone(RegistryOrganizationResolver.normalize_identifier("12 34"))
self.assertIsNone(RegistryOrganizationResolver.normalize_identifier("abc"))
def test_build_lookup_returns_empty_indexes_when_identifiers_absent(self):
lookup = RegistryOrganizationResolver.build_lookup([(None, None), ("", "")])
self.assertEqual(lookup.by_pair, {})
self.assertEqual(lookup.by_inn, {})
self.assertEqual(lookup.by_ogrn, {})
def test_resolve_organization_id_by_unique_inn_and_ogrn(self):
org_by_inn = OrganizationFactory(
mn_inn=7_701_001_001, mn_ogrn=10_277_001_000_001
)
org_by_ogrn = OrganizationFactory(
mn_inn=7_701_001_002, mn_ogrn=10_277_001_000_002
)
lookup = RegistryOrganizationResolver.build_lookup(
[
(org_by_inn.mn_inn, None),
(None, org_by_ogrn.mn_ogrn),
]
)
self.assertEqual(
RegistryOrganizationResolver.resolve_organization_id(
lookup=lookup,
inn=str(org_by_inn.mn_inn),
ogrn=None,
),
org_by_inn.id,
)
self.assertEqual(
RegistryOrganizationResolver.resolve_organization_id(
lookup=lookup,
inn=None,
ogrn=str(org_by_ogrn.mn_ogrn),
),
org_by_ogrn.id,
)
class ParserLoadLogServiceRetryTest(TestCase):
def test_create_load_log_with_next_batch_id_retries_after_integrity_error(self):
log = ParserLoadLogFactory.build(
source=ParserLoadLog.Source.INDUSTRIAL,
batch_id=2,
)
with patch.object(
ParserLoadLogService, "get_next_batch_id", side_effect=[1, 2]
), patch.object(
ParserLoadLogService,
"create_load_log",
side_effect=[IntegrityError("duplicate"), log],
) as create_mock:
(
created_log,
batch_id,
) = ParserLoadLogService.create_load_log_with_next_batch_id(
source=ParserLoadLog.Source.INDUSTRIAL
)
self.assertEqual(created_log, log)
self.assertEqual(batch_id, 2)
self.assertEqual(create_mock.call_count, 2)
def test_create_load_log_with_next_batch_id_raises_after_max_retries(self):
with patch.object(
ParserLoadLogService, "get_next_batch_id", return_value=1
), patch.object(
ParserLoadLogService,
"create_load_log",
side_effect=IntegrityError("duplicate"),
), self.assertRaisesMessage(
RuntimeError,
"Failed to allocate unique batch_id",
):
ParserLoadLogService.create_load_log_with_next_batch_id(
source=ParserLoadLog.Source.INDUSTRIAL,
max_retries=2,
)
class SmallParserServiceQueryTest(TestCase):
def test_industrial_product_service_query_helpers(self):
record = IndustrialProductRecordFactory(
inn="7701001001",
ogrn="1027700100001",
load_batch=7,
)
IndustrialProductRecordFactory(
inn="7701001001",
ogrn="1027700100002",
load_batch=8,
)
self.assertEqual(IndustrialProductService.find_by_inn("7701001001").count(), 2)
self.assertEqual(
IndustrialProductService.find_by_inn("7701001001", batch_id=7).count(),
1,
)
self.assertEqual(
IndustrialProductService.find_by_ogrn("1027700100001").first().id,
record.id,
)
def test_inspection_service_has_data_for_period(self):
InspectionRecordFactory(data_year=2026, data_month=3, is_federal_law_248=False)
InspectionRecordFactory(data_year=2026, data_month=4, is_federal_law_248=True)
self.assertTrue(InspectionService.has_data_for_period(2026, 3))
self.assertFalse(InspectionService.has_data_for_period(2026, 3, True))
self.assertTrue(InspectionService.has_data_for_period(2026, 4, True))
def test_procurement_service_find_by_customer_name_with_batch(self):
ProcurementRecordFactory(
customer_name="АО Тестовый заказчик",
load_batch=11,
)
ProcurementRecordFactory(
customer_name="АО Тестовый заказчик",
load_batch=12,
)
self.assertEqual(
ProcurementService.find_by_customer_name("Тестовый").count(), 2
)
self.assertEqual(
ProcurementService.find_by_customer_name("Тестовый", batch_id=11).count(),
1,
)
class FNSReportServiceHelpersTest(TestCase):
def test_exists_find_and_status_helpers(self):
report = FNSReportService.save_report(
external_id="EXT-100",
ogrn="1027700111111",
file_name="fin_EXT-100_1027700111111.xlsx",
file_hash="a" * 64,
source="api",
batch_id=1,
lines_data=[],
)
self.assertTrue(FNSReportService.exists_by_external_id("EXT-100"))
self.assertFalse(FNSReportService.exists_by_external_id("EXT-404"))
self.assertEqual(FNSReportService.find_by_external_id("EXT-100").id, report.id)
FNSReportService.mark_processing(report)
report.refresh_from_db()
self.assertEqual(report.status, report.Status.PROCESSING)
FNSReportService.mark_success(report)
report.refresh_from_db()
self.assertEqual(report.status, report.Status.SUCCESS)
FNSReportService.mark_failed(report, "boom")
report.refresh_from_db()
self.assertEqual(report.status, report.Status.FAILED)
self.assertEqual(report.error_message, "boom")