fix(payments): apply request timeout changes without restart
PAYMENT_REQUEST_TIMEOUT_SECONDS was read once in each provider's __init__ and baked into the aiohttp session, so admin overrides (applied in-process) only took effect after a container restart. Providers now hand HttpClientMixin a timeout source callable; the mixin builds the session with the current value and swaps in a fresh session when the value changes, closing the replaced one only after any in-flight request on it is bound by its own total timeout. Also: - check the Heleket payment-info success flag before reading the payload so a non-dict provider response cannot raise in the pending-payment reuse path - add PAYMENT_REQUEST_TIMEOUT_SECONDS to the FreeKassa settings stub in test_security.py (fixes three tests broken by the new field)
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import unittest
|
||||
from types import SimpleNamespace
|
||||
|
||||
from bot.payment_providers.shared.http_client import (
|
||||
HttpClientMixin,
|
||||
@@ -7,8 +8,8 @@ from bot.payment_providers.shared.http_client import (
|
||||
|
||||
|
||||
class _DummyHttpClient(HttpClientMixin):
|
||||
def __init__(self):
|
||||
self._init_http_client(total_timeout=20)
|
||||
def __init__(self, total_timeout=20):
|
||||
self._init_http_client(total_timeout=total_timeout)
|
||||
|
||||
|
||||
class PaymentHttpClientTests(unittest.IsolatedAsyncioTestCase):
|
||||
@@ -22,9 +23,33 @@ class PaymentHttpClientTests(unittest.IsolatedAsyncioTestCase):
|
||||
await client.close()
|
||||
|
||||
async def test_http_client_retries_only_before_headers_are_sent(self):
|
||||
self.assertTrue(
|
||||
_should_retry_transport_error(TimeoutError(), {"headers_sent": False})
|
||||
)
|
||||
self.assertFalse(
|
||||
_should_retry_transport_error(TimeoutError(), {"headers_sent": True})
|
||||
)
|
||||
self.assertTrue(_should_retry_transport_error(TimeoutError(), {"headers_sent": False}))
|
||||
self.assertFalse(_should_retry_transport_error(TimeoutError(), {"headers_sent": True}))
|
||||
|
||||
async def test_http_client_applies_runtime_timeout_changes(self):
|
||||
settings = SimpleNamespace(PAYMENT_REQUEST_TIMEOUT_SECONDS=20)
|
||||
client = _DummyHttpClient(total_timeout=lambda: settings.PAYMENT_REQUEST_TIMEOUT_SECONDS)
|
||||
try:
|
||||
first = await client._get_session()
|
||||
self.assertEqual(first.timeout.total, 20)
|
||||
self.assertIs(await client._get_session(), first)
|
||||
|
||||
settings.PAYMENT_REQUEST_TIMEOUT_SECONDS = 5
|
||||
second = await client._get_session()
|
||||
self.assertIsNot(second, first)
|
||||
self.assertEqual(second.timeout.total, 5)
|
||||
# The replaced session must stay usable for in-flight requests;
|
||||
# it is closed later, and close() always sweeps it up.
|
||||
self.assertFalse(first.closed)
|
||||
finally:
|
||||
await client.close()
|
||||
self.assertTrue(first.closed)
|
||||
self.assertTrue(second.closed)
|
||||
|
||||
async def test_http_client_falls_back_to_default_timeout_on_bad_source(self):
|
||||
client = _DummyHttpClient(total_timeout=lambda: None)
|
||||
try:
|
||||
session = await client._get_session()
|
||||
self.assertEqual(session.timeout.total, 20.0)
|
||||
finally:
|
||||
await client.close()
|
||||
|
||||
@@ -181,6 +181,7 @@ class FreeKassaServiceTests(unittest.TestCase):
|
||||
|
||||
settings = SimpleNamespace(
|
||||
DEFAULT_CURRENCY_SYMBOL="RUB",
|
||||
PAYMENT_REQUEST_TIMEOUT_SECONDS=15,
|
||||
trusted_proxies=["127.0.0.1"],
|
||||
)
|
||||
config = FreeKassaConfig(
|
||||
|
||||
Reference in New Issue
Block a user