checks.py: тождество сальдо по каждой строке, суммы родителя = Σ детей того же data_type (CURRENCY/QUANTITY исключены; обороты — по колонкам, сальдо — по колонкам или по нетто при свёрнутом сальдо родителя), «Итого» = корневой счёт (R-13: нет корня — «Итого» пусто/0; R-20: нет «Итого» — false и предупреждение). osv.py: OsvParser — лимит размера, чтение по сигнатуре, листы по содержимому (формы ББ/ОФР и прочие — пропуск с предупреждением), карта исходов D-6, кооперативный дедлайн, metrics (rows без TOTAL, warnings с «sheet=<имя> hierarchy=<стратегия>» первой записью по листу). get_parser(settings, rules_dir=…) → OsvParser (словари грузятся при создании: битый словарь — падение старта); PARSE_MAX_BYTES / PARSE_TIMEOUT_SECONDS в Settings и .env.*; app.py передаёт настройки. test_null_parser.py → test_parser_factory.py. Тесты: юнит-карта статусов, «Испорченная сумма», «Пустая ОСВ», два листа ОСВ, новый синоним без релиза кода (R-8); property-тест на 66 реальных файлах (все done, < 5 с, схема v1.1). test_app и интеграционный round-trip ждут ошибку реального парсера вместо NOT_IMPLEMENTED. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
51 lines
2.4 KiB
Python
51 lines
2.4 KiB
Python
"""Unit-тесты настроек `src/core/config.py` (spec «Старт с минимальным окружением»; R-35)."""
|
||
|
||
from collections.abc import Callable
|
||
|
||
import pytest
|
||
from pydantic import ValidationError
|
||
|
||
from src.core.config import Settings
|
||
|
||
SettingsFactory = Callable[..., Settings]
|
||
|
||
|
||
def test_minimal_environment_gives_defaults(minimal_settings: SettingsFactory) -> None:
|
||
settings = minimal_settings()
|
||
|
||
assert settings.DEBUG is True
|
||
assert settings.OPENBAO.MINIO.MINIO_BUCKET == "arm-ka"
|
||
assert settings.OPENBAO.NATS.NATS_URL == "nats://localhost:4222"
|
||
assert settings.OPENBAO.NATS.NATS_SEED == ""
|
||
assert settings.OPENBAO.NATS.NATS_JWT == ""
|
||
assert settings.NATS_ENABLED is True
|
||
assert settings.SERVICE_NAME == "tabular-parser"
|
||
assert settings.WORKER_CONCURRENCY == 1
|
||
assert settings.PUBLISH_RETRIES == 3 # noqa: PLR2004 - значение по умолчанию из spec
|
||
assert settings.DONE_CACHE_SIZE == 1000 # noqa: PLR2004 - значение по умолчанию из spec
|
||
assert settings.PARSE_MAX_BYTES == 50 * 1024 * 1024 # design D-8: 50 МиБ
|
||
assert settings.PARSE_TIMEOUT_SECONDS == 120 # noqa: PLR2004 - значение по умолчанию из design D-8
|
||
|
||
|
||
def test_parse_limits_must_be_positive(minimal_settings: SettingsFactory) -> None:
|
||
with pytest.raises(ValidationError, match="PARSE_MAX_BYTES"):
|
||
minimal_settings("PARSE_MAX_BYTES=0\n")
|
||
with pytest.raises(ValidationError, match="PARSE_TIMEOUT_SECONDS"):
|
||
minimal_settings("PARSE_TIMEOUT_SECONDS=0\n")
|
||
|
||
|
||
def test_publish_retries_zero_is_allowed(minimal_settings: SettingsFactory) -> None:
|
||
"""0 — без повторов: одна попытка публикации (NatsConnection принимает publish_retries ≥ 0)."""
|
||
assert minimal_settings("PUBLISH_RETRIES=0\n").PUBLISH_RETRIES == 0
|
||
|
||
|
||
def test_publish_retries_negative_is_rejected(minimal_settings: SettingsFactory) -> None:
|
||
with pytest.raises(ValidationError, match="PUBLISH_RETRIES"):
|
||
minimal_settings("PUBLISH_RETRIES=-1\n")
|
||
|
||
|
||
def test_service_name_other_than_contract_value_is_rejected(minimal_settings: SettingsFactory) -> None:
|
||
"""SERVICE_NAME — `service.name` конвертов контракта: другое значение — ошибка конфигурации при старте."""
|
||
with pytest.raises(ValidationError, match="SERVICE_NAME"):
|
||
minimal_settings("SERVICE_NAME=scan-gateway\n")
|