40cba6a567
Extend getUpdates parsing for group joins and update settings UI hints so Chat ID detection works for supergroups, not only private DMs.
215 lines
6.5 KiB
Python
215 lines
6.5 KiB
Python
"""Tests for Telegram settings endpoints and notify toggle hot-reload."""
|
|
|
|
from unittest.mock import AsyncMock, MagicMock, patch
|
|
|
|
import pytest
|
|
from fastapi import HTTPException
|
|
|
|
from server.config import settings
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_telegram_test_400_when_token_missing():
|
|
from server.api.settings import telegram_test_send
|
|
|
|
settings.TELEGRAM_BOT_TOKEN = ""
|
|
settings.TELEGRAM_CHAT_ID = ""
|
|
admin = MagicMock()
|
|
|
|
with pytest.raises(HTTPException) as exc:
|
|
await telegram_test_send(admin=admin)
|
|
assert exc.value.status_code == 400
|
|
assert "TELEGRAM_BOT_TOKEN" in exc.value.detail
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_telegram_test_400_when_chat_id_missing():
|
|
from server.api.settings import telegram_test_send
|
|
|
|
settings.TELEGRAM_BOT_TOKEN = "123456:ABC"
|
|
settings.TELEGRAM_CHAT_ID = ""
|
|
admin = MagicMock()
|
|
|
|
with pytest.raises(HTTPException) as exc:
|
|
await telegram_test_send(admin=admin)
|
|
assert exc.value.status_code == 400
|
|
assert "TELEGRAM_CHAT_ID" in exc.value.detail
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_telegram_test_surfaces_api_error():
|
|
from server.api.settings import telegram_test_send
|
|
|
|
settings.TELEGRAM_BOT_TOKEN = "123456:ABC"
|
|
settings.TELEGRAM_CHAT_ID = "999"
|
|
admin = MagicMock()
|
|
|
|
mock_resp = MagicMock()
|
|
mock_resp.status_code = 400
|
|
mock_resp.json.return_value = {"ok": False, "description": "Bad Request: chat not found"}
|
|
|
|
mock_client = AsyncMock()
|
|
mock_client.__aenter__.return_value = mock_client
|
|
mock_client.__aexit__.return_value = None
|
|
mock_client.post = AsyncMock(return_value=mock_resp)
|
|
|
|
with patch("httpx.AsyncClient", return_value=mock_client):
|
|
with pytest.raises(HTTPException) as exc:
|
|
await telegram_test_send(admin=admin)
|
|
|
|
assert exc.value.status_code == 502
|
|
assert "chat not found" in exc.value.detail
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_telegram_chats_surfaces_api_error_when_ok_false():
|
|
from server.api.settings import telegram_get_chats
|
|
|
|
settings.TELEGRAM_BOT_TOKEN = "123456:ABC"
|
|
admin = MagicMock()
|
|
|
|
wh_resp = MagicMock()
|
|
wh_resp.status_code = 200
|
|
wh_resp.json.return_value = {"ok": True, "result": {"url": ""}}
|
|
|
|
updates_resp = MagicMock()
|
|
updates_resp.status_code = 200
|
|
updates_resp.json.return_value = {"ok": False, "description": "Unauthorized"}
|
|
|
|
mock_client = AsyncMock()
|
|
mock_client.__aenter__.return_value = mock_client
|
|
mock_client.__aexit__.return_value = None
|
|
mock_client.get = AsyncMock(side_effect=[wh_resp, updates_resp])
|
|
mock_client.post = AsyncMock()
|
|
|
|
with patch("httpx.AsyncClient", return_value=mock_client):
|
|
with pytest.raises(HTTPException) as exc:
|
|
await telegram_get_chats(admin=admin)
|
|
|
|
assert exc.value.status_code == 502
|
|
assert "Unauthorized" in exc.value.detail
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_telegram_chats_deletes_webhook_before_get_updates():
|
|
from server.api.settings import telegram_get_chats
|
|
|
|
settings.TELEGRAM_BOT_TOKEN = "123456:ABC"
|
|
admin = MagicMock()
|
|
|
|
wh_resp = MagicMock()
|
|
wh_resp.status_code = 200
|
|
wh_resp.json.return_value = {"ok": True, "result": {"url": "https://example.com/hook"}}
|
|
|
|
del_resp = MagicMock()
|
|
del_resp.json.return_value = {"ok": True}
|
|
|
|
updates_resp = MagicMock()
|
|
updates_resp.status_code = 200
|
|
updates_resp.json.return_value = {
|
|
"ok": True,
|
|
"result": [
|
|
{
|
|
"message": {
|
|
"chat": {"id": 42, "type": "private", "first_name": "Test"},
|
|
},
|
|
},
|
|
],
|
|
}
|
|
|
|
mock_client = AsyncMock()
|
|
mock_client.__aenter__.return_value = mock_client
|
|
mock_client.__aexit__.return_value = None
|
|
mock_client.get = AsyncMock(side_effect=[wh_resp, updates_resp])
|
|
mock_client.post = AsyncMock(return_value=del_resp)
|
|
|
|
with patch("httpx.AsyncClient", return_value=mock_client):
|
|
result = await telegram_get_chats(admin=admin)
|
|
|
|
assert result["count"] == 1
|
|
assert result["chats"][0]["id"] == 42
|
|
mock_client.post.assert_called_once()
|
|
call_args = mock_client.post.call_args
|
|
assert "deleteWebhook" in call_args[0][0]
|
|
assert call_args[1]["params"]["drop_pending_updates"] == "false"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_telegram_chats_includes_my_chat_member_supergroup():
|
|
from server.api.settings import _extract_chats_from_telegram_updates
|
|
|
|
updates = [
|
|
{
|
|
"my_chat_member": {
|
|
"chat": {"id": -1001234567890, "title": "Ops Group", "type": "supergroup"},
|
|
},
|
|
},
|
|
{
|
|
"message": {
|
|
"chat": {"id": 42, "type": "private", "first_name": "Alice"},
|
|
},
|
|
},
|
|
]
|
|
chats = _extract_chats_from_telegram_updates(updates)
|
|
assert len(chats) == 2
|
|
assert chats[0]["id"] == -1001234567890
|
|
assert chats[0]["type"] == "supergroup"
|
|
assert chats[0]["title"] == "Ops Group"
|
|
assert chats[1]["id"] == 42
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_telegram_chats_includes_edited_message_and_callback():
|
|
from server.api.settings import _extract_chats_from_telegram_updates
|
|
|
|
updates = [
|
|
{
|
|
"edited_message": {
|
|
"chat": {"id": -10099, "title": "Alert Room", "type": "group"},
|
|
},
|
|
},
|
|
{
|
|
"callback_query": {
|
|
"message": {
|
|
"chat": {"id": -10088, "title": "Channel", "type": "channel"},
|
|
},
|
|
},
|
|
},
|
|
]
|
|
chats = _extract_chats_from_telegram_updates(updates)
|
|
ids = {c["id"] for c in chats}
|
|
assert -10099 in ids
|
|
assert -10088 in ids
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_notify_toggle_put_hot_reload():
|
|
from server.api.schemas import SettingUpdatePayload
|
|
from server.api.settings import set_setting
|
|
from server.domain.models import Setting
|
|
|
|
settings.NOTIFY_ALERT_CPU = "true"
|
|
admin = MagicMock(username="admin")
|
|
request = MagicMock()
|
|
request.client.host = "127.0.0.1"
|
|
db = MagicMock()
|
|
|
|
mock_setting = Setting(key="notify_alert_cpu", value="false")
|
|
|
|
with patch("server.api.settings.SettingRepositoryImpl") as repo_cls, patch(
|
|
"server.api.settings.AuditLogRepositoryImpl"
|
|
) as audit_cls:
|
|
repo = repo_cls.return_value
|
|
repo.set = AsyncMock(return_value=mock_setting)
|
|
audit_cls.return_value.create = AsyncMock()
|
|
|
|
await set_setting(
|
|
"notify_alert_cpu",
|
|
SettingUpdatePayload(value="false"),
|
|
request,
|
|
admin,
|
|
db,
|
|
)
|
|
|
|
assert settings.NOTIFY_ALERT_CPU == "false"
|