97 lines
7.2 KiB
Markdown
97 lines
7.2 KiB
Markdown
# Dev Report: orchestrator — check_tests_passed машинный вердикт (ET-013 fix)
|
||
Дата: 2026-06-04
|
||
Статус: DONE
|
||
|
||
## Задача
|
||
`check_tests_passed` (src/qg/checks.py:139) гейтила стадию testing→deploy наивным
|
||
substring-поиском `if "PASS" in content` по всему телу `13-test-report.md`. На ET-013
|
||
тестер выставил `verdict: BLOCKED` / `status: blocked` (провален P1 AC-19), но в теле
|
||
были слова «23 passed», «✅ PASS», «All checks passed» → substring нашёлся → True →
|
||
недоделанная фича уехала в deploy и доползла до Done. Нужно привести функцию к
|
||
машинному контракту, как у уже починённых `check_reviewer_verdict` и `check_deploy_status`.
|
||
|
||
## Сделано
|
||
- [x] ШАГ 0: изучил `check_reviewer_verdict` (эталон, читает `verdict:` из frontmatter)
|
||
- [x] Собрал РЕАЛЬНЫЕ значения вердикта по прошлым отчётам ET-001..ET-014
|
||
- [x] Переписал `check_tests_passed` + добавил `_parse_tests_verdict` (зеркало эталона)
|
||
- [x] Обновил/расширил тесты `TestCheckTestsPassed` (3 → 11 тестов)
|
||
- [x] Прогнал unit + полный pytest
|
||
- [x] Провалидировал на РЕАЛЬНЫХ отчётах прод-репозитория
|
||
- [x] Ветка `fix/tests-machine-verdict`, push, PR #24 (НЕ мержил)
|
||
|
||
## Изменённые файлы
|
||
- `src/qg/checks.py` — `check_tests_passed` теперь читает ТОЛЬКО frontmatter; новый
|
||
helper `_parse_tests_verdict`. НЕ трогал check_reviewer_verdict / check_deploy_status.
|
||
- `tests/test_qg.py` — класс `TestCheckTestsPassed` переписан под машинный контракт.
|
||
|
||
## Положительные значения вердикта по РЕАЛЬНЫМ образцам (verbatim)
|
||
| WI | verdict (raw) | status (raw) | трактовка |
|
||
|--------|-----------------------------|---------------------|-----------|
|
||
| ET-001 | `PASS` | `pass` | ✅ True |
|
||
| ET-002 | `PASS` | `pass` | ✅ True |
|
||
| ET-005 | `PASS` | `pass` | ✅ True |
|
||
| ET-006 | `ready-to-deploy` | `PASSED` | ✅ True |
|
||
| ET-007 | `PASS — ready-to-deploy` | `PASS` | ✅ True |
|
||
| ET-008 | `stage:ready-to-deploy` | `pass` | ✅ True |
|
||
| ET-009 | `PASS` | `PASS` | ✅ True |
|
||
| ET-011 | `PASS` | (нет) | ✅ True |
|
||
| ET-012 | `PASS` | `ready-to-deploy` | ✅ True |
|
||
| ET-013 | `BLOCKED` | `blocked` | ❌ False |
|
||
| ET-014 | `PASS` | (нет) | ✅ True |
|
||
|
||
**Вывод:** поле `verdict` у тестера НЕ единообразно. Чисто `verdict == "PASS"`
|
||
сломал бы ET-006 (`ready-to-deploy`) и ET-008 (`stage:ready-to-deploy`). Поэтому:
|
||
- **Positive tokens** (нормализация `.upper().strip()`, поиск токена в `verdict`+`status`):
|
||
`PASSED`, `PASS`, `READY-TO-DEPLOY`, `READY_TO_DEPLOY`, `GREEN`, `APPROVED`.
|
||
- **Negative tokens (авторитетны)**: `BLOCKED`, `FAILED`, `FAIL`, `REQUEST_CHANGES`, `REJECT`, `RED`.
|
||
- Логика: нет frontmatter / битый YAML / нет ни verdict ни status → False с причиной;
|
||
негативный токен в любом из полей → False (перебивает любой PASS); иначе позитивный
|
||
токен → True; иначе → False. Никогда не падает.
|
||
|
||
## Результат — до/после на ET-013 (на реальном файле прод-репо)
|
||
- **До:** substring `"PASS"` в теле → `(True, "Test report indicates PASS")` → deploy.
|
||
- **После:** `_parse_tests_verdict` → `(False, 'Test verdict: BLOCKED (BLOCKED)')` → блок.
|
||
|
||
Прогон на РЕАЛЬНЫХ отчётах (`/repos/_wt/enduro-trails/feature_ET-014-ui-z-index`):
|
||
```
|
||
ET-001 (True, 'Test verdict: PASS (PASS)')
|
||
ET-005 (True, 'Test verdict: PASS (PASS)')
|
||
ET-006 (True, 'Test verdict: READY-TO-DEPLOY (PASS)')
|
||
ET-007 (True, 'Test verdict: PASS — READY-TO-DEPLOY (PASS)')
|
||
ET-008 (True, 'Test verdict: STAGE:READY-TO-DEPLOY (PASS)')
|
||
ET-009 (True, 'Test verdict: PASS (PASS)')
|
||
ET-011 (True, 'Test verdict: PASS (PASS)')
|
||
ET-012 (True, 'Test verdict: PASS (PASS)')
|
||
ET-013 (False, 'Test verdict: BLOCKED (BLOCKED)') <-- bug fixed
|
||
ET-014 (True, 'Test verdict: PASS (PASS)')
|
||
```
|
||
Все 9 ранее прошедших WI остаются True; ET-013 → False. Регрессий нет.
|
||
|
||
## Обратная совместимость
|
||
Строгий машинный контракт сохранён БЕЗ ослабления: у всех реальных прошедших отчётов
|
||
ЕСТЬ frontmatter с положительным `verdict:`/`status:`, поэтому fallback на тело не нужен.
|
||
Единственная «несовместимость» была в старых pytest-фикстурах (`Result: PASS` в теле без
|
||
frontmatter) — они описывали старое substring-поведение. Согласно ТЗ их ПОЧИНИЛ под новый
|
||
контракт (добавил frontmatter `verdict:`), а не ослабил проверку. Отсутствие frontmatter
|
||
теперь = False (`test_no_frontmatter_fails`).
|
||
|
||
## Тесты (pytest, прод-контейнер на /repos/orchestrator)
|
||
- `TestCheckTestsPassed`: **11 passed** (включая главный кейс ET-013, FAILED, "23 passed"
|
||
в теле при BLOCKED, нет frontmatter, нет полей, битый YAML, файла нет).
|
||
- Полный прогон: **285 passed, 9 failed**. 9 failed = off-limits `test_webhooks.py`
|
||
HMAC/401 — подтверждено pre-existing: при застешенных моих правках на чистом main те же
|
||
webhook-тесты падают (их число плавает 9–10 от запуска к запуску, порядко-зависимы).
|
||
Мои изменения их не затрагивают (только `check_tests_passed` + его тесты).
|
||
*Прим.:* baseline в ТЗ заявлен 277+9; фактический на этом окружении 276+10 до правки →
|
||
285+9 после (net +9 passed: класс вырос 3→11 тестов, минус один плавающий webhook-фейл).
|
||
|
||
## Сдача
|
||
- Ветка: `fix/tests-machine-verdict` (от актуального origin/main, push выполнен).
|
||
- PR: **#24** — http://localhost:3000/admin/orchestrator/pulls/24
|
||
(внешний: https://git.mva154.duckdns.org/admin/orchestrator/pulls/24). НЕ мержил — на ревью Стрим.
|
||
- В main НЕ пушил.
|
||
|
||
## НЕ трогал
|
||
check_reviewer_verdict, check_deploy_status, gitea.py merge-gate, PLANE_STATES,
|
||
set_issue_done, launcher deployer-guard, HMAC, queue, stages.py mapping.
|