# DEV TASK — изолировать 3 теста от живого Plane API (сделать CI зелёным) **Проект:** orchestrator | **Сервер:** slin@82.22.50.71 (pw motoZ@yaz2010, ConnectTimeout=15, StrictHostKeyChecking=no) | **Репо (хост):** /home/slin/repos/orchestrator | **Контейнер:** orchestrator (порт 8500) **Ветка:** `fix/isolate-webhook-tests-from-plane` из свежего origin/main (`git fetch origin main && git checkout -B fix/isolate-webhook-tests-from-plane origin/main`). ## Контекст / зачем В PR #26 мы впервые включили Gitea CI для orchestrator. CI прогнал тесты в чистом окружении (БЕЗ сети к Plane) и **3 теста упали** — они дёргают живой Plane API и в CI получают `Connection refused`. Это **дефект изоляции тестов** (pre-existing), а не баг кода. Локально на хосте они «проходят» только потому что там живой Plane под боком. Нужно замокать сетевые вызовы Plane в этих 3 тестах → CI станет зелёным → гейт качества для self-hosting orchestrator заработает честно. **ВАЖНО: чинить ТОЛЬКО тесты (`tests/test_webhooks.py`). НЕ трогать `src/`, логику, поведение.** Задача — добавить недостающие `@patch`, тесты по сути не переписывать. ## Точная причина каждого падения (проверено на проде) ### Тест 1 — `test_plane_webhook_creates_task` (tests/test_webhooks.py:59) - Путь `work_item.created` → `handle_work_item_created` → внутри вызывается `fetch_issue_sequence_id(plane_id, plane_project_id)` (`src/webhooks/plane.py:448-449`) — **сетевой вызов к Plane**. В CI `Connection refused` → исключение прерывает цепочку ДО `INSERT INTO tasks` → `assert task is not None` падает. - Код задуман с fallback: если `fetch_issue_sequence_id` вернёт `None` → берётся `get_next_work_item_id` (DB-инкремент). Но реальный сетевой сбой бросает exception, а не None. - **Фикс теста:** добавить `@patch` на `src.webhooks.plane.fetch_issue_sequence_id` → `return_value = None` (штатный fallback на DB-инкремент, поведение кода НЕ меняется, просто без сети). Также замокать `src.webhooks.plane.add_comment` (вызывается на :534 после создания, тоже сетевой) — `new_callable=MagicMock`. - Образец: тест уже мокает `_create_gitea_branch` и `_create_initial_docs` (AsyncMock) — добавить ещё два @patch к этим. ### Тест 2 — `test_gitea_push_with_adr_advances_stage` (tests/test_webhooks.py:206) - Путь gitea push → `handle_push` → после advance вызывается `plane_notify_stage(work_item_id, ...)` (`src/webhooks/gitea.py:129`) — сетевой вызов к Plane → `Connection refused` → исключение валит до `mock_launcher.launch.assert_called_once()` (launch вызывается 0 раз / падает раньше). - **Фикс теста:** добавить `@patch("src.webhooks.gitea.plane_notify_stage")` (MagicMock). ### Тест 3 — `test_gitea_ci_failure_on_development_notifies_qg_failure` (tests/test_webhooks.py:278) - Тот же класс проблемы: путь gitea status=failure на development. Проверь точно, какой сетевой вызов Plane срабатывает на этом пути (вероятно `plane_notify_stage` или `add_comment` в gitea.py при notify_qg_failure-ветке ~:217-223). Замокать ровно тот, что лезет в сеть. - **Фикс теста:** добавить недостающий `@patch` на сетевой Plane-вызов в этом пути (`src.webhooks.gitea.plane_notify_stage` и/или соответствующий). НЕ трогать assert'ы про `mock_notify`/`check_ci_green`/stage. ## Рабочие образцы моков (уже в этом файле, копировать стиль) - `tests/test_webhooks.py:151-154` — `@patch("src.webhooks.plane.httpx.get")` (как мокают сеть в plane). - `tests/test_webhooks.py:241-246` — `@patch("src.webhooks.gitea.check_ci_green")` + `@patch("src.webhooks.gitea.launcher")` (как мокают gitea-путь). Тест `test_gitea_ci_success_advances_to_review` рядом — успешный близнец, посмотри, что он мокает, и сделай по аналогии. - Импорты уже есть: `from unittest.mock import patch, MagicMock, AsyncMock` (строка 5). ## Принцип (не нарушать) - Мокать НАДО **место вызова** (`src.webhooks.plane.X` / `src.webhooks.gitea.X`), а не источник в `plane_sync`, т.к. некоторые импорты локальные внутри функций — мок источника не перехватит. Смотри как сделано в conftest.py для send_telegram (там описано почему патчат именно место связывания). - Если функция импортируется ЛОКАЛЬНО внутри тела (`from ..plane_sync import add_comment` внутри функции) — патчить надо `src.plane_sync.add_comment` (источник), т.к. локальный импорт резолвится в рантайме из источника. Определи для каждого вызова: модульный импорт вверху файла → патчить место (`src.webhooks.X.name`); локальный импорт внутри функции → патчить источник (`src.plane_sync.name`). Это критично, проверь по факту, не угадывай. ## Проверка (выполнить и приложить пруф) 1. Прогнать ровно эти 3 теста как в CI (чистое окружение, read-only, без .env): ``` docker run --rm -v /home/slin/repos/orchestrator:/code:ro -w /code -e PYTHONPATH=/code \ --entrypoint python3 $(docker inspect orchestrator --format '{{.Config.Image}}') \ -m pytest tests/test_webhooks.py::test_plane_webhook_creates_task \ tests/test_webhooks.py::test_gitea_push_with_adr_advances_stage \ tests/test_webhooks.py::test_gitea_ci_failure_on_development_notifies_qg_failure -q ``` — должно быть **3 passed**. 2. Прогнать ВЕСЬ test_webhooks.py тем же способом — убедиться, что ничего не сломал (не появилось новых fail сверх известных 9 HMAC/401, которые из-за .env и в read-only без .env ведут себя стабильно — зафиксируй число). 3. После push ветки: убедиться, что Gitea Actions прогнал workflow и job test → **success** (`/api/v1/repos/admin/orchestrator/actions/tasks`, токен `docker exec orchestrator printenv ORCH_GITEA_TOKEN`). 4. `git log origin/main..origin/fix/isolate-webhook-tests-from-plane` показывает коммит ДО отчёта. ## Ограничения / правила - ⚠️ PR в Gitea, push в main ЗАПРЕЩЁН (pre-receive hook). НЕ мержить самому — мержит ревьюер (Стрим). Только ветка + PR. - Менять ТОЛЬКО `tests/test_webhooks.py`. НЕ трогать src/, off-limits тесты (9 HMAC/401), conftest.py, workflow-файл. - Conventional Commits, один коммит: `test: isolate webhook tests from live Plane API (fix CI)`. - ⚠️ **НЕ регистрировать новых раннеров, НЕ запускать ничего через nohup.** Раннер orchestrator уже есть (`mva154-runner-orch`). Если CI не подхватывает — СТОП, отчёт, НЕ плоди процессы. - Если факт ТЗ разошёлся с реальностью — СТОП, отчёт с вопросом. ## Результат - PR с правкой только `tests/test_webhooks.py`, 3 целевых теста зелёные в чистом окружении, workflow прогнан и job test = success, отчёт с пруфом. Сохрани отчёт в `tasks/orchestrator/reports/dev-2026-06-04-fix-plane-mocks.md`.