Files

135 lines
8.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# TESTING — приёмочный тест-план
Тестирует **заказчик** (Hermes) на реальном железе, а не исполнитель. Исполнитель пишет тесты;
приёмка идёт независимыми проверками ниже. Правило приёмки: **«зелёная сборка» ничего не доказывает,
пока мутация не валит нужный тест.**
## 0. Подготовка стенда
```bash
export MEMO_MODEL_DIR=/root/WORK/memo/models/siglip2
mkdir -p "$MEMO_MODEL_DIR"
curl -fSL -o "$MEMO_MODEL_DIR/text_model_int8.onnx" http://static.binom.pw/models/siglip2/text_model_int8.onnx # 283M
curl -fSL -o "$MEMO_MODEL_DIR/tokenizer.model" http://static.binom.pw/models/siglip2/tokenizer.model # 4.2M
# эталонный корпус: 12-15 заметок в 3 папках-коллекциях
export MEMO_ROOT=/root/WORK/memo-e2e
# infra/*.md (серверы, домены), work/jira/*.md, life/books/*.md — обычный markdown с заголовками
```
Пороговое требование: файлов не меньше 12 в трёх папках, в текстах есть и кириллица, и латиница,
и хотя бы по одному точному значению на папку (IP, номер тикета, год издания).
## T1. Индексация и её повтор
```bash
cd /root/WORK/memo && ./gradlew installDist -q
CLI=./memo-cli/build/install/memo/bin/memo
$CLI index "$MEMO_ROOT"; $CLI status "$MEMO_ROOT"
find "$MEMO_ROOT" -name index.db | sort # ожидается 3 пути, по одному на коллекцию
$CLI index "$MEMO_ROOT" # второй прогон
```
Ожидается: в каждой коллекции появился `.memo/index.db` (**ровно три** — вложенные папки без своих
заметок коллекциями не считаются); второй прогон **не переэмбеддивает** ни одного файла
(в выводе 0 обновлённых, `status` показывает те же `indexed_at`). Провал: нет `.memo/index.db`;
баз больше трёх (значит, коллекцией считается каждая папка с .md внутри); второй прогон молча
переиндексирует всё.
## T2. Смысловой поиск (вектор работает)
20 вопросов, сформулированных **другими словами**, чем в заметках. Ожидание: нужный раздел в top-5
не реже 16 раз из 20 (recall@5 ≥ 0.8). Формулировки и ожидаемый файл — в `e2e/questions.tsv` (создаёт заказчик).
```bash
$CLI search "$MEMO_ROOT/infra" "как пережить перезагрузку сервера, чтобы операция не умерла" --json
```
Провал: в результатах нет нужного файла; возвращаются чанки без текста (агент не сможет ими
воспользоваться); поиск идёт по всему корню вместо указанной папки.
## T3. Лексический поиск (BM25 работает)
Вопросы с точными значениями: IP-адрес, номер тикета, версия, год. Ожидание: точное совпадение найдено,
и именно через лексику.
```bash
$CLI search "$MEMO_ROOT/infra" "76.132" --json # должен найти заметку с этим IP
$CLI search "$MEMO_ROOT/infra" "76.132" --mode lex --json
```
Провал: находит «похожее по смыслу», но не строку с точным значением; `--mode lex` даёт тот же
результат, что `--mode vec` (значит, режимы не разделены).
## T4. Живая правка файла
```bash
echo "\n## Свежий раздел\nуникальное_слово_дзынь_47" >> "$MEMO_ROOT/infra/hosts.md"
sleep 1
$CLI search "$MEMO_ROOT/infra" "уникальное слово дзынь" --json # ≤1 с после правки
echo "## Ещё раздел\nвторое_слово_бамс_93" >> "$MEMO_ROOT/infra/hosts.md"
$CLI search "$MEMO_ROOT/infra" "второе слово бамс" --json # БЕЗ sleep: тот же вызов
```
Ожидается: первая правка подхвачена watcher'ом ≤1 с; вторая видна **в том же вызове поиска**
(stat-догон до поиска). Провал: поиск не видит свежую правку; `database is locked` при одновременной
записи watcher'ом и чтении (признак: WAL не включён).
## T5. MCP-сервер по протоколу (не по самоотчёту)
Инструменты подключаются к Hermes **только в новой сессии**, поэтому проверяем протоколом:
```bash
python3 scripts/mcp_probe.py --cmd "./memo-mcp/build/install/memo-mcp/bin/memo-mcp" \
--call memo_search --args '{"path":"'"$MEMO_ROOT"'/infra","query":"домены","k":3}'
```
Скрипт-зонд: пишет `initialize`, `notifications/initialized`, `tools/list`, `tools/call` в stdin процесса,
читает JSON-RPC-ответы. Ожидается: `tools/list` вернул 3 инструмента; `tools/call` — непустой
`content[0].text` с результатами; `isError` отсутствует.
**Тестовые ручки (то же без транспорта):**
```bash
$CLI mcp-probe --tool memo_search --args '{"path":"'"$MEMO_ROOT"'/infra","query":"домены"}'
# плюс режим "одного вызова": разобрать строку запроса и вернуть ответ, не поднимая stdio
```
Провал: сервер печатает что-то в stdout помимо JSON-RPC (ломает клиента); на `tools/call` отвечает
`-32601`; результат — пустая строка.
## T6. Офлайн
```bash
# отключаем внешний интерфейс у процесса: без сети индексация и поиск обязаны работать
$CLI index "$MEMO_ROOT" && $CLI search "$MEMO_ROOT" "любой вопрос" --json
```
Провал: любой сетевой вызов в рантайме (модель качается при каждом старте, эмбеддинг уходит в API).
## M. Мутационная приёмка (обязательна)
Для каждого пункта: сломать ровно одно место в боевом коде (python-replace с `assert old in s`),
прогнать `./gradlew cleanTest test`, **сверить XML-отчёты и их mtime**, затем `git checkout -- <файл>`.
| Мутация | Какой тест обязан упасть |
|---|---|
| Чанкер: резать по 4000 символов, игнорируя заголовки | T2 (recall), тест чанкера |
| Поиск: `mode=lex` уходит в вектор | T3, тест разделения режимов |
| RRF: убрать слияние (только вектор) | T3 |
| Индексация: игнорировать `mtime`/хэш, всегда переиндексировать | T1 (второй прогон не no-op) |
| Watcher: убрать вызов `pollEvents`/регистрацию | T4 (первая часть) |
| Поиск: убрать stat-догон перед поиском | T4 (вторая часть) |
| MCP: `tools/list` возвращает пустой список | T5 |
| Схема: `chunks_vec` дименсия 384 вместо 768 | T3-KNN, тест round-trip |
Мутация, которая **не валит ни одного теста**, означает дыру в покрытии: тест дописать, находку — в отчёт.
## Признаки провала исполнителя
- Вывод — «план реализации» текстом, `git status` пуст, exit 0.
- Тесты зелёные, но мутации не валят (тесты написаны под код, а не под поведение).
- Изменён боевой код вне заказанного списка файлов (`git diff --stat` не совпал с заказом).
- Появились зависимости, которых нет в `TASK.md` §2.
- Файлы модели или `.db` попали в коммит.