From 4762f8618782fb8f905ffb545440fab0a79c3d36 Mon Sep 17 00:00:00 2001 From: Aleksey Razorvin <> Date: Sat, 21 Mar 2026 13:03:33 +0500 Subject: [PATCH] docs: mark Sprint 2 complete, document migration restart bug Co-Authored-By: Claude Sonnet 4.6 --- DOC/СПРИНТЫ.md | 19 ++++--- DOC/ШАГИ/ШАГ_2026-03-21_008.md | 73 +++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 7 deletions(-) create mode 100644 DOC/ШАГИ/ШАГ_2026-03-21_008.md diff --git a/DOC/СПРИНТЫ.md b/DOC/СПРИНТЫ.md index 7cc1f04..70b90a2 100644 --- a/DOC/СПРИНТЫ.md +++ b/DOC/СПРИНТЫ.md @@ -44,22 +44,27 @@ --- -## Спринт 2 — Прохождение теста +## Спринт 2 — Прохождение теста ✅ **Результат:** Можно выбрать тест из списка и пройти его, увидеть результат и разбор ошибок. +**Статус:** Завершён и протестирован вручную в браузере. -- [ ] Модели БД: `TestAttempt`, `AttemptAnswer` -- [ ] API: `POST /api/attempts` — начать попытку (фиксируем время начала) -- [ ] API: `POST /api/attempts/{id}/submit` — завершить попытку, подсчитать результат -- [ ] API: `GET /api/attempts/{id}/result` — результат с разбором ошибок -- [ ] Фронт: страница прохождения теста +- [x] Модели БД: `TestAttempt`, `AttemptAnswer` +- [x] API: `POST /api/attempts` — начать попытку (фиксируем время начала) +- [x] API: `POST /api/attempts/{id}/submit` — завершить попытку, подсчитать результат +- [x] API: `GET /api/attempts/{id}/result` — результат с разбором ошибок +- [x] Фронт: страница прохождения теста - Случайный порядок вопросов - Таймер с обратным отсчётом (если задан) — автосабмит по истечении - Навигация назад (если разрешена настройкой теста) -- [ ] Фронт: страница результата +- [x] Фронт: страница результата - Балл и процент - Сдал / Не сдал (относительно порога) - Разбор ошибок: вопрос, ответ сотрудника, правильный ответ +- [x] Фронт: кнопка «Пройти тест» прямо в строке таблицы списка тестов + +### Баги, найденные и исправленные при тестировании +- [x] «Не удалось загрузить тест» × 2 при нажатии «Пройти тест» — миграция `002_attempts` не применилась, т.к. `--reload` перезапускает только код приложения, но не `entrypoint.sh` → исправлено: `docker compose restart backend` --- diff --git a/DOC/ШАГИ/ШАГ_2026-03-21_008.md b/DOC/ШАГИ/ШАГ_2026-03-21_008.md new file mode 100644 index 0000000..18dd9e1 --- /dev/null +++ b/DOC/ШАГИ/ШАГ_2026-03-21_008.md @@ -0,0 +1,73 @@ +# ШАГ 008 — Спринт 2: Баг «Не удалось загрузить тест» + +**Дата:** 2026-03-21 +**Контекст:** Мастер-класс по разработке системы тестирования сотрудников клиники. + +--- + +## Запрос + +> вот такой баг при нажатии кнопки «Пройти тест» — «Не удалось загрузить тест» × 2 + +--- + +## Симптом + +При нажатии кнопки «Пройти тест» фронт выполняет `POST /api/attempts` и получает ошибку. +В консоли backend: + +``` +sqlalchemy.exc.ProgrammingError: (asyncpg.exceptions.UndefinedTableError) +relation "test_attempts" does not exist +``` + +Ошибка появляется дважды — из-за React StrictMode, который в dev-режиме намеренно монтирует компоненты дважды. + +--- + +## Причина + +`uvicorn --reload` следит только за изменениями Python-файлов и перезапускает **процесс приложения**, но **не перезапускает контейнер целиком** и не выполняет `entrypoint.sh` повторно. + +Миграция `002_attempts` (создаёт таблицы `test_attempts` и `attempt_answers`) была добавлена уже после первого запуска стека — поэтому она ни разу не применялась. + +--- + +## Исправление + +```bash +docker compose restart backend +``` + +При перезапуске контейнер выполняет `entrypoint.sh` заново: + +``` +Running migrations... +INFO [alembic.runtime.migration] Context impl PostgresqlImpl. +INFO [alembic.runtime.migration] Will assume transactional DDL. +INFO [alembic.runtime.migration] Running upgrade 001 -> 002, attempts +Starting server... +INFO: Application startup complete. +``` + +Таблицы созданы — баг исчезает. + +--- + +## Правило для джуниора + +> `uvicorn --reload` ≠ перезапуск контейнера. +> +> Если ты добавил новую миграцию Alembic и стек уже работает — выполни `docker compose restart backend`, чтобы миграция применилась. +> +> `docker compose up` запускает контейнер только если он не запущен. `restart` — принудительно пересоздаёт его. + +--- + +## Следующие шаги + +- [x] Спринт 1: Инфраструктура + Создание тестов +- [x] Спринт 2: Прохождение теста + результат +- [ ] Спринт 3: Трекер результатов +- [ ] Спринт 4: Авторизация и роли +- [ ] Спринт 5: Уведомления в MAX