Files
mostovik-backend/tests/apps/parsers/test_source_cards_views.py
Aleksandr Meshchriakov b8a18d6da4
Some checks failed
CI/CD Pipeline / Quality Gate (push) Failing after 14s
CI/CD Pipeline / Build and Push Images (push) Has been skipped
CI/CD Pipeline / Deploy Dev in Dokploy (push) Has been skipped
CI/CD Pipeline / Internal Notify (push) Successful in 0s
feat: migrate parser data to source records
2026-05-19 20:21:31 +02:00

296 lines
10 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.
"""Tests for frontend source cards API."""
from __future__ import annotations
from pathlib import Path
from tempfile import TemporaryDirectory
from apps.core.models import BackgroundJob, JobStatus
from apps.parsers.models import ParserLoadLog
from apps.parsers.source_cards import SourceCardService
from django.test import override_settings
from django.urls import reverse
from organizations.source_ingestion import (
OrganizationSourceIngestionService,
SourceRecordInput,
)
from registers.models import Register
from rest_framework import status
from rest_framework.test import APITestCase
from tests.apps.parsers.factories import ParserLoadLogFactory
from tests.apps.registers.factories import RegistryMembershipPeriodFactory
from tests.apps.user.factories import UserFactory
from tests.utils.fixtures import fake
def _digits(length: int) -> str:
return "".join(str(fake.random_int(0, 9)) for _ in range(length))
def _save_source_record(
*,
source: str,
external_id: str,
inn: str = "",
ogrn: str = "",
organization_name: str = "",
title: str = "",
) -> None:
OrganizationSourceIngestionService.save_records(
source=source,
load_batch=1,
records=[
SourceRecordInput(
external_id=external_id,
title=title,
organization_name=organization_name or title or external_id,
inn=inn,
ogrn=ogrn,
)
],
)
class SourceCardsApiTestCase(APITestCase):
def setUp(self):
SourceCardService.clear_cache()
self.user = UserFactory.create_user()
self.admin = UserFactory.create_user(is_staff=True)
self.client.force_authenticate(self.user)
def test_source_cards_list_returns_aggregated_data(self):
report_ogrn = _digits(13)
_save_source_record(
source=ParserLoadLog.Source.FNS_REPORTS,
external_id=_digits(5),
ogrn=report_ogrn,
organization_name='ООО "Финансовая компания"',
title=f"fin_{_digits(5)}_{report_ogrn}.xlsx",
)
_save_source_record(
source=ParserLoadLog.Source.FNS_REPORTS,
external_id=_digits(5),
ogrn=report_ogrn,
organization_name='ООО "Финансовая компания"',
title=f"fin_{_digits(5)}_{report_ogrn}.xlsx",
)
ParserLoadLogFactory(
source=ParserLoadLog.Source.FNS_REPORTS,
status="success",
records_count=2,
)
BackgroundJob.objects.create(
task_id="job-inspections-active",
task_name="apps.parsers.tasks.sync_inspections",
status=JobStatus.STARTED,
progress=63,
progress_message="sync",
user_id=self.user.id,
meta={"source": ParserLoadLog.Source.INSPECTIONS},
)
response = self.client.get(reverse("api_v1:sources:source-cards-list"))
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertTrue(response.data["success"])
cards = {item["slug"]: item for item in response.data["data"]}
self.assertIn("financial-indicators", cards)
self.assertIn("planned-inspections", cards)
fns_card = cards["financial-indicators"]
self.assertEqual(fns_card["records_count"], 2)
self.assertEqual(fns_card["organizations_count"], 1)
self.assertEqual(fns_card["status"], "success")
self.assertFalse(fns_card["refresh_requires_params"])
inspections_card = cards["planned-inspections"]
self.assertEqual(inspections_card["status"], "in_progress")
self.assertEqual(inspections_card["progress"], 63)
def test_main_dashboard_returns_cached_source_cards_and_registry_stats(self):
registry, _created = Register.objects.get_or_create(
name="Реестр предприятий ОПК",
)
RegistryMembershipPeriodFactory(
registry=registry,
)
ParserLoadLogFactory(
source=ParserLoadLog.Source.FNS_REPORTS,
status="success",
records_count=1,
)
SourceCardService.clear_cache()
response = self.client.get(reverse("api_v1:stat:main-dashboard"))
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertTrue(response.data["success"])
self.assertEqual(response["X-Cache"], "MISS")
data = response.data["data"]
self.assertIn("source_cards", data)
self.assertIn("organization_stats", data)
self.assertGreaterEqual(len(data["source_cards"]), 1)
self.assertEqual(data["organization_stats"]["counts"]["opk"], 1)
self.assertEqual(data["cache_ttl_seconds"], 604800)
cached_response = self.client.get(reverse("api_v1:stat:main-dashboard"))
self.assertEqual(cached_response.status_code, status.HTTP_200_OK)
self.assertEqual(cached_response["X-Cache"], "HIT")
def test_main_dashboard_cache_is_warmed_after_successful_parser_load(self):
with self.captureOnCommitCallbacks(execute=True):
ParserLoadLogFactory(
source=ParserLoadLog.Source.FNS_REPORTS,
status="success",
records_count=1,
)
response = self.client.get(reverse("api_v1:stat:main-dashboard"))
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response["X-Cache"], "HIT")
def test_source_card_detail_returns_combined_minprom_stats(self):
shared_inn = _digits(10)
_save_source_record(
source=ParserLoadLog.Source.INDUSTRIAL,
external_id="industrial-1",
inn=shared_inn,
organization_name='ООО "Производитель"',
title="Сертификат промышленной продукции",
)
_save_source_record(
source=ParserLoadLog.Source.INDUSTRIAL_PRODUCTS,
external_id="product-1",
inn=shared_inn,
organization_name='ООО "Производитель"',
title="Промышленная продукция",
)
_save_source_record(
source=ParserLoadLog.Source.MANUFACTURES,
external_id="manufacturer-1",
inn=shared_inn,
organization_name='ООО "Производитель"',
title="Производитель",
)
ParserLoadLogFactory(
source=ParserLoadLog.Source.INDUSTRIAL,
status="success",
records_count=1,
)
ParserLoadLogFactory(
source=ParserLoadLog.Source.INDUSTRIAL_PRODUCTS,
status="success",
records_count=1,
)
ParserLoadLogFactory(
source=ParserLoadLog.Source.MANUFACTURES,
status="success",
records_count=1,
)
response = self.client.get(
reverse(
"api_v1:sources:source-cards-detail",
kwargs={"slug": "manufacturers-and-products"},
)
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertTrue(response.data["success"])
card = response.data["data"]
self.assertEqual(card["records_count"], 3)
self.assertEqual(card["organizations_count"], 1)
self.assertEqual(card["status"], "success")
self.assertEqual(len(card["source_items"]), 3)
self.assertEqual(card["source_items"][0]["latest_load"]["status"], "success")
def test_source_task_statuses_returns_table_rows(self):
ParserLoadLogFactory(
source=ParserLoadLog.Source.INDUSTRIAL,
status="success",
records_count=12,
)
ParserLoadLogFactory(
source=ParserLoadLog.Source.INSPECTIONS,
status="failed",
records_count=0,
)
response = self.client.get(reverse("api_v1:sources:source-cards-statuses"))
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertTrue(response.data["success"])
rows = response.data["data"]
self.assertGreaterEqual(len(rows), 1)
row = rows[0]
self.assertIn("row_number", row)
self.assertIn("source", row)
self.assertIn("actualized_at", row)
self.assertIn("next_update_at", row)
self.assertIn("records_count", row)
self.assertIn("organizations_count", row)
self.assertIn("status", row)
self.assertIn("status_label", row)
self.assertIn("active_tasks", row)
def test_refresh_creates_background_job_and_returns_task(self):
self.client.force_authenticate(self.admin)
with TemporaryDirectory() as tmp_dir, override_settings(
FNS_WATCH_DIRECTORY=str(Path(tmp_dir) / "watch"),
FNS_PROCESSED_DIRECTORY=str(Path(tmp_dir) / "processed"),
FNS_FAILED_DIRECTORY=str(Path(tmp_dir) / "failed"),
):
response = self.client.post(
reverse(
"api_v1:sources:source-cards-refresh",
kwargs={"slug": "financial-indicators"},
),
{},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED)
self.assertEqual(response.data["status"], "accepted")
self.assertEqual(set(response.data.keys()), {"task_id", "status"})
task_id = response.data["task_id"]
self.assertTrue(
BackgroundJob.objects.filter(
task_id=task_id,
task_name="apps.parsers.tasks.scan_fns_directory",
user_id=self.admin.id,
).exists()
)
def test_refresh_procurements_requires_region_code(self):
self.client.force_authenticate(self.admin)
response = self.client.post(
reverse(
"api_v1:sources:source-cards-refresh",
kwargs={"slug": "public-procurements"},
),
{},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertFalse(response.data["success"])
def test_refresh_forbidden_for_regular_user(self):
response = self.client.post(
reverse(
"api_v1:sources:source-cards-refresh",
kwargs={"slug": "financial-indicators"},
),
{},
format="json",
)
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)