arm-tabular-parser/api/tests/fixtures/test_rules_coverage.py
Andrey Epifancev af8ec918c5 feat(osv): шапка ОСВ без меток и «Счет: Код / Наименование» (ОСВ 63 Новоросметалл), «Итого» ↔ Σ уровня 0 по нетто
- header: период отдельной ячейкой (periods.yaml: standalone), тип данных отдельной ячейкой («Бухгалтерский»,
  data_types.yaml: standalone_header), организация — хвост названия ведомости; код счёта — из первой строки-счёта
  таблицы (с предупреждением), если «по счету» в шапке нет.
- classify/detect: текст строки может стоять правее колонки текста (колонка задаёт уровень), подписи «Код»/
  «Наименование» (osv_headers.yaml: column_captions) — не группировки, строка сумм без подписи в конце таблицы —
  «Итого» (с предупреждением).
- checks: сумма уровня 0 сверяется с «Итого» по правилу R-21 (свёрнутое сальдо «Итого» — по нетто), как у родителя.
- tests: osv_dkb_files, покрытие словарей включает osv-dkb/**; README.

Refs #11

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Claude-Session: cedd7915-2efe-41e5-988c-a9117033598b
2026-10-06 13:04:31 +04:00

67 lines
3.6 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.

"""Покрытие словарей `rules/*.yaml` (design D-10, ruling R-22): каждая запись распознавания срабатывает хотя бы раз на
реальных ОСВ и синтетике; мёртвый синоним — повод удалить его или добавить файл синтетики `s14_*`.
Запись — один `Pattern` словаря (`sheet_markers`, `statement_markers`, `groups`, `debit`, `credit`, `service_columns`,
`total_row`, `header_markers`, `mapping`, правила периодов, `year`, `end`, `header`/`row` типов данных,
`currency_aliases`). Перечень кодов `currency_codes` — список значений, не синонимы: в покрытие не входит. Записи
находятся обходом моделей `Rules`, поэтому новый паттерн в любом словаре попадает под проверку автоматически.
Файлы — реальные ОСВ `osv/**`, пакет ДКБ `osv-dkb/**` (диапазон месяцев, «Итого развернутое», шапка без меток) и
синтетика.
«Срабатывание» — `search`/`search_normalized` вернул совпадение. Счётчик — обёртка вокруг методов `Pattern` в тесте,
боевой код не меняется.
"""
from pathlib import Path
import pytest
from fixtures_helpers import osv_dkb_files, osv_fixture_files, synthetic_files
from rules_coverage import DICTIONARIES, MatchCounter, iter_patterns, uncovered
from src.parser import get_parser
from src.parser.osv import OsvParser
from src.parser.rules import DEFAULT_RULES_DIR, Rules, load_rules
pytestmark = pytest.mark.fixtures
@pytest.fixture(scope="module")
def coverage(fixtures_dir: Path) -> tuple[Rules, MatchCounter, int]:
monkeypatch = pytest.MonkeyPatch()
try:
counter = MatchCounter()
counter.install(monkeypatch)
rules = load_rules(DEFAULT_RULES_DIR)
base = get_parser()
assert isinstance(base, OsvParser)
parser = OsvParser(rules, max_bytes=base.max_bytes, timeout_seconds=base.timeout_seconds)
files = [*osv_fixture_files(fixtures_dir), *osv_dkb_files(fixtures_dir), *synthetic_files(fixtures_dir)]
for file in files:
parser.parse(file.read_bytes(), file.suffix.lstrip(".").upper()) # type: ignore[arg-type]
return rules, counter, len(files)
finally:
monkeypatch.undo()
def test_every_dictionary_entry_fires_on_fixtures_and_synthetic(coverage: tuple[Rules, MatchCounter, int]) -> None:
rules, counter, files = coverage
dead = uncovered(rules, counter)
assert dead == [], (
f"записи словарей, не сработавшие ни на одном из {files} файлов (удалите синоним или добавьте синтетику "
"s14_*):\n" + "\n".join(dead)
)
def test_coverage_sees_every_dictionary_entry_and_all_files(coverage: tuple[Rules, MatchCounter, int]) -> None:
"""Счётчик не пуст и обход видит записи всех четырёх словарей (защита от «покрытия» на пустом месте)."""
rules, counter, files = coverage
entries = list(iter_patterns(rules))
assert {file_name for file_name, _, _ in entries} == set(DICTIONARIES.values())
assert sum(counter.hits.values()) > len(entries)
assert files >= 72 # noqa: PLR2004 - реальные ОСВ из fixtures
assert not any(path.startswith("currency_codes") for _, path, _ in entries)