Files
orchestrator/tests/test_link_preview_disabled.py
claude-bot d826eacfcf 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>
2026-06-09 01:32:53 +03:00

160 lines
6.4 KiB
Python

"""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