"""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 _traffic_tariffs_payload(*, hwid_rub=None) -> dict: tariff: Dict[str, Any] = { "key": "traffic", "names": {"en": "Traffic"}, "descriptions": {"en": "Traffic"}, "squad_uuids": ["main"], "billing_model": "traffic", "traffic_packages": {"rub": [{"gb": 100, "price": 100}], "stars": []}, "hwid_device_limit": 3, "enabled": True, } if hwid_rub: tariff["hwid_device_packages"] = {"rub": hwid_rub, "stars": []} return {"default_tariff": "traffic", "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"]) def test_flag_is_false_for_traffic_tariff_even_with_packages(self): with tempfile.TemporaryDirectory() as tmpdir: settings = _make_settings( tmpdir, _traffic_tariffs_payload(hwid_rub=[{"count": 1, "price": 50}]), ) payload = _serialize_subscription( settings, _active(tariff_key="traffic", billing_model="traffic"), None, "en", ) self.assertFalse(payload["can_topup_devices"]) if __name__ == "__main__": # pragma: no cover unittest.main()