You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

22 KiB

Инструкция для тестировщика: версионирование тестов и AI-функции

Контур: новый Flask на :3108 (после E1.0–E1.3, E1.8). Старый Node — на :3107, для проверки этих задач используйте только :3108.

Перед началом:

  1. Откройте http://<host>:3108/login под учёткой автора (роль manager или admin). Если сидов нет — заведите пользователя в clinic_tests.users обычным способом.
  2. Для AI-задач: откройте http://<host>:3108/settings и убедитесь, что статус ключа = «Задан» и кнопка «Проверить подключение» возвращает зелёный OK · provider/model · NN мс. Если ключ не задан — AI-задачи нужно прогнать в негативном сценарии (см. блок A.0).
  3. Откройте http://<host>:3108/tests — это каталог.

Часть 1. Версионирование при правке после попыток

Что должно работать

Правило Поведение
Нет попыток Автор правит тест на месте, номер версии не меняется.
Есть ≥ 1 попытка Любое сохранение изменений создаёт новую версию (version + 1), старая становится неактивной, но сохраняется в БД и связана через parent_id.
Цепочка Все версии связаны (parent → child), на странице «Версии» видны все.
Каталог В списке видна только активная версия цепочки.
Переключение активной версии Автор может вручную сделать активной любую версию — остальные автоматически становятся неактивными.
Деактивация цепочки Тест можно скрыть целиком; данные не удаляются.
Корректность истории Каждая попытка привязана к той версии, по которой её проходили — разбор ошибок остаётся корректным после правок.

Сценарий 1.1. Правка теста до попыток (версия не растёт)

  1. В каталоге → «Создать тест», заполните название и описание → создать.
  2. В редакторе добавьте 2–3 вопроса по 3–4 варианта, сохраните.
  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). Поэтому факт попытки имитируется напрямую в БД — это норма для текущего этапа.

  1. Возьмите 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 = 'Ваш тест';
    
  2. Создайте «попытку» (минимальный 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());
    
  3. В 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:

  1. В редакторе не трогайте вопросы и варианты. Поменяйте только название или описание или проходной балл. Сохраните.

Ожидается:

  • Сообщение «Сохранено.» (без форка).
  • version не вырос; метаданные обновились на активной версии.

Логика: форк делается только если после попыток меняется содержание (вопросы/варианты). Чисто косметические правки шапки версию не плодят.

Сценарий 1.4. Каталог показывает только активную версию

  1. После 1.2 откройте /tests под автором и под не-автором (если есть назначения).

Ожидается:

  • В каталоге — одна карточка теста с пометкой v.2 (активная). Версия 1 в каталог не попадает, но видна автору на странице «Версии» теста.

Сценарий 1.5. Ручное переключение активной версии

  1. Получите id v1 и v2: SELECT id, version, is_active FROM test_versions WHERE test_id=...
  2. Сделайте активной v1:
    curl -X POST -b cookies.txt \
      http://<host>:3108/api/tests/<test_id>/versions/<v1_id>/activate
    
    (cookie сессии берёте из браузера или логином через /api/auth/login).

Ожидается:

  • Ответ { ok: true, activeVersionId: "<v1_id>" }.
  • В БД: is_active = true только у v1, у v2 — false.
  • В каталоге карточка теста снова показывает v.1.

Сценарий 1.6. Деактивация цепочки целиком

  1. В редакторе снимите чекбокс «Цепочка активна» и сохраните.

Ожидается:

  • В каталоге /tests теста больше не видно (ни в visible, ни у не-авторов).
  • У автора он появляется в блоке «Скрытые вами» (внизу каталога).
  • В БД: tests.is_active = false, версии и попытки нетронуты.
  • Включение чекбокса обратно возвращает тест в каталог.

Сценарий 1.7. Корректность истории по старым попыткам

