""" Tests for BATON-ARCH-013: Keep-alive mechanism / health endpoint. Acceptance criteria: 1. GET /health returns HTTP 200 OK. 2. Response body contains JSON with {"status": "ok"}. 3. Endpoint does not require authorization (no token, no secret header needed). 4. Keep-alive loop is started when APP_URL is set, and NOT started when APP_URL is unset. """ from __future__ import annotations import os os.environ.setdefault("BOT_TOKEN", "test-bot-token") os.environ.setdefault("CHAT_ID", "-1001234567890") os.environ.setdefault("WEBHOOK_SECRET", "test-webhook-secret") os.environ.setdefault("WEBHOOK_URL", "https://example.com/api/webhook/telegram") os.environ.setdefault("FRONTEND_ORIGIN", "http://localhost:3000") from unittest.mock import AsyncMock, patch import pytest from tests.conftest import make_app_client, temp_db # --------------------------------------------------------------------------- # Criterion 1 & 2 & 3 — GET /health → 200 OK, {"status": "ok"}, no auth # --------------------------------------------------------------------------- @pytest.mark.asyncio async def test_health_returns_200_ok(): """GET /health должен вернуть HTTP 200 без какого-либо заголовка авторизации.""" async with make_app_client() as client: response = await client.get("/health") assert response.status_code == 200 @pytest.mark.asyncio async def test_health_returns_status_ok(): """GET /health должен вернуть JSON содержащий {"status": "ok"}.""" async with make_app_client() as client: response = await client.get("/health") data = response.json() assert data.get("status") == "ok" # --------------------------------------------------------------------------- # Criterion 4 — keep-alive task lifecycle # --------------------------------------------------------------------------- @pytest.mark.asyncio async def test_keepalive_started_when_app_url_set(): """Keep-alive задача должна стартовать при наличии APP_URL.""" from backend.main import app with temp_db(): with patch("backend.telegram.set_webhook", new_callable=AsyncMock): with patch("backend.config.APP_URL", "https://example.com"): with patch("backend.main._keep_alive_loop", new_callable=AsyncMock) as mock_loop: async with app.router.lifespan_context(app): pass # asyncio.create_task вызывается с корутиной _keep_alive_loop — проверяем что она была вызвана assert mock_loop.called @pytest.mark.asyncio async def test_keepalive_not_started_when_app_url_unset(): """Keep-alive задача НЕ должна стартовать при отсутствии APP_URL.""" from backend.main import app with temp_db(): with patch("backend.telegram.set_webhook", new_callable=AsyncMock): with patch("backend.config.APP_URL", None): with patch("backend.main._keep_alive_loop", new_callable=AsyncMock) as mock_loop: async with app.router.lifespan_context(app): pass assert not mock_loop.called