# Acceptance Criteria: ET-015

**Work Item:** ET-015 — Healthcheck enduro-trails-app
**Базовые документы:** [01-brd.md](01-brd.md), [02-trz.md](02-trz.md)

## Формат

Каждый критерий записан как Gherkin-сценарий (`Given/When/Then`) и
имеет уникальный идентификатор `AC-NN`. Все критерии — обязательные,
если не указано иное.

---

## AC-01. Контейнер становится healthy после деплоя

**Given** на тестовой среде `mva154` смерджена ветка
`feature/ET-015-healthcheck-enduro-trails-app-`
**And** выполнен `make deploy-test`
**When** проходит ≤ 120 секунд после `docker compose up -d app`
**Then** `docker inspect enduro-trails-app-1 --format
'{{.State.Health.Status}}'` возвращает `healthy`
**And** `docker inspect ... --format '{{.State.Health.FailingStreak}}'`
возвращает `0`.

## AC-02. Контейнер остаётся healthy при штатной работе

**Given** контейнер `enduro-trails-app-1` в статусе `healthy`
**When** проходит 10 минут без вмешательства
**Then** статус остаётся `healthy`
**And** `FailingStreak == 0`
**And** в `docker inspect ... --format '{{json .State.Health.Log}}'`
последние 5 записей имеют `ExitCode: 0`.

## AC-03. Healthcheck не использует curl

**Given** ветка `feature/ET-015-healthcheck-enduro-trails-app-` смерджена
**When** выполняется `grep -n curl docker-compose.yml`
**Then** в выводе нет строки в секции `healthcheck` сервиса `app`
содержащей `curl`.

## AC-04. Образ не растёт за счёт установки curl/wget

**Given** PR с фиксом
**When** выполняется `git diff main -- Dockerfile`
**Then** в diff нет строк `apt-get install` для пакетов `curl` или
`wget`.

## AC-05. Healthcheck честно фиксирует unhealthy при отказе приложения

**Given** контейнер `enduro-trails-app-1` в статусе `healthy`
**When** uvicorn останавливается внутри контейнера (`docker exec
enduro-trails-app-1 sh -c 'kill -STOP 1'` или эквивалент через
останов python-процесса), и приложение перестаёт отвечать на
`http://localhost:5556/api/health`
**Then** в течение ≤ 120 секунд статус становится `unhealthy`.

> Примечание: в рамках интеграционного теста допускается имитировать
> отказ путём временной остановки контейнера-приложения и проверки,
> что Docker фиксирует переход.

## AC-06. Healthcheck-команда использует stdlib python

**Given** YAML `docker-compose.yml`
**When** парсится секция `app.healthcheck.test`
**Then** первый аргумент — `"CMD"`, второй — `"python"`, третий —
`"-c"`, четвёртый — однострочник, использующий **только** модули из
стандартной библиотеки Python 3.12 (`urllib`, `sys`).

## AC-07. Внутренний таймаут запроса меньше внешнего

**Given** секция `healthcheck` сервиса `app`
**When** читаются `timeout` (YAML-параметр) и `timeout=N` внутри
`urlopen(...)`
**Then** внутренний `timeout` строго меньше внешнего `timeout`
(`internal < external`), чтобы python успел корректно завершиться
и отдать exit code.

## AC-08. Эндпоинт /api/health не изменён

**Given** PR с фиксом
**When** выполняется `git diff main -- src/api/main.py`
**Then** в diff отсутствуют изменения функции `health()` и декоратора
`@app.get("/api/health")` (либо они затронуты тривиально — например,
вынос в роутер — но контракт ответа сохраняется: HTTP 200 + JSON с
полем `status`).

## AC-09. CHANGELOG обновлён

**Given** PR с фиксом
**When** открывается `CHANGELOG.md`
**Then** в секции `Unreleased` (или ближайшего невыпущенного релиза)
присутствует запись формата `fix(infra): ... healthcheck ... ET-015`.

## AC-10. ADR зафиксирован

**Given** PR с фиксом
**When** проверяется `docs/work-items/ET-015/06-adr/`
**Then** существует файл с ADR, описывающий решение «использовать
python urllib вместо curl/wget» с контекстом, решением, последствиями.

---

## Критерии приёмки выполнены, если

Все AC-01 … AC-10 проходят. Owner вручную проверяет на mva154:

```bash
ssh mva154 'docker inspect enduro-trails-app-1 \
  --format "{{.State.Health.Status}} (streak {{.State.Health.FailingStreak}})"'
# → healthy (streak 0)
```
