fix: preserve subscriptions on panel lookup failures
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import json
|
||||
import tempfile
|
||||
import unittest
|
||||
from datetime import datetime, timezone
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import AsyncMock, patch
|
||||
@@ -393,6 +393,147 @@ class SubscriptionServiceActivationDispatchTests(unittest.IsolatedAsyncioTestCas
|
||||
self.assertEqual(kwargs["payment_db_id"], 12)
|
||||
|
||||
|
||||
class SubscriptionServiceActiveDetailsTests(unittest.IsolatedAsyncioTestCase):
|
||||
def _local_active_sub(self) -> SimpleNamespace:
|
||||
return SimpleNamespace(
|
||||
subscription_id=7,
|
||||
user_id=42,
|
||||
panel_user_uuid="panel-user",
|
||||
panel_subscription_uuid="short-uuid",
|
||||
end_date=datetime.now(timezone.utc) + timedelta(days=10),
|
||||
is_active=True,
|
||||
status_from_panel="ACTIVE",
|
||||
traffic_limit_bytes=1000,
|
||||
traffic_used_bytes=100,
|
||||
tariff_key=None,
|
||||
tier_baseline_bytes=None,
|
||||
topup_balance_bytes=0,
|
||||
regular_bonus_bytes=0,
|
||||
regular_unlimited_override=False,
|
||||
premium_baseline_bytes=0,
|
||||
premium_topup_balance_bytes=0,
|
||||
premium_topup_used_bytes=0,
|
||||
premium_used_bytes=0,
|
||||
premium_bonus_bytes=0,
|
||||
premium_unlimited_override=False,
|
||||
premium_is_limited=False,
|
||||
premium_period_start_at=None,
|
||||
period_start_at=None,
|
||||
is_throttled=False,
|
||||
hwid_device_limit=None,
|
||||
extra_hwid_devices=0,
|
||||
)
|
||||
|
||||
async def test_get_active_subscription_details_preserves_local_subscription_on_panel_error(
|
||||
self,
|
||||
):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
settings = _make_settings(_tariffs_config_payload(), tmpdir)
|
||||
service = _make_service(settings)
|
||||
service.panel_service.get_user_by_uuid_lookup = AsyncMock(
|
||||
return_value={
|
||||
"ok": False,
|
||||
"user": None,
|
||||
"not_found": False,
|
||||
"failure_reason": "classification=panel_lookup_failed status_code=-1 "
|
||||
"message=Connection error",
|
||||
"response": {"error": True, "status_code": -1},
|
||||
}
|
||||
)
|
||||
service.panel_service.get_subscription_link = AsyncMock(
|
||||
return_value="https://panel.example.test/sub/short-uuid"
|
||||
)
|
||||
session = AsyncMock()
|
||||
db_user = SimpleNamespace(
|
||||
user_id=42,
|
||||
panel_user_uuid="panel-user",
|
||||
username="alice",
|
||||
language_code="en",
|
||||
)
|
||||
local_sub = self._local_active_sub()
|
||||
|
||||
with (
|
||||
patch(
|
||||
"bot.services.subscription_service_impl.lifecycle.user_dal.get_user_by_id",
|
||||
AsyncMock(return_value=db_user),
|
||||
),
|
||||
patch(
|
||||
"bot.services.subscription_service_impl.lifecycle.subscription_dal.get_active_subscription_by_user_id",
|
||||
AsyncMock(return_value=local_sub),
|
||||
),
|
||||
patch(
|
||||
"bot.services.subscription_service_impl.lifecycle.subscription_dal.deactivate_all_user_subscriptions",
|
||||
AsyncMock(),
|
||||
) as deactivate_all,
|
||||
patch(
|
||||
"bot.services.subscription_service_impl.lifecycle.user_dal.update_user",
|
||||
AsyncMock(),
|
||||
) as update_user,
|
||||
patch(
|
||||
"bot.services.subscription_service_impl.lifecycle.logging.warning",
|
||||
) as warning_log,
|
||||
):
|
||||
result = await service.get_active_subscription_details(session, user_id=42)
|
||||
|
||||
self.assertIsNotNone(result)
|
||||
self.assertFalse(result["is_panel_data"])
|
||||
self.assertEqual(result["end_date"], local_sub.end_date)
|
||||
self.assertEqual(result["config_link"], "https://panel.example.test/sub/short-uuid")
|
||||
deactivate_all.assert_not_awaited()
|
||||
update_user.assert_not_awaited()
|
||||
warning_text = " ".join(str(call) for call in warning_log.call_args_list)
|
||||
self.assertIn("panel access/API problem", warning_text)
|
||||
self.assertIn("status_code=-1", warning_text)
|
||||
self.assertIn("Connection error", warning_text)
|
||||
|
||||
async def test_get_active_subscription_details_clears_link_only_when_panel_confirms_absent(
|
||||
self,
|
||||
):
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
settings = _make_settings(_tariffs_config_payload(), tmpdir)
|
||||
service = _make_service(settings)
|
||||
service.panel_service.get_user_by_uuid_lookup = AsyncMock(
|
||||
return_value={
|
||||
"ok": False,
|
||||
"user": None,
|
||||
"not_found": True,
|
||||
"failure_reason": "classification=confirmed_not_found status_code=404",
|
||||
"response": {"error": True, "status_code": 404},
|
||||
}
|
||||
)
|
||||
session = AsyncMock()
|
||||
db_user = SimpleNamespace(
|
||||
user_id=42,
|
||||
panel_user_uuid="panel-user",
|
||||
username="alice",
|
||||
language_code="en",
|
||||
)
|
||||
|
||||
with (
|
||||
patch(
|
||||
"bot.services.subscription_service_impl.lifecycle.user_dal.get_user_by_id",
|
||||
AsyncMock(return_value=db_user),
|
||||
),
|
||||
patch(
|
||||
"bot.services.subscription_service_impl.lifecycle.subscription_dal.get_active_subscription_by_user_id",
|
||||
AsyncMock(return_value=self._local_active_sub()),
|
||||
),
|
||||
patch(
|
||||
"bot.services.subscription_service_impl.lifecycle.subscription_dal.deactivate_all_user_subscriptions",
|
||||
AsyncMock(),
|
||||
) as deactivate_all,
|
||||
patch(
|
||||
"bot.services.subscription_service_impl.lifecycle.user_dal.update_user",
|
||||
AsyncMock(),
|
||||
) as update_user,
|
||||
):
|
||||
result = await service.get_active_subscription_details(session, user_id=42)
|
||||
|
||||
self.assertIsNone(result)
|
||||
deactivate_all.assert_awaited_once_with(session, 42)
|
||||
update_user.assert_awaited_once_with(session, 42, {"panel_user_uuid": None})
|
||||
|
||||
|
||||
class SubscriptionDalPayloadTests(unittest.TestCase):
|
||||
def test_subscription_model_payload_drops_panel_only_keys(self):
|
||||
payload = _subscription_model_payload(
|
||||
|
||||
Reference in New Issue
Block a user