Andrey Epifancev d64ba71098 test(statements): синтетика st01…st05 и сценарии спеки на ней; параметры statement_file
Refs #14
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: cedd7915-2efe-41e5-988c-a9117033598b
2026-10-06 18:33:58 +04:00

130 lines
5.2 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.

"""Общие фикстуры тестов."""
import os
from collections.abc import Callable
from pathlib import Path
import pytest
from fixtures_helpers import (
fixtures_skip_reason,
osv_dkb_files,
osv_dkb_golden_files,
osv_fixture_files,
resolve_fixtures_dir,
statement_files,
statement_synthetic_files,
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
def statement_synthetic_skip_reason() -> str | None:
"""Причина пропуска: каталог fixtures есть, но `statements-xlsx/synthetic` отсутствует или пуст.
`None` — файлы есть или нет самих fixtures (тогда причину даёт `fixtures_skip_reason`)."""
root = resolve_fixtures_dir()
if not (root / "statements-xlsx").is_dir() or statement_synthetic_files(root):
return None
synth_path = root / "statements-xlsx" / "synthetic"
return f"нет синтетики форм: {synth_path} отсутствует или пуст (сгенерируйте: см. README fixtures)"
@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` (реальные ОСВ), `dkb_file` (пакет ДКБ), `dkb_golden_file` (ДКБ до порога снимка),
`synthetic_file` (синтетика ОСВ), `statement_file` (реальные формы) и `statement_synthetic_file` (синтетика форм);
без каталога — один параметр со skip."""
sources = {
"osv_file": (osv_fixture_files, fixtures_skip_reason),
"dkb_file": (osv_dkb_files, fixtures_skip_reason),
"dkb_golden_file": (osv_dkb_golden_files, fixtures_skip_reason),
"synthetic_file": (synthetic_files, lambda: fixtures_skip_reason() or synthetic_skip_reason()),
"statement_file": (statement_files, fixtures_skip_reason),
"statement_synthetic_file": (
statement_synthetic_files,
lambda: fixtures_skip_reason() or statement_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)