test(runner): гейт кейса по ОС (osOnly)

Фейк платформы, написанный как .cmd, на macOS не исполняется ни одним
портом — такие кейсы падали на маке вместо пропуска. runtimeOnly для
этого не годится: ограничение не по порту, а по ОС.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-07-29 21:26:29 +03:00
co-authored by Claude Opus 5
parent f6165c8d35
commit c0b4f3fb3b
8 changed files with 115 additions and 28 deletions
+2
View File
@@ -233,6 +233,8 @@ ibcmd-проход автоматически `○ skipped`, если рядом
| `noSnapshot` | нет | Непустая строка с причиной — кейс объявляет, что эталон не нужен (см. «Эталоны») | | `noSnapshot` | нет | Непустая строка с причиной — кейс объявляет, что эталон не нужен (см. «Эталоны») |
| `idempotent` | нет | `true` — повторный прогон с теми же аргументами должен дать байт-в-байт тот же `workDir` | | `idempotent` | нет | `true` — повторный прогон с теми же аргументами должен дать байт-в-байт тот же `workDir` |
| `runtimeOnly` | нет | `"powershell"` / `"python"` — кейс имеет смысл только на одном порте, на другом `○ skipped` | | `runtimeOnly` | нет | `"powershell"` / `"python"` — кейс имеет смысл только на одном порте, на другом `○ skipped` |
| `osOnly` | нет | `"win32"` / `"darwin"` / `"linux"` — кейс работает только на этой ОС (напр. фейк платформы написан как `.cmd`), на других `○ skipped` |
| `cwd` | нет | `"workDir"` — запустить навык из рабочего каталога (нужно, если кейс кладёт туда `.v8-project.json`) |
| `skipValidation` | нет | `true` — не запускать `postValidate` из `_skill.json` (только при `--with-validation`) | | `skipValidation` | нет | `true` — не запускать `postValidate` из `_skill.json` (только при `--with-validation`) |
### Ключи `expect` ### Ключи `expect`
@@ -25,5 +25,6 @@
}, },
"expectError": true, "expectError": true,
"noSnapshot": "проверяется командная строка в stdout, файлов на выходе нет", "noSnapshot": "проверяется командная строка в stdout, файлов на выходе нет",
"cwd": "workDir" "cwd": "workDir",
"osOnly": "win32"
} }
@@ -1,13 +1,29 @@
{ {
"name": "Доп. аргументы ibcmd доходят до платформы, секрет замаскирован (fake platform)", "name": "Доп. аргументы ibcmd доходят до платформы, секрет замаскирован (fake platform)",
"preRun": [ "preRun": [
{ "writeFile": { "path": "ibcmd.cmd", "content": "@echo off\r\necho ARGS: %*\r\nexit /b 0\r\n" } } {
"writeFile": {
"path": "ibcmd.cmd",
"content": "@echo off\r\necho ARGS: %*\r\nexit /b 0\r\n"
}
}
],
"args_extra": [
"-V8Path",
"{workDir}\\ibcmd.cmd",
"-InfoBasePath",
"{workDir}\\ib",
"-AdditionalIbcmdArguments",
"--verbose,--token=topsecret"
], ],
"args_extra": ["-V8Path", "{workDir}\\ibcmd.cmd", "-InfoBasePath", "{workDir}\\ib",
"-AdditionalIbcmdArguments", "--verbose,--token=topsecret"],
"expect": { "expect": {
"stdoutContains": ["ARGS: infobase create", "--verbose --token=topsecret", "--token=***"] "stdoutContains": [
"ARGS: infobase create",
"--verbose --token=topsecret",
"--token=***"
]
}, },
"expectError": true, "expectError": true,
"noSnapshot": "проверяется командная строка в stdout, файлов на выходе нет" "noSnapshot": "проверяется командная строка в stdout, файлов на выходе нет",
"osOnly": "win32"
} }
@@ -2,11 +2,25 @@
"name": "Доп. аргументы 1cv8 попадают в командную строку, py-порт (fake platform)", "name": "Доп. аргументы 1cv8 попадают в командную строку, py-порт (fake platform)",
"runtimeOnly": "python", "runtimeOnly": "python",
"preRun": [ "preRun": [
{ "writeFile": { "path": "fake.cmd", "content": "@echo off\r\necho ARGS: %*\r\nexit /b 0\r\n" } } {
"writeFile": {
"path": "fake.cmd",
"content": "@echo off\r\necho ARGS: %*\r\nexit /b 0\r\n"
}
}
], ],
"args_extra": ["-V8Path", "{workDir}\\fake.cmd", "-InfoBasePath", "{workDir}\\ib", "args_extra": [
"-AdditionalV8Arguments", "/UseHwLicenses+"], "-V8Path",
"expect": { "stdoutContains": "/DisableStartupDialogs /UseHwLicenses+" }, "{workDir}\\fake.cmd",
"-InfoBasePath",
"{workDir}\\ib",
"-AdditionalV8Arguments",
"/UseHwLicenses+"
],
"expect": {
"stdoutContains": "/DisableStartupDialogs /UseHwLicenses+"
},
"expectError": true, "expectError": true,
"noSnapshot": "py-порт печатает лог /Out, а не stdout платформы — проверяется строка запуска" "noSnapshot": "py-порт печатает лог /Out, а не stdout платформы — проверяется строка запуска",
"osOnly": "win32"
} }
@@ -1,12 +1,29 @@
{ {
"name": "Доп. аргументы 1cv8 доходят до платформы (fake platform)", "name": "Доп. аргументы 1cv8 доходят до платформы (fake platform)",
"preRun": [ "preRun": [
{ "writeFile": { "path": "fake.cmd", "content": "@echo off\r\necho ARGS: %*\r\nexit /b 0\r\n" } } {
"writeFile": {
"path": "fake.cmd",
"content": "@echo off\r\necho ARGS: %*\r\nexit /b 0\r\n"
}
}
],
"args_extra": [
"-V8Path",
"{workDir}\\fake.cmd",
"-InfoBasePath",
"{workDir}\\ib",
"-AdditionalV8Arguments",
"/UseHwLicenses+"
], ],
"args_extra": ["-V8Path", "{workDir}\\fake.cmd", "-InfoBasePath", "{workDir}\\ib",
"-AdditionalV8Arguments", "/UseHwLicenses+"],
"runtimeOnly": "powershell", "runtimeOnly": "powershell",
"expect": { "stdoutContains": ["ARGS: CREATEINFOBASE", "/DisableStartupDialogs /UseHwLicenses+"] }, "expect": {
"stdoutContains": [
"ARGS: CREATEINFOBASE",
"/DisableStartupDialogs /UseHwLicenses+"
]
},
"expectError": true, "expectError": true,
"noSnapshot": "проверяется командная строка в stdout, файлов на выходе нет" "noSnapshot": "проверяется командная строка в stdout, файлов на выходе нет",
"osOnly": "win32"
} }
@@ -1,11 +1,25 @@
{ {
"name": "Доп. аргументы доходят до 1cv8 при строковой склейке (fake platform)", "name": "Доп. аргументы доходят до 1cv8 при строковой склейке (fake platform)",
"preRun": [ "preRun": [
{ "writeFile": { "path": "fake.cmd", "content": "@echo off\r\necho ARGS: %*\r\nexit /b 1\r\n" } } {
"writeFile": {
"path": "fake.cmd",
"content": "@echo off\r\necho ARGS: %*\r\nexit /b 1\r\n"
}
}
], ],
"args_extra": ["-V8Path", "{workDir}\\fake.cmd", "-InfoBasePath", "{workDir}\\ib", "args_extra": [
"-AdditionalV8Arguments", "/ClearCache,/UseHwLicenses+"], "-V8Path",
"expect": { "stdoutContains": "/DisableStartupDialogs /ClearCache /UseHwLicenses+" }, "{workDir}\\fake.cmd",
"-InfoBasePath",
"{workDir}\\ib",
"-AdditionalV8Arguments",
"/ClearCache,/UseHwLicenses+"
],
"expect": {
"stdoutContains": "/DisableStartupDialogs /ClearCache /UseHwLicenses+"
},
"expectError": true, "expectError": true,
"noSnapshot": "проверяется командная строка в stdout, файлов на выходе нет" "noSnapshot": "проверяется командная строка в stdout, файлов на выходе нет",
"osOnly": "win32"
} }
@@ -1,13 +1,29 @@
{ {
"name": "Доп. аргументы уходят и во временную базу, и в сборку (fake platform)", "name": "Доп. аргументы уходят и во временную базу, и в сборку (fake platform)",
"preRun": [ "preRun": [
{ "writeFile": { "path": "fake.cmd", "content": "@echo off\r\necho ARGS: %*\r\nexit /b 0\r\n" } }, {
{ "writeFile": { "path": "src/Обработка.xml", "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<MetaDataObject xmlns=\"http://v8.1c.ru/8.3/MDClasses\"/>\n" } } "writeFile": {
"path": "fake.cmd",
"content": "@echo off\r\necho ARGS: %*\r\nexit /b 0\r\n"
}
},
{
"writeFile": {
"path": "src/Обработка.xml",
"content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<MetaDataObject xmlns=\"http://v8.1c.ru/8.3/MDClasses\"/>\n"
}
}
],
"args_extra": [
"-V8Path",
"{workDir}\\fake.cmd",
"-SourceFile",
"{workDir}\\src\\Обработка.xml",
"-OutputFile",
"{workDir}\\build\\Обработка.epf",
"-AdditionalV8Arguments",
"/UseHwLicenses+"
], ],
"args_extra": ["-V8Path", "{workDir}\\fake.cmd",
"-SourceFile", "{workDir}\\src\\Обработка.xml",
"-OutputFile", "{workDir}\\build\\Обработка.epf",
"-AdditionalV8Arguments", "/UseHwLicenses+"],
"runtimeOnly": "powershell", "runtimeOnly": "powershell",
"expect": { "expect": {
"stdoutContains": [ "stdoutContains": [
@@ -18,5 +34,6 @@
] ]
}, },
"expectError": true, "expectError": true,
"noSnapshot": "проверяются командные строки в stdout, платформа фейковая — EPF не создаётся" "noSnapshot": "проверяются командные строки в stdout, платформа фейковая — EPF не создаётся",
"osOnly": "win32"
} }
+6
View File
@@ -588,6 +588,12 @@ async function runCaseAsync(testCase, opts) {
let workDir = null; let workDir = null;
let inputFile = null; let inputFile = null;
// 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) {
return { id: testCase.id, skill: testCase.skillDir, name: testCase.name, passed: true, skipped: true, errors: [], elapsed: '0.0s' };
}
// runtimeOnly: gate a case to a single port (e.g. a .cmd fake platform only runs via // runtimeOnly: gate a case to a single port (e.g. a .cmd fake platform only runs via
// PowerShell's Start-Process; python's list-exec can't launch it). Skipped elsewhere. // PowerShell's Start-Process; python's list-exec can't launch it). Skipped elsewhere.
if (caseData.runtimeOnly && caseData.runtimeOnly !== opts.runtime) { if (caseData.runtimeOnly && caseData.runtimeOnly !== opts.runtime) {