Хелпер map_golden_snapshot, сценарий маппинг golden -> подтверждение -> консолидация 2024 Q3 -> свод, сопоставление Счастливочной, экспорт, независимая сверка склейки периодов. Тест помечен xfail(strict): на реальных данных нарастающий Q1 (is_cumulative=true) не привязывается к версии Q3 и не считается квартальным (fits / select_sources) — свод пуст; подробности в отчёте задачи. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
87 lines
4.2 KiB
Python
87 lines
4.2 KiB
Python
"""Поиск каталога реальных данных `fixtures` (соседний репозиторий workspace) для тестов с маркером `fixtures`."""
|
||
|
||
import json
|
||
import os
|
||
import uuid
|
||
from datetime import UTC, datetime
|
||
from pathlib import Path
|
||
from typing import TYPE_CHECKING
|
||
|
||
from sqlalchemy import select
|
||
from sqlalchemy.ext.asyncio import AsyncSession
|
||
|
||
if TYPE_CHECKING:
|
||
from src.contract.result import RecognitionResult
|
||
from src.models.directories import LegalEntity
|
||
from src.models.files import Document
|
||
|
||
FIXTURES_ENV = "ARM_KA_FIXTURES"
|
||
|
||
|
||
def resolve_fixtures_dir() -> Path:
|
||
"""Каталог fixtures: `ARM_KA_FIXTURES`, иначе соседний `../../fixtures` относительно `arm-core/api`.
|
||
|
||
Не проверяет существование: это делает вызывающий код (пропуск теста с причиной).
|
||
"""
|
||
explicit = os.environ.get(FIXTURES_ENV)
|
||
if explicit:
|
||
return Path(explicit)
|
||
return Path(__file__).resolve().parents[3] / "fixtures"
|
||
|
||
|
||
def fixtures_skip_reason() -> str | None:
|
||
"""Причина пропуска тестов с маркером `fixtures` или `None`, если каталог доступен."""
|
||
path = resolve_fixtures_dir()
|
||
if path.is_dir():
|
||
return None
|
||
return (
|
||
f"ARM_KA_FIXTURES недоступен: нет каталога {path} "
|
||
f"(задайте {FIXTURES_ENV} или положите fixtures рядом с arm-core)"
|
||
)
|
||
|
||
|
||
def golden_result(path: Path, *, job_id: uuid.UUID, file_id: uuid.UUID) -> "RecognitionResult":
|
||
"""Golden-снимок парсера как `RecognitionResult`: подставляет `job_id`/`file_id` и версию сервиса."""
|
||
from src.contract.result import RecognitionResult # noqa: PLC0415 - тяжёлые импорты лениво: цикл с conftest
|
||
|
||
data = json.loads(path.read_text("utf-8"))
|
||
data["service"] = {"version": "golden", **data["service"]}
|
||
return RecognitionResult.model_validate(data | {"job_id": str(job_id), "file_id": str(file_id)})
|
||
|
||
|
||
async def map_golden_snapshot(
|
||
session: AsyncSession, *, entity: "LegalEntity", path: Path, uploaded_by: uuid.UUID | None
|
||
) -> "list[Document]":
|
||
"""Golden-снимок `path` → файл ЮЛ `entity` и его документы ОСВ в БД теста (`flush`, без commit).
|
||
|
||
Как маппер: файл `PROCESSING` с текущим заданием `done`, затем боевой `map_file` с планом счетов seed и настоящим
|
||
`CounterpartyIndex` (справочник читается на момент вызова — ЮЛ и алиасы должны быть созданы до маппинга).
|
||
Возвращает документы файла (после автозамены прежних версий — только документы этого файла).
|
||
"""
|
||
# ленивые импорты: `conftest` импортирует этот модуль раньше `tests.factories` (цикл)
|
||
from src.models.accounting import ChartOfAccount # noqa: PLC0415
|
||
from src.models.enums import FileStatus, RecognitionStatus # noqa: PLC0415
|
||
from src.models.files import Document # noqa: PLC0415
|
||
from src.repositories.counterparties import CounterpartyIndex # noqa: PLC0415
|
||
from src.services.result_mapping import map_file # noqa: PLC0415
|
||
from tests.factories import make_stored_file # noqa: PLC0415
|
||
|
||
file, job = await make_stored_file(
|
||
session,
|
||
entity=entity,
|
||
status=FileStatus.PROCESSING,
|
||
job_status=RecognitionStatus.done,
|
||
original_name=path.name,
|
||
uploaded_by=uploaded_by,
|
||
)
|
||
chart = {
|
||
code: account_id for code, account_id in await session.execute(select(ChartOfAccount.code, ChartOfAccount.id))
|
||
}
|
||
index = await CounterpartyIndex.load(session)
|
||
result = golden_result(path, job_id=job.id, file_id=file.id)
|
||
await map_file(session, job=job, file=file, result=result, chart=chart, index=index, now=datetime.now(UTC))
|
||
documents = await session.execute(
|
||
select(Document).where(Document.stored_file_id == file.id).order_by(Document.osv_account_code)
|
||
)
|
||
return list(documents.scalars())
|