diff --git a/tests/skills/README.md b/tests/skills/README.md index e43d8154..3f86199c 100644 --- a/tests/skills/README.md +++ b/tests/skills/README.md @@ -274,7 +274,7 @@ ibcmd-проход автоматически `○ skipped`, если рядом | `noSnapshot` | нет | Непустая строка с причиной — кейс объявляет, что эталон не нужен (см. «Эталоны») | | `idempotent` | нет | `true` — повторный прогон с теми же аргументами должен дать байт-в-байт тот же `workDir` | | `runtimeOnly` | нет | `"powershell"` / `"python"` — кейс имеет смысл только на одном порте, на другом `○ skipped` | -| `osOnly` | нет | `"win32"` / `"darwin"` / `"linux"` — кейс работает только на этой ОС (напр. фейк платформы написан как `.cmd`), на других `○ skipped` | +| `osOnly` | нет | `"win32"` / `"darwin"` / `"linux"` — кейс работает только на этой ОС (напр. фейк платформы написан как `.cmd`), на других `○ skipped`. Принимает и массив: `["darwin","linux"]` | | `cwd` | нет | `"workDir"` — запустить навык из рабочего каталога (нужно, если кейс кладёт туда `.v8-project.json`) | | `skipValidation` | нет | `true` — не запускать `postValidate` из `_skill.json` (только при `--with-validation`) | | `skipPlatformVerify` | нет | Непустая строка с причиной — `verify-snapshots.mjs` пропускает кейс. Нужен, когда фикстура не является конфигурацией, которую можно загрузить (напр. фрагмент расширения) | @@ -318,7 +318,7 @@ ibcmd-проход автоматически `○ skipped`, если рядом | Форма шага | Описание | |---|---| | `{ "script": "<навык>/scripts/<файл>", "input": {...}, "args": { "-Flag": "{inputFile}" } }` | Прогон другого навыка для подготовки фикстуры. Плейсхолдеры: `{inputFile}`, `{workDir}` | -| `{ "writeFile": { "path": "<путь>", "content": "<строка или объект>" } }` | Записать произвольный файл в `workDir` (объект сериализуется в JSON) | +| `{ "writeFile": { "path": "<путь>", "content": "<строка или объект>" } }` | Записать произвольный файл в `workDir` (объект сериализуется в JSON). Ключ `"executable": true` ставит бит исполнения — нужен фейку платформы на *nix, где навык запускает её через `exec`; на Windows no-op | Дополнительно у шага-скрипта: `"cwd": "{workDir}"` — запустить шаг из рабочего каталога. Нужно, когда относительный путь в `args` (напр. `-OutputPath: "Template.xml"`) должен лечь в фикстуру, а diff --git a/tests/skills/cases/db-load-xml/clean-log-no-warning-posix.json b/tests/skills/cases/db-load-xml/clean-log-no-warning-posix.json new file mode 100644 index 00000000..81726892 --- /dev/null +++ b/tests/skills/cases/db-load-xml/clean-log-no-warning-posix.json @@ -0,0 +1,47 @@ +{ + "name": "Чистый лог на *nix: предупреждения нет", + "osOnly": [ + "darwin", + "linux" + ], + "runtimeOnly": "python", + "preRun": [ + { + "writeFile": { + "path": "fake.sh", + "content": "#!/bin/sh\n# Фейк платформы для *nix: вычитывает путь из /Out и кладёт туда готовый лог.\nSELF=$(dirname \"$0\")\nOUT=\"\"\nwhile [ $# -gt 0 ]; do\n if [ \"$1\" = \"/Out\" ]; then OUT=\"$2\"; fi\n shift\ndone\ncp \"$SELF/log.txt\" \"$OUT\"\nexit 0\n", + "executable": true + } + }, + { + "writeFile": { + "path": "log.txt", + "content": "Загрузка конфигурации из файлов.\r\nЗагрузка завершена. Суммарное время выполнения операции: 780 миллисекунд.\r\n" + } + }, + { + "writeFile": { + "path": "ib/1Cv8.1CD", + "content": "stub" + } + }, + { + "writeFile": { + "path": "src/Configuration.xml", + "content": "\n" + } + } + ], + "args_extra": [ + "-V8Path", + "{workDir}/fake.sh", + "-InfoBasePath", + "{workDir}/ib", + "-ConfigDir", + "{workDir}/src" + ], + "expect": { + "stdoutNotContains": "[warning] platform reported success, but the log contains" + }, + "noSnapshot": "навык ничего не пишет в рабочий каталог — проверяется разбор лога платформы" +} diff --git a/tests/skills/cases/db-load-xml/compat-mode-above-platform-posix.json b/tests/skills/cases/db-load-xml/compat-mode-above-platform-posix.json new file mode 100644 index 00000000..b191eb3e --- /dev/null +++ b/tests/skills/cases/db-load-xml/compat-mode-above-platform-posix.json @@ -0,0 +1,50 @@ +{ + "name": "Режим совместимости выше платформы на *nix: классифицируется как отказ (issue #63)", + "osOnly": [ + "darwin", + "linux" + ], + "runtimeOnly": "python", + "preRun": [ + { + "writeFile": { + "path": "fake.sh", + "content": "#!/bin/sh\n# Фейк платформы для *nix: вычитывает путь из /Out и кладёт туда готовый лог.\nSELF=$(dirname \"$0\")\nOUT=\"\"\nwhile [ $# -gt 0 ]; do\n if [ \"$1\" = \"/Out\" ]; then OUT=\"$2\"; fi\n shift\ndone\ncp \"$SELF/log.txt\" \"$OUT\"\nexit 0\n", + "executable": true + } + }, + { + "writeFile": { + "path": "log.txt", + "content": "Для работы с конфигурацией необходима версия платформы не меньше, чем 8.3.27.\r\nТекущая версия 8.3.24.1691.\r\n" + } + }, + { + "writeFile": { + "path": "ib/1Cv8.1CD", + "content": "stub" + } + }, + { + "writeFile": { + "path": "src/Configuration.xml", + "content": "\n" + } + } + ], + "args_extra": [ + "-V8Path", + "{workDir}/fake.sh", + "-InfoBasePath", + "{workDir}/ib", + "-ConfigDir", + "{workDir}/src" + ], + "expect": { + "stdoutContains": [ + "[warning] platform reported success, but the log contains 1 problem(s):", + "необходима версия платформы не меньше" + ] + }, + "noSnapshot": "навык ничего не пишет в рабочий каталог — проверяется разбор лога платформы" +} diff --git a/tests/skills/cases/db-load-xml/silent-rejection-detected-posix.json b/tests/skills/cases/db-load-xml/silent-rejection-detected-posix.json new file mode 100644 index 00000000..a6552abe --- /dev/null +++ b/tests/skills/cases/db-load-xml/silent-rejection-detected-posix.json @@ -0,0 +1,51 @@ +{ + "name": "Тихий отказ в логе на *nix: py-порт предупреждает (fake platform)", + "osOnly": [ + "darwin", + "linux" + ], + "runtimeOnly": "python", + "preRun": [ + { + "writeFile": { + "path": "fake.sh", + "content": "#!/bin/sh\n# Фейк платформы для *nix: вычитывает путь из /Out и кладёт туда готовый лог.\nSELF=$(dirname \"$0\")\nOUT=\"\"\nwhile [ $# -gt 0 ]; do\n if [ \"$1\" = \"/Out\" ]; then OUT=\"$2\"; fi\n shift\ndone\ncp \"$SELF/log.txt\" \"$OUT\"\nexit 0\n", + "executable": true + } + }, + { + "writeFile": { + "path": "log.txt", + "content": "Загрузка конфигурации из файлов.\r\nНеверное свойство объекта метаданных. Свойство LineNumberLength не входит в состав объекта метаданных TabularSection. - LineNumberLength\r\nЗагрузка завершена.\r\n" + } + }, + { + "writeFile": { + "path": "ib/1Cv8.1CD", + "content": "stub" + } + }, + { + "writeFile": { + "path": "src/Configuration.xml", + "content": "\n" + } + } + ], + "args_extra": [ + "-V8Path", + "{workDir}/fake.sh", + "-InfoBasePath", + "{workDir}/ib", + "-ConfigDir", + "{workDir}/src" + ], + "expect": { + "stdoutContains": [ + "[warning] platform reported success, but the log contains 1 problem(s):", + "Свойство LineNumberLength не входит в состав объекта метаданных" + ], + "stdoutNotContains": "-StrictLog" + }, + "noSnapshot": "навык ничего не пишет в рабочий каталог — проверяется разбор лога платформы" +} diff --git a/tests/skills/cases/db-load-xml/silent-rejection-strict-posix.json b/tests/skills/cases/db-load-xml/silent-rejection-strict-posix.json new file mode 100644 index 00000000..32ebf37f --- /dev/null +++ b/tests/skills/cases/db-load-xml/silent-rejection-strict-posix.json @@ -0,0 +1,49 @@ +{ + "name": "Тот же лог под -StrictLog на *nix: код возврата 1 (fake platform)", + "osOnly": [ + "darwin", + "linux" + ], + "runtimeOnly": "python", + "preRun": [ + { + "writeFile": { + "path": "fake.sh", + "content": "#!/bin/sh\n# Фейк платформы для *nix: вычитывает путь из /Out и кладёт туда готовый лог.\nSELF=$(dirname \"$0\")\nOUT=\"\"\nwhile [ $# -gt 0 ]; do\n if [ \"$1\" = \"/Out\" ]; then OUT=\"$2\"; fi\n shift\ndone\ncp \"$SELF/log.txt\" \"$OUT\"\nexit 0\n", + "executable": true + } + }, + { + "writeFile": { + "path": "log.txt", + "content": "Загрузка конфигурации из файлов.\r\nНеверное свойство объекта метаданных. Свойство LineNumberLength не входит в состав объекта метаданных TabularSection. - LineNumberLength\r\nЗагрузка завершена.\r\n" + } + }, + { + "writeFile": { + "path": "ib/1Cv8.1CD", + "content": "stub" + } + }, + { + "writeFile": { + "path": "src/Configuration.xml", + "content": "\n" + } + } + ], + "args_extra": [ + "-V8Path", + "{workDir}/fake.sh", + "-InfoBasePath", + "{workDir}/ib", + "-ConfigDir", + "{workDir}/src", + "-StrictLog" + ], + "expect": { + "stdoutContains": "[warning] platform reported success, but the log contains 1 problem(s):" + }, + "noSnapshot": "навык ничего не пишет в рабочий каталог — проверяется разбор лога платформы", + "expectError": true +} diff --git a/tests/skills/cases/db-update/clean-log-no-warning-posix.json b/tests/skills/cases/db-update/clean-log-no-warning-posix.json new file mode 100644 index 00000000..b3d02cc8 --- /dev/null +++ b/tests/skills/cases/db-update/clean-log-no-warning-posix.json @@ -0,0 +1,39 @@ +{ + "name": "Чистый лог на *nix: предупреждения нет", + "osOnly": [ + "darwin", + "linux" + ], + "runtimeOnly": "python", + "preRun": [ + { + "writeFile": { + "path": "fake.sh", + "content": "#!/bin/sh\n# Фейк платформы для *nix: вычитывает путь из /Out и кладёт туда готовый лог.\nSELF=$(dirname \"$0\")\nOUT=\"\"\nwhile [ $# -gt 0 ]; do\n if [ \"$1\" = \"/Out\" ]; then OUT=\"$2\"; fi\n shift\ndone\ncp \"$SELF/log.txt\" \"$OUT\"\nexit 0\n", + "executable": true + } + }, + { + "writeFile": { + "path": "log.txt", + "content": "Загрузка конфигурации из файлов.\r\nЗагрузка завершена. Суммарное время выполнения операции: 780 миллисекунд.\r\n" + } + }, + { + "writeFile": { + "path": "ib/1Cv8.1CD", + "content": "stub" + } + } + ], + "args_extra": [ + "-V8Path", + "{workDir}/fake.sh", + "-InfoBasePath", + "{workDir}/ib" + ], + "expect": { + "stdoutNotContains": "[warning] platform reported success, but the log contains" + }, + "noSnapshot": "навык ничего не пишет в рабочий каталог — проверяется разбор лога платформы" +} diff --git a/tests/skills/cases/db-update/compat-mode-above-platform-posix.json b/tests/skills/cases/db-update/compat-mode-above-platform-posix.json new file mode 100644 index 00000000..6251b023 --- /dev/null +++ b/tests/skills/cases/db-update/compat-mode-above-platform-posix.json @@ -0,0 +1,42 @@ +{ + "name": "Режим совместимости выше платформы на *nix: классифицируется как отказ (issue #63)", + "osOnly": [ + "darwin", + "linux" + ], + "runtimeOnly": "python", + "preRun": [ + { + "writeFile": { + "path": "fake.sh", + "content": "#!/bin/sh\n# Фейк платформы для *nix: вычитывает путь из /Out и кладёт туда готовый лог.\nSELF=$(dirname \"$0\")\nOUT=\"\"\nwhile [ $# -gt 0 ]; do\n if [ \"$1\" = \"/Out\" ]; then OUT=\"$2\"; fi\n shift\ndone\ncp \"$SELF/log.txt\" \"$OUT\"\nexit 0\n", + "executable": true + } + }, + { + "writeFile": { + "path": "log.txt", + "content": "Для работы с конфигурацией необходима версия платформы не меньше, чем 8.3.27.\r\nТекущая версия 8.3.24.1691.\r\n" + } + }, + { + "writeFile": { + "path": "ib/1Cv8.1CD", + "content": "stub" + } + } + ], + "args_extra": [ + "-V8Path", + "{workDir}/fake.sh", + "-InfoBasePath", + "{workDir}/ib" + ], + "expect": { + "stdoutContains": [ + "[warning] platform reported success, but the log contains 1 problem(s):", + "необходима версия платформы не меньше" + ] + }, + "noSnapshot": "навык ничего не пишет в рабочий каталог — проверяется разбор лога платформы" +} diff --git a/tests/skills/cases/db-update/silent-rejection-detected-posix.json b/tests/skills/cases/db-update/silent-rejection-detected-posix.json new file mode 100644 index 00000000..c04c4b89 --- /dev/null +++ b/tests/skills/cases/db-update/silent-rejection-detected-posix.json @@ -0,0 +1,43 @@ +{ + "name": "Тихий отказ в логе на *nix: py-порт предупреждает (fake platform)", + "osOnly": [ + "darwin", + "linux" + ], + "runtimeOnly": "python", + "preRun": [ + { + "writeFile": { + "path": "fake.sh", + "content": "#!/bin/sh\n# Фейк платформы для *nix: вычитывает путь из /Out и кладёт туда готовый лог.\nSELF=$(dirname \"$0\")\nOUT=\"\"\nwhile [ $# -gt 0 ]; do\n if [ \"$1\" = \"/Out\" ]; then OUT=\"$2\"; fi\n shift\ndone\ncp \"$SELF/log.txt\" \"$OUT\"\nexit 0\n", + "executable": true + } + }, + { + "writeFile": { + "path": "log.txt", + "content": "Загрузка конфигурации из файлов.\r\nНеверное свойство объекта метаданных. Свойство LineNumberLength не входит в состав объекта метаданных TabularSection. - LineNumberLength\r\nЗагрузка завершена.\r\n" + } + }, + { + "writeFile": { + "path": "ib/1Cv8.1CD", + "content": "stub" + } + } + ], + "args_extra": [ + "-V8Path", + "{workDir}/fake.sh", + "-InfoBasePath", + "{workDir}/ib" + ], + "expect": { + "stdoutContains": [ + "[warning] platform reported success, but the log contains 1 problem(s):", + "Свойство LineNumberLength не входит в состав объекта метаданных" + ], + "stdoutNotContains": "-StrictLog" + }, + "noSnapshot": "навык ничего не пишет в рабочий каталог — проверяется разбор лога платформы" +} diff --git a/tests/skills/cases/db-update/silent-rejection-strict-posix.json b/tests/skills/cases/db-update/silent-rejection-strict-posix.json new file mode 100644 index 00000000..b33e6394 --- /dev/null +++ b/tests/skills/cases/db-update/silent-rejection-strict-posix.json @@ -0,0 +1,41 @@ +{ + "name": "Тот же лог под -StrictLog на *nix: код возврата 1 (fake platform)", + "osOnly": [ + "darwin", + "linux" + ], + "runtimeOnly": "python", + "preRun": [ + { + "writeFile": { + "path": "fake.sh", + "content": "#!/bin/sh\n# Фейк платформы для *nix: вычитывает путь из /Out и кладёт туда готовый лог.\nSELF=$(dirname \"$0\")\nOUT=\"\"\nwhile [ $# -gt 0 ]; do\n if [ \"$1\" = \"/Out\" ]; then OUT=\"$2\"; fi\n shift\ndone\ncp \"$SELF/log.txt\" \"$OUT\"\nexit 0\n", + "executable": true + } + }, + { + "writeFile": { + "path": "log.txt", + "content": "Загрузка конфигурации из файлов.\r\nНеверное свойство объекта метаданных. Свойство LineNumberLength не входит в состав объекта метаданных TabularSection. - LineNumberLength\r\nЗагрузка завершена.\r\n" + } + }, + { + "writeFile": { + "path": "ib/1Cv8.1CD", + "content": "stub" + } + } + ], + "args_extra": [ + "-V8Path", + "{workDir}/fake.sh", + "-InfoBasePath", + "{workDir}/ib", + "-StrictLog" + ], + "expect": { + "stdoutContains": "[warning] platform reported success, but the log contains 1 problem(s):" + }, + "noSnapshot": "навык ничего не пишет в рабочий каталог — проверяется разбор лога платформы", + "expectError": true +} diff --git a/tests/skills/runner.mjs b/tests/skills/runner.mjs index 194e384b..b1f3ba2b 100644 --- a/tests/skills/runner.mjs +++ b/tests/skills/runner.mjs @@ -4,7 +4,7 @@ import { execFileSync, execFile } from 'child_process'; import { existsSync, mkdirSync, mkdtempSync, rmSync, readFileSync, writeFileSync, - readdirSync, statSync, cpSync, copyFileSync } from 'fs'; + readdirSync, statSync, cpSync, copyFileSync, chmodSync } from 'fs'; import { createHash } from 'crypto'; import { join, resolve, dirname, relative, basename, extname } from 'path'; import { tmpdir, cpus } from 'os'; @@ -713,7 +713,9 @@ async function runCaseAsync(testCase, opts) { // osOnly: gate a case to one OS (e.g. a fake platform written as a .cmd cannot run on // macOS/Linux at all, whatever the port). Values are process.platform strings. - if (caseData.osOnly && caseData.osOnly !== process.platform) { + // Значение — строка или массив строк process.platform: фейк платформы бывает нужен и на + // darwin, и на linux, а дублировать кейс ради второй ОС смысла нет. + if (caseData.osOnly && ![].concat(caseData.osOnly).includes(process.platform)) { return { id: testCase.id, skill: testCase.skillDir, name: testCase.name, passed: true, skipped: true, errors: [], elapsed: '0.0s' }; } @@ -745,6 +747,9 @@ async function runCaseAsync(testCase, opts) { : JSON.stringify(step.writeFile.content, null, 2); mkdirSync(dirname(wfPath), { recursive: true }); writeFileSync(wfPath, wfContent, 'utf8'); + // Бит исполнения: на *nix навык запускает платформу через exec, и фейк без +x + // не стартует вовсе. На Windows chmod — no-op. + if (step.writeFile.executable) chmodSync(wfPath, 0o755); continue; } const preScript = resolveScript(step.script, opts.runtime); @@ -1363,6 +1368,9 @@ async function runIntegrationOnce(test, opts, engine, labelEngine) { const abs = target.includes(':') || target.startsWith('/') ? target : join(workDir, target); mkdirSync(dirname(abs), { recursive: true }); writeFileSync(abs, step.content ?? '', 'utf8'); + // Бит исполнения: на *nix навык запускает платформу через exec, и фейк без +x + // не стартует вовсе. На Windows chmod — no-op. + if (step.executable) chmodSync(abs, 0o755); const stepElapsed = ((performance.now() - stepT0) / 1000).toFixed(1); stepResults.push({ name: step.name, passed: true, elapsed: `${stepElapsed}s` }); } catch (e) { diff --git a/tests/skills/verify-snapshots.mjs b/tests/skills/verify-snapshots.mjs index 0017b394..bb4831ff 100644 --- a/tests/skills/verify-snapshots.mjs +++ b/tests/skills/verify-snapshots.mjs @@ -13,7 +13,7 @@ import { execFileSync } from 'child_process'; import { existsSync, mkdirSync, mkdtempSync, rmSync, readFileSync, writeFileSync, - readdirSync, statSync, cpSync, copyFileSync } from 'fs'; + readdirSync, statSync, cpSync, copyFileSync, chmodSync } from 'fs'; import { join, resolve, dirname, basename } from 'path'; import { tmpdir } from 'os'; @@ -627,6 +627,9 @@ function runPreSteps(preRun, workDir, runtime, log) { : JSON.stringify(step.writeFile.content, null, 2); mkdirSync(dirname(wfPath), { recursive: true }); writeFileSync(wfPath, wfContent, 'utf8'); + // Бит исполнения: на *nix навык запускает платформу через exec, и фейк без +x + // не стартует вовсе. На Windows chmod — no-op. + if (step.writeFile.executable) chmodSync(wfPath, 0o755); log(`preRun: writeFile ${step.writeFile.path}`, true); continue; }