Files
remnawave-minishop/tests/test_webapp_assets.py
T

209 lines
8.8 KiB
Python

import json
import os
import tempfile
import unittest
from datetime import datetime, timezone
from pathlib import Path
from types import SimpleNamespace
from unittest.mock import patch
from bot.app.web import subscription_webapp
from config.settings import Settings
class WebAppAssetTests(unittest.IsolatedAsyncioTestCase):
def test_serialize_plans_prefers_tariffs_config_over_legacy_packages(self):
with tempfile.TemporaryDirectory() as tmpdir:
path = Path(tmpdir) / "tariffs.json"
path.write_text(
json.dumps(
{
"default_tariff": "standard",
"tariffs": [
{
"key": "standard",
"names": {"en": "Standard"},
"descriptions": {"en": "100 GB monthly"},
"squad_uuids": ["uuid"],
"billing_model": "period",
"monthly_gb": 100,
"hwid_device_limit": 5,
"hwid_device_packages": {
"rub": [{"count": 1, "price": 99}],
"stars": [{"count": 1, "price": 2500}],
},
"prices_rub": {"1": 150},
"prices_stars": {"1": 0},
"enabled_periods": [1],
"enabled": True,
},
{
"key": "traffic",
"names": {"en": "Traffic"},
"descriptions": {"en": "Pay as you go"},
"squad_uuids": ["uuid"],
"billing_model": "traffic",
"traffic_packages": {
"rub": [{"gb": 50, "price": 799}],
"stars": [{"gb": 50, "price": 2500}],
},
"enabled": True,
},
],
}
),
encoding="utf-8",
)
settings = Settings(
_env_file=None,
BOT_TOKEN="token",
POSTGRES_USER="app_user",
POSTGRES_PASSWORD="app_password",
TARIFFS_CONFIG_PATH=str(path),
TRAFFIC_PACKAGES="10:199",
)
plans = subscription_webapp._serialize_plans(settings, "en")
self.assertEqual([plan["tariff_key"] for plan in plans], ["standard", "traffic"])
self.assertEqual(plans[0]["sale_mode"], "subscription")
self.assertEqual(plans[0]["months"], 1)
self.assertEqual(plans[0]["hwid_device_limit"], 5)
self.assertEqual(plans[0]["hwid_device_packages"][0]["device_count"], 1)
self.assertEqual(plans[1]["sale_mode"], "traffic_package")
self.assertEqual(plans[1]["traffic_gb"], 50.0)
self.assertEqual(plans[1]["stars_price"], 2500)
def test_subscription_template_does_not_block_on_telegram_sdk(self):
html = subscription_webapp.TEMPLATE_PATH.read_text(encoding="utf-8")
self.assertNotIn("https://telegram.org/js/telegram-web-app.js", html)
self.assertNotIn("https://fonts.googleapis.com", html)
self.assertLess(html.index("/subscription_webapp.css"), html.index("WEBAPP_JS_SCRIPT"))
def test_https_webapp_logo_uses_same_origin_proxy(self):
settings = SimpleNamespace(WEBAPP_LOGO_URL="https://cdn.example.com/logo.png")
self.assertEqual(subscription_webapp._resolve_webapp_logo_url(settings), "/webapp-logo")
def test_telegram_avatar_url_uses_same_origin_account_route(self):
avatar = SimpleNamespace(
user_id=123,
image_bytes=b"avatar",
updated_at=datetime(2026, 5, 6, 12, 0, tzinfo=timezone.utc),
)
self.assertEqual(
subscription_webapp._telegram_avatar_url(avatar),
f"/api/account/avatar?v={int(avatar.updated_at.timestamp())}",
)
def test_select_compact_telegram_photo_size_prefers_small_suitable_photo(self):
small = SimpleNamespace(width=80, height=80, file_size=5000)
medium = SimpleNamespace(width=160, height=160, file_size=12000)
large = SimpleNamespace(width=640, height=640, file_size=90000)
self.assertIs(
subscription_webapp._select_compact_telegram_photo_size([small, large, medium]),
medium,
)
def test_serialize_plans_uses_traffic_packages_in_traffic_mode(self):
settings = Settings(
_env_file=None,
BOT_TOKEN="token",
POSTGRES_USER="app_user",
POSTGRES_PASSWORD="app_password",
TARIFFS_CONFIG_PATH="missing-tariffs.json",
TRAFFIC_PACKAGES="10:199,50:799",
STARS_TRAFFIC_PACKAGES="50:2500",
)
plans = subscription_webapp._serialize_plans(settings, "en")
self.assertEqual([plan["traffic_gb"] for plan in plans], [10.0, 50.0])
self.assertEqual(plans[0]["sale_mode"], "traffic")
self.assertEqual(plans[0]["price"], 199.0)
self.assertEqual(plans[1]["stars_price"], 2500)
def test_serialize_payment_methods_respects_runtime_provider_toggles(self):
settings = Settings(
_env_file=None,
BOT_TOKEN="token",
POSTGRES_USER="app_user",
POSTGRES_PASSWORD="app_password",
TARIFFS_CONFIG_PATH="missing-tariffs.json",
CRYPTOPAY_ENABLED=False,
FREEKASSA_ENABLED=False,
SEVERPAY_ENABLED=False,
YOOKASSA_ENABLED=False,
PLATEGA_ENABLED=False,
STARS_ENABLED=False,
)
configured_service = SimpleNamespace(configured=True)
app = {
"cryptopay_service": configured_service,
"freekassa_service": configured_service,
"severpay_service": configured_service,
"yookassa_service": configured_service,
"platega_service": configured_service,
}
methods = subscription_webapp._serialize_payment_methods(settings, app)
self.assertEqual(methods, [])
def test_serialize_plans_includes_stars_only_subscription_options(self):
settings = Settings(
_env_file=None,
BOT_TOKEN="token",
POSTGRES_USER="app_user",
POSTGRES_PASSWORD="app_password",
YOOKASSA_ENABLED=False,
CRYPTOPAY_ENABLED=False,
TARIFFS_CONFIG_PATH="missing-tariffs.json",
RUB_PRICE_1_MONTH=None,
STARS_PRICE_1_MONTH=250,
)
plans = subscription_webapp._serialize_plans(settings, "en")
self.assertEqual(len(plans), 1)
self.assertEqual(plans[0]["months"], 1)
self.assertEqual(plans[0]["price"], 0.0)
self.assertEqual(plans[0]["stars_price"], 250)
def test_resolve_webapp_js_asset_name_prefers_latest_minified_build(self):
with tempfile.TemporaryDirectory() as tmpdir:
asset_dir = Path(tmpdir)
(asset_dir / "subscription_webapp.js").write_text("console.log('fallback');", encoding="utf-8")
old_asset = asset_dir / "subscription_webapp.min.11111111.js"
new_asset = asset_dir / "subscription_webapp.min.22222222.js"
old_asset.write_text("console.log('old');", encoding="utf-8")
new_asset.write_text("console.log('new');", encoding="utf-8")
os.utime(old_asset, (1, 1))
os.utime(new_asset, (2, 2))
with patch.object(subscription_webapp, "ASSET_DIR", asset_dir):
self.assertEqual(
subscription_webapp._resolve_webapp_js_asset_name(),
"subscription_webapp.min.22222222.js",
)
async def test_js_asset_route_sets_immutable_cache_control_for_minified_asset(self):
with tempfile.TemporaryDirectory() as tmpdir:
asset_dir = Path(tmpdir)
minified_asset = asset_dir / "subscription_webapp.min.abcdef12.js"
minified_asset.write_text("console.log('minified');", encoding="utf-8")
request = SimpleNamespace(
app={"settings": SimpleNamespace(WEBAPP_ENABLED=True)},
match_info={"asset_hash": "abcdef12"},
)
with patch.object(subscription_webapp, "ASSET_DIR", asset_dir):
response = await subscription_webapp.js_asset_route(request)
self.assertEqual(response.headers["Cache-Control"], "public, max-age=31536000, immutable")
self.assertEqual(response.text, "console.log('minified');")