import unittest from types import SimpleNamespace from unittest.mock import AsyncMock, patch import bot.app.web.subscription_webapp # noqa: F401 from bot.app.web.admin_api_impl import stats as stats_module class AdminPanelStatsCacheTests(unittest.IsolatedAsyncioTestCase): async def asyncSetUp(self): stats_module._ADMIN_PANEL_STATS_CACHES.clear() stats_module._ADMIN_DB_STATS_CACHES.clear() async def asyncTearDown(self): stats_module._ADMIN_PANEL_STATS_CACHES.clear() stats_module._ADMIN_DB_STATS_CACHES.clear() def _settings(self): return SimpleNamespace( ADMIN_PANEL_STATS_CACHE_TTL_SECONDS=15, REDIS_URL="redis://redis:6379/0", REDIS_KEY_PREFIX="shop", ) def _panel_service(self): return SimpleNamespace( get_system_stats=AsyncMock(return_value={"users": {"totalUsers": 10}}), get_bandwidth_stats=AsyncMock(return_value={"current": 123}), get_nodes_statistics=AsyncMock(return_value={"nodes": []}), get_nodes_bandwidth_usage=AsyncMock(return_value={"topNodes": []}), get_nodes_online_lookups=AsyncMock(return_value={"byUuid": {}, "byName": {}}), ) async def test_admin_panel_stats_are_cached_between_requests(self): settings = self._settings() panel_service = self._panel_service() cache_store = {} async def fake_get(_settings, key): return cache_store.get(key) async def fake_set(_settings, key, value, ttl): cache_store[key] = value with ( patch("bot.infra.redis.cache_get_json", fake_get), patch("bot.infra.redis.cache_set_json", fake_set), ): first = await stats_module._load_admin_panel_stats(None, settings, panel_service) second = await stats_module._load_admin_panel_stats(None, settings, panel_service) self.assertEqual(first, second) panel_service.get_system_stats.assert_awaited_once() panel_service.get_bandwidth_stats.assert_awaited_once() panel_service.get_nodes_statistics.assert_awaited_once() panel_service.get_nodes_bandwidth_usage.assert_awaited_once() panel_service.get_nodes_online_lookups.assert_awaited_once() class AdminDbStatsCacheTests(unittest.IsolatedAsyncioTestCase): async def asyncSetUp(self): stats_module._ADMIN_DB_STATS_CACHES.clear() async def asyncTearDown(self): stats_module._ADMIN_DB_STATS_CACHES.clear() def _settings(self): return SimpleNamespace( ADMIN_DB_STATS_CACHE_TTL_SECONDS=15, REDIS_URL="redis://redis:6379/0", REDIS_KEY_PREFIX="shop", ) class _SessionFactory: def __call__(self): return self async def __aenter__(self): return SimpleNamespace() async def __aexit__(self, exc_type, exc, tb): return None async def test_admin_db_stats_are_cached_between_requests(self): settings = self._settings() cache_store = {} async def fake_get(_settings, key): return cache_store.get(key) async def fake_set(_settings, key, value, ttl): cache_store[key] = value user_stats = AsyncMock( return_value={ "total_users": 10, "banned_users": 1, "active_today": 2, "active_subscriptions": 8, "paid_subscriptions": 7, "trial_users": 1, "free_subscription_users": 0, "inactive_users": 2, "expired_subscription_users": 1, "referral_users": 3, } ) financial_stats = AsyncMock( return_value={ "today_revenue": 1.0, "week_revenue": 2.0, "month_revenue": 3.0, "all_time_revenue": 4.0, "today_payments_count": 1, "daily_series": [], } ) sync_status = AsyncMock( return_value=SimpleNamespace( status="success", last_sync_time=None, details=None, users_processed_from_panel=10, subscriptions_synced=7, ) ) recent_payments = AsyncMock( return_value=[ SimpleNamespace( payment_id=1, user_id=10, provider="test", provider_payment_id="provider-1", amount=123.45, currency="RUB", status="succeeded", description="Test payment", subscription_duration_months=1, sale_mode=None, tariff_key=None, purchased_gb=None, purchased_hwid_devices=None, created_at=None, ) ] ) with ( patch("bot.infra.redis.cache_get_json", fake_get), patch("bot.infra.redis.cache_set_json", fake_set), patch.object(stats_module.user_dal, "get_enhanced_user_statistics", user_stats), patch.object(stats_module.payment_dal, "get_financial_statistics", financial_stats), patch.object(stats_module.panel_sync_dal, "get_panel_sync_status", sync_status), patch.object( stats_module.payment_dal, "get_recent_payment_logs_with_user", recent_payments, ), ): first = await stats_module._load_admin_db_stats(settings, self._SessionFactory()) second = await stats_module._load_admin_db_stats(settings, self._SessionFactory()) self.assertEqual(first, second) self.assertEqual(first["recent_payments"][0]["payment_id"], 1) user_stats.assert_awaited_once() financial_stats.assert_awaited_once() sync_status.assert_awaited_once() recent_payments.assert_awaited_once() if __name__ == "__main__": unittest.main()