mirror of
https://github.com/BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot.git
synced 2026-02-24 05:11:15 +00:00
- Add pyproject.toml with uv and ruff configuration - Pin Python version to 3.13 via .python-version - Add Makefile commands: lint, format, fix - Apply ruff formatting to entire codebase - Remove unused imports (base64 in yookassa/simple_subscription) - Update .gitignore for new config files
157 lines
5.8 KiB
Python
157 lines
5.8 KiB
Python
import json
|
|
from pathlib import Path
|
|
from types import SimpleNamespace
|
|
from unittest.mock import AsyncMock
|
|
|
|
import pytest
|
|
from fastapi import FastAPI, status
|
|
|
|
from app.config import settings
|
|
from app.services.payment_service import PaymentService
|
|
|
|
|
|
# ensure backup directory exists before importing the unified app to avoid side effects during module import
|
|
_backup_dir = Path('data/backups')
|
|
_backup_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
from app.webserver.unified_app import create_unified_app
|
|
|
|
|
|
@pytest.mark.anyio
|
|
async def test_unified_app_health_reports_features(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
|
|
bot = AsyncMock()
|
|
dispatcher = SimpleNamespace(feed_update=AsyncMock())
|
|
payment_service = AsyncMock(spec=PaymentService)
|
|
|
|
miniapp_static_dir = tmp_path / 'miniapp'
|
|
miniapp_static_dir.mkdir()
|
|
|
|
monkeypatch.setattr(settings, 'WEB_API_ENABLED', True, raising=False)
|
|
monkeypatch.setattr(settings, 'TRIBUTE_ENABLED', True, raising=False)
|
|
monkeypatch.setattr(settings, 'WEBHOOK_URL', 'https://hooks.example.com', raising=False)
|
|
monkeypatch.setattr(settings, 'WEBHOOK_PATH', '/telegram-webhook', raising=False)
|
|
monkeypatch.setattr(settings, 'WEBHOOK_SECRET_TOKEN', 'super-secret', raising=False)
|
|
monkeypatch.setattr(settings, 'BOT_RUN_MODE', 'webhook', raising=False)
|
|
monkeypatch.setattr(settings, 'WEBHOOK_MAX_QUEUE_SIZE', 8, raising=False)
|
|
monkeypatch.setattr(settings, 'WEBHOOK_WORKERS', 1, raising=False)
|
|
monkeypatch.setattr(settings, 'WEBHOOK_ENQUEUE_TIMEOUT', 0.0, raising=False)
|
|
monkeypatch.setattr(settings, 'WEBHOOK_WORKER_SHUTDOWN_TIMEOUT', 1.0, raising=False)
|
|
monkeypatch.setattr(settings, 'MINIAPP_STATIC_PATH', str(miniapp_static_dir), raising=False)
|
|
|
|
app = create_unified_app(
|
|
bot,
|
|
dispatcher, # type: ignore[arg-type]
|
|
payment_service,
|
|
enable_telegram_webhook=True,
|
|
)
|
|
|
|
health_route = next(
|
|
route
|
|
for route in app.routes
|
|
if getattr(route, 'endpoint', None) and getattr(route.endpoint, '__name__', '') == 'unified_health'
|
|
)
|
|
|
|
assert getattr(health_route, 'path', None) == '/health/unified'
|
|
|
|
await app.router.startup()
|
|
try:
|
|
response = await health_route.endpoint() # type: ignore[func-returns-value]
|
|
finally:
|
|
await app.router.shutdown()
|
|
|
|
payload = json.loads(response.body.decode('utf-8')) # type: ignore[attr-defined]
|
|
|
|
assert payload['status'] == 'ok'
|
|
assert payload['web_api_enabled'] is True
|
|
assert payload['bot_run_mode'] == 'webhook'
|
|
assert payload['telegram_webhook']['enabled'] is True
|
|
assert payload['telegram_webhook']['running'] is True
|
|
assert payload['telegram_webhook']['path'] == '/telegram-webhook'
|
|
assert payload['telegram_webhook']['secret_configured'] is True
|
|
assert payload['payment_webhooks']['enabled'] is True
|
|
assert payload['payment_webhooks']['providers']['tribute'] is True
|
|
assert payload['miniapp_static']['mounted'] is True
|
|
assert payload['miniapp_static']['path'].endswith('miniapp')
|
|
|
|
|
|
def _build_unified_app(monkeypatch: pytest.MonkeyPatch, docs_enabled: bool) -> FastAPI:
|
|
bot = AsyncMock()
|
|
dispatcher = SimpleNamespace(feed_update=AsyncMock())
|
|
payment_service = AsyncMock(spec=PaymentService)
|
|
|
|
monkeypatch.setattr(settings, 'WEB_API_ENABLED', False, raising=False)
|
|
monkeypatch.setattr(settings, 'WEB_API_DOCS_ENABLED', docs_enabled, raising=False)
|
|
monkeypatch.setattr(settings, 'MINIAPP_STATIC_PATH', 'miniapp', raising=False)
|
|
|
|
return create_unified_app(
|
|
bot,
|
|
dispatcher, # type: ignore[arg-type]
|
|
payment_service,
|
|
enable_telegram_webhook=False,
|
|
)
|
|
|
|
|
|
@pytest.mark.anyio
|
|
async def test_unified_app_health_path_without_admin(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
bot = AsyncMock()
|
|
dispatcher = SimpleNamespace(feed_update=AsyncMock())
|
|
payment_service = AsyncMock(spec=PaymentService)
|
|
|
|
monkeypatch.setattr(settings, 'WEB_API_ENABLED', False, raising=False)
|
|
monkeypatch.setattr(settings, 'MINIAPP_STATIC_PATH', 'miniapp', raising=False)
|
|
|
|
app = create_unified_app(
|
|
bot,
|
|
dispatcher, # type: ignore[arg-type]
|
|
payment_service,
|
|
enable_telegram_webhook=False,
|
|
)
|
|
|
|
health_route = next(
|
|
route
|
|
for route in app.routes
|
|
if getattr(route, 'endpoint', None) and getattr(route.endpoint, '__name__', '') == 'unified_health'
|
|
)
|
|
|
|
assert getattr(health_route, 'path', None) == '/health'
|
|
|
|
|
|
def test_unified_app_docs_disabled(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
app = _build_unified_app(monkeypatch, docs_enabled=False)
|
|
|
|
assert app.docs_url is None
|
|
assert app.redoc_url is None
|
|
assert app.openapi_url is None
|
|
|
|
registered_paths = {getattr(route, 'path', None) for route in app.routes}
|
|
assert '/doc' not in registered_paths
|
|
|
|
|
|
@pytest.mark.anyio
|
|
async def test_unified_app_docs_enabled_with_alias(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
app = _build_unified_app(monkeypatch, docs_enabled=True)
|
|
|
|
assert app.docs_url == '/docs'
|
|
assert app.openapi_url == '/openapi.json'
|
|
|
|
alias_route = next(
|
|
(route for route in app.routes if getattr(route, 'path', None) == '/doc'),
|
|
None,
|
|
)
|
|
assert alias_route is not None
|
|
assert getattr(alias_route, 'include_in_schema', True) is False
|
|
|
|
redoc_route = next(
|
|
(route for route in app.routes if getattr(route, 'path', None) == '/redoc'),
|
|
None,
|
|
)
|
|
assert redoc_route is not None
|
|
assert getattr(redoc_route, 'include_in_schema', True) is False
|
|
|
|
response = await alias_route.endpoint() # type: ignore[func-returns-value]
|
|
assert response.status_code == status.HTTP_307_TEMPORARY_REDIRECT
|
|
assert response.headers['location'] == '/docs'
|
|
|
|
redoc_response = await redoc_route.endpoint() # type: ignore[func-returns-value]
|
|
assert b'ReDoc' in redoc_response.body # type: ignore[attr-defined]
|