Journal column references require the referenced document attribute to
actually exist at load time. Previously the test DSL relied on the
verify-snapshots stub mechanism, which creates minimal Document stubs
without the specific attributes the column refs point to → load failed
with "Неизвестный объект метаданных - Document.ПриходнаяНакладная.Attribute.Склад".
This was listed as D5 in the 2026-04-05 FINDINGS log ("low priority,
complex to implement").
Now the test case declares preRun steps that create both documents
with the exact attributes its journal columns reference (Склад on one,
Контрагент on both). Column "Контрагент" gained explicit references
(was a shorthand string before) because the platform rejects journal
columns without at least one reference at load time.
Regenerated the snapshot (gained Documents/ subtree from preRun output).
verify-snapshots --skill meta-compile is now 30/30 green with -StrictLog.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Регресс-тесты навыков
Snapshot-тестирование скриптов навыков: навык получает вход → генерирует файлы → результат сравнивается с эталоном.
Быстрые, файловые, без зависимости от платформы 1С.
Запуск
node tests/skills/runner.mjs # все кейсы
node tests/skills/runner.mjs cases/meta-compile # один навык
node tests/skills/runner.mjs cases/meta-compile/catalog-basic # один кейс
node tests/skills/runner.mjs --verbose # подробный вывод (дерево)
node tests/skills/runner.mjs --update-snapshots # обновить эталоны
node tests/skills/runner.mjs --runtime python # запуск на PY-версиях
node tests/skills/runner.mjs --json report.json # JSON-отчёт
Exit code: 0 = все прошли, 1 = есть падения.
Что делать при падении
- Смотри case id в выводе — это путь к файлу кейса (можно перезапустить:
node runner.mjs <case-id>) - Открой
.jsonкейса — посмотри что на входе - Открой
snapshots/<кейс>/— посмотри эталон - Если изменение намеренное (доработка навыка) — обнови эталон:
node runner.mjs <case-id> --update-snapshots - Если баг — починить скрипт навыка и перезапустить тест
Как добавить навык
- Создать папку
tests/skills/cases/<имя-навыка>/ - Положить
_skill.json— описание навыка для раннера - Добавить кейсы — по одному
.jsonфайлу на кейс
Формат _skill.json
{
"script": "meta-compile/scripts/meta-compile",
"setup": "empty-config",
"args": [
{ "flag": "-JsonPath", "from": "inputFile" },
{ "flag": "-OutputDir", "from": "workDir" }
],
"snapshot": {
"root": "workDir",
"normalizeUuids": true
}
}
| Поле | Описание |
|---|---|
script |
Путь от .claude/skills/, без расширения. Раннер добавит .ps1 (по умолчанию) или .py |
setup |
Фикстура: "empty-config", "base-config", "none", "fixture:<name>" (из fixtures/ папки навыка), "external:<path>" (реальная выгрузка, read-only, skip если недоступна) |
args |
Маппинг параметров навыка (см. ниже) |
snapshot |
Настройки сравнения: root ("workDir" или "outputPath") и normalizeUuids |
Значения from в args
| Значение | Что подставляется |
|---|---|
"inputFile" |
Путь к temp-файлу с case.input (JSON) |
"workDir" |
Рабочая директория (копия фикстуры) |
"outputPath" |
workDir + case.outputPath |
"workPath" |
workDir + значение из params.<field>. Поле указывается в mapping.field (по умолчанию objectPath) |
"case.<field>" |
Значение из params.<field> (приоритет) или корня кейса |
"switch" |
Флаг без значения (напр. -Detailed) |
"literal" |
Фиксированное значение из mapping.value |
Как добавить кейс
Положить .json файл в папку навыка. Имя файла = имя кейса.
Позитивный кейс (минимальный)
{
"name": "Простой справочник",
"input": { "type": "Catalog", "name": "Валюты" }
}
Раннер проверит: exitCode=0 + выход совпадает со snapshot (если есть).
С параметрами навыка
{
"name": "Обзор справочника",
"params": { "objectPath": "Catalogs/Номенклатура" },
"expect": { "stdoutContains": "Номенклатура" }
}
params — параметры для навыка. Используются через case.<field> и workPath в _skill.json.
С дополнительными CLI-аргументами
{
"name": "Конфигурация с поставщиком",
"params": { "name": "Бухгалтерия" },
"args_extra": ["-Vendor", "Тест", "-Version", "2.0.1"]
}
args_extra — дополнительные аргументы, не описанные в _skill.json, передаются навыку как есть.
С предварительными шагами
{
"name": "Добавление реквизита к справочнику",
"preRun": [
{
"script": "meta-compile/scripts/meta-compile",
"input": { "type": "Catalog", "name": "Контрагенты" },
"args": { "-JsonPath": "{inputFile}", "-OutputDir": "{workDir}" }
}
],
"params": { "objectPath": "Catalogs/Контрагенты" },
"input": { "operations": [{ "op": "add-attribute", "name": "ИНН", "type": "String", "length": 12 }] }
}
preRun — шаги подготовки перед основным навыком. Каждый шаг: script (путь без расширения), input (JSON), args (маппинг с {workDir} и {inputFile} плейсхолдерами).
Кейс с реальной выгрузкой
{
"name": "Реальный справочник Номенклатура (БП)",
"setup": "external:C:/WS/tasks/cfsrc/acc_8.3.24",
"params": { "objectPath": "Catalogs/Номенклатура" },
"expect": { "stdoutContains": "Номенклатура" }
}
setup: "external:<path>" — использует реальную выгрузку конфигурации 1С как read-only рабочую директорию (без копирования). Если путь недоступен — тест пропускается (○ skipped), не падает. Подходит для info/validate навыков, которые не модифицируют файлы.
Негативный кейс
{
"name": "Ошибка: пустое имя",
"input": { "type": "Catalog", "name": "" },
"expectError": true
}
expectError: true — ожидается exitCode≠0. Строковое значение — проверит наличие в stderr.
Все поля кейса
| Поле | Обязательно | Описание |
|---|---|---|
name |
да | Название теста (отображается в отчёте) |
input |
нет | JSON-объект, передаётся навыку через temp-файл |
params |
нет | Параметры для case.<field> и workPath маппинга |
setup |
нет | Переопределение setup из _skill.json |
outputPath |
нет | Относительный путь для навыков с -OutputPath |
args_extra |
нет | Массив дополнительных CLI-аргументов |
preRun |
нет | Массив шагов подготовки (создание объектов и т.п.) |
expect |
нет | Дополнительные проверки: files, stdoutContains |
expectError |
нет | true или строка — ожидается ошибка |
Эталоны (snapshots)
Эталон — директория snapshots/<имя-кейса>/ внутри папки навыка. Содержит ожидаемый выход навыка после нормализации.
Создание / обновление эталонов
node tests/skills/runner.mjs --update-snapshots # все кейсы
node tests/skills/runner.mjs cases/meta-compile --update-snapshots # один навык
node tests/skills/runner.mjs cases/meta-compile/enum --update-snapshots # один кейс
Когда обновлять
- После намеренного изменения логики навыка (новый выход — новый эталон)
- После сертификации: загрузить результат в 1С (
db-load-xml), убедиться что платформа приняла, затем--update-snapshots - Не обновлять если падение — неожиданный побочный эффект (это баг)
Нормализация
Перед сравнением (и при сохранении) применяется:
- UUID →
UUID-001,UUID-002... (по порядку появления, ссылочная целостность сохраняется) - BOM (U+FEFF) — удаляется
- Line endings —
\r\n→\n
Структура
tests/skills/
runner.mjs # тест-раннер
README.md # этот файл
.cache/ # кэш фикстур (в .gitignore)
cases/
<навык>/
_skill.json # конфиг навыка
<кейс>.json # тестовый случай
snapshots/
<кейс>/ # эталон
fixtures/ # broken-фикстуры (для validate-навыков)
<имя>/ # сломанный XML, ссылка: "setup": "fixture:<имя>"