From d870915dc0ed253474d8b86c2f990b170afcc363 Mon Sep 17 00:00:00 2001 From: 3252a8 <3252a8@proton.me> Date: Mon, 18 May 2026 21:40:45 +0300 Subject: [PATCH] fix: apply persisted provider overrides on startup --- backend/bot/payment_providers/registry.py | 12 +++++++++++- backend/bot/services/settings_override_service.py | 12 +++++++++++- tests/test_payment_provider_registry.py | 2 +- tests/test_security.py | 2 +- tests/test_settings.py | 2 +- tests/test_webapp_assets.py | 4 ++-- 6 files changed, 27 insertions(+), 7 deletions(-) diff --git a/backend/bot/payment_providers/registry.py b/backend/bot/payment_providers/registry.py index 7251248..13b9fc2 100644 --- a/backend/bot/payment_providers/registry.py +++ b/backend/bot/payment_providers/registry.py @@ -48,14 +48,24 @@ def get_provider_spec(method: str) -> Optional[PaymentProviderSpec]: return None -def build_provider_configs() -> Dict[str, ProviderConfigBundle]: +def build_provider_configs(*, force: bool = False) -> Dict[str, ProviderConfigBundle]: """Instantiate per-provider BaseSettings models declared on each SPEC. Returns a mapping ``service_key`` → ``ProviderConfigBundle(config, presentation)``. For SPECs that share a service (Platega SBP + Platega Crypto), only the first one's presentation lands in the shared bundle — per-SPEC presentation overrides live separately in ``_provider_presentations`` keyed by ``spec.id``. + + Idempotent by default: if bundles already exist in the process-wide cache + we return them as-is. Pass ``force=True`` to rebuild from env (used by + tests after monkeypatching env vars). The non-force path is critical for + runtime: ``load_overrides_from_db`` builds bundles first and then writes + DB-persisted overrides into them — a later non-force rebuild from + ``build_core_services`` would otherwise wipe those overrides. """ + if _provider_configs and not force: + return dict(_provider_configs) + from .base import provider_env_file env_file = provider_env_file() diff --git a/backend/bot/services/settings_override_service.py b/backend/bot/services/settings_override_service.py index 96089b4..dc890b5 100644 --- a/backend/bot/services/settings_override_service.py +++ b/backend/bot/services/settings_override_service.py @@ -196,7 +196,17 @@ def write_appearance_backup(settings: Settings) -> None: async def load_overrides_from_db(settings: Settings, async_session_factory: sessionmaker) -> int: - """Fetch overrides from the DB and apply them to the in-memory settings.""" + """Fetch overrides from the DB and apply them to the in-memory settings. + + Provider env-configs live on per-provider BaseSettings bundles instead of + the central Settings model. Apply needs those bundles to already exist, + otherwise provider-owned overrides (e.g. ``HELEKET_ENABLED``) silently + drop on the floor. Build them up-front; the call is idempotent so the + later ``build_core_services`` invocation reuses these same instances. + """ + from bot.payment_providers import build_provider_configs + + build_provider_configs() try: async with async_session_factory() as session: diff --git a/tests/test_payment_provider_registry.py b/tests/test_payment_provider_registry.py index 6991a5f..1c90e99 100644 --- a/tests/test_payment_provider_registry.py +++ b/tests/test_payment_provider_registry.py @@ -204,7 +204,7 @@ def test_payment_method_keyboard_uses_custom_telegram_text_without_changing_call monkeypatch.setenv("WATA_ENABLED", "True") monkeypatch.setenv("PAYMENT_WATA_TELEGRAM_LABEL_EN", "Wata custom") monkeypatch.setenv("PAYMENT_WATA_TELEGRAM_EMOJI", "💸") - build_provider_configs() + build_provider_configs(force=True) settings = Settings( _env_file=None, diff --git a/tests/test_security.py b/tests/test_security.py index 2ee4e49..0bb6985 100644 --- a/tests/test_security.py +++ b/tests/test_security.py @@ -398,7 +398,7 @@ class AdminSettingsSecurityTests(unittest.IsolatedAsyncioTestCase): get_provider_bundle, ) - build_provider_configs() + build_provider_configs(force=True) bundle = get_provider_bundle("yookassa_service") if bundle and bundle.config is not None: bundle.config.SECRET_KEY = "super-secret" diff --git a/tests/test_settings.py b/tests/test_settings.py index cdcd2b3..b5bf73b 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -166,7 +166,7 @@ class SettingsTests(unittest.TestCase): os.environ["PAYMENT_YOOKASSA_TELEGRAM_LABEL_EN"] = "Bank card" os.environ["PAYMENT_YOOKASSA_TELEGRAM_EMOJI"] = "💳" try: - build_provider_configs() + build_provider_configs(force=True) presentation = get_spec_presentation("yookassa") self.assertIsNotNone(presentation) self.assertEqual(presentation.WEBAPP_LABEL_RU, "Карта") diff --git a/tests/test_webapp_assets.py b/tests/test_webapp_assets.py index d37a77d..8f1e86a 100644 --- a/tests/test_webapp_assets.py +++ b/tests/test_webapp_assets.py @@ -340,7 +340,7 @@ class WebAppAssetTests(unittest.IsolatedAsyncioTestCase): current_provider_configs, ) - build_provider_configs() + build_provider_configs(force=True) configs = current_provider_configs() for service_key in ("cryptopay_service", "yookassa_service"): bundle = configs.get(service_key) @@ -377,7 +377,7 @@ class WebAppAssetTests(unittest.IsolatedAsyncioTestCase): get_spec_presentation, ) - build_provider_configs() + build_provider_configs(force=True) bundle = get_provider_bundle("yookassa_service") if bundle and bundle.config is not None: bundle.config.ENABLED = True