work_item: ORCH-017 title: "Прямые ссылки на BRD и Plane-таску в Telegram-уведомлении об апруве" notes: > Тесты изолируют сеть: send_telegram/httpx мокируются, проверяется СФОРМИРОВАННЫЙ текст (и/или reply_markup, если выбран вариант кнопок), а не реальная отправка. БД tasks наполняется фикстурой (work_item_id, repo, branch, plane_issue_id). Маппинг на критерии — в поле acceptance. tests: - id: TC-01 type: unit description: "notify_approve_requested формирует текст с кликабельной ссылкой на 01-brd.md (Gitea branch-view)" module: tests/test_notify_approve_links.py setup: "task в tasks с work_item_id=ORCH-017, repo=orchestrator, branch=feature/ORCH-017-..., gitea_public_url задан; send_telegram замокан" expected: PASS acceptance: [AC-1, AC-3] - id: TC-02 type: unit description: "Текст содержит ссылку на Plane-issue с внешним web-URL + workspace + project + plane_issue_id" module: tests/test_notify_approve_links.py setup: "plane_web_url(ORCH_PLANE_WEB_URL) и workspace заданы; project резолвится по repo; plane_issue_id в tasks" expected: PASS acceptance: [AC-2, AC-3] - id: TC-03 type: unit description: "При пустом gitea_public_url BRD-ссылка строится на gitea_url (фоллбэк); при пустом plane_web_url — на plane_api_url" module: tests/test_notify_approve_links.py expected: PASS acceptance: [AC-3] - id: TC-04 type: unit description: "Сохранён призыв перевести задачу в статус Approved (подстрока 'Approved' присутствует)" module: tests/test_notify_approve_links.py expected: PASS acceptance: [AC-4] - id: TC-05 type: unit description: "send_telegram вызван ровно один раз (пингующее сообщение), без disable_notification=True" module: tests/test_notify_approve_links.py setup: "mock send_telegram, assert call_count == 1 и аргумент disable_notification не True" expected: PASS acceptance: [AC-5] - id: TC-06 type: unit description: "Graceful: branch=None / plane_issue_id=None — функция не бросает исключение, сообщение всё равно отправляется" module: tests/test_notify_approve_links.py setup: "task без branch и без plane_issue_id; убедиться что send_telegram всё равно вызван, отсутствующая ссылка опущена" expected: PASS acceptance: [AC-6] - id: TC-07 type: unit description: "Plane web-URL не задан и plane_api_url пуст — Plane-ссылка опускается, BRD-ссылка остаётся, орк не падает" module: tests/test_notify_approve_links.py expected: PASS acceptance: [AC-6] - id: TC-08 type: unit description: "Сохранён parse_mode=HTML; динамические подписи экранированы, HTML-разметка ссылок валидна" module: tests/test_notify_approve_links.py expected: PASS acceptance: [AC-7] - id: TC-09 type: unit description: "Регрессия трекера: update_task_tracker по-прежнему работает (silent edit), новое сообщение его не дублирует" module: tests/test_telegram_tracker.py expected: PASS acceptance: [AC-5, AC-8] - id: TC-10 type: integration description: "Поток analysis-approved: _handle_analysis_approved_flow при готовых артефактах вызывает notify_approve_requested; БД tasks даёт корректные repo/branch/plane_issue_id для ссылок" module: tests/test_analysis_approve_flow_links.py setup: "замокать сетевые вызовы Plane/Gitea/Telegram; убедиться, что check_analysis_approved/переходы стадий не изменены" expected: PASS acceptance: [AC-1, AC-2, AC-8] # Условные тесты — включаются ТОЛЬКО если Owner выбрал вариант inline-кнопок (01-brd §8.1) - id: TC-11 type: unit description: "(Условный) Вариант кнопок: payload содержит reply_markup.inline_keyboard с кнопками '📄 Открыть BRD' и '✅ К задаче в Plane' с верными url" module: tests/test_notify_approve_links.py expected: PASS condition: "only if inline-buttons variant chosen" acceptance: [AC-1, AC-2] - id: TC-12 type: unit description: "(Условный) Обратная совместимость send_telegram: вызовы без reply_markup работают как раньше (payload без поля reply_markup)" module: tests/test_telegram_tracker.py expected: PASS condition: "only if inline-buttons variant chosen" acceptance: [AC-5]