arm-tabular-parser/api/tests/unit/test_config_openbao_app.py
Andrey Epifancev be67828cbb feat(config): несекретные настройки из OpenBao через OPENBAO__APP__<ИМЯ> (#22)
Тот же источник настроек, что в arm-core#44: приоритет обычный env -> OPENBAO__APP__* -> .env -> умолчание;
неизвестное имя — ошибка при старте; ключ APP не ломает OpenBAOSettings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EAKRDNzXtRrwmWz9z6PZu3
2026-10-07 18:39:31 +04:00

112 lines
4.1 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.

"""Несекретные настройки из OpenBao: `OPENBAO__APP__<ИМЯ>` (issue #22).
Приоритет: обычный env -> `OPENBAO__APP__*` -> `.env` -> значение по умолчанию.
"""
import os
from pathlib import Path
import pytest
from pydantic import ValidationError
from src.core.config import Settings
REQUIRED_ENV = {
"OPENBAO__MINIO__MINIO_URL": "localhost:9000",
"OPENBAO__MINIO__MINIO_ADMIN_LOGIN": "login",
"OPENBAO__MINIO__MINIO_ADMIN_PASSWORD": "password",
"OPENBAO__MINIO__MINIO_BUCKET": "bucket",
"OPENBAO__NATS__NATS_URL": "nats://localhost:4222",
}
FLAT_KEYS = {"DEBUG", "NATS_ENABLED", "PARSE_TIMEOUT_SECONDS", "PARSE_MAX_BYTES", "PUBLISH_RETRIES"}
TIMEOUT = 300
PORT = 9000
DOTENV_RETRIES = 5
@pytest.fixture
def env(monkeypatch: pytest.MonkeyPatch) -> pytest.MonkeyPatch:
"""Чистое окружение с обязательными OPENBAO__*; остальное из процесса убрано."""
for key in list(os.environ):
if key.startswith(("OPENBAO__", "SERVER__")) or key in FLAT_KEYS:
monkeypatch.delenv(key)
for key, value in REQUIRED_ENV.items():
monkeypatch.setenv(key, value)
return monkeypatch
def _settings(env_file: Path | None = None) -> Settings:
return Settings(_env_file=env_file) # type: ignore[call-arg]
def test_app_value_applied(env: pytest.MonkeyPatch) -> None:
env.setenv("OPENBAO__APP__PARSE_TIMEOUT_SECONDS", str(TIMEOUT))
env.setenv("OPENBAO__APP__DEBUG", "true")
s = _settings()
assert s.PARSE_TIMEOUT_SECONDS == TIMEOUT
assert s.DEBUG is True
assert s.OPENBAO.NATS.NATS_URL == "nats://localhost:4222"
def test_default_without_app(env: pytest.MonkeyPatch) -> None:
assert _settings().PARSE_TIMEOUT_SECONDS == 120 # noqa: PLR2004 - значение по умолчанию
def test_plain_env_overrides_app(env: pytest.MonkeyPatch) -> None:
env.setenv("OPENBAO__APP__NATS_ENABLED", "false")
env.setenv("NATS_ENABLED", "true")
assert _settings().NATS_ENABLED is True
def test_app_overrides_dotenv(env: pytest.MonkeyPatch, tmp_path: Path) -> None:
dotenv = tmp_path / ".env"
dotenv.write_text(f"NATS_ENABLED=true\nPUBLISH_RETRIES={DOTENV_RETRIES}\n", encoding="utf-8")
env.setenv("OPENBAO__APP__NATS_ENABLED", "false")
s = _settings(dotenv)
assert s.NATS_ENABLED is False # OPENBAO__APP__ сильнее .env
assert s.PUBLISH_RETRIES == DOTENV_RETRIES
def test_types_parsed_like_plain_env(env: pytest.MonkeyPatch) -> None:
env.setenv("OPENBAO__APP__NATS_ENABLED", "0")
env.setenv("OPENBAO__APP__PARSE_MAX_BYTES", "1048576")
s = _settings()
assert s.NATS_ENABLED is False
assert s.PARSE_MAX_BYTES == 1048576 # noqa: PLR2004
def test_invalid_value_rejected(env: pytest.MonkeyPatch) -> None:
env.setenv("OPENBAO__APP__PARSE_TIMEOUT_SECONDS", "0")
with pytest.raises(ValidationError, match="PARSE_TIMEOUT_SECONDS"):
_settings()
def test_unknown_name_is_config_error(env: pytest.MonkeyPatch) -> None:
env.setenv("OPENBAO__APP__PARSE_TIMEOUT_SECONDZ", "300")
with pytest.raises(ValueError, match="PARSE_TIMEOUT_SECONDZ"):
_settings()
def test_openbao_group_not_settable_via_app(env: pytest.MonkeyPatch) -> None:
env.setenv("OPENBAO__APP__OPENBAO__NATS__NATS_URL", "nats://x:4222")
with pytest.raises(ValueError, match="OPENBAO"):
_settings()
def test_app_key_does_not_break_openbao_validation(env: pytest.MonkeyPatch, tmp_path: Path) -> None:
"""Ключ APP не попадает в OpenBAOSettings (там extra=forbid) — ни из env, ни из env-файла."""
dotenv = tmp_path / ".env"
dotenv.write_text("OPENBAO__APP__NATS_ENABLED=false\n", encoding="utf-8")
env.setenv("OPENBAO__APP__DEBUG", "true")
s = _settings(dotenv)
assert s.DEBUG is True
assert not hasattr(s.OPENBAO, "APP")
def test_nested_group_still_works(env: pytest.MonkeyPatch) -> None:
env.setenv("OPENBAO__MINIO__MINIO_SECURE", "false")
env.setenv("OPENBAO__APP__SERVER__PORT", str(PORT))
s = _settings()
assert s.OPENBAO.MINIO.MINIO_SECURE is False
assert s.server.PORT == PORT