Files
g2s-aggregator/tests/config/test_config_sections.py
T
2026-04-18 00:33:45 +03:00

316 lines
11 KiB
Python

from decimal import Decimal
from pathlib import Path
import pytest
from pydantic import ValidationError
from app import config as config_module
from app.config import Settings, get_settings
PROJECT_ROOT = Path(__file__).resolve().parents[2]
TEST_CONFIG_FILE = PROJECT_ROOT / "config.test.yaml"
MISSING_REQUIRED_SECTION_CONFIG_FILE = (
PROJECT_ROOT / "tests" / "config" / "fixtures" / "config.missing_adapter.yaml"
)
MISSING_ADDRESS_SUGGESTIONS_CONFIG_FILE = (
PROJECT_ROOT
/ "tests"
/ "config"
/ "fixtures"
/ "config.missing_address_suggestions.yaml"
)
DEFAULT_CONFIG_FILE_FIXTURE = (
PROJECT_ROOT / "tests" / "config" / "fixtures" / "config.default.yaml"
)
TEST_OVERRIDE_CONFIG_FILE_FIXTURE = (
PROJECT_ROOT / "tests" / "config" / "fixtures" / "config.test.override.yaml"
)
INVALID_PRICE_MULTIPLIER_CONFIG_FILE = (
PROJECT_ROOT / "tests" / "config" / "fixtures" / "config.invalid_price_multiplier.yaml"
)
MISSING_PRICE_MULTIPLIER_CONFIG_FILE = (
PROJECT_ROOT / "tests" / "config" / "fixtures" / "config.missing_price_multiplier.yaml"
)
MISSING_OBSERVABILITY_CONFIG_FILE = (
PROJECT_ROOT / "tests" / "config" / "fixtures" / "config.missing_observability.yaml"
)
MISSING_OBSERVABILITY_ENDPOINT_CONFIG_FILE = (
PROJECT_ROOT
/ "tests"
/ "config"
/ "fixtures"
/ "config.missing_observability_endpoint.yaml"
)
_DADATA_COUNTRIES = ("RU", "BY", "KZ")
_YANDEX_COUNTRIES = ("AM", "AZ", "KG", "MD", "TJ", "TM", "UZ")
_TOMTOM_COUNTRIES = (
"AL",
"AT",
"BE",
"BG",
"CH",
"CZ",
"DE",
"DK",
"EE",
"ES",
"FI",
"FR",
"GB",
"GR",
"HR",
"HU",
"IE",
"IT",
"LT",
"LV",
"NL",
"NO",
"PL",
"PT",
"RO",
"RS",
"SE",
"SI",
"SK",
"UA",
)
def _expected_country_mapping() -> dict[str, str]:
return {
**{country_code: "dadata" for country_code in _DADATA_COUNTRIES},
**{
country_code: "yandex_geosuggest"
for country_code in _YANDEX_COUNTRIES
},
**{country_code: "tomtom" for country_code in _TOMTOM_COUNTRIES},
}
def _use_runtime_config_files(
monkeypatch: pytest.MonkeyPatch,
*,
test_config_file: Path,
default_config_file: Path | None = None,
) -> None:
monkeypatch.setattr(config_module, "TEST_CONFIG_FILE", str(test_config_file))
if default_config_file is not None:
monkeypatch.setattr(
config_module,
"DEFAULT_CONFIG_FILE",
str(default_config_file),
)
get_settings.cache_clear()
def test_configuration_sections_are_loaded_from_yaml_file(
monkeypatch: pytest.MonkeyPatch,
) -> None:
_use_runtime_config_files(monkeypatch, test_config_file=TEST_CONFIG_FILE)
settings = Settings()
assert settings.controller.api_prefix == "/api/v1"
assert settings.controller.request_id_header == "X-Request-ID"
assert settings.service.provider_timeout_seconds == 10.0
assert settings.service.max_parallel_providers == 8
assert settings.business_logic.weight_round_scale == 2
assert settings.business_logic.provider_price_multiplier == Decimal("1.0")
assert settings.repository.redis_dsn == "redis://localhost:6379/0"
assert settings.repository.price_cache_ttl_seconds == 900
assert settings.adapter.cdek_base_url == "https://api.cdek.ru/v2"
assert settings.adapter.cdek_client_id == "test-client-id"
assert settings.adapter.cdek_client_secret == "test-client-secret"
assert settings.adapter.cdek_retry_attempts == 2
assert settings.adapter.cdek_retry_backoff_seconds == 0.2
assert settings.adapter.cdek_timeout_seconds == 10.0
assert settings.adapter.cdek_cache_ttl_seconds == 900
assert settings.tbank_payment.init_url == "https://securepay.tinkoff.ru/v2/Init"
assert settings.tbank_payment.auth.terminal_key == "test-terminal-key"
assert settings.tbank_payment.auth.password == "test-password"
assert settings.tbank_payment.timeout_seconds == 10.0
assert settings.tbank_payment.retry_attempts == 2
assert settings.tbank_payment.retry_backoff_seconds == 0.2
assert (
settings.postgres.dsn
== "postgresql+asyncpg://postgres:postgres@localhost:5432/g2s_aggregator_test"
)
assert settings.address_suggestions.country_to_provider == _expected_country_mapping()
assert (
settings.address_suggestions.dadata.url
== "https://suggestions.dadata.ru/suggestions/api/4_1/rs/suggest/address"
)
assert settings.address_suggestions.dadata.api_key == "test-dadata-api-key"
assert settings.address_suggestions.dadata.timeout_seconds == 7.5
assert (
settings.address_suggestions.yandex_geosuggest.url
== "https://suggest-maps.yandex.ru/v1/suggest"
)
assert (
settings.address_suggestions.yandex_geosuggest.api_key
== "test-yandex-geosuggest-api-key"
)
assert settings.address_suggestions.yandex_geosuggest.timeout_seconds == 6.5
assert settings.address_suggestions.tomtom.url == "https://api.tomtom.com/search/2/search"
assert settings.address_suggestions.tomtom.api_key == "test-tomtom-api-key"
assert settings.address_suggestions.tomtom.timeout_seconds == 5.5
assert settings.observability.enabled is False
assert settings.observability.service_name == "g2s-aggregator-test"
assert settings.observability.otlp_endpoint == "http://localhost:4317"
assert settings.observability.otlp_insecure is True
def test_get_settings_fails_when_required_yaml_section_is_missing(
monkeypatch: pytest.MonkeyPatch,
) -> None:
_use_runtime_config_files(
monkeypatch,
test_config_file=MISSING_REQUIRED_SECTION_CONFIG_FILE,
)
with pytest.raises(ValidationError) as error:
get_settings()
locations = {tuple(item["loc"]) for item in error.value.errors()}
assert ("adapter",) in locations
get_settings.cache_clear()
def test_get_settings_fails_when_address_suggestions_section_is_missing(
monkeypatch: pytest.MonkeyPatch,
) -> None:
_use_runtime_config_files(
monkeypatch,
test_config_file=MISSING_ADDRESS_SUGGESTIONS_CONFIG_FILE,
)
with pytest.raises(ValidationError) as error:
get_settings()
locations = {tuple(item["loc"]) for item in error.value.errors()}
assert ("address_suggestions",) in locations
get_settings.cache_clear()
def test_get_settings_returns_cached_instance(monkeypatch: pytest.MonkeyPatch) -> None:
_use_runtime_config_files(monkeypatch, test_config_file=TEST_CONFIG_FILE)
first = get_settings()
second = get_settings()
assert first is second
assert first.service.provider_timeout_seconds == 10.0
assert first.business_logic.provider_price_multiplier == Decimal("1.0")
assert first.tbank_payment.auth.terminal_key == "test-terminal-key"
assert first.postgres.dsn.endswith("/g2s_aggregator_test")
assert first.address_suggestions.country_to_provider["RU"] == "dadata"
assert first.observability.service_name == "g2s-aggregator-test"
get_settings.cache_clear()
def test_get_settings_uses_config_test_yaml_in_pytest_environment(
monkeypatch: pytest.MonkeyPatch,
) -> None:
_use_runtime_config_files(
monkeypatch,
test_config_file=TEST_OVERRIDE_CONFIG_FILE_FIXTURE,
default_config_file=DEFAULT_CONFIG_FILE_FIXTURE,
)
settings = get_settings()
assert settings.service.provider_timeout_seconds == 17.0
assert settings.controller.api_prefix == "/from-config-test-yaml"
assert settings.business_logic.provider_price_multiplier == Decimal("1.25")
assert settings.adapter.cdek_client_id == "test-id"
assert settings.tbank_payment.auth.terminal_key == "override-terminal-key"
assert settings.tbank_payment.auth.password == "override-password"
assert settings.tbank_payment.timeout_seconds == 4.25
assert settings.tbank_payment.retry_attempts == 1
assert settings.tbank_payment.retry_backoff_seconds == 0.05
assert (
settings.postgres.dsn
== "postgresql+asyncpg://postgres:postgres@localhost:5432/override"
)
assert settings.address_suggestions.country_to_provider == {
"RU": "dadata",
"AM": "yandex_geosuggest",
"DE": "tomtom",
}
assert settings.address_suggestions.dadata.api_key == "override-dadata-key"
assert settings.address_suggestions.dadata.timeout_seconds == 3.0
assert settings.address_suggestions.yandex_geosuggest.api_key == "override-yandex-key"
assert settings.address_suggestions.yandex_geosuggest.timeout_seconds == 4.5
assert settings.address_suggestions.tomtom.api_key == "override-tomtom-key"
assert settings.address_suggestions.tomtom.timeout_seconds == 5.25
assert settings.observability.enabled is True
assert settings.observability.service_name == "override-config-service"
assert settings.observability.otlp_endpoint == "http://override-collector:4317"
assert settings.observability.otlp_insecure is False
get_settings.cache_clear()
def test_get_settings_fails_when_provider_price_multiplier_is_invalid(
monkeypatch: pytest.MonkeyPatch,
) -> None:
_use_runtime_config_files(
monkeypatch,
test_config_file=INVALID_PRICE_MULTIPLIER_CONFIG_FILE,
)
with pytest.raises(ValidationError) as error:
get_settings()
locations = {tuple(item["loc"]) for item in error.value.errors()}
assert ("business_logic", "provider_price_multiplier") in locations
get_settings.cache_clear()
def test_get_settings_fails_when_provider_price_multiplier_is_missing(
monkeypatch: pytest.MonkeyPatch,
) -> None:
_use_runtime_config_files(
monkeypatch,
test_config_file=MISSING_PRICE_MULTIPLIER_CONFIG_FILE,
)
with pytest.raises(ValidationError) as error:
get_settings()
locations = {tuple(item["loc"]) for item in error.value.errors()}
assert ("business_logic", "provider_price_multiplier") in locations
get_settings.cache_clear()
def test_get_settings_fails_when_observability_section_is_missing(
monkeypatch: pytest.MonkeyPatch,
) -> None:
_use_runtime_config_files(
monkeypatch,
test_config_file=MISSING_OBSERVABILITY_CONFIG_FILE,
)
with pytest.raises(ValidationError) as error:
get_settings()
locations = {tuple(item["loc"]) for item in error.value.errors()}
assert ("observability",) in locations
get_settings.cache_clear()
def test_get_settings_fails_when_observability_endpoint_is_missing(
monkeypatch: pytest.MonkeyPatch,
) -> None:
_use_runtime_config_files(
monkeypatch,
test_config_file=MISSING_OBSERVABILITY_ENDPOINT_CONFIG_FILE,
)
with pytest.raises(ValidationError) as error:
get_settings()
locations = {tuple(item["loc"]) for item in error.value.errors()}
assert ("observability", "otlp_endpoint") in locations
get_settings.cache_clear()