184 lines
7.2 KiB
Python
184 lines
7.2 KiB
Python
"""Tests for the ``can_topup_devices`` flag emitted by ``_serialize_subscription``.
|
|
|
|
The flag drives whether the Web App shows the "buy HWID devices" button on the
|
|
Devices screen. Before this flag existed, the button was rendered for any
|
|
active subscription with a finite ``max_devices``, even if the user's tariff
|
|
had no HWID device packages configured. Clicking led the user into an empty
|
|
modal with "no options available" — confusing.
|
|
|
|
These tests pin the visibility contract:
|
|
|
|
* unlimited subscribers (max_devices == 0) → flag is False (no point in top-up);
|
|
* tariff without HWID packages → flag is False;
|
|
* tariff with rub-only or stars-only packages → flag is True;
|
|
* legacy mode without a tariffs catalog → flag is False;
|
|
* malformed tariff lookup → flag is False (and does not raise).
|
|
"""
|
|
|
|
import json
|
|
import tempfile
|
|
import unittest
|
|
from datetime import datetime, timedelta, timezone
|
|
from pathlib import Path
|
|
from typing import Any, Dict, Optional
|
|
|
|
# Importing the facade populates ``webapp.serializers`` with helpers like
|
|
# ``_format_remaining`` that come from ``_runtime`` via the facade init dance.
|
|
import bot.app.web.subscription_webapp # noqa: F401
|
|
from bot.app.web.webapp.serializers import _serialize_subscription
|
|
from config.settings import Settings
|
|
|
|
|
|
def _tariffs_payload(*, hwid_rub=None, hwid_stars=None, has_premium=False) -> dict:
|
|
tariff: Dict[str, Any] = {
|
|
"key": "standard",
|
|
"names": {"en": "Standard"},
|
|
"descriptions": {"en": "Base"},
|
|
"squad_uuids": ["main"],
|
|
"billing_model": "period",
|
|
"monthly_gb": 100,
|
|
"prices_rub": {"1": 100},
|
|
"prices_stars": {"1": 0},
|
|
"enabled_periods": [1],
|
|
"hwid_device_limit": 3,
|
|
"enabled": True,
|
|
}
|
|
if has_premium:
|
|
tariff.update(
|
|
premium_squad_uuids=["premium-squad"],
|
|
premium_monthly_gb=20,
|
|
)
|
|
if hwid_rub or hwid_stars:
|
|
tariff["hwid_device_packages"] = {
|
|
"rub": hwid_rub or [],
|
|
"stars": hwid_stars or [],
|
|
}
|
|
return {"default_tariff": "standard", "tariffs": [tariff]}
|
|
|
|
|
|
def _make_settings(tmpdir: str, payload: Optional[dict] = None, **overrides: Any) -> Settings:
|
|
values: Dict[str, Any] = {
|
|
"_env_file": None,
|
|
"BOT_TOKEN": "token",
|
|
"POSTGRES_USER": "u",
|
|
"POSTGRES_PASSWORD": "p",
|
|
}
|
|
if payload is not None:
|
|
config_path = Path(tmpdir) / "tariffs.json"
|
|
config_path.write_text(json.dumps(payload), encoding="utf-8")
|
|
values["TARIFFS_CONFIG_PATH"] = str(config_path)
|
|
values.update(overrides)
|
|
return Settings(**values)
|
|
|
|
|
|
def _active(**overrides) -> Dict[str, Any]:
|
|
"""Build a minimal ``active`` dict shaped like ``get_active_subscription_details``."""
|
|
base: Dict[str, Any] = {
|
|
"tariff_key": "standard",
|
|
"status_from_panel": "ACTIVE",
|
|
"end_date": datetime.now(timezone.utc) + timedelta(days=30),
|
|
"traffic_limit_bytes": 0,
|
|
"traffic_used_bytes": 0,
|
|
"premium_baseline_bytes": 0,
|
|
"premium_topup_balance_bytes": 0,
|
|
"premium_topup_used_bytes": 0,
|
|
"premium_used_bytes": 0,
|
|
"premium_limit_bytes": 0,
|
|
"max_devices": 3,
|
|
"base_hwid_device_limit": 3,
|
|
"extra_hwid_devices": 0,
|
|
"billing_model": "period",
|
|
"tariff_name": "Standard",
|
|
"tariff_description": "Base",
|
|
"premium_title": None,
|
|
"config_link": None,
|
|
"connect_button_url": None,
|
|
"tier_baseline_bytes": 0,
|
|
"topup_balance_bytes": 0,
|
|
"premium_bonus_bytes": 0,
|
|
"regular_bonus_bytes": 0,
|
|
"regular_unlimited_override": False,
|
|
"premium_unlimited_override": False,
|
|
"premium_is_limited": False,
|
|
"premium_squad_labels": [],
|
|
"premium_node_labels": [],
|
|
"period_start_at": None,
|
|
"is_throttled": False,
|
|
"traffic_limit_strategy": "",
|
|
}
|
|
base.update(overrides)
|
|
return base
|
|
|
|
|
|
class CanTopupDevicesFlagTests(unittest.TestCase):
|
|
def test_flag_is_true_when_tariff_has_rub_packages_and_limit_is_finite(self):
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
settings = _make_settings(
|
|
tmpdir,
|
|
_tariffs_payload(hwid_rub=[{"count": 1, "price": 50}]),
|
|
)
|
|
payload = _serialize_subscription(settings, _active(max_devices=3), None, "en")
|
|
self.assertTrue(payload["can_topup_devices"])
|
|
|
|
def test_flag_is_true_when_only_stars_packages_are_configured(self):
|
|
# The bot only renders rub packages today, but the Web App grid renders
|
|
# both. The flag drives the Web App, so stars-only must still be True.
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
settings = _make_settings(
|
|
tmpdir,
|
|
_tariffs_payload(hwid_stars=[{"count": 1, "price": 100}]),
|
|
)
|
|
payload = _serialize_subscription(settings, _active(max_devices=3), None, "en")
|
|
self.assertTrue(payload["can_topup_devices"])
|
|
|
|
def test_flag_is_false_when_max_devices_is_zero(self):
|
|
# Unlimited subscribers: hwid_devices topup is a dead end.
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
settings = _make_settings(
|
|
tmpdir,
|
|
_tariffs_payload(hwid_rub=[{"count": 1, "price": 50}]),
|
|
)
|
|
payload = _serialize_subscription(settings, _active(max_devices=0), None, "en")
|
|
self.assertFalse(payload["can_topup_devices"])
|
|
|
|
def test_flag_is_false_when_tariff_has_no_hwid_packages(self):
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
settings = _make_settings(tmpdir, _tariffs_payload())
|
|
payload = _serialize_subscription(settings, _active(), None, "en")
|
|
self.assertFalse(payload["can_topup_devices"])
|
|
|
|
def test_flag_is_false_when_packages_dict_is_empty(self):
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
settings = _make_settings(
|
|
tmpdir,
|
|
_tariffs_payload(hwid_rub=[], hwid_stars=[]),
|
|
)
|
|
payload = _serialize_subscription(settings, _active(), None, "en")
|
|
self.assertFalse(payload["can_topup_devices"])
|
|
|
|
def test_flag_is_false_in_legacy_mode_without_tariff_catalog(self):
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
settings = _make_settings(tmpdir, payload=None) # no tariffs catalog
|
|
payload = _serialize_subscription(
|
|
settings, _active(tariff_key=None, max_devices=3), None, "en"
|
|
)
|
|
self.assertFalse(payload["can_topup_devices"])
|
|
|
|
def test_flag_is_false_when_tariff_key_does_not_resolve(self):
|
|
# If the user's stored ``tariff_key`` was renamed/removed, the helper
|
|
# raises inside ``settings.tariffs_config.require``; we should swallow
|
|
# that and emit False, not 500.
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
settings = _make_settings(
|
|
tmpdir,
|
|
_tariffs_payload(hwid_rub=[{"count": 1, "price": 50}]),
|
|
)
|
|
payload = _serialize_subscription(
|
|
settings, _active(tariff_key="missing-tariff"), None, "en"
|
|
)
|
|
self.assertFalse(payload["can_topup_devices"])
|
|
|
|
|
|
if __name__ == "__main__": # pragma: no cover
|
|
unittest.main()
|