- 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
107 lines
3.9 KiB
Python
107 lines
3.9 KiB
Python
"""Общие фикстуры тестов."""
|
||
|
||
import os
|
||
from collections.abc import Callable
|
||
from pathlib import Path
|
||
|
||
import pytest
|
||
from fixtures_helpers import (
|
||
fixtures_skip_reason,
|
||
osv_fixture_files,
|
||
resolve_fixtures_dir,
|
||
synthetic_files,
|
||
synthetic_skip_reason,
|
||
)
|
||
|
||
from src.core.config import Settings
|
||
|
||
# Переменные, которые читает Settings: убираются из окружения процесса, чтобы тест видел только свой файл
|
||
_SETTINGS_ENV_PREFIXES = (
|
||
"OPENBAO__",
|
||
"SERVER__",
|
||
"DEBUG",
|
||
"ROOT_PATH",
|
||
"VERSION",
|
||
"SERVICE_NAME",
|
||
"NATS_ENABLED",
|
||
"WORKER_CONCURRENCY",
|
||
"PUBLISH_RETRIES",
|
||
"DONE_CACHE_SIZE",
|
||
"PARSE_MAX_BYTES",
|
||
"PARSE_TIMEOUT_SECONDS",
|
||
)
|
||
|
||
# Минимальное окружение spec «Старт с минимальным окружением»: MinIO, адрес NATS и DEBUG; без seed/jwt, БД и SMB
|
||
MINIMAL_ENV = """\
|
||
DEBUG=true
|
||
OPENBAO__MINIO__MINIO_URL=localhost:9000
|
||
OPENBAO__MINIO__MINIO_ADMIN_LOGIN=minioadmin
|
||
OPENBAO__MINIO__MINIO_ADMIN_PASSWORD=minioadmin
|
||
OPENBAO__MINIO__MINIO_BUCKET=arm-ka
|
||
OPENBAO__NATS__NATS_URL=nats://localhost:4222
|
||
"""
|
||
|
||
|
||
@pytest.fixture
|
||
def minimal_settings(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Callable[[str], Settings]:
|
||
"""Фабрика Settings только из временного `.env` с `MINIMAL_ENV` и строками `extra` (окружение процесса скрыто)."""
|
||
for name in list(os.environ):
|
||
if name.upper().startswith(_SETTINGS_ENV_PREFIXES):
|
||
monkeypatch.delenv(name)
|
||
|
||
def build(extra: str = "") -> Settings:
|
||
env_file = tmp_path / ".env"
|
||
env_file.write_text(MINIMAL_ENV + extra, encoding="utf-8")
|
||
return Settings(_env_file=env_file) # type: ignore[call-arg]
|
||
|
||
return build
|
||
|
||
|
||
@pytest.fixture(scope="session")
|
||
def synthetic_dir(fixtures_dir: Path) -> Path:
|
||
"""`fixtures/osv/synthetic`; тест пропускается с причиной, если синтетики в каталоге нет."""
|
||
reason = synthetic_skip_reason()
|
||
if reason is not None:
|
||
pytest.skip(reason)
|
||
return fixtures_dir / "osv" / "synthetic"
|
||
|
||
|
||
@pytest.fixture(scope="session")
|
||
def fixtures_dir() -> Path:
|
||
"""Каталог `../fixtures`; тест пропускается с причиной, если каталога нет."""
|
||
reason = fixtures_skip_reason()
|
||
if reason is not None:
|
||
pytest.skip(reason)
|
||
return resolve_fixtures_dir()
|
||
|
||
|
||
def pytest_generate_tests(metafunc: pytest.Metafunc) -> None:
|
||
"""Параметры `osv_file` (реальные ОСВ) и `synthetic_file` (синтетика); без каталога — один параметр со skip."""
|
||
sources = {
|
||
"osv_file": (osv_fixture_files, fixtures_skip_reason),
|
||
"synthetic_file": (synthetic_files, lambda: fixtures_skip_reason() or synthetic_skip_reason()),
|
||
}
|
||
for argname, (listing, skip_reason) in sources.items():
|
||
if argname not in metafunc.fixturenames:
|
||
continue
|
||
reason = skip_reason()
|
||
if reason is not None:
|
||
metafunc.parametrize(
|
||
argname, [pytest.param(None, marks=pytest.mark.skip(reason=reason))], ids=["нет-фикстур"]
|
||
)
|
||
continue
|
||
root = resolve_fixtures_dir()
|
||
files = listing(root)
|
||
metafunc.parametrize(argname, files, ids=[path.relative_to(root).as_posix() for path in files])
|
||
|
||
|
||
def pytest_collection_modifyitems(config: pytest.Config, items: list[pytest.Item]) -> None:
|
||
"""Тесты с маркером `fixtures` пропускаются, если каталог фикстур недоступен."""
|
||
reason = fixtures_skip_reason()
|
||
if reason is None:
|
||
return
|
||
skip = pytest.mark.skip(reason=reason)
|
||
for item in items:
|
||
if item.get_closest_marker("fixtures"):
|
||
item.add_marker(skip)
|