arm-tabular-parser/api/tests/fixtures/test_osv_property.py
Andrey Epifancev e705d75520 feat(parser): контрольные суммы §1.4, статусы, OsvParser как парсер по умолчанию, лимиты
checks.py: тождество сальдо по каждой строке, суммы родителя = Σ детей того же data_type
(CURRENCY/QUANTITY исключены; обороты — по колонкам, сальдо — по колонкам или по нетто при
свёрнутом сальдо родителя), «Итого» = корневой счёт (R-13: нет корня — «Итого» пусто/0;
R-20: нет «Итого» — false и предупреждение).
osv.py: OsvParser — лимит размера, чтение по сигнатуре, листы по содержимому (формы ББ/ОФР и
прочие — пропуск с предупреждением), карта исходов D-6, кооперативный дедлайн, metrics
(rows без TOTAL, warnings с «sheet=<имя> hierarchy=<стратегия>» первой записью по листу).
get_parser(settings, rules_dir=…) → OsvParser (словари грузятся при создании: битый словарь —
падение старта); PARSE_MAX_BYTES / PARSE_TIMEOUT_SECONDS в Settings и .env.*; app.py передаёт
настройки. test_null_parser.py → test_parser_factory.py.
Тесты: юнит-карта статусов, «Испорченная сумма», «Пустая ОСВ», два листа ОСВ, новый синоним
без релиза кода (R-8); property-тест на 66 реальных файлах (все done, < 5 с, схема v1.1).
test_app и интеграционный round-trip ждут ошибку реального парсера вместо NOT_IMPLEMENTED.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-05 14:37:21 +04:00

54 lines
2.5 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.

"""Property-тест парсера на всех реальных ОСВ `../fixtures/osv/**` (design D-10; spec «Золотые снимки и
property-тесты», «Контрольные суммы определяют статус», «Пустая ОСВ», «Форматы, лимиты, время»).
Инварианты проверяются независимо от снимков: статус `done`, один документ, контрольные суммы §1.4 сошлись, корень =
счёт шапки, период распознан, разбор быстрее 5 с, результат валиден по контракту v1.1. Пустая ОСВ (R-13) определяется
свойством — в `rows` только TOTAL, — а не списком файлов.
"""
import time
from pathlib import Path
from typing import cast
import pytest
from result_contract import assert_valid_result
from src.contract import FileType, OsvDocument, ResultStatus
from src.parser import get_parser
MAX_PARSE_SECONDS = 5
@pytest.mark.fixtures
def test_real_osv_parses_done_with_invariants(osv_file: Path) -> None:
parser = get_parser()
data = osv_file.read_bytes()
started = time.monotonic()
outcome = parser.parse(data, cast(FileType, osv_file.suffix.lstrip(".").upper()))
seconds = time.monotonic() - started
assert outcome.error is None, outcome.error
assert outcome.status is ResultStatus.DONE, outcome.metrics
(document,) = outcome.documents
assert isinstance(document, OsvDocument)
checks = document.checks
assert checks is not None
assert checks.total_row_matches_root is True
assert checks.balance_identity_violations == []
assert checks.parent_sum_violations == []
assert document.header.period_quarter is not None
data_rows = [row for row in document.rows if row.group_type != "TOTAL"]
assert outcome.metrics is not None
assert outcome.metrics.rows == len(data_rows)
if data_rows:
assert outcome.metrics.rows > 0
assert document.header.account_code_raw == document.rows[0].account_code_raw
else:
# Пустая ОСВ (R-13): только «Итого»
assert [row.group_type for row in document.rows] == ["TOTAL"]
assert outcome.metrics.warnings is not None
assert "нет строк данных" in outcome.metrics.warnings
assert seconds < MAX_PARSE_SECONDS, f"{osv_file.name}: {seconds:.2f} с"
assert_valid_result(outcome)