arm-tabular-parser/api/tests/fixtures/test_rules_coverage.py
Andrey Epifancev fafe11349b test(fixtures): 72 реальных ОСВ — Счастливочная 1 кв 2025 (02, 58, 62, 66, 67, 76), 88 golden-снимков
EXPECTED_FILES 66 → 72, порог покрытия словарей, имя теста без числа, CLAUDE.md. Снимки — arm-ka-fixtures#7.
make test-fixtures: 873 passed (ошибка только у roundtrip без NATS).

Refs #10

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-06 12:24:18 +04:00

64 lines
3.4 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`, поэтому новый паттерн в любом словаре попадает под проверку автоматически.
«Срабатывание» — `search`/`search_normalized` вернул совпадение. Счётчик — обёртка вокруг методов `Pattern` в тесте,
боевой код не меняется.
"""
from pathlib import Path
import pytest
from fixtures_helpers import 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), *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)