22 KiB
Инструкция для тестировщика: версионирование тестов и AI-функции
Контур: новый Flask на
:3108(после E1.0–E1.3, E1.8). Старый Node — на:3107, для проверки этих задач используйте только:3108.
Перед началом:
- Откройте
http://<host>:3108/loginпод учёткой автора (рольmanagerилиadmin). Если сидов нет — заведите пользователя вclinic_tests.usersобычным способом. - Для AI-задач: откройте
http://<host>:3108/settingsи убедитесь, что статус ключа = «Задан» и кнопка «Проверить подключение» возвращает зелёный OK · provider/model · NN мс. Если ключ не задан — AI-задачи нужно прогнать в негативном сценарии (см. блок A.0). - Откройте
http://<host>:3108/tests— это каталог.
Часть 1. Версионирование при правке после попыток
Что должно работать
| Правило | Поведение |
|---|---|
| Нет попыток | Автор правит тест на месте, номер версии не меняется. |
| Есть ≥ 1 попытка | Любое сохранение изменений создаёт новую версию (version + 1), старая становится неактивной, но сохраняется в БД и связана через parent_id. |
| Цепочка | Все версии связаны (parent → child), на странице «Версии» видны все. |
| Каталог | В списке видна только активная версия цепочки. |
| Переключение активной версии | Автор может вручную сделать активной любую версию — остальные автоматически становятся неактивными. |
| Деактивация цепочки | Тест можно скрыть целиком; данные не удаляются. |
| Корректность истории | Каждая попытка привязана к той версии, по которой её проходили — разбор ошибок остаётся корректным после правок. |
Сценарий 1.1. Правка теста до попыток (версия не растёт)
- В каталоге → «Создать тест», заполните название и описание → создать.
- В редакторе добавьте 2–3 вопроса по 3–4 варианта, сохраните.
- Откройте этот же тест в редакторе ещё раз, измените:
- название;
- описание;
- текст одного вопроса;
- пометьте один вариант как правильный иначе;
- удалите/добавьте вариант.
- «Сохранить».
Ожидается:
- Сообщение «Сохранено.» (без слов «создана новая версия»).
- В БД:
SELECT version FROM test_versions WHERE test_id = '<id>'→ одна строка сversion = 1, is_active = true. - Эндпоинт
GET /api/tests/<id>/versions→ массив из 1 элемента,hasAttempts: false.
Сценарий 1.2. Появление первой попытки → форк новой версии
Прохождение теста (UI) пока не реализовано в Flask-контуре (запланировано в E1.4). Поэтому факт попытки имитируется напрямую в БД — это норма для текущего этапа.
- Возьмите
idтеста иidактивной версии:SELECT t.id AS test_id, tv.id AS version_id FROM tests t JOIN test_versions tv ON tv.test_id = t.id AND tv.is_active WHERE t.title = 'Ваш тест'; - Создайте «попытку» (минимальный INSERT, любой пользователь, любое
состояние; нам нужен только сам факт записи в
test_attempts):INSERT INTO test_attempts (id, test_version_id, user_id, status, created_at) VALUES (gen_random_uuid(), '<version_id>', '<any_user_id>', 'completed', now()); - В UI откройте редактор того же теста, измените хотя бы один вопрос (текст / правильность варианта / число вариантов) и «Сохранить».
Ожидается:
- Сообщение «Сохранено (создана новая версия — есть попытки прохождения).»
- В БД:
SELECT version, is_active, parent_id FROM test_versions WHERE test_id=...→ две строки:version = 1, is_active = false, parent_id = NULL(старая, не удалена);version = 2, is_active = true, parent_id = <id версии 1>(новая).
- Старая попытка по-прежнему ссылается на
version_idиз v1, и её ответы/вопросы остаются те же — разбор ошибок не «съехал».
Сценарий 1.3. Правка только метаданных после попыток (без форка)
После сценария 1.2:
- В редакторе не трогайте вопросы и варианты. Поменяйте только название или описание или проходной балл. Сохраните.
Ожидается:
- Сообщение «Сохранено.» (без форка).
versionне вырос; метаданные обновились на активной версии.
Логика: форк делается только если после попыток меняется содержание (вопросы/варианты). Чисто косметические правки шапки версию не плодят.
Сценарий 1.4. Каталог показывает только активную версию
- После 1.2 откройте
/testsпод автором и под не-автором (если есть назначения).
Ожидается:
- В каталоге — одна карточка теста с пометкой
v.2(активная). Версия 1 в каталог не попадает, но видна автору на странице «Версии» теста.
Сценарий 1.5. Ручное переключение активной версии
- Получите id v1 и v2:
SELECT id, version, is_active FROM test_versions WHERE test_id=... - Сделайте активной v1:
(cookie сессии берёте из браузера или логином черезcurl -X POST -b cookies.txt \ http://<host>:3108/api/tests/<test_id>/versions/<v1_id>/activate/api/auth/login).
Ожидается:
- Ответ
{ ok: true, activeVersionId: "<v1_id>" }. - В БД:
is_active = trueтолько у v1, у v2 —false. - В каталоге карточка теста снова показывает
v.1.
Сценарий 1.6. Деактивация цепочки целиком
- В редакторе снимите чекбокс «Цепочка активна» и сохраните.
Ожидается:
- В каталоге
/testsтеста больше не видно (ни в visible, ни у не-авторов). - У автора он появляется в блоке «Скрытые вами» (внизу каталога).
- В БД:
tests.is_active = false, версии и попытки нетронуты. - Включение чекбокса обратно возвращает тест в каталог.
Сценарий 1.7. Корректность истории по старым попыткам
Полноценный разбор пользовательских ответов появится в E1.4 вместе с UI прохождения. Сейчас минимально проверяем, что данные старой версии не повреждены.
- После 1.2 в БД:
SELECT q.text FROM questions q JOIN test_versions tv ON tv.id = q.test_version_id WHERE tv.test_id = '<test_id>' AND tv.version = 1; - Ожидается: видны вопросы в том виде, в каком они были до правки
(а не текущая версия v2). Эта же выборка должна совпадать с
q.test_version_idлюбой попытки, которую вы создали в 1.2.
Что фиксировать как баг
- После правки с попытками в БД остался один
test_versions-ряд (нет форка). - После правки без попыток появилась
version = 2(лишний форк). - При активации одной версии другие не сбросились в
is_active = false. - Каталог показывает неактивную версию или скрытый тест.
- Сообщение в UI после сохранения не совпадает с реальным поведением (форк есть, текст «Сохранено.» без уточнения, или наоборот).
Часть 2. AI-функции (E1.2 + E1.8)
Заметка о ключе
Изначально в ТЗ предполагалось хранить ключ в БД и вводить на /settings.
По согласованию ключ общий и хранится в ENV контейнера
(DEEPSEEK_API_KEY / OPENAI_API_KEY). Страница /settings остаётся,
но в ней — только статус и кнопка проверки подключения. Поле ввода ключа в UI не нужно (это не баг).
A.0. Негативный кейс — ключ не задан
- На сервере уберите
DEEPSEEK_API_KEYиз окружения и перезапустите контейнер. - Откройте
/settings.
Ожидается:
- Бейдж «Не задан» (красный).
- Блок «Как задать ключ» с примером
.env. - Кнопка «Проверить подключение» возвращает красный блок с текстом про незаданный ключ.
- В редакторе при нажатии «Сгенерировать по сетке» / «по названию» / «Проверить тест» / «Улучшить тест» / «AI: вопрос» появляется confirm:
«… Открыть Настройки?» → согласие открывает
/settings.
После проверки верните ключ и docker compose ... up -d — переходим к позитивным сценариям.
A.1. /settings → «Проверить подключение»
- На
/settingsнажмите «Проверить подключение».
Ожидается:
- В течение нескольких секунд — зелёный блок:
OK · <provider> / <model> · <ms> мси сэмпл ответа. - Provider/Model совпадают с ENV (
deepseek+deepseek-chatпо умолчанию).
A.2. «Сгенерировать тест по названию» (E1.8)
- Создайте новый пустой тест (никаких вопросов).
- В редакторе нажмите «Сгенерировать по названию».
- На запрос «Сколько вопросов?» введите, например,
8; «Сколько вариантов?» —4. - Дождитесь готовности → confirm «Применить как черновик?».
Ожидается:
- Кнопка активна только когда поле «Название» заполнено. Если очистить название — нажатие даёт алерт «Сначала заполните название теста.» и фокус возвращается в название.
- Появляется ровно ~8 вопросов по ~4 варианта (модель может слегка отклониться по инструкции, это допустимо).
- Тексты — на русском, по теме названия.
- В каждом вопросе хотя бы один вариант помечен как правильный.
- Отказ в confirm не меняет редактор; согласие — заменяет.
- Сохранение работает, в БД появляется версия с этими вопросами.
A.3. «Сгенерировать тест по сетке» (E1.2 — было)
- Откройте тест, в котором уже руками настроены 5 вопросов, по 3 варианта в каждом, 2 из вопросов помечены как «Несколько правильных».
- Нажмите «Сгенерировать по сетке».
Ожидается:
- Возвращается ровно 5 вопросов.
- В тех же позициях, что у вас стояли «Несколько правильных», — у новых вопросов несколько правильных вариантов.
- Число вариантов в каждом вопросе совпадает.
- При несовпадении сетки эндпоинт вернул бы 502 с кодом
llm_shape(модель «не попала») — допустимая редкая ошибка, повторите.
A.4. «Проверить тест» (E1.8)
- Создайте тест с парой нарочно слабых мест:
- один вопрос с длинной мутной формулировкой;
- один вопрос, где все варианты слишком похожи или в качестве «дистракторов» — очевидная ерунда.
- Нажмите «Проверить тест».
Ожидается:
- Открывается модалка «Проверка теста».
- Есть цветная плашка с одним из вердиктов: Годен / Есть замечания / Серьёзные проблемы, и краткое резюме (1–2 предложения).
- Ниже — список разделов («Чёткость формулировок», «Качество дистракторов», «Охват темы», «Сбалансированность сложности»). Разделы без замечаний пропускаются.
- В списке — конкретные пункты на русском, по делу.
- Закрытие модалки крестиком или кнопкой «Закрыть» работает.
A.5. «Улучшить тест» (E1.8) — массовое было → стало
- Возьмите тест из A.4 (с ≥ 3 вопросами).
- Нажмите «Улучшить тест».
Ожидается:
- Открывается модалка с заголовком «Улучшение теста» и подсказкой «Отмечено N из M».
- Каждый изменённый вопрос — отдельная карточка:
- чекбокс «Вопрос #N» (по умолчанию отмечен);
- две колонки Было / Стало;
- изменённый текст в «Было» зачёркнут, в «Стало» — выделен;
- правильные варианты помечены ✓.
- Снимите галку с одного-двух вопросов и нажмите «Применить выбранное».
- В редакторе только отмеченные вопросы заменены на улучшенные; остальные остались как были.
- Появляется надпись «Изменения применены. Не забудьте сохранить.» — нажмите «Сохранить» и проверьте версионирование (см. Часть 1).
- Сетка не меняется: число вопросов, число вариантов в каждом и значение «Несколько правильных» совпадают с исходными. Если модель «слетела» — эндпоинт возвращает 502 с
llm_shapeи UI показывает алерт; это не баг логики.
A.6. AI-кнопка на конкретном вопросе (E1.2)
Сценарий «новый вопрос»:
- Добавьте вопрос, поле текста оставьте пустым, число вариантов = 4, «Несколько правильных» — выкл.
- Нажмите «AI: вопрос/переформулировать» на этом вопросе.
Ожидается: заполнен текст вопроса и все 4 варианта; ровно один помечен правильным; внизу — статус «AI: вопрос сгенерирован.»
Сценарий «переформулировать»:
- Возьмите готовый вопрос с заполненным текстом.
- Нажмите ту же кнопку.
Ожидается: меняется только текст вопроса (вариант ответа и правильность не трогаются), статус «AI: формулировка обновлена.»
A.7. Импорт документа (E1.3)
- Подготовьте файл
sample.pdfилиsample.docxсо связным текстом (1–3 страницы) на русском. - В редакторе → AI-панель → «Импорт документа» → выберите файл.
Ожидается:
- Прогресс «Загружаем «sample.pdf»…».
- Confirm «Сгенерировано: «