kin: BATON-007 При нажатии на кнопку происходит анимация и сообщение что сигнал отправлен, но в телеграм группу ничего не приходит.
This commit is contained in:
parent
726bb0a82c
commit
e21bcb1eb4
1 changed files with 121 additions and 2 deletions
|
|
@ -15,6 +15,7 @@ Physical delivery to an actual Telegram group is outside unit test scope.
|
|||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
import os
|
||||
|
||||
os.environ.setdefault("BOT_TOKEN", "test-bot-token")
|
||||
|
|
@ -30,9 +31,9 @@ from unittest.mock import AsyncMock, patch
|
|||
import httpx
|
||||
import pytest
|
||||
import respx
|
||||
from httpx import AsyncClient
|
||||
from httpx import AsyncClient, ASGITransport
|
||||
|
||||
from tests.conftest import make_app_client
|
||||
from tests.conftest import make_app_client, temp_db
|
||||
|
||||
# Valid UUID v4 constants — must not collide with UUIDs in other test files
|
||||
_UUID_A = "d0000001-0000-4000-8000-000000000001"
|
||||
|
|
@ -40,6 +41,7 @@ _UUID_B = "d0000002-0000-4000-8000-000000000002"
|
|||
_UUID_C = "d0000003-0000-4000-8000-000000000003"
|
||||
_UUID_D = "d0000004-0000-4000-8000-000000000004"
|
||||
_UUID_E = "d0000005-0000-4000-8000-000000000005"
|
||||
_UUID_F = "d0000006-0000-4000-8000-000000000006"
|
||||
|
||||
|
||||
async def _register(client: AsyncClient, uuid: str, name: str) -> str:
|
||||
|
|
@ -260,3 +262,120 @@ async def test_repeated_signals_produce_incrementing_signal_ids():
|
|||
assert r2.json()["signal_id"] > r1.json()["signal_id"], (
|
||||
"Second signal must have a higher signal_id than the first"
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Director revision: regression #1214, #1226
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_send_message_uses_negative_chat_id_from_config():
|
||||
"""Regression #1226: send_message must POST to Telegram with a negative chat_id.
|
||||
|
||||
Root cause of BATON-007: CHAT_ID=5190015988 (positive = user ID) was set in .env
|
||||
instead of -5190015988 (negative = group ID). This test inspects the actual
|
||||
chat_id value in the HTTP request body — not just call_count.
|
||||
"""
|
||||
from backend import config as _cfg
|
||||
from backend.telegram import send_message
|
||||
|
||||
send_url = f"https://api.telegram.org/bot{_cfg.BOT_TOKEN}/sendMessage"
|
||||
|
||||
with respx.mock(assert_all_called=False) as mock:
|
||||
route = mock.post(send_url).mock(
|
||||
return_value=httpx.Response(200, json={"ok": True})
|
||||
)
|
||||
await send_message("regression #1226")
|
||||
|
||||
assert route.called
|
||||
body = json.loads(route.calls[0].request.content)
|
||||
chat_id = body["chat_id"]
|
||||
assert chat_id == _cfg.CHAT_ID, (
|
||||
f"Expected chat_id={_cfg.CHAT_ID!r}, got {chat_id!r}"
|
||||
)
|
||||
assert str(chat_id).startswith("-"), (
|
||||
f"Regression #1226: chat_id must be negative (group ID), got: {chat_id!r}. "
|
||||
"Positive chat_id is a user ID, not a Telegram group."
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_send_message_4xx_does_not_trigger_retry_loop():
|
||||
"""Regression #1214: on Telegram 4xx (wrong chat_id), retry loop must NOT run.
|
||||
|
||||
Only one HTTP call should be made. Retrying a 4xx is pointless — it will
|
||||
keep failing. send_message must break immediately on any 4xx response.
|
||||
"""
|
||||
from backend import config as _cfg
|
||||
from backend.telegram import send_message
|
||||
|
||||
send_url = f"https://api.telegram.org/bot{_cfg.BOT_TOKEN}/sendMessage"
|
||||
|
||||
with respx.mock(assert_all_called=False) as mock:
|
||||
route = mock.post(send_url).mock(
|
||||
return_value=httpx.Response(
|
||||
400,
|
||||
json={"ok": False, "error_code": 400, "description": "Bad Request: chat not found"},
|
||||
)
|
||||
)
|
||||
await send_message("retry test #1214")
|
||||
|
||||
assert route.call_count == 1, (
|
||||
f"Regression #1214: expected exactly 1 HTTP call on 4xx, got {route.call_count}. "
|
||||
"send_message must break immediately on client errors — no retry loop."
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_signal_endpoint_returns_200_on_telegram_4xx(caplog):
|
||||
"""Regression: /api/signal must return 200 even when Telegram Bot API returns 4xx.
|
||||
|
||||
When CHAT_ID is wrong (or any Telegram 4xx), the error must be logged by
|
||||
send_message but the /api/signal endpoint must still return 200 — the signal
|
||||
was saved to DB, only the Telegram notification failed.
|
||||
"""
|
||||
from backend import config as _cfg
|
||||
from backend.main import app
|
||||
|
||||
send_url = f"https://api.telegram.org/bot{_cfg.BOT_TOKEN}/sendMessage"
|
||||
tg_set_url = f"https://api.telegram.org/bot{_cfg.BOT_TOKEN}/setWebhook"
|
||||
get_me_url = f"https://api.telegram.org/bot{_cfg.BOT_TOKEN}/getMe"
|
||||
|
||||
with temp_db():
|
||||
with respx.mock(assert_all_called=False) as mock_tg:
|
||||
mock_tg.get(get_me_url).mock(
|
||||
return_value=httpx.Response(200, json={"ok": True, "result": {"username": "testbot"}})
|
||||
)
|
||||
mock_tg.post(tg_set_url).mock(
|
||||
return_value=httpx.Response(200, json={"ok": True, "result": True})
|
||||
)
|
||||
mock_tg.post(send_url).mock(
|
||||
return_value=httpx.Response(
|
||||
400,
|
||||
json={"ok": False, "error_code": 400, "description": "Bad Request: chat not found"},
|
||||
)
|
||||
)
|
||||
|
||||
async with app.router.lifespan_context(app):
|
||||
transport = ASGITransport(app=app)
|
||||
async with AsyncClient(transport=transport, base_url="http://testserver") as client:
|
||||
reg = await client.post("/api/register", json={"uuid": _UUID_F, "name": "Tg4xxUser"})
|
||||
assert reg.status_code == 200, f"Register failed: {reg.text}"
|
||||
api_key = reg.json()["api_key"]
|
||||
|
||||
with caplog.at_level(logging.ERROR, logger="backend.telegram"):
|
||||
resp = await client.post(
|
||||
"/api/signal",
|
||||
json={"user_id": _UUID_F, "timestamp": 1742478000000},
|
||||
headers={"Authorization": f"Bearer {api_key}"},
|
||||
)
|
||||
await asyncio.sleep(0)
|
||||
|
||||
assert resp.status_code == 200, (
|
||||
f"Expected /api/signal to return 200 even when Telegram returns 4xx, got {resp.status_code}"
|
||||
)
|
||||
assert any("400" in r.message for r in caplog.records), (
|
||||
"Expected ERROR log containing '400' when Telegram returns 4xx. "
|
||||
"Error must be logged, not silently swallowed."
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue