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]
