docs(dev): README — замечания ревью задачи 5.3 (T3.3–T3.6)

- T3.3: риск общих queue group подтверждён чтением тестов tabular-parser и
  scan-gateway (test_roundtrip, боевые *.request); тесты arm-core изолированы
  (временная БД, уникальный subject результатов); команда проверки подписчиков.
- T3.4: «Остаточные тома» — down -v только с --profile services.
- T3.5: GET / отвечает 307 на // и затем 404.
- T3.6: up -d --build <сервис> пересоздаёт сервис и зависимости: arm-core-migrate
  пересоздаётся вместе с arm-core, scan-emulator со scan-gateway — нет (проверено).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
This commit is contained in:
Andrey Epifancev 2026-10-04 06:16:59 +04:00
parent e705080e55
commit e98a9239bf

View File

@ -110,8 +110,9 @@ curl -s -X POST localhost:8000/api/v1/dev/token -H 'Content-Type: application/js
curl -s localhost:8000/api/v1/me -H "Authorization: Bearer <JWT>" # email, роли KA_RF, права
```
Образ `arm-core` содержит только бэкенд: фронт (`web/`) в `Dockerfile` не собирается, `GET /` отвечает 404. UI —
`npm run dev` из `../../arm-core/web` (см. README `arm-core`).
Образ `arm-core` содержит только бэкенд: фронт (`web/`) в `Dockerfile` не собирается. `GET /` отвечает **307** с
редиректом на `//`, а тот — 404 (`curl -L` показывает 404 на `http://127.0.0.1:8000//`; редирект на `//` — особенность
`arm-core`, вынесена в его follow-up). UI — `npm run dev` из `../../arm-core/web` (см. README `arm-core`).
**Ожидание готовности и коды возврата `--wait`** (compose 2.34, проверено 2026-10-04):
@ -124,12 +125,22 @@ curl -s localhost:8000/api/v1/me -H "Authorization: Bearer <JWT>" # email,
Для скриптов указывайте долгоживущие сервисы явно — форма не зависит от состава профиля.
**Пересборка после изменения кода сервиса:** `docker compose --profile services up -d --build` (или
`up -d --build <сервис>`). Образ `arm-core:dev` собирает только сервис `arm-core`, образ `scan-gateway:dev` — только
`scan-gateway`; `arm-core-migrate` и `scan-emulator` используют готовый тег (`pull_policy: never`): две сборки одного
тега давали разные ID образа, и каждый следующий `up` пересоздавал контейнеры. Поэтому `arm-core-migrate` или
`scan-emulator` в одиночку (`up -d scan-emulator`) на машине без образа не стартуют — сначала соберите его
(`docker compose --profile services build scan-gateway`) или поднимайте профиль целиком.
**Пересборка после изменения кода сервиса:** `docker compose --profile services up -d --build` — пересобирает образы
и пересоздаёт все контейнеры, чей образ сменился. Образ `arm-core:dev` собирает только сервис `arm-core`, образ
`scan-gateway:dev` — только `scan-gateway`; `arm-core-migrate` и `scan-emulator` используют готовый тег
(`pull_policy: never`): две сборки одного тега давали разные ID образа, и каждый следующий `up` пересоздавал контейнеры.
Поэтому `arm-core-migrate` или `scan-emulator` в одиночку (`up -d scan-emulator`) на машине без образа не стартуют —
сначала соберите его (`docker compose --profile services build scan-gateway`) или поднимайте профиль целиком.
Форма `up -d --build <сервис>` пересоздаёт только сервис и его зависимости (проверено 2026-10-04 на образе,
пересобранном с `--no-cache`):
| Команда | Что пересоздано | Что осталось на старом образе |
|---|---|---|
| `up -d --build arm-core` | `arm-core` и `arm-core-migrate` (он — зависимость `arm-core`, миграции нового кода выполняются) | — |
| `up -d --build scan-gateway` | только `scan-gateway` | **`scan-emulator`** (не зависимость шлюза) — до следующего `docker compose --profile services up -d`, который его пересоздаёт |
После правок кода `scan-gateway`, затрагивающих эмулятор, пересобирайте профиль целиком.
**Остановка и сброс профиля** — только с `--profile services`: без него compose не видит контейнеры профиля
(см. «Сброс окружения»).
@ -238,10 +249,18 @@ postgres nats minio`, затем `make test-integration` (или `make check-ful
эмулятор ИС Сканирование поднимается в процессе интеграционного теста; контейнер `scan-emulator` профиля `services`
тестами не используется. Общее описание проверок — [`checks.md`](checks.md).
> **Не запускайте интеграционные тесты при поднятом профиле `services`** (не проверялось, вывод из устройства обмена):
> контейнеры подписаны на те же subject'ы общего NATS в тех же queue group (`arm-core` на `recognition.result`,
> `tabular-parser` и `scan-gateway` на свои `*.request`), поэтому NATS может отдать сообщение теста контейнеру, а не
> экземпляру из теста. Перед тестами: `docker compose --profile services stop arm-core tabular-parser scan-gateway scan-emulator`.
> **Не запускайте интеграционные тесты `tabular-parser` и `scan-gateway` при поднятом профиле `services`.** Их
> сквозные тесты (`api/tests/integration/test_roundtrip.py`, у `scan-gateway` ещё `test_nats_minio_integration.py`)
> публикуют запросы в боевые `recognition.tabular.request` / `recognition.image.request`, а экземпляр сервиса из теста
> подписан в ту же queue group (`tabular-parser` / `scan-gateway`), что и контейнер. NATS отдаёт каждое сообщение группы
> одному подписчику — запрос теста может уйти контейнеру, и тест упадёт по таймауту ожидания ack (так прямо сказано в
> docstring теста `scan-gateway`). Риск подтверждён чтением кода тестов, тесты при поднятом профиле не запускались.
> Интеграционные тесты `arm-core` от контейнеров изолированы: временная БД на сессию (рабочая `armka` не используется)
> и уникальный subject результатов на тест. Перед тестами сервисов:
> `docker compose --profile services stop tabular-parser scan-gateway scan-emulator`; проверить, что чужих подписчиков
> нет: `curl -s 'http://127.0.0.1:8222/subsz?subs=1' | grep -o '"subject": *"recognition[^"]*"'` — строк
> `recognition.tabular.request` / `recognition.image.request` быть не должно (`recognition.result` — подписка контейнера
> `arm-core`, тестам `arm-core` не мешает).
## Проверка контракта
@ -274,5 +293,6 @@ postgres nats minio`, затем `make test-integration` (или `make check-ful
отдельности: образ ещё не собран — `docker compose --profile services build` (см. «Профиль `services`»).
- **`minio-init` завис или завершился с ошибкой:** `docker compose logs minio-init`; чаще всего `minio` не стал healthy —
`docker compose logs minio`. Повторный `docker compose up -d` идемпотентен.
- **Остаточные тома от прошлых запусков** (старый пароль БД, бакет не тот): `docker compose down -v` и запуск заново.
- **Остаточные тома от прошлых запусков** (старый пароль БД, бакет не тот): `docker compose --profile services down -v`
и запуск заново (без `--profile services` контейнеры профиля и том `hotfolder` остаются, см. «Сброс окружения»).
- **`up -d --wait` возвращает код 1:** см. раздел «Запуск инфраструктуры» — указывайте сервисы явно.