fix: disable Telegram link-preview in tracker notifications (ORCH-080)
Add "disable_web_page_preview": True to the JSON payload of both low-level Telegram primitives — send_telegram (POST /sendMessage) and edit_telegram (POST /editMessageText). Telegram no longer expands the Plane "Modern project management" link-preview banner under every tracker card (bump/edit) and notify/alert message, which the default bump mode (ORCH-067) was duplicating on each transition. Single-point fix at the primitive level — all consumers (update_task_tracker, notify_approve_requested, notify_error, stage alerts from launcher/stage_engine) inherit it without code changes. parse_mode: HTML is preserved so the ORCH-NNN issue link stays clickable; disable_notification, bump/edit logic, the one-card-per-task invariant, return contracts and never-raise are untouched. Unconditional, no kill-switch (ADR-001). Tests: tests/test_link_preview_disabled.py (TC-01..06). Docs: CHANGELOG, CLAUDE.md, docs/architecture/README.md (Notifications component). Refs: ORCH-080 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
159
tests/test_link_preview_disabled.py
Normal file
159
tests/test_link_preview_disabled.py
Normal file
@@ -0,0 +1,159 @@
|
||||
"""ORCH-080 — suppress Telegram link-preview in tracker/notify primitives.
|
||||
|
||||
Both low-level primitives ``send_telegram`` (POST /sendMessage) and
|
||||
``edit_telegram`` (POST /editMessageText) must add
|
||||
``"disable_web_page_preview": True`` to their JSON payload, so the Plane
|
||||
"Modern project management" banner no longer expands under every tracker card /
|
||||
notification. The clickable issue link must stay clickable -> ``parse_mode:
|
||||
"HTML"`` is preserved in both payloads, and the never-raise / return contracts
|
||||
are unchanged.
|
||||
|
||||
Network is isolated: ``src.notifications.httpx`` is patched; creds are stubbed.
|
||||
Test ids TC-01..TC-06 from 04-test-plan.yaml.
|
||||
"""
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
os.environ.setdefault("ORCH_PLANE_API_TOKEN", "test-token")
|
||||
os.environ.setdefault("ORCH_GITEA_TOKEN", "test-token")
|
||||
|
||||
_test_db = os.path.join(tempfile.gettempdir(), "test_orchestrator_link_preview.db")
|
||||
os.environ.setdefault("ORCH_DB_PATH", _test_db)
|
||||
|
||||
from src import notifications as N # noqa: E402
|
||||
|
||||
# conftest._no_telegram autouse-patches src.notifications.send_telegram to a
|
||||
# no-op for every test (prod-leak guard). Capture the REAL implementation at
|
||||
# import time (before any fixture runs) so these payload tests can exercise it.
|
||||
_REAL_SEND = N.send_telegram
|
||||
|
||||
|
||||
def _patch_tg_creds(monkeypatch):
|
||||
monkeypatch.setattr(N._get_settings(), "telegram_bot_token", "T", raising=False)
|
||||
monkeypatch.setattr(N._get_settings(), "telegram_chat_id", "C", raising=False)
|
||||
|
||||
|
||||
def _ok_resp(message_id=42):
|
||||
resp = MagicMock()
|
||||
resp.json.return_value = {"ok": True, "result": {"message_id": message_id}}
|
||||
return resp
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# TC-01 — send_telegram sets disable_web_page_preview: True
|
||||
# --------------------------------------------------------------------------- #
|
||||
def test_send_telegram_disables_link_preview(monkeypatch):
|
||||
_patch_tg_creds(monkeypatch)
|
||||
with patch("src.notifications.httpx") as hx:
|
||||
hx.post.return_value = _ok_resp()
|
||||
_REAL_SEND("hello")
|
||||
payload = hx.post.call_args.kwargs["json"]
|
||||
assert payload["disable_web_page_preview"] is True
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# TC-02 — edit_telegram sets disable_web_page_preview: True
|
||||
# --------------------------------------------------------------------------- #
|
||||
def test_edit_telegram_disables_link_preview(monkeypatch):
|
||||
_patch_tg_creds(monkeypatch)
|
||||
with patch("src.notifications.httpx") as hx:
|
||||
hx.post.return_value = _ok_resp()
|
||||
N.edit_telegram(1, "hello")
|
||||
payload = hx.post.call_args.kwargs["json"]
|
||||
assert payload["disable_web_page_preview"] is True
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# TC-03 — parse_mode HTML preserved in both payloads (clickable <a href>)
|
||||
# --------------------------------------------------------------------------- #
|
||||
def test_send_telegram_keeps_parse_mode_html(monkeypatch):
|
||||
_patch_tg_creds(monkeypatch)
|
||||
with patch("src.notifications.httpx") as hx:
|
||||
hx.post.return_value = _ok_resp()
|
||||
_REAL_SEND("hello")
|
||||
assert hx.post.call_args.kwargs["json"]["parse_mode"] == "HTML"
|
||||
|
||||
|
||||
def test_edit_telegram_keeps_parse_mode_html(monkeypatch):
|
||||
_patch_tg_creds(monkeypatch)
|
||||
with patch("src.notifications.httpx") as hx:
|
||||
hx.post.return_value = _ok_resp()
|
||||
N.edit_telegram(1, "hello")
|
||||
assert hx.post.call_args.kwargs["json"]["parse_mode"] == "HTML"
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# TC-04 — send_telegram preserves existing fields + disable_notification arg
|
||||
# --------------------------------------------------------------------------- #
|
||||
def test_send_telegram_preserves_existing_fields(monkeypatch):
|
||||
_patch_tg_creds(monkeypatch)
|
||||
with patch("src.notifications.httpx") as hx:
|
||||
hx.post.return_value = _ok_resp()
|
||||
_REAL_SEND("body", disable_notification=True)
|
||||
payload = hx.post.call_args.kwargs["json"]
|
||||
assert payload["chat_id"] == "C"
|
||||
assert payload["text"] == "body"
|
||||
assert payload["parse_mode"] == "HTML"
|
||||
assert payload["disable_notification"] is True
|
||||
|
||||
|
||||
def test_send_telegram_disable_notification_default_false(monkeypatch):
|
||||
_patch_tg_creds(monkeypatch)
|
||||
with patch("src.notifications.httpx") as hx:
|
||||
hx.post.return_value = _ok_resp()
|
||||
_REAL_SEND("body")
|
||||
assert hx.post.call_args.kwargs["json"]["disable_notification"] is False
|
||||
|
||||
|
||||
def test_edit_telegram_preserves_existing_fields(monkeypatch):
|
||||
_patch_tg_creds(monkeypatch)
|
||||
with patch("src.notifications.httpx") as hx:
|
||||
hx.post.return_value = _ok_resp()
|
||||
N.edit_telegram(7, "body")
|
||||
payload = hx.post.call_args.kwargs["json"]
|
||||
assert payload["chat_id"] == "C"
|
||||
assert payload["message_id"] == 7
|
||||
assert payload["text"] == "body"
|
||||
assert payload["parse_mode"] == "HTML"
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# TC-05 — return contracts unchanged
|
||||
# --------------------------------------------------------------------------- #
|
||||
def test_send_telegram_returns_message_id(monkeypatch):
|
||||
_patch_tg_creds(monkeypatch)
|
||||
with patch("src.notifications.httpx") as hx:
|
||||
hx.post.return_value = _ok_resp(message_id=99)
|
||||
assert _REAL_SEND("x") == 99
|
||||
|
||||
|
||||
def test_send_telegram_returns_none_without_creds(monkeypatch):
|
||||
monkeypatch.setattr(N._get_settings(), "telegram_bot_token", "", raising=False)
|
||||
monkeypatch.setattr(N._get_settings(), "telegram_chat_id", "", raising=False)
|
||||
assert _REAL_SEND("x") is None
|
||||
|
||||
|
||||
def test_edit_telegram_returns_edit_ok(monkeypatch):
|
||||
_patch_tg_creds(monkeypatch)
|
||||
with patch("src.notifications.httpx") as hx:
|
||||
hx.post.return_value = _ok_resp()
|
||||
assert N.edit_telegram(1, "x") == N.EDIT_OK
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# TC-06 — never-raise: httpx.post raising -> None / EDIT_FAILED
|
||||
# --------------------------------------------------------------------------- #
|
||||
def test_send_telegram_never_raises(monkeypatch):
|
||||
_patch_tg_creds(monkeypatch)
|
||||
with patch("src.notifications.httpx") as hx:
|
||||
hx.post.side_effect = Exception("boom")
|
||||
assert _REAL_SEND("x") is None
|
||||
|
||||
|
||||
def test_edit_telegram_never_raises(monkeypatch):
|
||||
_patch_tg_creds(monkeypatch)
|
||||
with patch("src.notifications.httpx") as hx:
|
||||
hx.post.side_effect = Exception("boom")
|
||||
assert N.edit_telegram(1, "x") == N.EDIT_FAILED
|
||||
Reference in New Issue
Block a user