feat: add telegram guardrails for trials and referrals
This commit is contained in:
@@ -55,6 +55,7 @@ ADMIN_TARIFF_SETTINGS_PAGE_KEYS = {
|
||||
"admin_tariffs_trial_title",
|
||||
"admin_tariffs_trial_subtitle",
|
||||
"admin_tariffs_trial_enabled",
|
||||
"admin_tariffs_trial_without_telegram_enabled",
|
||||
"admin_tariffs_trial_days",
|
||||
"admin_tariffs_trial_traffic",
|
||||
"admin_tariffs_trial_strategy",
|
||||
@@ -69,6 +70,18 @@ ADMIN_TARIFF_SETTINGS_PAGE_KEYS = {
|
||||
"admin_tariffs_trial_group_reset_hint",
|
||||
"admin_tariffs_trial_group_squads",
|
||||
"admin_tariffs_trial_group_squads_hint",
|
||||
"admin_tariffs_referral_title",
|
||||
"admin_tariffs_referral_subtitle",
|
||||
"admin_tariffs_referral_group_welcome",
|
||||
"admin_tariffs_referral_group_welcome_hint",
|
||||
"admin_tariffs_referral_welcome_bonus_days",
|
||||
"admin_tariffs_referral_without_telegram",
|
||||
"admin_tariffs_referral_group_rules",
|
||||
"admin_tariffs_referral_group_rules_hint",
|
||||
"admin_tariffs_referral_one_bonus_per_referee",
|
||||
"admin_tariffs_referral_legacy_refs",
|
||||
"admin_tariffs_referral_disposable_domains",
|
||||
"admin_tariffs_referral_disposable_domains_hint",
|
||||
"admin_tariffs_legacy_title",
|
||||
"admin_tariffs_legacy_subtitle",
|
||||
"admin_tariffs_legacy_period",
|
||||
@@ -243,6 +256,7 @@ def test_trial_required_settings_reject_empty_values():
|
||||
"TRIAL_DURATION_DAYS",
|
||||
"TRIAL_TRAFFIC_LIMIT_GB",
|
||||
"TRIAL_TRAFFIC_STRATEGY",
|
||||
"TRIAL_WITHOUT_TELEGRAM_ENABLED",
|
||||
):
|
||||
with pytest.raises(ValueError):
|
||||
coerce_value(get_field_by_key(key), "")
|
||||
@@ -321,8 +335,20 @@ def test_legacy_tariff_settings_are_separated_from_payment_settings():
|
||||
assert manifest["MONTH_1_ENABLED"]["section_order"] == 11
|
||||
assert manifest["TRIAL_ENABLED"]["section"] == "pricing"
|
||||
assert manifest["TRIAL_ENABLED"]["subsection"] == "trial"
|
||||
assert manifest["TRIAL_WITHOUT_TELEGRAM_ENABLED"]["section"] == "pricing"
|
||||
assert manifest["TRIAL_WITHOUT_TELEGRAM_ENABLED"]["subsection"] == "trial"
|
||||
assert manifest["TRIAL_SQUAD_UUIDS"]["section"] == "pricing"
|
||||
assert manifest["TRIAL_SQUAD_UUIDS"]["subsection"] == "trial"
|
||||
assert manifest["REFERRAL_WELCOME_BONUS_DAYS"]["section"] == "pricing"
|
||||
assert manifest["REFERRAL_WELCOME_BONUS_DAYS"]["subsection"] == "referral"
|
||||
assert manifest["REFERRAL_WELCOME_BONUS_WITHOUT_TELEGRAM_ENABLED"]["section"] == "pricing"
|
||||
assert manifest["REFERRAL_WELCOME_BONUS_WITHOUT_TELEGRAM_ENABLED"]["subsection"] == "referral"
|
||||
assert manifest["REFERRAL_ONE_BONUS_PER_REFEREE"]["section"] == "pricing"
|
||||
assert manifest["REFERRAL_ONE_BONUS_PER_REFEREE"]["subsection"] == "referral"
|
||||
assert manifest["LEGACY_REFS"]["section"] == "pricing"
|
||||
assert manifest["LEGACY_REFS"]["subsection"] == "referral"
|
||||
assert manifest["DISPOSABLE_EMAIL_DOMAINS"]["section"] == "pricing"
|
||||
assert manifest["DISPOSABLE_EMAIL_DOMAINS"]["subsection"] == "referral"
|
||||
|
||||
|
||||
def test_platega_settings_share_one_admin_subsection():
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
from datetime import datetime, timezone
|
||||
from types import SimpleNamespace
|
||||
from unittest import IsolatedAsyncioTestCase
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
import bot.app.web.subscription_webapp # noqa: F401
|
||||
from bot.app.web.webapp import auth as auth_module
|
||||
|
||||
|
||||
class WebAppReferralWelcomeBonusTests(IsolatedAsyncioTestCase):
|
||||
async def test_disposable_email_referral_welcome_bonus_requires_telegram(self):
|
||||
settings = SimpleNamespace(
|
||||
REFERRAL_WELCOME_BONUS_DAYS=3,
|
||||
REFERRAL_WELCOME_BONUS_WITHOUT_TELEGRAM_ENABLED=True,
|
||||
DISPOSABLE_EMAIL_DOMAINS="mailinator.com",
|
||||
)
|
||||
user = SimpleNamespace(
|
||||
user_id=42,
|
||||
referred_by_id=7,
|
||||
telegram_id=None,
|
||||
email="person@mailinator.com",
|
||||
)
|
||||
subscription_service = SimpleNamespace(
|
||||
has_active_subscription=AsyncMock(return_value=False),
|
||||
extend_active_subscription_days=AsyncMock(),
|
||||
)
|
||||
request = SimpleNamespace(
|
||||
app={"settings": settings, "subscription_service": subscription_service}
|
||||
)
|
||||
|
||||
result = await auth_module._apply_referral_welcome_bonus_if_needed(
|
||||
request,
|
||||
SimpleNamespace(),
|
||||
user,
|
||||
"ABC123",
|
||||
)
|
||||
|
||||
self.assertIsNone(result)
|
||||
subscription_service.has_active_subscription.assert_not_awaited()
|
||||
subscription_service.extend_active_subscription_days.assert_not_awaited()
|
||||
|
||||
async def test_linked_telegram_allows_disposable_email_referral_welcome_bonus(self):
|
||||
end_date = datetime(2026, 1, 9, 3, 4, tzinfo=timezone.utc)
|
||||
settings = SimpleNamespace(
|
||||
REFERRAL_WELCOME_BONUS_DAYS=3,
|
||||
REFERRAL_WELCOME_BONUS_WITHOUT_TELEGRAM_ENABLED=True,
|
||||
DISPOSABLE_EMAIL_DOMAINS="mailinator.com",
|
||||
)
|
||||
user = SimpleNamespace(
|
||||
user_id=42,
|
||||
referred_by_id=7,
|
||||
telegram_id=123456,
|
||||
email="person@mailinator.com",
|
||||
)
|
||||
session = SimpleNamespace()
|
||||
subscription_service = SimpleNamespace(
|
||||
has_active_subscription=AsyncMock(return_value=False),
|
||||
extend_active_subscription_days=AsyncMock(return_value=end_date),
|
||||
)
|
||||
request = SimpleNamespace(
|
||||
app={"settings": settings, "subscription_service": subscription_service}
|
||||
)
|
||||
|
||||
result = await auth_module._apply_referral_welcome_bonus_if_needed(
|
||||
request,
|
||||
session,
|
||||
user,
|
||||
"ABC123",
|
||||
)
|
||||
|
||||
self.assertEqual(result, end_date)
|
||||
subscription_service.has_active_subscription.assert_awaited_once_with(session, 42)
|
||||
subscription_service.extend_active_subscription_days.assert_awaited_once_with(
|
||||
session,
|
||||
42,
|
||||
3,
|
||||
reason="referral_welcome_bonus",
|
||||
)
|
||||
@@ -115,3 +115,95 @@ class WebAppTrialActivationTests(IsolatedAsyncioTestCase):
|
||||
mark_trial_activated.assert_awaited_once_with(session, 42)
|
||||
self.assertEqual(session.commit_count, 2)
|
||||
self.assertEqual(session.rollback_count, 0)
|
||||
|
||||
async def test_email_only_trial_activation_requires_telegram_when_disabled(self):
|
||||
session = _Session()
|
||||
settings = SimpleNamespace(
|
||||
TRIAL_ENABLED=True,
|
||||
TRIAL_DURATION_DAYS=7,
|
||||
TRIAL_TRAFFIC_LIMIT_GB=10,
|
||||
TRIAL_WITHOUT_TELEGRAM_ENABLED=False,
|
||||
DISPOSABLE_EMAIL_DOMAINS="",
|
||||
LOG_TRIAL_ACTIVATIONS=False,
|
||||
)
|
||||
db_user = SimpleNamespace(
|
||||
user_id=42,
|
||||
telegram_id=None,
|
||||
is_banned=False,
|
||||
email="email-only@example.com",
|
||||
)
|
||||
subscription_service = SimpleNamespace(activate_trial_subscription=AsyncMock())
|
||||
request = SimpleNamespace(
|
||||
app={
|
||||
"settings": settings,
|
||||
"async_session_factory": _SessionFactory(session),
|
||||
"subscription_service": subscription_service,
|
||||
}
|
||||
)
|
||||
|
||||
with (
|
||||
patch.object(billing_module, "_require_user_id", return_value=42),
|
||||
patch.object(
|
||||
billing_module,
|
||||
"_enforce_webapp_rate_limit",
|
||||
AsyncMock(return_value=None),
|
||||
),
|
||||
patch.object(
|
||||
billing_module.user_dal,
|
||||
"get_user_by_id",
|
||||
AsyncMock(return_value=db_user),
|
||||
),
|
||||
):
|
||||
response = await billing_module.activate_trial_route(request)
|
||||
|
||||
payload = json.loads(response.text)
|
||||
self.assertEqual(response.status, 400)
|
||||
self.assertEqual(payload["error"], "trial_telegram_required")
|
||||
self.assertEqual(payload["message"], "telegram_required")
|
||||
subscription_service.activate_trial_subscription.assert_not_awaited()
|
||||
|
||||
async def test_disposable_email_trial_activation_requires_telegram(self):
|
||||
session = _Session()
|
||||
settings = SimpleNamespace(
|
||||
TRIAL_ENABLED=True,
|
||||
TRIAL_DURATION_DAYS=7,
|
||||
TRIAL_TRAFFIC_LIMIT_GB=10,
|
||||
TRIAL_WITHOUT_TELEGRAM_ENABLED=True,
|
||||
DISPOSABLE_EMAIL_DOMAINS="mailinator.com,temp-mail.org",
|
||||
LOG_TRIAL_ACTIVATIONS=False,
|
||||
)
|
||||
db_user = SimpleNamespace(
|
||||
user_id=42,
|
||||
telegram_id=None,
|
||||
is_banned=False,
|
||||
email="person@mailinator.com",
|
||||
)
|
||||
subscription_service = SimpleNamespace(activate_trial_subscription=AsyncMock())
|
||||
request = SimpleNamespace(
|
||||
app={
|
||||
"settings": settings,
|
||||
"async_session_factory": _SessionFactory(session),
|
||||
"subscription_service": subscription_service,
|
||||
}
|
||||
)
|
||||
|
||||
with (
|
||||
patch.object(billing_module, "_require_user_id", return_value=42),
|
||||
patch.object(
|
||||
billing_module,
|
||||
"_enforce_webapp_rate_limit",
|
||||
AsyncMock(return_value=None),
|
||||
),
|
||||
patch.object(
|
||||
billing_module.user_dal,
|
||||
"get_user_by_id",
|
||||
AsyncMock(return_value=db_user),
|
||||
),
|
||||
):
|
||||
response = await billing_module.activate_trial_route(request)
|
||||
|
||||
payload = json.loads(response.text)
|
||||
self.assertEqual(response.status, 400)
|
||||
self.assertEqual(payload["error"], "trial_telegram_required")
|
||||
self.assertEqual(payload["message"], "disposable_email")
|
||||
subscription_service.activate_trial_subscription.assert_not_awaited()
|
||||
|
||||
Reference in New Issue
Block a user