Манифест statements_manifest.yaml по 18 реальным файлам statements-xlsx (сверен прогоном парсера, R-13, OQ-137), property-тест по манифесту, снимки форм в golden_helpers/golden_update/test_golden, словари форм в покрытии, синтетика st06 для вариантов заголовков. Мёртвые записи словаря удалены (name_caption, метки okved и okud_label) либо покрыты st06. Refs #14 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: cedd7915-2efe-41e5-988c-a9117033598b
78 lines
3.4 KiB
Python
78 lines
3.4 KiB
Python
"""Механизм покрытия словарей (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
|
||
assert ("statement_headers.yaml", "form_titles.BALANCE[0]") in keys
|
||
assert ("statement_periods.yaml", "numeric_date") 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]
|