- Добавлен content_bot/landing_utils.py: получение ссылок MAX и Telegram из lk_integration по slug партнёра, нормализация URL и автоисправление записей в БД. - landing_page больше не берёт Telegram из настроек max_bot; удалён устаревший fallback на url_telegram. - Добавлены тесты нормализации URL и формирования кнопок лендинга.main
parent
e5a823c99f
commit
a88aeba01a
@ -0,0 +1,123 @@
|
||||
import json
|
||||
from typing import Callable, Optional, Tuple
|
||||
|
||||
from max_bot.models import Client, Integration
|
||||
|
||||
|
||||
def normalize_telegram_url(url: Optional[str]) -> Optional[str]:
|
||||
"""Приводит ссылку на Telegram-бота к виду https://t.me/bot_name."""
|
||||
if not url or not str(url).strip():
|
||||
return None
|
||||
|
||||
value = str(url).strip()
|
||||
if value.startswith('@'):
|
||||
value = value[1:]
|
||||
|
||||
if value.startswith('https://'):
|
||||
return value
|
||||
if value.startswith('http://'):
|
||||
return f'https://{value[7:]}'
|
||||
if value.startswith('t.me/'):
|
||||
return f'https://{value}'
|
||||
|
||||
return f'https://t.me/{value.lstrip("/")}'
|
||||
|
||||
|
||||
def normalize_max_bot_url(url: Optional[str]) -> Optional[str]:
|
||||
"""Приводит ссылку на MAX-бота к виду https://max.ru/bot_id."""
|
||||
if not url or not str(url).strip():
|
||||
return None
|
||||
|
||||
value = str(url).strip()
|
||||
if value.startswith('https://max.ru/'):
|
||||
return value
|
||||
if value.startswith('http://max.ru/'):
|
||||
return f'https://max.ru/{value[len("http://max.ru/"):]}'
|
||||
if value.startswith('max.ru/'):
|
||||
return f'https://{value}'
|
||||
|
||||
return f'https://max.ru/{value.lstrip("/")}'
|
||||
|
||||
|
||||
def _load_settings(integration: Integration) -> dict:
|
||||
if not integration.setting:
|
||||
return {}
|
||||
try:
|
||||
settings = json.loads(integration.setting)
|
||||
except json.JSONDecodeError:
|
||||
return {}
|
||||
return settings if isinstance(settings, dict) else {}
|
||||
|
||||
|
||||
def _save_settings(integration: Integration, settings: dict) -> None:
|
||||
integration.setting = json.dumps(settings, ensure_ascii=False)
|
||||
integration.save(update_fields=['setting', 'updated_at'])
|
||||
|
||||
|
||||
def get_integration_url(
|
||||
client: Client,
|
||||
partner_slug: str,
|
||||
setting_key: str = 'url',
|
||||
normalizer: Optional[Callable[[Optional[str]], Optional[str]]] = None,
|
||||
persist_fix: bool = True,
|
||||
) -> Optional[str]:
|
||||
"""
|
||||
Возвращает URL из lk_integration по slug партнёра.
|
||||
При необходимости нормализует и сохраняет исправленное значение в БД.
|
||||
"""
|
||||
integration = Integration.objects.filter(
|
||||
client=client,
|
||||
partner__slug=partner_slug,
|
||||
status=True,
|
||||
).select_related('partner').first()
|
||||
if not integration:
|
||||
return None
|
||||
|
||||
settings = _load_settings(integration)
|
||||
raw_url = settings.get(setting_key)
|
||||
if not raw_url:
|
||||
return None
|
||||
|
||||
normalized = normalizer(raw_url) if normalizer else raw_url
|
||||
if persist_fix and normalized and normalized != raw_url:
|
||||
settings[setting_key] = normalized
|
||||
_save_settings(integration, settings)
|
||||
|
||||
return normalized
|
||||
|
||||
|
||||
def cleanup_max_bot_integration(client: Client) -> None:
|
||||
"""Удаляет устаревший ключ url_telegram из настроек max_bot."""
|
||||
integration = Integration.objects.filter(
|
||||
client=client,
|
||||
partner__slug='max_bot',
|
||||
status=True,
|
||||
).first()
|
||||
if not integration:
|
||||
return
|
||||
|
||||
settings = _load_settings(integration)
|
||||
if 'url_telegram' not in settings:
|
||||
return
|
||||
|
||||
del settings['url_telegram']
|
||||
_save_settings(integration, settings)
|
||||
|
||||
|
||||
def get_landing_bot_urls(client: Client) -> Tuple[Optional[str], Optional[str]]:
|
||||
"""Возвращает (max_bot_url, telegram_url) для лендинга."""
|
||||
cleanup_max_bot_integration(client)
|
||||
|
||||
max_bot_url = get_integration_url(
|
||||
client=client,
|
||||
partner_slug='max_bot',
|
||||
setting_key='url',
|
||||
normalizer=normalize_max_bot_url,
|
||||
)
|
||||
telegram_url = get_integration_url(
|
||||
client=client,
|
||||
partner_slug='telegram_bot',
|
||||
setting_key='url',
|
||||
normalizer=normalize_telegram_url,
|
||||
)
|
||||
return max_bot_url, telegram_url
|
||||
@ -1,3 +1,99 @@
|
||||
from django.test import TestCase
|
||||
from django.test import TestCase, Client as DjangoClient
|
||||
from unittest.mock import patch, MagicMock
|
||||
|
||||
# Create your tests here.
|
||||
from content_bot.landing_utils import (
|
||||
normalize_telegram_url,
|
||||
normalize_max_bot_url,
|
||||
get_landing_bot_urls,
|
||||
)
|
||||
|
||||
|
||||
class LandingUrlNormalizationTests(TestCase):
|
||||
def test_normalize_telegram_full_url(self):
|
||||
self.assertEqual(
|
||||
normalize_telegram_url('https://t.me/cheguevaraclub_bot'),
|
||||
'https://t.me/cheguevaraclub_bot',
|
||||
)
|
||||
|
||||
def test_normalize_telegram_without_scheme(self):
|
||||
self.assertEqual(
|
||||
normalize_telegram_url('t.me/celentanouu_bot'),
|
||||
'https://t.me/celentanouu_bot',
|
||||
)
|
||||
|
||||
def test_normalize_telegram_bot_name(self):
|
||||
self.assertEqual(
|
||||
normalize_telegram_url('barLeto_bot'),
|
||||
'https://t.me/barLeto_bot',
|
||||
)
|
||||
|
||||
def test_normalize_max_bot_full_url(self):
|
||||
self.assertEqual(
|
||||
normalize_max_bot_url('https://max.ru/id0323343968_bot'),
|
||||
'https://max.ru/id0323343968_bot',
|
||||
)
|
||||
|
||||
def test_normalize_max_bot_short_id(self):
|
||||
self.assertEqual(
|
||||
normalize_max_bot_url('d0323350531_bot'),
|
||||
'https://max.ru/d0323350531_bot',
|
||||
)
|
||||
|
||||
|
||||
class LandingBotUrlsTests(TestCase):
|
||||
@patch('content_bot.landing_utils.Integration')
|
||||
def test_get_landing_bot_urls_uses_separate_integrations(self, mock_integration):
|
||||
client = MagicMock()
|
||||
max_integration = MagicMock()
|
||||
max_integration.setting = '{"url":"d0323350531_bot"}'
|
||||
telegram_integration = MagicMock()
|
||||
telegram_integration.setting = '{"url":"https://t.me/cheguevaraclub_bot"}'
|
||||
|
||||
def filter_side_effect(**kwargs):
|
||||
qs = MagicMock()
|
||||
slug = kwargs.get('partner__slug')
|
||||
if slug == 'max_bot':
|
||||
qs.first.return_value = max_integration
|
||||
elif slug == 'telegram_bot':
|
||||
qs.first.return_value = telegram_integration
|
||||
else:
|
||||
qs.first.return_value = None
|
||||
return qs
|
||||
|
||||
mock_integration.objects.filter.side_effect = filter_side_effect
|
||||
|
||||
with patch('content_bot.landing_utils._save_settings') as mock_save:
|
||||
max_url, telegram_url = get_landing_bot_urls(client)
|
||||
|
||||
self.assertEqual(max_url, 'https://max.ru/d0323350531_bot')
|
||||
self.assertEqual(telegram_url, 'https://t.me/cheguevaraclub_bot')
|
||||
self.assertEqual(mock_save.call_count, 1)
|
||||
|
||||
|
||||
class LandingPageViewTests(TestCase):
|
||||
@patch('content_bot.views.get_landing_bot_urls')
|
||||
@patch('content_bot.views.Qr')
|
||||
@patch('content_bot.views.get_object_or_404')
|
||||
def test_landing_page_passes_urls_to_template(self, mock_get_object, mock_qr, mock_get_urls):
|
||||
client_obj = MagicMock()
|
||||
client_obj.id = 1089
|
||||
client_obj.brand = 'Test Brand'
|
||||
client_obj.logo = ''
|
||||
mock_get_object.return_value = client_obj
|
||||
|
||||
qr_obj = MagicMock()
|
||||
qr_obj.brand = 'QR Brand'
|
||||
qr_obj.logo = 'logo.png'
|
||||
qr_obj.description = 'Description'
|
||||
mock_qr.objects.filter.return_value.first.return_value = qr_obj
|
||||
|
||||
mock_get_urls.return_value = (
|
||||
'https://max.ru/d0323350531_bot',
|
||||
'https://t.me/cheguevaraclub_bot',
|
||||
)
|
||||
|
||||
response = DjangoClient().get('/content/landing/1089/')
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, 'https://max.ru/d0323350531_bot')
|
||||
self.assertContains(response, 'https://t.me/cheguevaraclub_bot')
|
||||
|
||||
Loading…
Reference in new issue