diff --git a/docs/work-items/ET-015/13-test-report.md b/docs/work-items/ET-015/13-test-report.md new file mode 100644 index 0000000..e5cb381 --- /dev/null +++ b/docs/work-items/ET-015/13-test-report.md @@ -0,0 +1,205 @@ +--- +type: test-report +work_item_id: ET-015 +verdict: READY_TO_DEPLOY +version: 1 +--- + +# Test Report ET-015 — Healthcheck enduro-trails-app + +**Branch:** `feature/ET-015-healthcheck-enduro-trails-app-` +**Base:** `main` +**Tester:** agent:tester +**Date:** 2026-06-05 +**Test plan:** [04-test-plan.yaml](04-test-plan.yaml) +**Acceptance criteria:** [03-acceptance-criteria.md](03-acceptance-criteria.md) + +## TL;DR + +**16/16** ST + UT тестов пройдено. E2E-02 (`/api/health` снаружи на mva154) +возвращает `HTTP 200` за **0.111 s**. Эндпоинт `src/api/main.py::health()` не +изменён. Интеграционные IT-01..IT-04 и E2E-01 закрываются на этапе деплоя +(требуют live docker compose / ssh mva154) — это явно заложено в +`04-test-plan.yaml::done_when`. + +**Вердикт: READY_TO_DEPLOY.** + +## Окружение + +| Параметр | Значение | +|----------|----------| +| Python | 3.12.13 | +| pytest | 8.3.3 | +| Repo HEAD | `d501bcb` (reviewer auto-commit) | +| Доступ к mva154 | через HTTPS (curl недоступен → проверка через python urllib) | +| Docker в окружении tester | **недоступен** (`docker: command not found`) | + +Pre-flight: `GET https://openclaw.mva154.duckdns.org/enduro/api/health` → +`HTTP 200`, body `{"status":"ok","db_path":"/app/data/centralfederal.sqlite","db_exists":true}`, +time `0.111 s`. Тестовая среда жива. + +## Результаты + +### Static (ST-*) и Unit (UT-*) + +Запуск: + +``` +python3 -m pytest tests/static/test_healthcheck_compose.py \ + tests/unit/test_healthcheck_oneliner.py -v +``` + +Итог: **16 passed in 2.92s**. + +| ID | Имя | AC | Результат | +|----|-----|----|-----------| +| ST-01 | `test_st01_healthcheck_does_not_use_curl` | AC-03 | PASS | +| ST-02 | `test_st02_dockerfile_does_not_apt_install_curl_or_wget` | AC-04 | PASS | +| ST-03 | `test_st03_healthcheck_uses_python_and_stdlib` | AC-06 | PASS | +| ST-04 | `test_st04_internal_timeout_less_than_external` (3 < 5) | AC-07 | PASS | +| ST-05 | `git diff main..HEAD -- src/api/main.py` (empty) | AC-08 | PASS | +| ST-06 | `test_st06_changelog_mentions_et015` | AC-09 | PASS | +| ST-07 | `test_st07_adr_exists` (ADR-020) | AC-10 | PASS | +| ST-reg | `test_app_healthcheck_target_is_local_api_health` | regression | PASS | +| ST-reg | `test_app_healthcheck_has_start_period` (20s) | regression | PASS | +| ST-reg | `test_app_healthcheck_preserves_baseline_params[interval-30]` | regression | PASS | +| ST-reg | `test_app_healthcheck_preserves_baseline_params[retries-3]` | regression | PASS | +| UT-01 | `test_ut01_returns_zero_on_http_200` | AC-06 | PASS | +| UT-02 | `test_ut02_returns_nonzero_when_port_unused` | AC-05, AC-06 | PASS | +| UT-03 | `test_ut03_returns_nonzero_on_non_2xx[301]` | AC-06 | PASS | +| UT-03 | `test_ut03_returns_nonzero_on_non_2xx[404]` | AC-06 | PASS | +| UT-03 | `test_ut03_returns_nonzero_on_non_2xx[500]` | AC-06 | PASS | +| UT-03 | `test_ut03_returns_nonzero_on_non_2xx[503]` | AC-06 | PASS | + +Важная техническая деталь: unit-тесты one-liner'а **читают исходную +команду из `docker-compose.yml`** (а не дублируют её) — если в будущем +кто-то изменит one-liner в compose и сломает контракт exit-кода, UT-01/02/03 +немедленно покраснеют. + +### Integration (IT-*) — на стороне deployer + +IT-01..IT-04 требуют локального `docker compose` и доступа к +`/home/slin/enduro-trails/data` — в среде tester'а Docker недоступен +(`docker: command not found`). Согласно `04-test-plan.yaml` +эти тесты автоматизируемые, но физически выполняются: + +- IT-01 (healthy за ≤ 120s) — закрывается deployer'ом сразу после + `make deploy-test` на mva154. +- IT-02 (стабилен 5 минут) — закрывается мониторингом после деплоя. +- IT-03 (переход в unhealthy при остановке uvicorn) — рекомендуется + отдельным smoke-шагом в post-deploy чек-листе; **не блокирует deploy**, + т.к. unit UT-02 уже доказал, что one-liner возвращает ненулевой exit-code + при недоступном порту. +- IT-04 (не требует ребилда) — статически подтверждается тем, что + `git diff main..HEAD -- Dockerfile` пуст, образ не меняется + (что также проверяет ST-02). + +**Передача:** IT-01/IT-02/IT-03 → deployer (см. ниже секцию «Pending»). + +### E2E + +| ID | Имя | Результат | +|----|-----|-----------| +| E2E-01 | После `make deploy-test` контейнер healthy на mva154 (3 замера) | **Pending** — закрывается deployer'ом | +| E2E-02 | Приложение продолжает отвечать снаружи | **PASS** — `HTTP 200`, `0.111 s` (см. pre-flight) | + +### Полный pytest-набор репозитория + +`python3 -m pytest tests/` не собирается из-за пред-существующих +проблем окружения: отсутствуют `shapely`, `defusedxml`, +`mapbox_vector_tile` (15 collection errors). Это **не связано с ET-015** +(изменение чисто инфраструктурное — `docker-compose.yml`, CHANGELOG, +docs/tests; `src/api/` не трогается). Зафиксировано как наблюдение, +не блокирующее этот work item. + +## Visual / UI тесты + +Файл `docs/work-items/ET-015/04b-ui-test-cases.md` **отсутствует** +(инфраструктурная задача, UI не задействован). Шаг 4 теста-плана +пропущен согласно инструкции tester'а. + +## Покрытие Acceptance Criteria + +| AC | Тесты | Статус | +|----|-------|--------| +| AC-01 | IT-01, E2E-01 | Pending (deployer) | +| AC-02 | IT-02, E2E-01 | Pending (deployer) | +| AC-03 | ST-01 | **PASS** | +| AC-04 | ST-02, IT-04 | **PASS (static)** | +| AC-05 | UT-02, IT-03 | **PASS (unit)** + Pending (IT-03 на deployer) | +| AC-06 | ST-03, UT-01, UT-03 (4 кейса) | **PASS** | +| AC-07 | ST-04 (3 < 5) | **PASS** | +| AC-08 | ST-05, E2E-02 | **PASS** | +| AC-09 | ST-06 | **PASS** | +| AC-10 | ST-07 | **PASS** | + +## Findings + +### P0 (blocker) +Нет. + +### P1 (must-fix) +Нет. + +### P2 (should-fix) +Нет. + +### P3 (nice-to-have) + +- **P3-T1.** Pre-существующие сбои окружения при сборе общего + pytest-набора (`shapely`, `defusedxml`, `mapbox_vector_tile` + отсутствуют). Не относится к ET-015, но мешает запускать общий + smoke за один проход. Рекомендуется отдельной задачей привести + test-окружение в порядок (CI-образ или `requirements-test.txt`). +- **P3-T2.** В среде tester'а отсутствует `curl` — пришлось делать + E2E-02 через `python -m urllib.request`. Результат идентичен + (HTTP 200, ~111 ms), но в чек-листе деплоя стоит оставить + команду `curl -sS` именно как написана в плане. + +(Все три P3 из review (`12-review.md`) перенесены как известные +вопросы документации/стиля, не блокирующие.) + +## Pending (передаётся deployer'у) + +Эти проверки **обязательны** до закрытия задачи, но физически +выполняются на mva154 после `make deploy-test`: + +1. **IT-01 / E2E-01 — healthy за ≤ 120 s после деплоя.** + ``` + ssh mva154 'docker inspect enduro-trails-app-1 \ + --format "{{.State.Health.Status}} (streak {{.State.Health.FailingStreak}})"' + # ожидается: healthy (streak 0) + ``` +2. **IT-02 / E2E-01 — стабилен через 5 и 10 минут.** + Повторить команду выше через 5 и 10 минут после деплоя. +3. **IT-03 — переход в unhealthy при отказе.** *(опционально, smoke)* + ``` + ssh mva154 'docker exec enduro-trails-app-1 sh -c "pkill -STOP -f uvicorn"' + # подождать ≤ 120s + ssh mva154 'docker inspect enduro-trails-app-1 --format "{{.State.Health.Status}}"' + # ожидается: unhealthy + ssh mva154 'docker compose restart app' # вернуть в строй + ``` + +## Команды воспроизведения + +```bash +# ST + UT +python3 -m pytest tests/static/test_healthcheck_compose.py \ + tests/unit/test_healthcheck_oneliner.py -v +# E2E-02 +python3 -c "import urllib.request,time; t=time.time(); \ + r=urllib.request.urlopen('https://openclaw.mva154.duckdns.org/enduro/api/health', timeout=10); \ + print(r.status, f'{time.time()-t:.3f}s', r.read().decode())" +``` + +## Вердикт + +**READY_TO_DEPLOY (stage:ready-to-deploy).** + +Все автоматизируемые статические и unit-проверки пройдены (16/16). +Эндпоинт `/api/health` на test-среде жив, отдаёт 200 за ~111 ms. +`src/api/main.py` и `Dockerfile` не изменены — поведение приложения +гарантированно сохранено. P0/P1/P2 пусты. Передаю deployer'у; +финальные AC-01/AC-02 закрываются после `make deploy-test` по чек-листу +в секции «Pending».