# ШАГ 006 — Спринт 1: Инфраструктура + Создание тестов **Дата:** 2026-03-21 **Контекст:** Мастер-класс по разработке системы тестирования сотрудников клиники. --- ## Запрос > готов. поехали (начинаем Спринт 1) --- ## Ответ Реализован Спринт 1 в полном объёме: инфраструктура + создание тестов без авторизации. Спринт протестирован вручную в браузере, найдены и исправлены 4 бага. --- ## Структура проекта после спринта ``` qa_test_app/ ├── docker-compose.yml ← поднимает весь стек одной командой ├── .env.example ├── nginx/ │ └── nginx.conf ← / → frontend, /api/ → backend ├── backend/ │ ├── Dockerfile │ ├── entrypoint.sh ← ждёт БД, запускает миграции, стартует сервер │ ├── requirements.txt │ ├── alembic.ini │ ├── alembic/ │ │ ├── env.py ← async alembic, берёт URL из settings │ │ ├── script.py.mako │ │ └── versions/ │ │ └── 001_init.py ← создаёт таблицы tests, questions, answers │ └── app/ │ ├── config.py ← настройки через pydantic-settings │ ├── database.py ← async SQLAlchemy engine + session │ ├── main.py ← FastAPI app, /api/health │ ├── models/test.py ← ORM модели: Test, Question, Answer │ ├── schemas/test.py ← Pydantic схемы с валидацией │ └── api/tests.py ← REST эндпоинты └── frontend/ ├── Dockerfile ├── package.json ├── vite.config.ts ├── index.html └── src/ ├── App.tsx ← роутер + провайдеры ├── api/ │ ├── client.ts ← axios с baseURL=/api │ └── tests.ts ← типы + функции запросов └── pages/ ├── TestList/ ← список тестов + кнопка создать ├── TestCreate/ ← форма создания теста └── TestDetail/ ← просмотр теста с вопросами ``` --- ## API эндпоинты | Метод | URL | Описание | |-------|-----|----------| | GET | `/api/health` | Проверка работы сервера | | GET | `/api/tests` | Список тестов | | GET | `/api/tests/{id}` | Детали теста с вопросами и ответами | | POST | `/api/tests` | Создать тест | --- ## Схема БД ``` tests id, title, description, passing_score, time_limit, allow_navigation_back, is_active, version, created_at questions id, test_id → tests.id, text, order answers id, question_id → questions.id, text, is_correct ``` --- ## Валидация **Backend (Pydantic):** - Тест: минимум 7 вопросов, passing_score 0–100 - Вопрос: минимум 3 варианта ответа, хотя бы 1 правильный **Frontend (Ant Design Form):** - Те же правила воспроизведены на клиенте - Nested Form.List для динамических вопросов и ответов - Таймер показывается только при включённом переключателе (shouldUpdate) --- ## Как запустить ```bash docker compose up --build ``` Открыть браузер: `http://localhost` - Список тестов → кнопка «Создать тест» - Заполнить форму → нажать «Создать тест» - Перейти к созданному тесту и увидеть все вопросы и ответы - `http://localhost/api/health` → `{"status": "ok"}` - `http://localhost/api/docs` → Swagger UI FastAPI - `http://localhost/api/redoc` → ReDoc документация --- ## Баги, найденные и исправленные при ручном тестировании | # | Симптом | Причина | Исправление | |---|---------|---------|-------------| | 1 | `permission denied` на `entrypoint.sh` | `docker-compose.yml` монтирует `./backend:/app` — volume перекрывает файлы образа, включая результат `chmod +x` из Dockerfile | `CMD ["bash", "entrypoint.sh"]` вместо `CMD ["./entrypoint.sh"]` | | 2 | `No module named 'app'` в Alembic | Python не добавляет WORKDIR в `sys.path` автоматически | `ENV PYTHONPATH=/app` в Dockerfile | | 3 | nginx: `host not found in upstream "backend"` | nginx резолвит upstream-хосты **при старте**, а backend ещё не поднялся | Docker DNS resolver `127.0.0.11` + `set $backend` — резолвинг откладывается до момента запроса | | 4 | `http://localhost/api/docs` → 404 | FastAPI по умолчанию отдаёт Swagger по `/docs`, а через nginx путь становится `/api/docs` → `backend:8000/api/docs` (не существует) | Явно указать `docs_url="/api/docs"`, `redoc_url="/api/redoc"`, `openapi_url="/api/openapi.json"` в FastAPI | > **Для джуниора:** все четыре бага — типичные для первого запуска Docker + FastAPI + nginx. Запомни их, встретишь снова. --- ## Ключевые решения для джуниора **Async SQLAlchemy 2.0:** - `async_sessionmaker` + `AsyncSession` — не блокируем event loop при запросах к БД - `selectinload` для жадной загрузки связей (вместо N+1 запросов) **Alembic async:** - `async_engine_from_config` + `connection.run_sync(do_run_migrations)` - URL берётся из `app.config.settings` — одно место для конфига **Docker Compose healthcheck:** - `db` объявляет `healthcheck`, `backend` ждёт `condition: service_healthy` - Дополнительно `entrypoint.sh` вызывает `pg_isready` для надёжности **TanStack Query:** - `useQuery` для чтения данных — кэш, loading state, error state из коробки - `useMutation` для создания — `invalidateQueries` обновляет список после успеха --- ## Следующие шаги - [x] Спринт 1: Инфраструктура + Создание тестов - [ ] Спринт 2: Прохождение теста + результаты - [ ] Спринт 3: Трекер результатов - [ ] Спринт 4: Авторизация и роли - [ ] Спринт 5: Уведомления в MAX