- 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
109 lines
4.2 KiB
Python
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
|