arm-tabular-parser/api/tests/unit/test_rules_coverage_helpers.py
Andrey Epifancev bc143ef140 test: golden-снимки, линтер координат, покрытие словарей; make golden-update
- golden_helpers, scripts/golden_update.py (--check), test_golden на 66 ОСВ и синтетике; make golden-update/golden-check/test-fixtures
- тест покрытия словарей (R-22): счётчик срабатываний Pattern; из osv_headers.yaml удалены неиспользуемые service_columns kpp/code/quantity/currency
- синтетика s14–s16 в генераторе и таблице ожиданий; skip вместо падения при отсутствии osv/synthetic (T11.1); комментарий R-21 в s07 (T11.2)
- линтер: предел адреса 10 вместо 4 (T3.1), обращение cell(3, 4) (T3.8); реальные файлы не фиксируют BU/RUB (T3.9)
- README: golden-процесс; отмечены Task 12 и пункты 4.2, 4.3

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
2026-10-05 15:28:41 +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, 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())
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]