# 🧹 Проверять ответ обработчика на отказ репозитория

- **Тип:** chore
- **Категория:** Очередь — Подставной репозиторий заводится там же, где тесты конвейера: одна сборка теста на два слоя
- **Зачем:** Тесты обработчиков идут по настоящему sqlite без управляемых отказов: класс «база недоступна во время запроса» не проверен нигде.
- **Теги:** review-2026-08-23

Тесты обработчиков ходят по настоящему sqlite во временном каталоге. Управляемых
отказов у него нет, поэтому весь класс «база недоступна во время запроса» не
проверен нигде: что отвечает обработчик, когда репозиторий отказал, известно
только чтением кода.

Проверять надо не отказ базы, а ответ на него: код, тело единой формы и то, что
причина не попала в ответ пользователю. Для этого репозиторию нужна подставная
реализация, отказывающая по требованию.

Гипотеза прохода `review-autotests` (A4) ревью change
`2026-08-23-storage-without-pocketbase`, понижённая триажем до наблюдения:
оракула у неё нет ровно потому, что управляемых отказов не существует. Сам пробел
проверен — `grep` по тестам пакета пуст.

## Затрагивает

- тесты `internal/controller/http` — сборка теста и способ подставить
  репозиторий;
- договор ядра с хранилищем `internal/contract` — интерфейсы, по которым
  подставляется отказ;
- единая форма ответа об отказе `internal/controller/http/errors.go`.

## Критерии приёмки

- Тест подставляет отказ репозитория. **Оракул:** тест — подставной
  репозиторий отдаёт ошибку на названном методе, и обработчик её получает.
- Ответ на отказ идёт единой формой и не несёт причины. **Оракул:** тест на
  каждом читающем обработчике — код `500`, тело эталонной формы, текста ошибки
  базы в теле нет.
- Отказ виден владельцу сервиса. **Оракул:** тот же тест с перехваченным
  журналом — строка уровня `ERROR` с причиной есть.

## Рамки

Настоящую базу из тестов не убираем: подставной репозиторий нужен для отказов, а
не вместо неё. Формы ответов и коды не меняем — задача проверяет то, что уже
объявлено.