Полноценный разбор пользовательских ответов появится в E1.4 вместе с UI прохождения. Сейчас минимально проверяем, что данные старой версии не повреждены.

  1. После 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;
    
  2. Ожидается: видны вопросы в том виде, в каком они были до правки (а не текущая версия 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. Негативный кейс — ключ не задан

  1. На сервере уберите DEEPSEEK_API_KEY из окружения и перезапустите контейнер.
  2. Откройте /settings.

Ожидается:

  • Бейдж «Не задан» (красный).
  • Блок «Как задать ключ» с примером .env.
  • Кнопка «Проверить подключение» возвращает красный блок с текстом про незаданный ключ.
  • В редакторе при нажатии «Сгенерировать по сетке» / «по названию» / «Проверить тест» / «Улучшить тест» / «AI: вопрос» появляется confirm: «… Открыть Настройки?» → согласие открывает /settings.

После проверки верните ключ и docker compose ... up -d — переходим к позитивным сценариям.

A.1. /settings → «Проверить подключение»

  1. На /settings нажмите «Проверить подключение».

Ожидается:

  • В течение нескольких секунд — зелёный блок: OK · <provider> / <model> · <ms> мс и сэмпл ответа.
  • Provider/Model совпадают с ENV (deepseek + deepseek-chat по умолчанию).

A.2. «Сгенерировать тест по названию» (E1.8)

  1. Создайте новый пустой тест (никаких вопросов).
  2. В редакторе нажмите «Сгенерировать по названию».
  3. На запрос «Сколько вопросов?» введите, например, 8; «Сколько вариантов?» — 4.
  4. Дождитесь готовности → confirm «Применить как черновик?».

Ожидается:

  • Кнопка активна только когда поле «Название» заполнено. Если очистить название — нажатие даёт алерт «Сначала заполните название теста.» и фокус возвращается в название.
  • Появляется ровно ~8 вопросов по ~4 варианта (модель может слегка отклониться по инструкции, это допустимо).
  • Тексты — на русском, по теме названия.
  • В каждом вопросе хотя бы один вариант помечен как правильный.
  • Отказ в confirm не меняет редактор; согласие — заменяет.
  • Сохранение работает, в БД появляется версия с этими вопросами.

A.3. «Сгенерировать тест по сетке» (E1.2 — было)

  1. Откройте тест, в котором уже руками настроены 5 вопросов, по 3 варианта в каждом, 2 из вопросов помечены как «Несколько правильных».
  2. Нажмите «Сгенерировать по сетке».

Ожидается:

  • Возвращается ровно 5 вопросов.
  • В тех же позициях, что у вас стояли «Несколько правильных», — у новых вопросов несколько правильных вариантов.
  • Число вариантов в каждом вопросе совпадает.
  • При несовпадении сетки эндпоинт вернул бы 502 с кодом llm_shape (модель «не попала») — допустимая редкая ошибка, повторите.

A.4. «Проверить тест» (E1.8)

  1. Создайте тест с парой нарочно слабых мест:
    • один вопрос с длинной мутной формулировкой;
    • один вопрос, где все варианты слишком похожи или в качестве «дистракторов» — очевидная ерунда.
  2. Нажмите «Проверить тест».

Ожидается:

  • Открывается модалка «Проверка теста».
  • Есть цветная плашка с одним из вердиктов: Годен / Есть замечания / Серьёзные проблемы, и краткое резюме (1–2 предложения).
  • Ниже — список разделов («Чёткость формулировок», «Качество дистракторов», «Охват темы», «Сбалансированность сложности»). Разделы без замечаний пропускаются.
  • В списке — конкретные пункты на русском, по делу.
  • Закрытие модалки крестиком или кнопкой «Закрыть» работает.

A.5. «Улучшить тест» (E1.8) — массовое было → стало

  1. Возьмите тест из A.4 (с ≥ 3 вопросами).
  2. Нажмите «Улучшить тест».

Ожидается:

  • Открывается модалка с заголовком «Улучшение теста» и подсказкой «Отмечено N из M».
  • Каждый изменённый вопрос — отдельная карточка:
    • чекбокс «Вопрос #N» (по умолчанию отмечен);
    • две колонки Было / Стало;
    • изменённый текст в «Было» зачёркнут, в «Стало» — выделен;
    • правильные варианты помечены ✓.
  • Снимите галку с одного-двух вопросов и нажмите «Применить выбранное».
  • В редакторе только отмеченные вопросы заменены на улучшенные; остальные остались как были.
  • Появляется надпись «Изменения применены. Не забудьте сохранить.» — нажмите «Сохранить» и проверьте версионирование (см. Часть 1).
  • Сетка не меняется: число вопросов, число вариантов в каждом и значение «Несколько правильных» совпадают с исходными. Если модель «слетела» — эндпоинт возвращает 502 с llm_shape и UI показывает алерт; это не баг логики.

A.6. AI-кнопка на конкретном вопросе (E1.2)

Сценарий «новый вопрос»:

  1. Добавьте вопрос, поле текста оставьте пустым, число вариантов = 4, «Несколько правильных» — выкл.
  2. Нажмите «AI: вопрос/переформулировать» на этом вопросе.

Ожидается: заполнен текст вопроса и все 4 варианта; ровно один помечен правильным; внизу — статус «AI: вопрос сгенерирован.»

Сценарий «переформулировать»:

  1. Возьмите готовый вопрос с заполненным текстом.
  2. Нажмите ту же кнопку.

Ожидается: меняется только текст вопроса (вариант ответа и правильность не трогаются), статус «AI: формулировка обновлена.»

A.7. Импорт документа (E1.3)

  1. Подготовьте файл sample.pdf или sample.docx со связным текстом (1–3 страницы) на русском.
  2. В редакторе → AI-панель → «Импорт документа» → выберите файл.

Ожидается:

  • Прогресс «Загружаем «sample.pdf»…».
  • Confirm «Сгенерировано: «