fix: merge active sub email account with expired sub telegram account
This commit is contained in:
@@ -1042,7 +1042,11 @@ async def _sync_panel_identity_for_user(
|
|||||||
if user.email:
|
if user.email:
|
||||||
payload["email"] = user.email
|
payload["email"] = user.email
|
||||||
if expire_at is not None:
|
if expire_at is not None:
|
||||||
|
if expire_at.tzinfo is None:
|
||||||
|
expire_at = expire_at.replace(tzinfo=timezone.utc)
|
||||||
payload["expireAt"] = expire_at.isoformat(timespec="milliseconds").replace("+00:00", "Z")
|
payload["expireAt"] = expire_at.isoformat(timespec="milliseconds").replace("+00:00", "Z")
|
||||||
|
if expire_at > datetime.now(timezone.utc):
|
||||||
|
payload["status"] = "ACTIVE"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await subscription_service.panel_service.update_user_details_on_panel(
|
await subscription_service.panel_service.update_user_details_on_panel(
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import json
|
import json
|
||||||
import unittest
|
import unittest
|
||||||
|
from datetime import datetime, timedelta, timezone
|
||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
from unittest.mock import AsyncMock, patch
|
from unittest.mock import AsyncMock, patch
|
||||||
|
|
||||||
@@ -76,6 +77,43 @@ class AccountLinkingPanelTests(unittest.IsolatedAsyncioTestCase):
|
|||||||
self.assertEqual(payload["email"], "linked@example.com")
|
self.assertEqual(payload["email"], "linked@example.com")
|
||||||
self.assertEqual(payload["telegramId"], 42)
|
self.assertEqual(payload["telegramId"], 42)
|
||||||
|
|
||||||
|
async def test_merged_panel_identity_reactivates_expired_target_with_transferred_time(self):
|
||||||
|
expire_at = datetime.now(timezone.utc) + timedelta(days=30)
|
||||||
|
panel_service = SimpleNamespace(
|
||||||
|
delete_user_from_panel=AsyncMock(return_value=True),
|
||||||
|
update_user_details_on_panel=AsyncMock(return_value={"uuid": "panel-target"}),
|
||||||
|
)
|
||||||
|
request = SimpleNamespace(
|
||||||
|
app={"subscription_service": SimpleNamespace(panel_service=panel_service)}
|
||||||
|
)
|
||||||
|
user = SimpleNamespace(
|
||||||
|
user_id=42,
|
||||||
|
panel_user_uuid="panel-target",
|
||||||
|
telegram_id=42,
|
||||||
|
email="linked@example.com",
|
||||||
|
username="alice",
|
||||||
|
first_name="Alice",
|
||||||
|
last_name=None,
|
||||||
|
)
|
||||||
|
|
||||||
|
result = await _sync_merged_panel_identity_for_user(
|
||||||
|
request,
|
||||||
|
user,
|
||||||
|
source_panel_uuid="panel-email",
|
||||||
|
final_panel_uuid="panel-target",
|
||||||
|
expire_at=expire_at,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertTrue(result)
|
||||||
|
panel_service.delete_user_from_panel.assert_awaited_once_with(
|
||||||
|
"panel-email",
|
||||||
|
log_response=False,
|
||||||
|
)
|
||||||
|
_, payload = panel_service.update_user_details_on_panel.await_args.args[:2]
|
||||||
|
expected_expire_at = expire_at.isoformat(timespec="milliseconds").replace("+00:00", "Z")
|
||||||
|
self.assertEqual(payload["expireAt"], expected_expire_at)
|
||||||
|
self.assertEqual(payload["status"], "ACTIVE")
|
||||||
|
|
||||||
async def test_telegram_merge_defers_panel_sync_until_source_cleanup(self):
|
async def test_telegram_merge_defers_panel_sync_until_source_cleanup(self):
|
||||||
current_user = SimpleNamespace(
|
current_user = SimpleNamespace(
|
||||||
user_id=-100,
|
user_id=-100,
|
||||||
|
|||||||
+107
-1
@@ -1,5 +1,5 @@
|
|||||||
import unittest
|
import unittest
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
from unittest.mock import AsyncMock, patch
|
from unittest.mock import AsyncMock, patch
|
||||||
|
|
||||||
@@ -131,3 +131,109 @@ class UserDalMergeTests(unittest.IsolatedAsyncioTestCase):
|
|||||||
self.assertIn("user_payment_methods", delete_tables)
|
self.assertIn("user_payment_methods", delete_tables)
|
||||||
self.assertIn("promo_code_activations", delete_tables)
|
self.assertIn("promo_code_activations", delete_tables)
|
||||||
session.delete.assert_awaited_once_with(source)
|
session.delete.assert_awaited_once_with(source)
|
||||||
|
|
||||||
|
async def test_merge_users_moves_active_email_subscription_onto_expired_telegram_account(self):
|
||||||
|
before = datetime.now(timezone.utc)
|
||||||
|
source = SimpleNamespace(
|
||||||
|
user_id=-100,
|
||||||
|
email="paid@example.com",
|
||||||
|
telegram_id=None,
|
||||||
|
panel_user_uuid="panel-email",
|
||||||
|
email_verified_at=before,
|
||||||
|
username=None,
|
||||||
|
first_name=None,
|
||||||
|
last_name=None,
|
||||||
|
language_code="ru",
|
||||||
|
telegram_photo_url=None,
|
||||||
|
channel_subscription_verified=False,
|
||||||
|
channel_subscription_checked_at=None,
|
||||||
|
channel_subscription_verified_for=None,
|
||||||
|
lifetime_used_traffic_bytes=0,
|
||||||
|
referred_by_id=None,
|
||||||
|
referral_code=None,
|
||||||
|
)
|
||||||
|
target = SimpleNamespace(
|
||||||
|
user_id=42,
|
||||||
|
email=None,
|
||||||
|
telegram_id=42,
|
||||||
|
panel_user_uuid="panel-telegram",
|
||||||
|
email_verified_at=None,
|
||||||
|
username="old",
|
||||||
|
first_name=None,
|
||||||
|
last_name=None,
|
||||||
|
language_code="ru",
|
||||||
|
telegram_photo_url=None,
|
||||||
|
channel_subscription_verified=False,
|
||||||
|
channel_subscription_checked_at=None,
|
||||||
|
channel_subscription_verified_for=None,
|
||||||
|
lifetime_used_traffic_bytes=0,
|
||||||
|
referred_by_id=None,
|
||||||
|
referral_code=None,
|
||||||
|
)
|
||||||
|
source_active_sub = SimpleNamespace(
|
||||||
|
end_date=before + timedelta(days=30),
|
||||||
|
is_active=True,
|
||||||
|
skip_notifications=False,
|
||||||
|
last_notification_sent=before,
|
||||||
|
status_from_panel="ACTIVE",
|
||||||
|
panel_user_uuid="panel-email",
|
||||||
|
)
|
||||||
|
expired_target_sub = SimpleNamespace(
|
||||||
|
end_date=before - timedelta(days=3),
|
||||||
|
is_active=False,
|
||||||
|
skip_notifications=False,
|
||||||
|
last_notification_sent=before,
|
||||||
|
status_from_panel="EXPIRED",
|
||||||
|
panel_user_uuid="panel-telegram",
|
||||||
|
)
|
||||||
|
session = SimpleNamespace(
|
||||||
|
execute=AsyncMock(side_effect=lambda stmt: FakeResult()),
|
||||||
|
delete=AsyncMock(),
|
||||||
|
flush=AsyncMock(),
|
||||||
|
refresh=AsyncMock(),
|
||||||
|
)
|
||||||
|
|
||||||
|
async def fake_get_user_by_id(_session, user_id):
|
||||||
|
if user_id == source.user_id:
|
||||||
|
return source
|
||||||
|
if user_id == target.user_id:
|
||||||
|
return target
|
||||||
|
return None
|
||||||
|
|
||||||
|
async def fake_get_active_subscription(_session, user_id, panel_user_uuid=None):
|
||||||
|
if user_id == source.user_id and panel_user_uuid == source.panel_user_uuid:
|
||||||
|
return source_active_sub
|
||||||
|
return None
|
||||||
|
|
||||||
|
async def fake_get_latest_subscription(_session, user_id, panel_user_uuid=None, **_kwargs):
|
||||||
|
if user_id == target.user_id and panel_user_uuid == target.panel_user_uuid:
|
||||||
|
return expired_target_sub
|
||||||
|
return None
|
||||||
|
|
||||||
|
with (
|
||||||
|
patch("db.dal.user_dal.get_user_by_id", side_effect=fake_get_user_by_id),
|
||||||
|
patch(
|
||||||
|
"db.dal.user_dal._get_active_subscription_for_user",
|
||||||
|
side_effect=fake_get_active_subscription,
|
||||||
|
),
|
||||||
|
patch(
|
||||||
|
"db.dal.user_dal._get_latest_subscription_for_user",
|
||||||
|
side_effect=fake_get_latest_subscription,
|
||||||
|
),
|
||||||
|
):
|
||||||
|
merged = await user_dal.merge_users(
|
||||||
|
session,
|
||||||
|
source_user_id=source.user_id,
|
||||||
|
target_user_id=target.user_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertIs(merged, target)
|
||||||
|
self.assertEqual(target.email, "paid@example.com")
|
||||||
|
self.assertTrue(expired_target_sub.is_active)
|
||||||
|
self.assertEqual(expired_target_sub.status_from_panel, "ACTIVE_EXTENDED_BY_MERGE")
|
||||||
|
self.assertIsNone(expired_target_sub.last_notification_sent)
|
||||||
|
self.assertGreater(expired_target_sub.end_date, before + timedelta(days=29))
|
||||||
|
self.assertLess(expired_target_sub.end_date, before + timedelta(days=31))
|
||||||
|
self.assertFalse(source_active_sub.is_active)
|
||||||
|
self.assertTrue(source_active_sub.skip_notifications)
|
||||||
|
self.assertEqual(source_active_sub.status_from_panel, "MERGED_INTO_ACCOUNT")
|
||||||
|
|||||||
Reference in New Issue
Block a user