"""Golden-снимки разбора (design D-10): результат парсера для каждого файла `fixtures/osv/**` и синтетики равен снимку в `fixtures/golden/expected_osv_parse/` (зеркало дерева `osv/`), без volatile-полей. Снимок — регрессия, а не оракул (оракулы — ручная сверка «Итого» и property-тест). Обновление — `make golden-update`, отдельным коммитом в репозиторий fixtures с просмотром diff. """ from pathlib import Path import pytest from golden_helpers import golden_path, render, snapshot_text, unified_diff from src.parser import Parser, get_parser pytestmark = pytest.mark.fixtures MISSING = "нет снимка: выполните make golden-update ({path})" MAX_DIFF_CHARS = 6000 @pytest.fixture(scope="module") def parser() -> Parser: return get_parser() def check_snapshot(fixtures_dir: Path, file: Path, parser: Parser) -> None: snapshot = golden_path(fixtures_dir, file) if not snapshot.is_file(): pytest.fail(MISSING.format(path=snapshot.relative_to(fixtures_dir).as_posix()), pytrace=False) stored = snapshot.read_text(encoding="utf-8") current = snapshot_text(render(parser, file)) if stored != current: diff = unified_diff(stored, current, name=snapshot.relative_to(fixtures_dir).as_posix()) if len(diff) > MAX_DIFF_CHARS: diff = ( diff[:MAX_DIFF_CHARS] + f"\n… (diff обрезан, всего {len(diff)} символов; полный — make golden-update)" ) pytest.fail( "результат разбора отличается от снимка (намеренная правка парсера — make golden-update и коммит " f"в fixtures с просмотром diff):\n{diff}", pytrace=False, ) def test_osv_file_matches_golden_snapshot(fixtures_dir: Path, osv_file: Path, parser: Parser) -> None: check_snapshot(fixtures_dir, osv_file, parser) def test_synthetic_file_matches_golden_snapshot(fixtures_dir: Path, synthetic_file: Path, parser: Parser) -> None: check_snapshot(fixtures_dir, synthetic_file, parser) def test_every_snapshot_has_a_source_file(fixtures_dir: Path) -> None: """Лишний снимок (файл удалён или переименован) — ошибка: `make golden-update` его удаляет.""" from fixtures_helpers import osv_fixture_files, synthetic_files # noqa: PLC0415 - только для этого теста expected = { golden_path(fixtures_dir, file) for file in [*osv_fixture_files(fixtures_dir), *synthetic_files(fixtures_dir)] } directory = fixtures_dir / "golden" / "expected_osv_parse" present = set(directory.rglob("*.json")) if directory.is_dir() else set() assert sorted(path.relative_to(fixtures_dir).as_posix() for path in present - expected) == []