arm-tabular-parser/api/tests/unit/test_rules_coverage_helpers.py
Andrey Epifancev be5ad8ce86 refactor: удалён собственный конвейер форм и словари форм — шаги 2–8 в arm_statements (#54)
Удалены модули statements/{checks,columns,detect,header,lines,merge,statement,legacy_models}.py, словари
rules/statement_{headers,periods,checks}.yaml и их тесты. Словари форм и шаги 2–8 — в пакете arm_statements.
Rules без полей statement_*; покрытие словарей ОСВ (DICTIONARIES) без словарей форм. Документация сервиса
переписана под адаптер форм, контракт 2.0 и коды OSV_*.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BJKMLHdxMwSUeYcdkwqX4W
2026-10-18 21:53:29 +04:00

76 lines
3.3 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.

"""Механизм покрытия словарей (R-22) без каталога fixtures: обход записей, счётчик срабатываний, сообщение о мёртвых."""
import shutil
from pathlib import Path
import pytest
from rules_coverage import DICTIONARIES, PATTERNLESS, MatchCounter, iter_patterns, uncovered
from src.parser.rules import DEFAULT_RULES_DIR, Pattern, load_rules
def test_every_pattern_of_every_dictionary_is_enumerated_with_file_and_key() -> None:
rules = load_rules()
entries = list(iter_patterns(rules))
assert {file_name for file_name, _, _ in entries} == set(DICTIONARIES.values()) - PATTERNLESS
keys = {(file_name, path) for file_name, path, _ in entries}
assert ("osv_headers.yaml", "sheet_markers[0]") in keys
assert ("osv_headers.yaml", "groups.opening[0]") in keys
assert ("osv_groups.yaml", "mapping[0][0]") in keys
assert ("periods.yaml", "rules[0].pattern") in keys
assert ("periods.yaml", "year") in keys
assert ("data_types.yaml", "currency_aliases.RUB[0]") in keys
# R-22: перечень кодов ISO — значения, не синонимы
assert not any("currency_codes" in path for _, path, _ in entries)
assert all(isinstance(pattern, Pattern) for _, _, pattern in entries)
def test_counter_counts_matches_of_search_and_search_normalized_only(monkeypatch: pytest.MonkeyPatch) -> None:
rules = load_rules()
counter = MatchCounter()
counter.install(monkeypatch)
total = rules.headers.total_row[0]
inn = rules.headers.service_columns["inn"][0]
assert total.search("Итого") is not None
assert inn.search_normalized("инн") is not None
assert inn.search_normalized("нет такого") is None
dead = uncovered(rules, counter)
assert not any("total_row[0]" in line or "service_columns.inn[0]" in line for line in dead)
assert any("sheet_markers[0]" in line for line in dead)
def test_message_lists_file_key_and_pattern_of_a_dead_entry(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
rules_dir = tmp_path / "rules"
shutil.copytree(DEFAULT_RULES_DIR, rules_dir)
headers = rules_dir / "osv_headers.yaml"
headers.write_text(
headers.read_text(encoding="utf-8").replace(
"total_row:\n - '^итого\\b'", "total_row:\n - '^итого\\b'\n - '^никогда не встречается$'"
),
encoding="utf-8",
)
rules = load_rules(rules_dir)
counter = MatchCounter()
counter.install(monkeypatch)
assert rules.headers.total_row[0].search("Итого по счету") is not None
assert rules.headers.total_row[1].search("Итого по счету") is None
dead = uncovered(rules, counter)
assert "osv_headers.yaml: total_row[1]: '^никогда не встречается$'" in dead
assert not any("total_row[0]" in line for line in dead)
def test_counter_does_not_change_matching_results(monkeypatch: pytest.MonkeyPatch) -> None:
pattern = Pattern.compile(r"\bбу\b")
before = [pattern.search("данные БУ"), pattern.search("нет")]
MatchCounter().install(monkeypatch)
after = [pattern.search("данные БУ"), pattern.search("нет")]
assert [m.group(0) if m else None for m in after] == [m.group(0) if m else None for m in before]