arm-tabular-parser/api/tests/unit/test_golden_sync.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

109 lines
4.2 KiB
Python

"""`sync_snapshots` и `scripts/golden_update.py`: запись, сравнение, diff, лишние снимки (на временном каталоге)."""
import sys
from pathlib import Path
import pytest
from golden_helpers import golden_path, render, snapshot_text, sync_snapshots
from src.parser import get_parser
from tests.synthetic.generate import generate_all
FILES = 2 # s01 и s02
API_ROOT = Path(__file__).resolve().parents[2]
@pytest.fixture(scope="module")
def fixtures_root(tmp_path_factory: pytest.TempPathFactory) -> Path:
root = tmp_path_factory.mktemp("fixtures")
generate_all(root / "osv" / "synthetic")
return root
@pytest.fixture
def root(fixtures_root: Path, tmp_path: Path) -> Path:
"""Копия дерева `osv/` на каждый тест: снимки не пересекаются между тестами."""
copy = tmp_path / "fx"
(copy / "osv" / "synthetic").mkdir(parents=True)
for source in sorted((fixtures_root / "osv" / "synthetic").glob("s0[12]*")):
(copy / "osv" / "synthetic" / source.name).write_bytes(source.read_bytes())
return copy
def files_of(root: Path) -> list[Path]:
return sorted((root / "osv" / "synthetic").glob("s*"))
def test_first_run_creates_snapshots_equal_to_render(root: Path) -> None:
parser = get_parser()
report = sync_snapshots(root, files_of(root), parser, write=True)
assert len(report.created) == FILES
assert report.changed == [] and report.orphans == [] and report.unchanged == 0
for file in files_of(root):
assert golden_path(root, file).read_text(encoding="utf-8") == snapshot_text(render(parser, file))
assert (root / "golden" / "expected_osv_parse" / "synthetic" / "s01_variant2_header.json").is_file()
def test_second_run_reports_nothing_and_check_does_not_write(root: Path) -> None:
parser = get_parser()
sync_snapshots(root, files_of(root), parser, write=True)
again = sync_snapshots(root, files_of(root), parser, write=False)
assert again.unchanged == FILES
assert not again.differs
assert again.diffs == []
def test_changed_snapshot_gives_diff_and_check_leaves_it_untouched(root: Path) -> None:
parser = get_parser()
sync_snapshots(root, files_of(root), parser, write=True)
target = golden_path(root, files_of(root)[0])
original = target.read_text(encoding="utf-8")
target.write_text(original.replace('"done"', '"needs_check"', 1), encoding="utf-8")
checked = sync_snapshots(root, files_of(root), parser, write=False)
assert checked.changed == [target.relative_to(root)]
assert '- "status": "needs_check"' in checked.diffs[0]
assert '+ "status": "done"' in checked.diffs[0]
assert target.read_text(encoding="utf-8") != original
written = sync_snapshots(root, files_of(root), parser, write=True)
assert written.changed == [target.relative_to(root)]
assert target.read_text(encoding="utf-8") == original
def test_orphan_snapshot_is_reported_and_removed_on_write(root: Path) -> None:
parser = get_parser()
sync_snapshots(root, files_of(root), parser, write=True)
orphan = root / "golden" / "expected_osv_parse" / "synthetic" / "s99_gone.json"
orphan.write_text("{}\n", encoding="utf-8")
checked = sync_snapshots(root, files_of(root), parser, write=False)
assert checked.orphans == [orphan.relative_to(root)]
assert orphan.exists()
sync_snapshots(root, files_of(root), parser, write=True)
assert not orphan.exists()
def test_script_check_exit_codes(
root: Path, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
) -> None:
monkeypatch.setenv("ARM_KA_FIXTURES", str(root))
monkeypatch.syspath_prepend(str(API_ROOT / "scripts"))
sys.modules.pop("golden_update", None)
import golden_update # noqa: PLC0415 - модуль-скрипт
assert golden_update.main(["--check"]) == 1
assert golden_update.main([]) == 0
assert golden_update.main(["--check"]) == 0
monkeypatch.setenv("ARM_KA_FIXTURES", str(root / "нет"))
capsys.readouterr()
assert golden_update.main([]) == golden_update.EXIT_NO_FIXTURES
assert "каталог фикстур не найден" in capsys.readouterr().err