--- 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».