Аннотация: В этом групповом проекте ты продолжишь работу над MCP-сервером для Лавки «Школы 21». В предыдущем групповом проекте команда спроектировала tools, собрала архитектуру domain / storage / gateway, обработала ошибки и проверила сервер через MCP Inspector. Теперь нужно сделать следующий шаг: проверить, можно ли этому серверу доверять, описать тест-кейсы, покрыть ключевую логику автоматическими тестами, настроить GitLab CI и упаковать проект в Docker.
Привет! Это твой 9-й и 10-й день заплыва - проверка на прочность, финальный командный проект. Твоя роль сегодня двойная - QA Engineer и DevOps. Ты не просто плывёшь — ты проверяешь, выдерживает ли корабль шторм. Это контроль качества, надёжность и автоматизация. Ты превратишь требования в тест-кейсы, бизнес-правила — в автоматические проверки, а разрозненный код — в воспроизводимый продукт. Это не просто проверка кода — это проверка всей команды. Вы покажете, что продукт можно доверять, что он готов к использованию. Один последний рывок — и вы на финише.
Этот проект полезен не только будущим разработчикам. QA-инженер здесь учится превращать требования и контракты tools в тест-кейсы. DevOps/SRE — запускать приложение воспроизводимо и автоматически проверять его после изменений. BSA и проджект-менеджер — связывать требования, ошибки и приемку. Продуктовый специалист — понимать, какие пользовательские сценарии должны быть защищены проверками. Специалист по кибербезопасности — замечать небезопасные настройки, секреты и некорректную обработку ошибок.
Удачи!
Как учиться в «Школе 21»:
Как работать с проектом:
src/.develop и ведите разработку в ней. Пушить в GitVerse нужно именно ветку develop.src/ склонированного репозитория.materials.materials/starter-kit/ в корень своего проекта.materials/. Это не провал. Важнее пройти практику тестирования и деплоя, чем потратить весь день на восстановление непонятной кодовой базы.develop. Для задач создавайте отдельные ветки: feature/mcp-audit, feature/test-cases, feature/tests, feature/docker, feature/ci, feature/handoff.src/, тесты — в tests/, документацию — в docs/. Dockerfile, .dockerignore, docker-compose.yml и .gitlab-ci.ymlai-logs.md./Users/<name>/....Рекомендуемое распределение ролей (один человек может иметь несколько ролей):
| Роль | За что отвечает |
|---|---|
| Team Lead / Release Lead | координация, ветки, merge requests, итоговый handoff, презентация результата, README, test summary, скриншоты, оформление результатов |
| QA Lead | test_cases.md, ручные проверки, контроль покрытия сценариев |
| Developer | unit- и integration-тесты, исправление дефектов, рефакторинг под тестируемость |
| DevOps | Dockerfile, docker-compose, GitLab CI, команды запуска, проверка воспроизводимости |
Роли можно распределить иначе, но каждый участник должен понимать общий результат и уметь объяснить не только «свою» часть.
Дисклеймер:
ai-logs.md. Ответственность за качество тест-кейсов, автоматических тестов, Dockerfile и CI-пайплайна лежит на команде.В предыдущем групповом проекте вы собрали MCP-сервер для Лавки «Школы 21». У него появились tools: показать каталог, получить товар, оформить заказ, отменить заказ, узнать статус, подтвердить выдачу и проверить баланс коинов. Вы разделили код на domain, storage и gateway, подключили SQLite, описали контракты в JSON-схемах, добавили обработку ошибок и проверили сервер через MCP Inspector.
На первый взгляд продукт готов: сервер запускается, tools видны, несколько вызовов прошли успешно. Но в реальной разработке этого недостаточно.
Представь, что ИИ-агент участника Основы действительно пользуется вашим сервером. Участник просит: «Подбери мне худи размера M и оформи заказ». Агент вызывает create_order. Сервер отвечает ok: true. Коины списались. Остаток уменьшился. Заказ появился.
Теперь менее удобная ситуация: пользователь пытается купить товар, которого нет; отменить уже выданный заказ; подтвердить выдачу второй раз; купить худи, когда размер закончился; заказать больше лимита. Если команда не проверила такие сценарии, она не может уверенно сказать: «Сервер готов к передаче дальше».
Тест-кейс — это зафиксированное ожидание: при таких условиях, с такими входными данными, система должна повести себя вот так. Автоматический тест — это способ проверять это ожидание снова и снова без ручной работы. Docker — это способ запускать проект одинаково на разных компьютерах. CI — это автоматическая проверка после push или merge request.
Основной сценарий проекта — MCP-сервер Лавки «Школы 21». Личный кейс из выбранных участниками сеттингов вынесен в бонусную часть. Так у всех команд остается единая обязательная база для тестирования и деплоя.
Минимально должны быть покрыты следующие обязательные tools:
| Tool | Что делает |
|---|---|
list_products |
показывает каталог мерча и актуальные остатки |
get_product |
показывает детали одного товара |
create_order |
оформляет заказ, списывает коины и уменьшает остаток |
cancel_order |
отменяет заказ до выдачи, возвращает коины и остаток |
get_order_status |
возвращает статус заказа |
confirm_pickup |
подтверждает выдачу заказа |
get_coin_balance |
показывает баланс пользователя |
Минимально должны быть проверены конфликтные сценарии:
| Сценарий | Пример ожидаемого поведения |
|---|---|
| Недостаточно коинов | заказ не создается, возвращается INSUFFICIENT_COINS |
| Товар не существует | возвращается PRODUCT_NOT_FOUND |
| Размер отсутствует или закончился | возвращается SIZE_NOT_FOUND или OUT_OF_STOCK |
| Пользователь превысил лимит | возвращается USER_LIMIT_EXCEEDED |
| Попытка отменить выданный заказ | возвращается ORDER_STATUS_ERROR |
| Повторное подтверждение выдачи | возвращается ORDER_STATUS_ERROR |
| Покупка последней единицы товара | первый заказ проходит, следующий заказ получает OUT_OF_STOCK, остаток не уходит в минус |
Ошибки должны возвращаться в едином формате:
{
"ok": false,
"error_code": "INSUFFICIENT_COINS",
"message": "Не удалось оформить заказ: у пользователя student_02 недостаточно коинов. Нужно 500, доступно 120.",
"details": {
"required": 500,
"available": 120
}
}
Понятное сообщение об ошибке должно отвечать на три вопроса:
Плохое сообщение:
Error: failed
Понятное сообщение:
Не удалось оформить заказ: размер XL для товара hoodie закончился. Выберите другой размер или другой товар.
В этом проекте вы будете работать с несколькими уровнями проверки:
| Уровень | Что проверяет | Пример |
|---|---|---|
| Test cases | ожидаемое поведение системы на языке сценариев | «нельзя купить товар при недостатке коинов» |
| Unit tests | отдельные чистые функции domain.py |
can_purchase() возвращает отказ при превышении лимита |
| Integration tests | совместную работу gateway, storage и domain |
вызов обработчика create_order меняет данные и возвращает корректный ответ |
| Проверка формата ответа | единый контракт успешных и ошибочных ответов внутри integration-тестов | ошибка содержит ok, error_code, message |
| MCP Inspector smoke tests | поведение сервера через MCP-интерфейс | tool виден и вызывается в Inspector |
| Docker checks | воспроизводимый запуск | проект собирается и тесты проходят внутри контейнера |
| CI checks | автоматический запуск проверок после push или merge request | pipeline в GitLab запускает pytest |
Важное уточнение: MCP-сервер на stdio — это не обычный веб-сайт на localhost:8000. Поэтому в этом проекте не нужно делать отдельный web-адаптер и health-check endpoint. Достаточно, чтобы сервер можно было упаковать в Docker, а тесты проходили внутри контейнера и в GitLab CI.
Можно спрашивать:
Нельзя спрашивать:
После получения ответа от ИИ всегда обсуждайте его в команде, адаптируйте под свой проект и фиксируйте все значимые промпты и выводы в ai-logs.md.
Цель этого проекта: превратить учебный MCP-сервер в воспроизводимый мини-продукт для Лавки «Школы 21»: с тест-кейсами, автоматическими проверками, Docker-запуском, CI и понятной документацией.
Прежде чем вы начнете писать тесты и настраивать конвейеры, вы должны создать единое цифровое пространство для команды. Без этого шага все остальные задания (CI, публикация образа) просто не смогут работать в командном режиме. До этого момента ваш код жил в репозитории одного из участников.
Зачем мы это делаем:
Ваша задача: создать место, где GitVerse сможет запускать workflow. (этот шаг выполняется с нуля, даже если код предыдущего проекта хранится в другом Git-сервисе).
Действия:
1. Каждый участник зарегистрируйте личную учетную запись на https://gitverse.ru/, подтвердите email и войдите в систему. Используйте личный пароль, который не повторяется в других сервисах. По возможности включите дополнительную защиту учетной записи.
2. Выберите владельца репозитория. Только владелец создает новый приватный репозиторий с именем school21-lavka-mcp.
3. При создании выберите видимость Private. Создавайте пустой репозиторий: не добавляйте README, .gitignore и лицензию через интерфейс, если эти файлы уже есть в исходной кодовой базе.
4. Не используйте mirror-import. GitVerse не запускает CI/CD для mirror-репозиториев. Обычный перенос Git-истории через новый remote не является зеркалированием.
5. В настройках репозитория добавьте участников команды как соавторов с правом записывать код. Не передавайте им пароль владельца или token. Каждый работает под своей учетной записью.
6. Выберите версию MCP-сервера из предыдущего проекта, которую будете использовать как исходную кодовую базу. Зафиксируйте:
Не объединяйте несколько разных реализаций участников на этом этапе.
7. Участник, ответственный за перенос, подготовьте локальную копию выбранного репозитория.
Если репозиторий еще не находится на вашем компьютере:
git clone <URL_ИСХОДНОГО_РЕПОЗИТОРИЯ>
cd <ИМЯ_КАТАЛОГА>
git switch <ИМЯ_ВЕТКИ>
Если локальная копия уже есть, перейдите в ее каталог и получите последние изменения:
cd <ПУТЬ_К_РЕПОЗИТОРИЮ>
git switch <ИМЯ_ВЕТКИ>
git pull
8. Проверьте состояние выбранной версии:
git status
git branch --show-current
git rev-parse HEAD
git remote -v
Убедитесь, что текущий commit совпадает с выбранной командой версией. Сначала сохраните или осознанно удалите незакоммиченные изменения. Не переносите .env, базы, токены и временные файлы.
9. Сохраните старый remote и добавьте GitVerse:
git remote rename origin previous-origin
git remote add origin https://gitverse.ru/<owner>/school21-lavka-mcp.git
git remote -v
Если remote origin отсутствовал, пропустите команду git remote rename.
10. Если GitVerse не принимает пароль учетной записи при HTTPS push, создайте в профиле отдельный token с доступом Репозитории. При запросе credentials укажите свой GitVerse-логин как username, а repository token как password. Разрешите системному credential manager сохранить его. Не вставляйте token в URL remote, shell history, .env или файлы проекта. Это не тот же credential, что package token GV_REGISTRY_TOKEN из задания 8: не переиспользуйте один token для разных назначений.
11. Создайте.gitignore в репозитории, в который включите виртуальные окружения, кэшированные файлы python, переменные окружения, локальные базы, локальные отчеты, системные файлы macos или windows.
12. Подготовьте и отправьте develop:
git switch develop
git push -u origin develop
Если ветки еще нет:
git switch -c develop
git push -u origin develop
13. В GitVerse откройте Настройки → Ветки и назначьте develop главной веткой. Это обязательно для ручного запуска: GitVerse показывает и запускает workflow_dispatch только для workflow-файла из главной ветки.
14. Остальные участники клонируют уже GitVerse-репозиторий:
git clone https://gitverse.ru/<owner>/school21-lavka-mcp.git
cd school21-lavka-mcp
git switch develop
15. Откройте Настройки → Репозиторий, включите тумблер CI/CD и сохраните настройки. Сам workflow появится после добавления файла в .gitverse/workflows/. Не создавайте и не регистрируйте self-hosted runner.
16. Создайте docs/gitverse_setup.md и запишите:
develop назначена главной и рабочей веткой;Не записывайте в этот файл пароли и токены.
Результат:
develop назначена главной и рабочей веткой.docs/gitverse_setup.md без секретов.Перед тем как писать тесты и Dockerfile, нужно понять состояние проекта. Это похоже на приемку кода от другой команды: сначала не исправляй всё подряд, а зафиксируй, что уже есть, что работает, что сломано и на что можно опираться. Особенно это важно, поскольку вы выбрали код только одного из участников команды — вам всем важно в него погрузиться и разобраться.
Если у вас нет кода из проекта 5: В папке materials/starter-kit/ находится MCP-сервер Лавки с 7 обязательными и 3 дополнительными tools. Скопируйте его содержимое в корень вашего репозитория и используйте как основу. Если у вас есть свой код — используйте его, предварительно убедившись, что все tools работают.
Ваша задача: Провести входную диагностику MCP-сервера, проверить его запуск и работоспособность через MCP Inspector, зафиксировать результаты в docs/mcp_audit.md и, при необходимости, исправить мелкие ошибки для прохождения минимального quality gate.
1. Склонируйте репозиторий MCP-сервера из предыдущего группового проекта.
2. Создайте ветку git checkout -b develop
Если develop уже есть, создайте рабочую ветку:
git checkout develop
git checkout -b feature/mcp-audit
3. Проверьте структуру проекта. Минимально в репозитории должны быть:
contracts/
tools_schemas.json
src/
domain.py
storage.py
gateway.py
server.py
errors.py
README.md или HANDOFF.md
ai-logs.md
Если части файлов нет, не скрывайте это. Зафиксируйте состояние в docs/mcp_audit.md.
4. Установите зависимости и попробуйте запустить сервер:
python -m pip install -r requirements.txt
python src/server.py
Если requirements.txt отсутствует, создайте черновик вручную. Минимально вам могут понадобиться:
fastmcp
pytest
jsonschema
5. Проверьте запуск через MCP Inspector: npx @modelcontextprotocol/inspector python src/server.py
6. Проверьте входной quality gate:
| Проверка | Ожидаемый результат | Статус |
|---|---|---|
python src/server.py запускается |
сервер стартует без ошибки импорта | passed / failed |
| Inspector видит tools | отображаются 7 обязательных tools | passed / failed |
contracts/tools_schemas.json есть |
файл читается и содержит схемы | passed / failed |
list_products вызывается |
возвращает ok: true и список товаров | passed / failed |
get_coin_balance вызывается |
возвращает баланс пользователя | passed / failed |
create_order вызывается |
успешный заказ создается | passed / failed |
| Ошибки не падают стеком наружу | возвращается ok: false, error_code, message |
passed / failed |
README/HANDOFF помогает запустить проект |
инструкции достаточно для нового участника | passed / failed |
7. Если сервер не проходит quality gate, зафиксируйте проблему и выберите один из путей:
| Ситуация | Что делать |
|---|---|
| Ошибка мелкая: не хватает зависимости, неправильный импорт, неверный путь к базе | исправить в рамках задания 1 |
| Ошибка средняя: один-два tools не работают, но архитектура понятна | исправить и продолжить проект |
| Ошибка критичная: сервер не запускается, структура непонятна, tools отсутствуют | использовать starter-kit из materials/ и перенести туда ваши контракты и бизнес-правила |
Не тратьте весь проект на спасение полностью сломанного кода. Важная часть задания — честно диагностировать состояние и выбрать рабочий путь.
8. Откройте и заполните файл docs/mcp_audit.md по шаблону и создайте файл docs/known_issues.md, который дублирует в себя список известных проблем в свободной форме — с этим файлом вы еще будете продолжать работать далее.
Результат: В папке src добавлены: docs/mcp_audit.md — входная диагностика сервера, docs/known_issues.md — список известных проблем, если они есть. Исправления, необходимые для прохождения минимального quality gate.
Поздравляем, теперь вся ваша команда погрузилась в то, как работает выбранный вами проект и какие подводные камни он в себе содержит!
В предыдущем групповом проекте вы описывали tools как контракты: входные параметры, успешные ответы и формат ошибок. Это было не зря — теперь эти контракты станут основой для тестирования.
Если контракт говорит, что create_order принимает user_id, product_id, size, quantity, значит должны быть тест-кейсы для корректных и некорректных значений этих параметров. Если бизнес-правило говорит, что нельзя купить больше лимита, значит должен быть тест-кейс на превышение лимита.
Ваша задача: Создать понятное описание контрактов tools (docs/tools_contract.md) и на его основе разработать минимум 14 тест-кейсов (positive, negative, boundary), сохранив их в docs/test_cases.md.
1. Откройте contracts/tools_schemas.json, README/HANDOFF и материалы предыдущего проекта.
2. Создайте файл docs/tools_contract.md. Для каждого tool кратко опишите:
## create_order
Что делает: оформляет заказ на товар из Лавки.
Вход:
- user_id: string, обязательный
- product_id: string, обязательный
- size: string, обязательный
- quantity: integer, обязательный или по умолчанию 1
Успешный ответ:
- ok: true
- data.order_id: string
- data.status: created
Ошибки:
- PRODUCT_NOT_FOUND
- SIZE_NOT_FOUND
- OUT_OF_STOCK
- INSUFFICIENT_COINS
- USER_LIMIT_EXCEEDED
3. Сначала без ИИ придумайте минимум 7 тест-кейсов. Они должны покрывать:
4. Затем используйте ИИ как QA-ассистента. Пример промпта:
У нас есть MCP-сервер для Лавки Школы 21.
Tools: list_products, get_product, create_order, cancel_order, get_order_status, confirm_pickup, get_coin_balance.
Бизнес-правила: нельзя купить товар без достаточного баланса, нельзя купить отсутствующий размер, нельзя превысить лимит, нельзя отменить picked_up заказ, нельзя повторно подтвердить выдачу.
Предложи дополнительные test cases: positive, negative, boundary.
Не пиши код. Сформируй таблицу с колонками: ID, Tool, Scenario, Preconditions, Input, Expected Result, Type.
5. Сравните предложения ИИ со своими. Удалите лишнее, исправьте неточные сценарии, добавьте пропущенные. Промпт и ответ сохраните в файл ai-logs.md. Также добавьте комментарии команды: что взяли и что отклонили и почему.
6. Создайте docs/test_cases.md. Минимально нужно 14 тест-кейсов:
create_order, cancel_order, confirm_pickup;create_order, cancel_order и confirm_pickup покрыты и успешным, и ошибочным сценарием.Формат таблицы test_cases.md:
| ID | Tool | Сценарий | Предусловие | Входные данные | Ожидаемый результат | Тип | Источник | Автоматизирован? |
|---|---|---|---|---|---|---|---|---|
| TC-001 | list_products | Получить каталог | В базе есть товары | {} | ok=true, список товаров не пустой | positive | команда | да |
В колонке «Источник» указывайте происхождение идеи: команда, ИИ, ИИ + доработка команды, из найденного бага.
В колонке «Автоматизирован?» пишите да, нет, частично или вручную. Не копируйте в таблицу код теста целиком. Здесь нужен сценарий проверки: что было на входе, что команда сделала и какой результат ожидала.
Результат: В папке src: docs/tools_contract.md — человекочитаемое описание контрактов tools, docs/test_cases.md — таблица минимум из 14 тест-кейсов, ai-logs.md — промпт для генерации дополнительных тест-кейсов, ответ ИИ и комментарии команды: что взяли, что отклонили и почему.
Супер! Вы заложили фундамент для дальнейшего тестирования — оно больше не будет иметь хаотичный характер, и у вас выше шансы, что будет покрыто большинство возможных ошибок, и финальный продукт будет качественнее.
Теперь следующий шаг — создание тестового окружения.
Ваша задача: Создать тестовое окружение: папку tests/fixtures/ с seed-данными, скрипт сброса тестовой базы (scripts/reset_test_db.py) и smoke-тест (tests/test_smoke.py), проверяющий, что окружение работает.
Тест не должен зависеть от случайного состояния локальной базы. Если один пир запустил тест после десяти ручных заказов, а другой — на чистой базе, результат должен быть одинаковым. Поэтому вам нужна изолированная тестовая база и понятные стартовые данные.
В этом проекте не нужно с нуля проектировать большой набор тестовых данных. Используйте seed-данные из предыдущего MCP-проекта или starter-kit из materials/data. Задача — сделать так, чтобы тесты могли каждый раз начинаться с одинакового состояния.
1. Создайте папку tests/fixtures/
2. В этой папке подготовьте минимальный тестовый набор данных. Можно использовать SQL-файл, Python-файл или JSON. Главное — чтобы тесты могли быстро создать одинаковое состояние.
Минимальный набор:
Пользователи:
student_01: 1000 коинов
student_02: 300 коинов
student_poor: 20 коинов
Товары:
hoodie: 500 коинов, размеры S=3, M=2, L=1, XL=0, лимит 1
tshirt: 250 коинов, размеры S=10, M=8, L=6, XL=4, лимит 2
stickers: 50 коинов, размер one_size=50, лимит 5
Заказы:
order_created: статус created
order_cancelled: статус cancelled
order_picked_up: статус picked_up
3. Создайте функцию или скрипт сброса тестовой базы. Например: scripts/reset_test_db.py
Скрипт должен:
4. Настройте код так, чтобы тесты использовали отдельную базу. Не используйте рабочий файл lavka.db внутри тестов.
Рекомендуемый подход:
import os
from pathlib import Path
DB_PATH = Path(os.getenv("LAVKA_DB_PATH", "data/test_lavka.db"))
DB_PATH.parent.mkdir(parents=True, exist_ok=True)
Скрипт сброса и тесты должны использовать один и тот же путь из LAVKA_DB_PATH. Если переменная не задана, для тестового окружения используется data/test_lavka.db.”
python scripts/reset_test_db.py
pytest tests/test_smoke.py -v
test_smoke.py должен проверять, что:
Кстати, fun fact! Первые smoke-тесты проводили печники, когда после сборки печи, ее затапливали и смотрели, откуда будет идти дым: только ли из положенных мест?
Результат: В папке src: tests/fixtures/ — тестовые данные или файлы для создания тестового состояния, scripts/reset_test_db.py или эквивалентный способ сброса тестовой базы, tests/test_smoke.py — минимальная проверка, что тестовое окружение работает. Раздел в README: как пересоздать тестовую базу.
Теперь нужно превратить часть test cases в код. В этом проекте достаточно двух уровней: unit-тесты для domain.py и integration-тесты для цепочки gateway → storage → domain → response.
Ваша задача: Написать минимум 6 unit-тестов для domain.py и минимум 4 integration-теста для обработчиков tools. Все тесты должны проходить локально.
domain.py — самый удобный слой для тестирования. Он не должен знать о SQLite, MCP, JSON и файлах. Это чистая бизнес-логика: можно ли купить, можно ли отменить, можно ли подтвердить выдачу, как изменятся баланс и остаток.
1. Убедитесь, что domain.py содержит чистые функции. Примеры:
def can_purchase(user_balance: int, product_price: int, already_purchased: int, user_limit: int) -> tuple[bool, str]:
...
def can_cancel(order_status: str) -> tuple[bool, str]:
...
def can_confirm_pickup(order_status: str) -> tuple[bool, str]:
...
2. Если функция сразу читает SQLite или вызывает MCP, вынесите бизнес-правило в отдельную чистую функцию.
3. Создайте файл tests/test_domain.py
4. Напишите минимум 6 unit-тестов. Минимальный набор:
| Тест | Что проверяет |
|---|---|
test_can_purchase_when_balance_and_limit_are_ok |
покупка разрешена |
test_can_purchase_rejects_insufficient_coins |
недостаточно коинов |
test_can_purchase_rejects_user_limit |
превышен лимит |
test_can_cancel_created_order |
заказ в статусе created можно отменить |
test_can_cancel_rejects_picked_up_order |
выданный заказ нельзя отменить |
test_can_confirm_pickup_rejects_cancelled_order |
отмененный заказ нельзя выдать |
5. Соблюдайте структуру Arrange — Act — Assert:
def test_can_purchase_rejects_insufficient_coins():
# Arrange
user_balance = 100
product_price = 500
already_purchased = 0
user_limit = 1
# Act
allowed, reason = can_purchase(user_balance, product_price, already_purchased, user_limit)
# Assert
assert allowed is False
assert "коинов" in reason.lower() or "coins" in reason.lower()
6. Запустите pytest tests/test_domain.py -v
Unit-тесты проверяют бизнес-правила по отдельности. Но MCP-сервер работает как цепочка: gateway получает входные данные, storage читает состояние, domain принимает решение, storage меняет данные, gateway возвращает ответ.
1. Сделайте так, чтобы основную логику tools можно было вызывать из тестов без ручного запуска MCP Inspector. Например, вынесите обработчики в обычные функции:
def handle_create_order(payload: dict, storage: Storage) -> dict:
...
@mcp.tool() может вызывать эту функцию, а тесты могут вызывать ее напрямую.
2. Создайте файл tests/test_gateway_integration.py
3. Напишите минимум 4 integration-теста:
| Тест | Что проверяет |
|---|---|
| успешный create_order | заказ создан, баланс уменьшился, остаток уменьшился, ответ ok=true |
| create_order при недостатке коинов | заказ не создан, ответ ok=false, error_code=INSUFFICIENT_COINS |
| cancel_order для created | статус изменился на cancelled, баланс и остаток восстановлены |
| confirm_pickup для picked_up повторно | ответ ok=false, error_code=ORDER_STATUS_ERROR |
4. В integration-тестах обязательно проверьте формат ответа:
Для успеха:
assert response["ok"] is True
assert "data" in response
Для ошибки:
assert response["ok"] is False
assert "error_code" in response
assert "message" in response
assert "Traceback" not in response["message"]
5. Запустите все тесты:
pytest tests/ -v
Результат: В папке src: tests/test_domain.py — минимум 6 unit-тестов, tests/test_gateway_integration.py — минимум 4 integration-теста. Все тесты проходят командой pytest tests/ -v. При необходимости — рефакторинг src/domain.py, src/gateway.py, src/storage.py, чтобы код был тестируемым.
Автоматические тесты проверяют большую часть логики, но они не заменяют проверку через MCP-интерфейс. Нужно убедиться, что tools действительно видны и вызываются таким образом, как их будет видеть внешний ИИ-агент.
Ваша задача: Провести ручную smoke-проверку через MCP Inspector, зафиксировать результаты в docs/inspector_check.md и, при необходимости, обновить docs/known_issues.md.
Действия:
1. Запустите MCP Inspector:
npx @modelcontextprotocol/inspector python src/server.py
2. Проверьте минимум 5 tools через Inspector:
3. Заполните файл docs/inspector_check.md. Опишите в нем результат проверки выше
4. Если нашли дефект, не скрывайте его. Исправьте или добавьте в docs/known_issues.md.
5. Используйте ИИ для анализа непонятных логов, если нужно. В ai-logs.md сохраните:
Результат: В папке src: docs/inspector_check.md, logs/inspector_log.txt или скриншоты в screenshots/. Обновленный docs/known_issues.md, если остались известные проблемы. Обновленный ai-logs.md.
CI, Continuous Integration — это автоматическая проверка проекта после изменений. Команда делает push или открывает merge request, GitVerse запускает pipeline, а pipeline показывает: тесты прошли или проект сломан.
Без CI команда часто узнает о проблемах слишком поздно: «у меня локально работало», «я забыл запустить тесты», «на ноутбуке другого участника другая версия Python». CI снижает этот риск.
Workflow хранится в .gitverse/workflows/ci.yml. В этом задании он только запускает тесты. Публикацию образа вы добавите в задании 8.
Ваша задача: настроить автоматический CI-пайплайн в GitVerse
Действия:
1. В терминале, находясь в корне локального репозитория, создайте ветку:
git switch develop
git pull
git switch -c feature/ci
Создайте файл workflow:
mkdir -p .gitverse/workflows
touch .gitverse/workflows/ci.yml
2. Откройте .gitverse/workflows/ci.yml в редакторе и соберите workflow по каркасу:
name: TODO
on:
push:
branches: [TODO]
pull_request:
branches: [TODO]
workflow_dispatch:
jobs:
tests:
runs-on: TODO
env:
LAVKA_DB_PATH: TODO
steps:
- name: Получить код
uses: TODO
- name: Установить Python
uses: TODO
with:
python-version: TODO
# Добавьте шаги установки зависимостей,
# сброса базы, запуска тестов
# и сохранения JUnit artifact.
Workflow должен:
develop;workflow_dispatch;tests на ubuntu-latest;actions/checkout@v4;actions/setup-python@v5;requirements.txt;data/test_lavka_ci.db через LAVKA_DB_PATH;python scripts/reset_test_db.py;python -m pytest tests/ -v --junitxml=reports/junit.xml
reports/junit.xml через actions/upload-artifact@v4 на 3 дня.Каталог reports/ должен быть создан до запуска тестов.
Используйте python -m pytest, чтобы тесты запускались тем же интерпретатором Python, который установлен в workflow.
Не используйте continue-on-error, allow_failure, || true и другие способы скрыть падение тестов. if: always() допустим только для загрузки JUnit artifact.
3. До отправки workflow повторите основные команды локально:
export LAVKA_DB_PATH=data/test_lavka_ci.db
mkdir -p reports
python scripts/reset_test_db.py
python -m pytest tests/ -v --junitxml=reports/junit.xml
Убедитесь, что тесты проходят и появился reports/junit.xml. Не добавляйте временную базу и локальный отчет в commit.
4. Отправьте workflow в GitVerse:
git add .gitverse/workflows/ci.yml
git commit -m "ci: add GitVerse test workflow"
git push -u origin feature/ci
В интерфейсе GitVerse создайте запрос на слияние:
feature/ci;develop.Если после создания открылась несуществующая страница, вернитесь в раздел запросов и проверьте список. Также убедитесь, что ветка feature/ci отправлена и содержит изменения относительно develop.
5. В разделе CI/CD откройте workflow CI и проверьте:
Если запуск по событию pull_request не произошел из-за ограничения платформы, зафиксируйте это в docs/known_issues.md. После объединения workflow обязан автоматически запуститься по push в develop.
6. Проверьте сценарий «красный → зеленый»:
Не объединяйте заведомо красную версию.
7. Обновите README. Опишите события запуска, job tests, версию Python, путь тестовой базы, команду тестирования, JUnit artifact и место просмотра результатов в GitVerse.
Команда должна уметь объяснить:
.gitverse/workflows/ci.yml.В предыдущем задании вы научились проверять код. Теперь мы делаем шаг к поставке (Delivery). Мы упакуем наше приложение в Docker-образ — “контейнер”, который можно запустить на любом сервере, где есть Docker.
Примечание: GitVerse использует Hosted Runners (общие виртуальные машины). В целях безопасности эти раннеры не имеют доступа к docker.sock (системному сокету Docker). Это значит, что вы не можете выполнить команду docker build стандартным способом.
Вместо этого используем Kaniko — инструмент от Google, который собирает образы внутри контейнера без использования Docker-демона. Это стандартный подход для облачных CI/CD (как GitLab, так и GitVerse).
Сборка образа происходит только вручную (workflow_dispatch) и только из ветки develop, когда есть уверенность, что все тесты прошли. Это защищает нас от публикации сломанного продукта.
Ваша задача: упаковать MCP-сервер в Docker-образ и опубликовать его в GitVerse Packages.
1. Подготовьте рабочую ветку
Получите актуальную версию develop и создайте ветку:
git switch develop
git pull --ff-only origin develop
git switch -c feature/image-publish
Если ветка уже существует, перед продолжением добавьте в неё актуальные изменения из develop.
2. Создайте .dockerignore
В корне репозитория создайте .dockerignore.
Исключите из build context:
.git и .docker/;.venv и Python-кеши;.env;Не исключайте src/, tests/, scripts/ и requirements.txt: они нужны для build-time проверок.
Объясните, чем .dockerignore отличается от .gitignore и почему каталог .docker/ не должен попадать в образ.
3. Создайте Dockerfile
Создайте Dockerfile на базе Python 3.11. Он должен:
requirements.txt;PYTHONPATH;LAVKA_DB_PATH для build-time тестов;Обязательные build-time проверки:
RUN python scripts/reset_test_db.py
RUN python -m pytest tests/ -v
Не используйте:
RUN python -m scripts/reset_test_db.py
reset_test_db.py запускается как файл по пути, а pytest — как установленный Python-модуль.
Если одна из проверок завершается с ошибкой, сборка должна остановиться и образ не должен публиковаться.
4. Подготовьте доступ к GitVerse Packages
Эти действия выполняет владелец репозитория.
1. Создайте отдельный package token с правом записи.
2. Сохраните token как repository secret:
GV_REGISTRY_TOKEN
3. Создайте repository variable:
GV_REGISTRY_OWNER=<логин владельца>
Используйте точный логин из URL репозитория. Например, для:
gitverse.ru/<username>/school21-lavka-mcp
значение переменной — username.
В workflow используйте только:
$
$
Не записывайте значение token в файлы проекта или логи.
5. Добавьте ручную публикацию в workflow
Откройте .gitverse/workflows/ci.yml и добавьте в jobs: вторую job — publish_image.
Используйте каркас:
publish_image:
needs: tests
if: >-
$
runs-on: ubuntu-latest
steps:
- name: Получить исходный код
uses: actions/checkout@v4
with:
path: source
- name: Подготовить registry credentials
# Проверьте наличие GV_REGISTRY_OWNER и GV_REGISTRY_TOKEN.
# Создайте .docker/config.json во временном workspace.
# Значение auth — base64 от <owner>:<token>.
- name: Собрать и опубликовать образ
uses: docker://gcr.io/kaniko-project/executor:v1.24.0
env:
DOCKER_CONFIG: $/.docker
with:
args: >-
--context=dir://$/source
--dockerfile=$/source/Dockerfile
--destination=gitverse.ru/$/school21-lavka-mcp:$
--digest-file=$/image-digest.txt
- name: Сохранить digest
uses: actions/upload-artifact@v4
with:
name: image-digest
path: $/image-digest.txt
retention-days: 3
Registry config должен находиться вне каталога source. Kaniko получает только source как build context, поэтому credentials не должны быть доступны команде COPY . ..
Команда самостоятельно реализует создание корректного .docker/config.json и проверку наличия secret и variable.
6. Проверьте поведение в pull request
Отправьте ветку и создайте запрос на слияние:
feature/image-publish → develop
При запуске по pull request ожидается:
tests — выполнена
publish_image — пропущена
Пропуск publish_image в pull request является ожидаемым результатом: публикация разрешена только для ручного запуска из develop.
7. Выполните ручную публикацию
После зелёных тестов:
Не используйте кнопку повторного запуска pull request: она повторяет событие pull_request, поэтому publish_image снова будет пропущена.
Успешный запуск должен создать:
8. Найдите опубликованный пакет
Сначала откройте раздел Пакеты в профиле владельца.
Найдите:
school21-lavka-mcp
Если пакет не отображается во вкладке репозитория, привяжите его к репозиторию через настройки пакета.
Image reference должен иметь вид:
gitverse.ru/<owner>/school21-lavka-mcp:<commit-sha>
<owner> и <commit-sha> — placeholders. Угловые скобки не вводятся буквально.
9. Заполните release-документ
Создайте или обновите docs/gitverse_release.md:
Commit SHA:
Workflow URL:
Image reference:
Digest:
Package URL:
Build-time checks:
Runtime limitation:
Если готовый container не запускался, укажите:
Подтверждены Kaniko build и build-time тесты.
Итоговый container не запускался, поскольку hosted runner не предоставляет docker.sock.
gitverse.ru//school21-lavka-mcp — переменная GV_REGISTRY_OWNER не задана или имеет неверное имя.Invalid auth configuration file — secret пустой либо .docker/config.json сформирован некорректно.No module named scripts/reset_test_db.py — файл ошибочно запущен через python -m; используйте python scripts/reset_test_db.py..dockerignore и Dockerfile.publish_image зависит от успешной job tests.develop.docs/gitverse_release.md содержит фактические ссылки и ограничения.Теперь это готовый переносимый артефакт: его можно скачать на другой компьютер, запустить без установки Python-зависимостей, подключить как MCP-сервер или развернуть на сервере. GitVerse хранит Docker-образы в Container Registry по адресу вида gitverse.ru/владелец/образ:тег.
Для запуска на личном компьютере с установленным докером ты можешь вызвать его такой командой:
docker run --rm -i \
gitverse.ru/<owner>/school21-lavka-mcp:<commit-sha>
Этот же образ можно использовать:
GitVerse отдельно публикует примеры deployment workflow для Kubernetes и Cloud.ru, причём ожидается, что образ уже собран и доступен в registry.
Проект почти готов. Представьте, что завтра вас переведут в другой отдел, а за ваш сервер будет отвечать новый разработчик (или команда). Ваша задача — сделать так, чтобы этот человек смог:
docs/gitverse_release.md;docs/test_summary.md: test cases, автоматизация, Inspector, GitVerse tests job, Kaniko build и найденные дефекты.docs/handoff.md с порядком проверки от clone до просмотра опубликованного package.docs/known_issues.md, ai-logs.md и docs/gitverse_release.md соответствуют фактическому состоянию.git grep -nE 'GV_REGISTRY_TOKEN|gvpat_|/Users/|BEGIN (RSA|OPENSSH) PRIVATE KEY'
git status --short
Упоминание имени secret допустимо; его значение — нет. 6. Проведите внутри команды handoff-репетицию: другой участник читает документы, запускает Python-тесты и находит workflow/package в GitVerse.
docs/test_summary.md и docs/handoff.md описывают реальный результат.Это задание необязательное, но очень рекомендованное. Подход тестирования и деплоя, отработанный на Лавке, можно применить и к личному сеттингу из предыдущих проектов (переговорки, консультации, мероприятия, оборудование, спортзал). Это задание покажет, что ваши навыки переносимы на другие домены.
Твоя задача: Продолжить один из пяти сеттингов, создать папку bonus_personal_case/, описать 5 тест-кейсов (2 positive, 2 negative, 1 boundary), реализовать одно изменённое domain-правило и написать 2 автоматических теста для него.Действия:
1. Создайте папку:
bonus_personal_case/
2. Создайте bonus_personal_case/personal_case_mapping.md. Шаблон возьмите в папке docs
3. Опишите 5 test cases для личного кейса в bonus_personal_case/test_cases.md. Шаблон возьмите в папке docs
Минимально:
4. Реализуйте или опишите одно измененное domain-правило. Например:
5. Напишите 2 автоматических теста для этого правила в:
bonus_personal_case/test_personal_case_domain.py
6. Заполните bonus_personal_case/adaptation_summary.md: Шаблон можно взять в папке docs.
Результат: В папке src находится папка bonus/<ваш_ник> с mapping, test cases, одним domain-правилом, двумя тестами и коротким summary.ваш_ник>
Основная часть (групповая) проекта в папке src:
Dockerfile – инструкция для сборки образа..dockerignore – исключаемые из контекста сборки файлы.README.md – обновлённая документация с инструкциями по установке, запуску, тестированию и работе с GitVerse.ai-logs.md – все значимые диалоги с ИИ (промпты, ответы, комментарии команды).В папке .gitverse/workflows/:
ci.yml – конфигурация CI/CD для GitVerse (тесты и ручная публикация образа через Kaniko).В папке docs/ (документация и отчёты):
gitverse_setup.md – настройка командного репозитория (владелец, соавторы, видимость, назначение develop главной веткой, подтверждение работы CI).mcp_audit.md – входная диагностика сервера (результаты запуска, проверка quality gate, выбранный путь).known_issues.md – список известных проблем, обнаруженных в процессе работы (если есть).tools_contract.md – человекочитаемое описание контрактов всех обязательных tools.test_cases.md – таблица минимум из 14 тест-кейсов (positive, negative, boundary) с указанием источника и статуса автоматизации.inspector_check.md – результаты ручной smoke-проверки через MCP Inspector (вызовы минимум 5 tools, включая конфликтный сценарий).test_summary.md – сводка по тестированию: количество кейсов, покрытие, найденные дефекты, результаты прогонов.handoff.md – пошаговая инструкция для нового разработчика: от клонирования до запуска контейнера и просмотра пакета.gitverse_release.md – информация о собранном и опубликованном Docker-образе (commit SHA, ссылка на workflow, image reference, digest, ограничения).В папке tests/:
test_smoke.py – проверка работоспособности тестового окружения.test_domain.py – минимум 6 unit-тестов для бизнес-логики (domain.py).test_gateway_integration.py – минимум 4 интеграционных теста для цепочки gateway → storage → domain.В папке tests/fixtures/ (тестовые данные):
В папке scripts/:
reset_test_db.py – скрипт для сброса тестовой базы данных (удаление, создание таблиц, заполнение фикстурами).Рекомендуется добавить скриншоты успешного запуска MCP Inspector (screenshots/inspector_check.png или logs/inspector_log.txt) и зелёного пайплайна в GitVerse (screenshots/ci_success.png), но это не является строгим требованием и может быть заменено текстовыми логами.
bonus/<ваш_ник>/ с файлами:
personal_case_mapping.md – описание выбранного личного кейса.test_cases.md – 5 тест-кейсов (2 positive, 2 negative, 1 boundary).test_personal_case_domain.py – 2 автоматических теста для изменённого domain-правила.adaptation_summary.md – краткий итог адаптации.tools_schemas.json), а не «на глаз»?domain.py легче тестировать, чем server.py?Traceback нельзя возвращать ИИ-агенту как обычное сообщение об ошибке?image) отличается от контейнера (container)?docker build, и почему в GitVerse используется именно Kaniko?Dockerfile отличается от docker-compose.yml?stdio не открывается как обычный веб-сайт на localhost?variables) и секреты (secrets)? Приведите пример использования.continue-on-error или || true)?workflow_dispatch), а не автоматически после каждого пуша в develop?.dockerignore важно исключить .env и локальные базы данных, но нельзя исключать src/, tests/ и scripts/?Поздравляем! Заплыв завершается, проплыл весь путь бассейна и вынырнул. Ура!
Впереди у тебя еще 2 дня для выполнения бонусного заплыва и сдачи нормативов в виде тестового экзамена.
Представляешь, в первый день ты стоял на бортике, не зная, куда плыть. А сегодня ты стоишь на финише, оглядываясь на пройденный путь разработки: от идеи до релиза. Теперь ты умеешь тестировать, контейнеризовать и автоматизировать развёртывание своих приложений.
Ты не просто выполнил проекты — ты прошёл путь трансформации, попробовал себя в разных ролях. Это начало. Настоящее обучение в «Школе 21» еще впереди. Ты уйдешь с бассейна с портфолио из 8-ми или 9-ти проектов, с новыми друзьями, с пониманием того, как работают команды, и с уверенностью, что можешь больше, чем думал в первый день.
Гордись собой. Делай следующий шаг. Мир ждёт твоих продуктов.
Удачи на проверках!
💡Нажми сюда https://forms.yandex.ru/cloud/6a49492fd046888bb849b9d7 , чтобы поделиться с нами обратной связью на этот проект. Это поможет команде продукта сделать обучение лучше.