mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-09-05 09:40:52 +03:00
test(runner): строгий режим снэпшотов — отсутствие эталона не проходит молча
compareSnapshot при отсутствии каталога эталона возвращал {match:true,
reason:'no snapshot (skipped)'}, причём reason никуда не выводился. Кейс без
эталона был молча зелёным, а «намеренно нет» и «эталон потерялся / не создан
при добавлении кейса» — неразличимы. README закреплял это как штатное
(«совпадает со snapshot (если есть)»).
Теперь эталон обязателен везде, кроме expectError, readonly external: и явного
opt-out. Диагностика — на месте кейса, с готовой командой; сводной статистики
не добавляем (вне контекста она ничего не сообщает).
- noSnapshot: "<причина>" — легальный пропуск. Причина обязательна: отключение
сверки должно стоить автору формулировки, а ревьюеру быть видно в diff'е;
осмысленность причины рантайм проверить не может. true/"" → падение.
- Нет эталона и нет opt-out → падение с рецептом (команда --update-snapshots
либо подсказка объявить noSnapshot).
- Мёртвый эталон (noSnapshot + существующий каталог) → падение: не сверяется,
но выглядит покрытием.
- updateSnapshot пропускает кейсы с noSnapshot — иначе --update-snapshots сам
порождал бы противоречие. Опечатка в имени поля fail-safe: opt-out не
сработает, кейс упадёт как «эталон отсутствует».
- Диагностика вынесена в общий snapshotErrors() — обе ветки (runCase /
runCaseAsync) больше не дублируют логику.
Размечены 3 кейса meta-validate: навык только читает и печатает, эталон
зафиксировал бы выход preRun (meta-compile), а не проверяемого навыка.
Проверка: до разметки сюита падала ровно на этих 3 кейсах (независимое
подтверждение аудита). Негативные сценарии проверены все пять: потерянный
эталон, мёртвый эталон, noSnapshot без причины, update на opt-out кейсе
(не создаёт), update на обычном (создаёт байт-в-байт прежний).
Полная сюита 566/566 ps1; python 563 passed + 3 skipped — идентично HEAD.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
b194834f2b
commit
3b5444e69d
+48
-3
@@ -129,7 +129,11 @@ ibcmd-проход автоматически `○ skipped`, если рядом
|
||||
}
|
||||
```
|
||||
|
||||
Раннер проверит: exitCode=0 + выход совпадает со snapshot (если есть).
|
||||
Раннер проверит: exitCode=0 + выход совпадает с эталоном.
|
||||
|
||||
Эталон **обязателен**: если его нет и кейс не объявил `noSnapshot`, тест падает. Иначе потерянный
|
||||
(или не созданный при добавлении кейса) эталон неотличим от намеренного отсутствия — тест зелёный,
|
||||
хотя выход не проверяется.
|
||||
|
||||
### С параметрами навыка
|
||||
|
||||
@@ -226,19 +230,60 @@ ibcmd-проход автоматически `○ skipped`, если рядом
|
||||
| `preRun` | нет | Массив шагов подготовки (создание объектов и т.п.) |
|
||||
| `expect` | нет | Дополнительные проверки: `files`, `stdoutContains` (строка/массив), `stdoutNotContains` (строка/массив) |
|
||||
| `expectError` | нет | `true` или строка — ожидается ошибка |
|
||||
| `noSnapshot` | нет | Непустая строка с причиной — кейс объявляет, что эталон не нужен (см. «Эталоны») |
|
||||
|
||||
## Эталоны (snapshots)
|
||||
|
||||
Эталон — директория `snapshots/<имя-кейса>/` внутри папки навыка. Содержит ожидаемый выход навыка после нормализации.
|
||||
|
||||
### Когда эталон обязателен
|
||||
|
||||
Всегда, кроме трёх случаев:
|
||||
|
||||
- `expectError` — проверяется факт ошибки, выхода нет;
|
||||
- `setup: "external:<path>"` — рабочая директория read-only, эталон физически не создать;
|
||||
- кейс объявил `noSnapshot` (см. ниже).
|
||||
|
||||
Во всех остальных случаях отсутствующий (или пустой) эталон — **падение** с подсказкой, что делать.
|
||||
|
||||
### `noSnapshot` — когда эталон не нужен
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Валидатор: ссылочный тип разрешается",
|
||||
"noSnapshot": "meta-validate только читает и печатает — эталон зафиксировал бы выход preRun, а не проверяемого навыка; проверяется stdout",
|
||||
"expect": { "stdoutContains": "16. Reference types:" }
|
||||
}
|
||||
```
|
||||
|
||||
Типичный случай — навык ничего не пишет в рабочую директорию (info/validate): эталон зафиксировал бы
|
||||
выход `preRun`, а не проверяемого навыка, и дублировал бы эталоны того навыка.
|
||||
|
||||
**Причина обязательна** — непустая строка; `true` не принимается и валит кейс. Смысл в том, что
|
||||
отключение сверки должно стоить автору формулировки, а ревьюеру быть видно в diff'е: проверить
|
||||
осмысленность причины рантайм не может.
|
||||
|
||||
Если у кейса стоит `noSnapshot`, но каталог эталона существует — тоже падение: такой эталон
|
||||
не сверяется и создаёт ложное впечатление покрытия. Удалите каталог либо снимите `noSnapshot`.
|
||||
|
||||
### Создание / обновление эталонов
|
||||
|
||||
```bash
|
||||
node tests/skills/runner.mjs --update-snapshots # все кейсы
|
||||
node tests/skills/runner.mjs cases/meta-compile/enum --update-snapshots # один кейс — предпочтительно
|
||||
node tests/skills/runner.mjs cases/meta-compile --update-snapshots # один навык
|
||||
node tests/skills/runner.mjs cases/meta-compile/enum --update-snapshots # один кейс
|
||||
node tests/skills/runner.mjs --update-snapshots # все кейсы
|
||||
```
|
||||
|
||||
> Прогон по навыку/сюите **перезаписывает эталоны всех** кейсов сразу: если побочно поехал вывод
|
||||
> соседнего кейса, его эталон обновится вместе с целевым и непреднамеренная регрессия замаскируется.
|
||||
> Поэтому по умолчанию — точечно по кейсу, а после массового пересъёма обязательно проверяйте
|
||||
> `git diff` по `snapshots/`: каждая ± строка должна быть ожидаемой.
|
||||
>
|
||||
> Массовый пересъём легитимен, когда изменение вывода и правда затрагивает многих — например, правка
|
||||
> `meta-compile` меняет фикстуры ~20 навыков, чьи кейсы строятся его `preRun`-прогоном.
|
||||
>
|
||||
> Кейсы с `noSnapshot` пропускаются — эталон им не создаётся.
|
||||
|
||||
### Когда обновлять
|
||||
|
||||
- После **намеренного** изменения логики навыка (новый выход — новый эталон)
|
||||
|
||||
Reference in New Issue
Block a user