9 changed files with 247 additions and 4 deletions
@ -0,0 +1,31 @@
|
||||
{ |
||||
"name": "clinic-tests-backend", |
||||
"version": "1.0.0", |
||||
"description": "Backend for Clinic Tests application", |
||||
"main": "src/index.js", |
||||
"type": "module", |
||||
"scripts": { |
||||
"start": "node src/index.js", |
||||
"dev": "node --watch src/index.js", |
||||
"test": "node --test src/services/testChainService.test.js", |
||||
"lint": "eslint src/", |
||||
"lint:fix": "eslint src/ --fix", |
||||
"format": "prettier --write src/" |
||||
}, |
||||
"keywords": [], |
||||
"author": "", |
||||
"license": "ISC", |
||||
"dependencies": { |
||||
"bcrypt": "^5.1.1", |
||||
"cookie-parser": "^1.4.7", |
||||
"cors": "^2.8.5", |
||||
"dotenv": "^16.4.5", |
||||
"express": "^4.21.0", |
||||
"jsonwebtoken": "^9.0.2", |
||||
"pg": "^8.12.0" |
||||
}, |
||||
"devDependencies": { |
||||
"eslint": "^8.57.0", |
||||
"prettier": "^3.3.3" |
||||
} |
||||
} |
||||
@ -0,0 +1,18 @@
|
||||
/** |
||||
* Логика «цепочки» теста: попытки и версии (см. docs/revision_task/card1.md V.2). |
||||
* @param {import('pg').Pool | { query: Function }} pool пул или объект с методом query(sql, params) |
||||
* @param {string} testId UUID теста (tests.id) |
||||
* @returns {Promise<boolean>} true, если по любой версии этой цепочки есть хотя бы одна попытка |
||||
*/ |
||||
export async function hasAnyAttemptForTest(pool, testId) { |
||||
const { rows } = await pool.query( |
||||
`SELECT EXISTS (
|
||||
SELECT 1 |
||||
FROM test_attempts ta |
||||
INNER JOIN test_versions tv ON ta.test_version_id = tv.id |
||||
WHERE tv.test_id = $1 |
||||
) AS has_any`,
|
||||
[testId] |
||||
); |
||||
return rows[0].has_any === true; |
||||
} |
||||
@ -0,0 +1,23 @@
|
||||
import { test } from 'node:test'; |
||||
import assert from 'node:assert/strict'; |
||||
import { hasAnyAttemptForTest } from './testChainService.js'; |
||||
|
||||
test('hasAnyAttemptForTest: false, если в базе пусто', async () => { |
||||
const pool = { |
||||
async query() { |
||||
return { rows: [{ has_any: false }] }; |
||||
}, |
||||
}; |
||||
const result = await hasAnyAttemptForTest(pool, '00000000-0000-0000-0000-000000000001'); |
||||
assert.equal(result, false); |
||||
}); |
||||
|
||||
test('hasAnyAttemptForTest: true, если есть попытка', async () => { |
||||
const pool = { |
||||
async query() { |
||||
return { rows: [{ has_any: true }] }; |
||||
}, |
||||
}; |
||||
const result = await hasAnyAttemptForTest(pool, '00000000-0000-0000-0000-000000000001'); |
||||
assert.equal(result, true); |
||||
}); |
||||
@ -0,0 +1,32 @@
|
||||
# Идеи и пожелания по доработкам (для согласования с заказчиком) |
||||
|
||||
*Язык простой, без жаргона разработки. Сюда попадает всё, что всплыло в обсуждениях и ещё не вошло в жёсткое ТЗ.* |
||||
|
||||
**Как пользоваться:** приоритеты и «да/нет» фиксируем отдельно; пункты **не** удаляем — переносим в раздел **Решено** с кратким итогом, если идея закрыта или отклонена. |
||||
|
||||
--- |
||||
|
||||
## На рассмотрении |
||||
|
||||
| № | Суть (что даст клинике) | Примечание | |
||||
|---|-------------------------|------------| |
||||
| 1 | **Напоминания** о сроке теста в мессенджере (когда срок близок или прошёл) | Связь с будущим HR-приложением в MAX; не путать с самим прохождением теста — только напоминание зайти в кабинет. | |
||||
| 2 | **Один раз скачать** свод по отделу или по клинике в **таблицу** (для руководителя), без «технических» деталей | Уточнить, нужен ли **Excel** / PDF и какие столбцы обязательны. | |
||||
| 3 | **Памятка рядом с тестом** — кратко: зачем тест, на что обратить внимание (текст от автора) | Улучшает вовлечение; не подменяет **описание** теста, если оно уже есть. | |
||||
| 4 | **Сравнение** «как сотрудник ответил в прошлый раз» с текущим прохождением | Для **повторных** тестов по той же теме; важна приватность и согласие кадров. | |
||||
| 5 | **Крупный шрифт** и **контраст** в режиме «стресс/смена» для сотрудников, много работающих в перчатках с экраном | Доступность; опциональная **тема** в настройках профиля. | |
||||
| 6 | **Печатная** версия **итога** (сдал/не сдал) для **личного** дела — один лист, без лишнего | Не путать с полноценной «выгрузкой для 1С»; это про человеко-понятный **итог** для сотрудника. | |
||||
| 7 | **Повтор** одного вопроса в конце теста — «самопроверка» (опционально, как у автора) | Снижает нервозность; выключаемо **на уровне** теста, чтобы на экзамене не мешать. | |
||||
| 8 | **Аудит:** кто из администраторов менял активную **версию** теста и когда | Для **разборов** при споре «кому показывали старую/новую редакцию». | |
||||
|
||||
--- |
||||
|
||||
## Решено или «не делаем в этой волне» |
||||
|
||||
| № | Суть | Итог | |
||||
|---|------|------| |
||||
| — | *Пока пусто* | | |
||||
|
||||
--- |
||||
|
||||
*Обновляйте дату: **2026-04-23** (создание файла).* |
||||
@ -0,0 +1,68 @@
|
||||
# Единый журнал проверок по спринтам |
||||
|
||||
**Для кого этот документ.** Часть проверок делается на стороне разработки (миграции, автотесты, что можно прогнать без браузера). Часть — **с участием заказчика**: ниже даны **пошаговые задания**; вы отвечаете в переписке одним словом **ОК** или **не ОК** (и при желании коротко, что не так). Разработка переносит ответ в таблицу в колонку «Закреплено». |
||||
|
||||
**Ветка / коммит последней привязки:** `dev` (обновлять при релизе на проверку) |
||||
|
||||
**Адрес стенда (когда появится):** *(заполнить)* |
||||
|
||||
--- |
||||
|
||||
## Спринт 1 — Версии тестов и честная история прогонов |
||||
|
||||
**Смысл для бизнеса.** Если руководитель поправил тест после того, как кто-то уже прошёл его, **старые результаты** должны оставаться привязаны к **той редакции**, по которой человек реально отвечал — без путаницы в разборе ошибок. |
||||
|
||||
### Раздел A — Проверки без участия заказчика (разработка / ассистент) |
||||
|
||||
| № | Что проверено | Статус | Дата | |
||||
|---|----------------|--------|------| |
||||
| A1 | В проекте есть миграция базы: связь версий «родитель» (`parent_id`) и правило «только одна активная версия на тест» | [ ] | | |
||||
| A2 | Линтер (`npm run lint`) без **новых** ошибок в добавленных файлах; в проекте есть старые замечания линтера | частично | 2026-04-24 | |
||||
| A3 | Автотесты: функция «есть ли уже хотя бы одна попытка по этому тесту» (`npm test`) | [x] готово | 2026-04-23 | |
||||
| A4 | Запрос «здоров ли сервер» по адресу `/api/health` при запущенном backend | [ ] | | |
||||
|
||||
**Техническая заметка:** реализация `hasAnyAttemptForTest` в `backend/src/services/testChainService.js`, тесты в `testChainService.test.js`. |
||||
|
||||
--- |
||||
|
||||
### Раздел B — Поручения заказчику (шаги с ответом ОК / не ОК) |
||||
|
||||
**Как отвечать.** Пройдите шаг. Напишите в чат, например: «S1-01 ОК» или «S1-01 не ОК: не открывается список». Одна строка на шаг. |
||||
|
||||
| Код шага | Что сделать (пока экраны в разработке — шаги будут дополняться) | Ваш ответ | Зафиксировано | |
||||
|----------|------------------------------------------------------------------|-----------|----------------| |
||||
| S1-00 | Открыть файл **идей** [BACKLOG_IDEAS.md](BACKLOG_IDEAS.md). Подходит ли формат (таблица, без кода)? Напишите **S1-00 ОК** или **не ОК** и одну фразу. | | | |
||||
| S1-01 | *(позже)* Создать тест и несколько раз сохранить **до** того, как кто-то прошёл тест; убедиться, что номер версии не растёт. Пока экрана нет — напишите «пропуск» или «не ОК: нет экрана». | | | |
||||
| S1-02 | *(позже)* После прохождения теста сотрудником изменить тест и сохранить; увидеть **новую** версию в истории. | | | |
||||
| S1-03 | *(позже)* Открыть **старый** результат прохождения и убедиться, что вопросы совпадают с той редакцией, что была при прохождении. | | | |
||||
|
||||
**Итог спринта 1:** дата __________ комментарий заказчика одной фразой: _________________________ |
||||
|
||||
--- |
||||
|
||||
## Спринт 2 — *(заготовка)* |
||||
|
||||
### Раздел A — автопроверки |
||||
|
||||
| № | Описание | Статус | Дата | |
||||
|---|----------|--------|------| |
||||
| | | [ ] | | |
||||
|
||||
### Раздел B — поручения заказчику |
||||
|
||||
| Код | Действие | Ответ | Зафиксировано | |
||||
|-----|----------|-------|----------------| |
||||
| | | | | |
||||
|
||||
--- |
||||
|
||||
## Сводка по спринтам (для статус-встречи) |
||||
|
||||
| Спринт | Тема простыми словами | Раздел A | Раздел B | |
||||
|--------|------------------------|----------|----------| |
||||
| 1 | Версии, история прогонов | в работе | ждёт шагов | |
||||
| 2 | *(по мере появления)* | | | |
||||
|
||||
--- |
||||
|
||||
*Связанные файлы: [sprint-01-testing.md](sprint-01-testing.md) (черновик чек-листа), [card1.md](card1.md) (задачи).* |
||||
@ -0,0 +1,67 @@
|
||||
# Спринт 1 — тестирование (версионирование) |
||||
|
||||
**Актуальный ведённый журнал (авто + ручные шаги, ответы ОК/не ОК):** [TESTING_JOURNAL.md](TESTING_JOURNAL.md) — *этот файл остаётся как подробный черновик сценариев*. |
||||
|
||||
Ниже: **автоматизировано / проверено при разработке** и **ручная приёмка** — дублирует структуру; статусы переносите в `TESTING_JOURNAL.md`. |
||||
|
||||
**Окружение:** зафиксировать ветку/коммит, URL стенда, тестовые учётки (роль автора, роль сотрудника). |
||||
|
||||
--- |
||||
|
||||
## 1. Автоматизировано и self-check (разработка) |
||||
|
||||
_Отмечайте [ ] → [x] по мере выполнения._ |
||||
|
||||
### 1.1 Автотесты и статический анализ |
||||
|
||||
- [ ] Unit-тесты правила «0 попыток — правка на месте без новой версии» |
||||
- [ ] Unit-тесты: после появления попытки — сохранение создаёт новую версию, старая неактивна |
||||
- [ ] Тест: попытка хранит `version_id` совпадающий с версией, по которой проходили |
||||
- [ ] Integration/API: нельзя «потерять» цепочку при смене активной версии |
||||
- [ ] Линтеры/CI зелёные на MR спринта 1 |
||||
|
||||
### 1.2 Смоук вручную (быстрый) перед передачей |
||||
|
||||
- [ ] Создать тест, несколько раз сохранить **до** назначения/прохождения — версия одна |
||||
- [ ] Назначить, пройти тест, снова изменить тест — новая версия, старая в истории |
||||
- [ ] Список тестов у сотрудника без дублей цепочки |
||||
- [ ] Смена активной версии: новые прохождения идут по новой активной; старая попытка в разборе по старой версии |
||||
|
||||
--- |
||||
|
||||
## 2. Ручная приёмка (я / заказчик) |
||||
|
||||
_Сценарии для прогона в «боевом» темпе, без доступа к коду._ |
||||
|
||||
### 2.1 Автор: жизненный цикл без попыток |
||||
|
||||
- [ ] Создать тест, изменить вопросы/порог — всё в одной версии, номер версии ожидаемо стабилен |
||||
- [ ] Убедиться, что в UI явно видно, что тест ещё «ни разу не проходили» (если предусмотрено ТЗ/UX) |
||||
|
||||
### 2.2 Сотрудник + первая попытка |
||||
|
||||
- [ ] Назначить тест, пройти его полностью |
||||
- [ ] Как автор изменить вопрос/варианты, сохранить — появляется **новая** версия; старая доступна в истории |
||||
|
||||
### 2.3 Корректность данных |
||||
|
||||
- [ ] Открыть разбор/результат **старой** попытки: формулировки вопросов и правильные ответы соответствуют **той** версии, с которой проходили |
||||
- [ ] Новое назначение/новое прохождение — по **актуальной активной** версии |
||||
|
||||
### 2.4 Управление версиями |
||||
|
||||
- [ ] История версий отображается полностью и понятно (номера, даты при наличии) |
||||
- [ ] Переключение **активной** версии на предыдущую: списки обновляются; новая попытка идёт по выбранной версии |
||||
- [ ] **Деактивация** теста: цепочка не светится сотруднику; данные на месте, старые результаты открываются (если доступ по ролям предусмотрен) |
||||
|
||||
### 2.5 Визуальная согласованность |
||||
|
||||
- [ ] Экраны редактора и списков **визуально** согласованы с остальным internal web (отступы, шрифты, кнопки, таблицы, ошибки) — **без отклонения** от принятого дизайна |
||||
|
||||
### 2.6 Негатив |
||||
|
||||
- [ ] Попытка не может «сломать» цепочку (ошибки пользователю понятны) |
||||
|
||||
--- |
||||
|
||||
**Итог приёмки спринта 1:** дата __________, подпись/комментарий _________________________ |
||||
Loading…
Reference in new issue