feat: show optional serivce description before payment
This commit is contained in:
@@ -16,6 +16,12 @@ SUPPORT_RELATED_SETTINGS = (
|
||||
"SUPPORT_TICKET_RATE_LIMIT_PER_HOUR",
|
||||
)
|
||||
|
||||
SUBSCRIPTION_PURCHASE_DESCRIPTION_SETTINGS = (
|
||||
"SUBSCRIPTION_PURCHASE_DESCRIPTION_ENABLED",
|
||||
"SUBSCRIPTION_PURCHASE_DESCRIPTION_RU",
|
||||
"SUBSCRIPTION_PURCHASE_DESCRIPTION_EN",
|
||||
)
|
||||
|
||||
|
||||
def _manifest_by_key() -> dict[str, dict]:
|
||||
return {item["key"]: item for item in manifest_payload()}
|
||||
@@ -50,3 +56,15 @@ def test_support_settings_i18n_keys_exist_in_admin_locales():
|
||||
field = manifest[setting_key]
|
||||
assert field["i18n_label_key"] in messages
|
||||
assert field["i18n_description_key"] in messages
|
||||
|
||||
|
||||
def test_subscription_purchase_description_settings_i18n_keys_exist():
|
||||
manifest = _manifest_by_key()
|
||||
|
||||
for language in ("ru", "en"):
|
||||
messages = _locale(language)
|
||||
for setting_key in SUBSCRIPTION_PURCHASE_DESCRIPTION_SETTINGS:
|
||||
field = manifest[setting_key]
|
||||
assert field["section"] == "pricing"
|
||||
assert field["i18n_label_key"] in messages
|
||||
assert field["i18n_description_key"] in messages
|
||||
|
||||
@@ -51,8 +51,13 @@ class ConfigureLoggingTests(unittest.TestCase):
|
||||
# Confirm the format string structure by emitting a record into a buffer.
|
||||
formatter = handler.formatter
|
||||
record = logging.LogRecord(
|
||||
name="test", level=logging.INFO, pathname=__file__, lineno=1,
|
||||
msg="hello", args=(), exc_info=None,
|
||||
name="test",
|
||||
level=logging.INFO,
|
||||
pathname=__file__,
|
||||
lineno=1,
|
||||
msg="hello",
|
||||
args=(),
|
||||
exc_info=None,
|
||||
)
|
||||
rendered = formatter.format(record) if formatter else ""
|
||||
# Format is "%(asctime)s - %(name)s - %(levelname)s - %(message)s".
|
||||
|
||||
@@ -155,15 +155,11 @@ class LiveGitFallbackTests(unittest.TestCase):
|
||||
return _resolve()
|
||||
|
||||
def test_tag_with_zero_commits_since_returns_bare_tag(self):
|
||||
result = self._run_with_git(
|
||||
{"tag": "v2.0.0", "sha": "abcdef1", "commits_since_tag": "0"}
|
||||
)
|
||||
result = self._run_with_git({"tag": "v2.0.0", "sha": "abcdef1", "commits_since_tag": "0"})
|
||||
self.assertEqual(result, "v2.0.0")
|
||||
|
||||
def test_tag_plus_distance_plus_sha_format(self):
|
||||
result = self._run_with_git(
|
||||
{"tag": "v2.0.0", "sha": "abcdef1", "commits_since_tag": "7"}
|
||||
)
|
||||
result = self._run_with_git({"tag": "v2.0.0", "sha": "abcdef1", "commits_since_tag": "7"})
|
||||
self.assertEqual(result, "v2.0.0+7.gabcdef1")
|
||||
|
||||
def test_sha_only_when_no_tag(self):
|
||||
|
||||
@@ -33,9 +33,7 @@ class _FakeYooKassaService:
|
||||
def __init__(self, configured: bool = True, response: Optional[Dict[str, Any]] = None) -> None:
|
||||
self.configured = configured
|
||||
self.calls: List[Dict[str, Any]] = []
|
||||
self._response = response if response is not None else {
|
||||
"id": "pay-1", "status": "pending"
|
||||
}
|
||||
self._response = response if response is not None else {"id": "pay-1", "status": "pending"}
|
||||
|
||||
async def create_payment(self, **kwargs):
|
||||
self.calls.append(kwargs)
|
||||
|
||||
@@ -29,16 +29,22 @@ from config.settings import Settings
|
||||
# Strip all provider env so per-provider BaseSettings models don't pick up
|
||||
# real credentials from the local .env file during tests.
|
||||
_PROVIDER_ENV_PREFIXES = (
|
||||
"FREEKASSA_", "PLATEGA_", "SEVERPAY_", "WATA_", "HELEKET_",
|
||||
"CRYPTOPAY_", "YOOKASSA_", "STARS_",
|
||||
"FREEKASSA_",
|
||||
"PLATEGA_",
|
||||
"SEVERPAY_",
|
||||
"WATA_",
|
||||
"HELEKET_",
|
||||
"CRYPTOPAY_",
|
||||
"YOOKASSA_",
|
||||
"STARS_",
|
||||
)
|
||||
|
||||
|
||||
def _clean_env() -> dict[str, str]:
|
||||
return {
|
||||
k: v for k, v in os.environ.items()
|
||||
if not any(k.startswith(p) for p in _PROVIDER_ENV_PREFIXES)
|
||||
and not k.startswith("PAYMENT_")
|
||||
k: v
|
||||
for k, v in os.environ.items()
|
||||
if not any(k.startswith(p) for p in _PROVIDER_ENV_PREFIXES) and not k.startswith("PAYMENT_")
|
||||
}
|
||||
|
||||
|
||||
@@ -116,9 +122,7 @@ class BuildServicesWiringTests(unittest.TestCase):
|
||||
panel_webhook = services["panel_webhook_service"]
|
||||
subscription = services["subscription_service"]
|
||||
self.assertIsInstance(panel_webhook, PanelWebhookService)
|
||||
self.assertIs(
|
||||
getattr(panel_webhook, "subscription_service", None), subscription
|
||||
)
|
||||
self.assertIs(getattr(panel_webhook, "subscription_service", None), subscription)
|
||||
|
||||
def test_factory_returns_every_documented_service(self):
|
||||
"""Guards against silently dropping a service from the bundle. The
|
||||
|
||||
@@ -59,18 +59,14 @@ class MigrationDocumentationFactsTests(unittest.TestCase):
|
||||
def test_doc_lists_every_running_container_in_current_compose(self):
|
||||
"""The architecture table must reflect what ``docker compose up``
|
||||
actually produces today."""
|
||||
missing = sorted(
|
||||
name for name in EXPECTED_CONTAINER_NAMES if name not in self.doc
|
||||
)
|
||||
missing = sorted(name for name in EXPECTED_CONTAINER_NAMES if name not in self.doc)
|
||||
self.assertFalse(
|
||||
missing,
|
||||
f"migration-to-minishop.md is missing container names from current compose: {missing}",
|
||||
)
|
||||
|
||||
def test_doc_lists_every_volume_in_current_compose(self):
|
||||
missing = sorted(
|
||||
name for name in EXPECTED_VOLUME_NAMES if name not in self.doc
|
||||
)
|
||||
missing = sorted(name for name in EXPECTED_VOLUME_NAMES if name not in self.doc)
|
||||
self.assertFalse(
|
||||
missing,
|
||||
f"migration-to-minishop.md is missing volume names from current compose: {missing}",
|
||||
@@ -185,8 +181,7 @@ class MigrationScriptCoverageTests(unittest.TestCase):
|
||||
missing = sorted(EXPECTED_CONTAINER_NAMES - self.known)
|
||||
self.assertFalse(
|
||||
missing,
|
||||
f"KNOWN_CONTAINERS missing split-arch entries: {missing}\n"
|
||||
f"actual: {sorted(self.known)}",
|
||||
f"KNOWN_CONTAINERS missing split-arch entries: {missing}\nactual: {sorted(self.known)}",
|
||||
)
|
||||
|
||||
def test_known_containers_still_covers_legacy_eras(self):
|
||||
|
||||
@@ -86,9 +86,7 @@ class HandleWebhookQueueingTests(unittest.IsolatedAsyncioTestCase):
|
||||
captured: List[dict] = []
|
||||
|
||||
async def fake_enqueue(settings, provider, payload, *, event_id=None):
|
||||
captured.append(
|
||||
{"provider": provider, "payload": payload, "event_id": event_id}
|
||||
)
|
||||
captured.append({"provider": provider, "payload": payload, "event_id": event_id})
|
||||
return True
|
||||
|
||||
body = json.dumps(
|
||||
@@ -121,9 +119,7 @@ class HandleWebhookQueueingTests(unittest.IsolatedAsyncioTestCase):
|
||||
async def fake_handle_event(event_name, user_payload):
|
||||
background_seen.append((event_name, user_payload))
|
||||
|
||||
body = json.dumps(
|
||||
{"name": "user.expired", "payload": {"telegramId": 7}}
|
||||
).encode()
|
||||
body = json.dumps({"name": "user.expired", "payload": {"telegramId": 7}}).encode()
|
||||
|
||||
with (
|
||||
patch.object(pws, "enqueue_webhook_event", fake_enqueue),
|
||||
|
||||
@@ -97,11 +97,7 @@ def test_yookassa_provider_keeps_autorenew_entrypoints_local():
|
||||
|
||||
|
||||
def test_every_payment_method_has_registry_driven_webapp_creator():
|
||||
missing = [
|
||||
spec.id
|
||||
for spec in iter_provider_specs()
|
||||
if spec.create_webapp_payment is None
|
||||
]
|
||||
missing = [spec.id for spec in iter_provider_specs() if spec.create_webapp_payment is None]
|
||||
|
||||
assert missing == []
|
||||
|
||||
@@ -192,10 +188,7 @@ def test_provider_presentation_ignores_cross_language_override():
|
||||
|
||||
settings = SimpleNamespace(PAYMENT_YOOKASSA_WEBAPP_LABEL_RU="Карта")
|
||||
|
||||
assert (
|
||||
resolve_provider_presentation(spec, settings, language="en").webapp_label
|
||||
== "Bank card"
|
||||
)
|
||||
assert resolve_provider_presentation(spec, settings, language="en").webapp_label == "Bank card"
|
||||
|
||||
|
||||
def test_payment_method_keyboard_uses_custom_telegram_text_without_changing_callback(monkeypatch):
|
||||
@@ -255,12 +248,15 @@ def test_provider_callbacks_are_built_from_specs():
|
||||
)
|
||||
== "pay_stars:1:42:subscription"
|
||||
)
|
||||
assert stars.callback_data(
|
||||
value="1",
|
||||
rub_price=150,
|
||||
stars_price=None,
|
||||
sale_mode="subscription",
|
||||
) is None
|
||||
assert (
|
||||
stars.callback_data(
|
||||
value="1",
|
||||
rub_price=150,
|
||||
stars_price=None,
|
||||
sale_mode="subscription",
|
||||
)
|
||||
is None
|
||||
)
|
||||
|
||||
|
||||
def test_provider_visibility_uses_service_configuration():
|
||||
|
||||
@@ -87,9 +87,7 @@ class SendPaymentSuccessEmailTests(unittest.IsolatedAsyncioTestCase):
|
||||
DEFAULT_CURRENCY_SYMBOL="RUB",
|
||||
SUBSCRIPTION_MINI_APP_URL="https://app.example.com/",
|
||||
)
|
||||
user = _FakeUser(
|
||||
user_id=42, email="buyer@example.com", language_code="en"
|
||||
)
|
||||
user = _FakeUser(user_id=42, email="buyer@example.com", language_code="en")
|
||||
|
||||
with (
|
||||
patch.object(payments_module, "render_payment_success", fake_render),
|
||||
|
||||
@@ -61,9 +61,7 @@ class SkipPathTests(unittest.IsolatedAsyncioTestCase):
|
||||
async def test_no_inviter_returns_empty_payload(self):
|
||||
settings = _make_settings()
|
||||
subscription_service = AsyncMock()
|
||||
service, _bot = _make_service(
|
||||
settings=settings, subscription_service=subscription_service
|
||||
)
|
||||
service, _bot = _make_service(settings=settings, subscription_service=subscription_service)
|
||||
# Referred_by_id is None → bail out immediately.
|
||||
with patch(
|
||||
"bot.services.referral_service.user_dal.get_user_by_id",
|
||||
@@ -85,16 +83,14 @@ class SkipPathTests(unittest.IsolatedAsyncioTestCase):
|
||||
# when the same referee buys multiple subscriptions.
|
||||
settings = _make_settings(REFERRAL_ONE_BONUS_PER_REFEREE=True)
|
||||
subscription_service = AsyncMock()
|
||||
service, _bot = _make_service(
|
||||
settings=settings, subscription_service=subscription_service
|
||||
)
|
||||
service, _bot = _make_service(settings=settings, subscription_service=subscription_service)
|
||||
with (
|
||||
patch(
|
||||
"bot.services.referral_service.user_dal.get_user_by_id",
|
||||
AsyncMock(
|
||||
side_effect=lambda session, uid: _make_user(uid, referred_by_id=1)
|
||||
if uid == 42
|
||||
else _make_user(uid)
|
||||
side_effect=lambda session, uid: (
|
||||
_make_user(uid, referred_by_id=1) if uid == 42 else _make_user(uid)
|
||||
)
|
||||
),
|
||||
),
|
||||
patch(
|
||||
@@ -118,15 +114,13 @@ class SkipPathTests(unittest.IsolatedAsyncioTestCase):
|
||||
settings = _make_settings(REFERRAL_ONE_BONUS_PER_REFEREE=False)
|
||||
subscription_service = AsyncMock()
|
||||
subscription_service.has_active_subscription = AsyncMock(return_value=True)
|
||||
service, _bot = _make_service(
|
||||
settings=settings, subscription_service=subscription_service
|
||||
)
|
||||
service, _bot = _make_service(settings=settings, subscription_service=subscription_service)
|
||||
with patch(
|
||||
"bot.services.referral_service.user_dal.get_user_by_id",
|
||||
AsyncMock(
|
||||
side_effect=lambda session, uid: _make_user(uid, referred_by_id=1)
|
||||
if uid == 42
|
||||
else _make_user(uid)
|
||||
side_effect=lambda session, uid: (
|
||||
_make_user(uid, referred_by_id=1) if uid == 42 else _make_user(uid)
|
||||
)
|
||||
),
|
||||
):
|
||||
result = await service.apply_referral_bonuses_for_payment(
|
||||
@@ -156,16 +150,14 @@ class InviterBonusTests(unittest.IsolatedAsyncioTestCase):
|
||||
)
|
||||
new_end = datetime(2026, 1, 1, tzinfo=timezone.utc)
|
||||
subscription_service.extend_active_subscription_days = AsyncMock(return_value=new_end)
|
||||
service, bot = _make_service(
|
||||
settings=settings, subscription_service=subscription_service
|
||||
)
|
||||
service, bot = _make_service(settings=settings, subscription_service=subscription_service)
|
||||
|
||||
with patch(
|
||||
"bot.services.referral_service.user_dal.get_user_by_id",
|
||||
AsyncMock(
|
||||
side_effect=lambda session, uid: _make_user(uid, referred_by_id=1)
|
||||
if uid == 42
|
||||
else _make_user(uid)
|
||||
side_effect=lambda session, uid: (
|
||||
_make_user(uid, referred_by_id=1) if uid == 42 else _make_user(uid)
|
||||
)
|
||||
),
|
||||
):
|
||||
result = await service.apply_referral_bonuses_for_payment(
|
||||
@@ -204,17 +196,15 @@ class InviterBonusTests(unittest.IsolatedAsyncioTestCase):
|
||||
return_value={"ok": True}
|
||||
)
|
||||
|
||||
service, bot = _make_service(
|
||||
settings=settings, subscription_service=subscription_service
|
||||
)
|
||||
service, bot = _make_service(settings=settings, subscription_service=subscription_service)
|
||||
|
||||
with (
|
||||
patch(
|
||||
"bot.services.referral_service.user_dal.get_user_by_id",
|
||||
AsyncMock(
|
||||
side_effect=lambda session, uid: _make_user(uid, referred_by_id=1)
|
||||
if uid == 42
|
||||
else _make_user(uid)
|
||||
side_effect=lambda session, uid: (
|
||||
_make_user(uid, referred_by_id=1) if uid == 42 else _make_user(uid)
|
||||
)
|
||||
),
|
||||
),
|
||||
patch(
|
||||
@@ -262,16 +252,14 @@ class RefereeBonusTests(unittest.IsolatedAsyncioTestCase):
|
||||
subscription_service.extend_active_subscription_days = AsyncMock(
|
||||
return_value=referee_new_end
|
||||
)
|
||||
service, _bot = _make_service(
|
||||
settings=settings, subscription_service=subscription_service
|
||||
)
|
||||
service, _bot = _make_service(settings=settings, subscription_service=subscription_service)
|
||||
|
||||
with patch(
|
||||
"bot.services.referral_service.user_dal.get_user_by_id",
|
||||
AsyncMock(
|
||||
side_effect=lambda session, uid: _make_user(uid, referred_by_id=1)
|
||||
if uid == 42
|
||||
else _make_user(uid)
|
||||
side_effect=lambda session, uid: (
|
||||
_make_user(uid, referred_by_id=1) if uid == 42 else _make_user(uid)
|
||||
)
|
||||
),
|
||||
):
|
||||
# 3-month plan → 10-day referee bonus, 21-day inviter bonus.
|
||||
@@ -303,16 +291,14 @@ class RefereeBonusTests(unittest.IsolatedAsyncioTestCase):
|
||||
subscription_service = AsyncMock()
|
||||
subscription_service.has_active_subscription = AsyncMock(return_value=False)
|
||||
subscription_service.extend_active_subscription_days = AsyncMock(return_value=None)
|
||||
service, _bot = _make_service(
|
||||
settings=settings, subscription_service=subscription_service
|
||||
)
|
||||
service, _bot = _make_service(settings=settings, subscription_service=subscription_service)
|
||||
|
||||
with patch(
|
||||
"bot.services.referral_service.user_dal.get_user_by_id",
|
||||
AsyncMock(
|
||||
side_effect=lambda session, uid: _make_user(uid, referred_by_id=1)
|
||||
if uid == 42
|
||||
else _make_user(uid)
|
||||
side_effect=lambda session, uid: (
|
||||
_make_user(uid, referred_by_id=1) if uid == 42 else _make_user(uid)
|
||||
)
|
||||
),
|
||||
):
|
||||
result = await service.apply_referral_bonuses_for_payment(
|
||||
@@ -331,16 +317,14 @@ class RefereeBonusTests(unittest.IsolatedAsyncioTestCase):
|
||||
subscription_service = AsyncMock()
|
||||
subscription_service.has_active_subscription = AsyncMock(return_value=False)
|
||||
subscription_service.extend_active_subscription_days = AsyncMock()
|
||||
service, _bot = _make_service(
|
||||
settings=settings, subscription_service=subscription_service
|
||||
)
|
||||
service, _bot = _make_service(settings=settings, subscription_service=subscription_service)
|
||||
|
||||
with patch(
|
||||
"bot.services.referral_service.user_dal.get_user_by_id",
|
||||
AsyncMock(
|
||||
side_effect=lambda session, uid: _make_user(uid, referred_by_id=1)
|
||||
if uid == 42
|
||||
else _make_user(uid)
|
||||
side_effect=lambda session, uid: (
|
||||
_make_user(uid, referred_by_id=1) if uid == 42 else _make_user(uid)
|
||||
)
|
||||
),
|
||||
):
|
||||
result = await service.apply_referral_bonuses_for_payment(
|
||||
@@ -359,9 +343,7 @@ class GenerateReferralLinkTests(unittest.IsolatedAsyncioTestCase):
|
||||
async def test_includes_bot_username_and_referral_code(self):
|
||||
settings = _make_settings()
|
||||
subscription_service = AsyncMock()
|
||||
service, _bot = _make_service(
|
||||
settings=settings, subscription_service=subscription_service
|
||||
)
|
||||
service, _bot = _make_service(settings=settings, subscription_service=subscription_service)
|
||||
|
||||
with (
|
||||
patch(
|
||||
@@ -383,9 +365,7 @@ class GenerateReferralLinkTests(unittest.IsolatedAsyncioTestCase):
|
||||
async def test_returns_none_when_user_missing(self):
|
||||
settings = _make_settings()
|
||||
subscription_service = AsyncMock()
|
||||
service, _bot = _make_service(
|
||||
settings=settings, subscription_service=subscription_service
|
||||
)
|
||||
service, _bot = _make_service(settings=settings, subscription_service=subscription_service)
|
||||
|
||||
with patch(
|
||||
"bot.services.referral_service.user_dal.get_user_by_id",
|
||||
@@ -401,9 +381,7 @@ class GenerateReferralLinkTests(unittest.IsolatedAsyncioTestCase):
|
||||
async def test_returns_none_when_referral_code_unavailable(self):
|
||||
settings = _make_settings()
|
||||
subscription_service = AsyncMock()
|
||||
service, _bot = _make_service(
|
||||
settings=settings, subscription_service=subscription_service
|
||||
)
|
||||
service, _bot = _make_service(settings=settings, subscription_service=subscription_service)
|
||||
|
||||
with (
|
||||
patch(
|
||||
|
||||
@@ -162,6 +162,22 @@ class SettingsTests(unittest.TestCase):
|
||||
|
||||
self.assertFalse(settings.SUPPORT_ADMIN_EMAIL_NOTIFICATIONS_ENABLED)
|
||||
|
||||
def test_subscription_purchase_description_is_localized_and_toggleable(self):
|
||||
settings = Settings(
|
||||
_env_file=None,
|
||||
BOT_TOKEN="token",
|
||||
POSTGRES_USER="app_user",
|
||||
POSTGRES_PASSWORD="app_password",
|
||||
SUBSCRIPTION_PURCHASE_DESCRIPTION_RU="Русский текст",
|
||||
SUBSCRIPTION_PURCHASE_DESCRIPTION_EN="English text",
|
||||
)
|
||||
|
||||
self.assertEqual(settings.subscription_purchase_description("ru"), "Русский текст")
|
||||
self.assertEqual(settings.subscription_purchase_description("en"), "English text")
|
||||
|
||||
settings.SUBSCRIPTION_PURCHASE_DESCRIPTION_ENABLED = False
|
||||
self.assertEqual(settings.subscription_purchase_description("ru"), "")
|
||||
|
||||
def test_payment_button_presentation_env_values_are_available(self):
|
||||
"""Presentation overrides now live on each provider's BaseSettings
|
||||
model instead of the central Settings — verify they're loaded from
|
||||
|
||||
@@ -5,6 +5,7 @@ from types import SimpleNamespace
|
||||
|
||||
from bot.app.web import subscription_webapp
|
||||
from bot.handlers.user import referral
|
||||
from bot.handlers.user.subscription.core import _with_subscription_purchase_description
|
||||
from bot.keyboards.inline.user_keyboards import (
|
||||
get_bot_interface_inline_keyboard,
|
||||
get_information_links_keyboard,
|
||||
@@ -101,6 +102,30 @@ class UserBotMenuTests(unittest.TestCase):
|
||||
self.assertIn("set_lang_ru:bot", self._callback_data(language_markup))
|
||||
self.assertIn("subscribe_period:1:bot", self._callback_data(subscription_markup))
|
||||
|
||||
def test_subscription_purchase_description_is_prepended_before_period_selection(self):
|
||||
settings = SimpleNamespace(
|
||||
subscription_purchase_description=lambda language: f"Description {language}"
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
_with_subscription_purchase_description(
|
||||
"Choose period",
|
||||
settings,
|
||||
"en",
|
||||
include=True,
|
||||
),
|
||||
"Description en\n\nChoose period",
|
||||
)
|
||||
self.assertEqual(
|
||||
_with_subscription_purchase_description(
|
||||
"Choose traffic",
|
||||
settings,
|
||||
"en",
|
||||
include=False,
|
||||
),
|
||||
"Choose traffic",
|
||||
)
|
||||
|
||||
def test_payment_navigation_context_keeps_bot_menu_source(self):
|
||||
settings = SimpleNamespace(
|
||||
payment_methods_order=[],
|
||||
|
||||
@@ -58,9 +58,7 @@ class FakeRedis:
|
||||
bucket.insert(0, value)
|
||||
return len(bucket)
|
||||
|
||||
async def brpop(
|
||||
self, key: str, timeout: int = 0
|
||||
) -> Optional[Tuple[str, str]]:
|
||||
async def brpop(self, key: str, timeout: int = 0) -> Optional[Tuple[str, str]]:
|
||||
bucket = self._lists.get(key)
|
||||
if bucket:
|
||||
return key, bucket.pop()
|
||||
@@ -230,9 +228,7 @@ class RedisLockTests(unittest.IsolatedAsyncioTestCase):
|
||||
settings = _make_settings()
|
||||
async with redis_infra.redis_lock(settings, "panel-sync", ttl_seconds=30) as first:
|
||||
self.assertTrue(first)
|
||||
async with redis_infra.redis_lock(
|
||||
settings, "panel-sync", ttl_seconds=30
|
||||
) as second:
|
||||
async with redis_infra.redis_lock(settings, "panel-sync", ttl_seconds=30) as second:
|
||||
self.assertFalse(second)
|
||||
# After exit, the lock is released and can be re-acquired.
|
||||
async with redis_infra.redis_lock(settings, "panel-sync", ttl_seconds=30) as again:
|
||||
|
||||
Reference in New Issue
Block a user