"""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)