test(skills): expected-skips.mjs — ожидаемое число пропусков вместо запомненного

«skipped» — норма, а не падение, но само число ни о чём не говорит, пока не с чем сверить,
а запоминать его нельзя: оно растёт с набором кейсов. Прежняя сверка жила в личной памятке
как grep по 'external:|runtimeOnly|osOnly' и уже сломалась — она считает скипом ЛЮБОЙ osOnly,
а posix-кейсы фейка платформы на маке как раз выполняются (grep давал 67 против 59 реальных).

Скрипт повторяет правила гейтинга раннера и живёт рядом с ним, поэтому расходиться им негде.
--list печатает пропуски поимённо с причиной. Сверено: win32/powershell 8, win32/python 11,
darwin/python 59 — совпало с прогонами.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-08-13 15:55:07 +03:00
co-authored by Claude Opus 5
parent 64c629a897
commit 26ca7276e6
2 changed files with 78 additions and 0 deletions
+15
View File
@@ -21,6 +21,21 @@ node tests/skills/runner.mjs --help # полный
Exit code: 0 = все прошли, 1 = есть падения.
### Сколько кейсов должно быть пропущено
`○ skipped` — норма, а не падение: кейс может гейтиться по ОС (`osOnly`), по порту
(`runtimeOnly`) или требовать внешней выгрузки (`setup: external:`), которой на машине нет.
Но само число ни о чём не говорит, пока не с чем сверить, а запоминать его нельзя — оно растёт
с набором кейсов.
```bash
node tests/skills/expected-skips.mjs --runtime python # ожидаемое число для этой ОС и порта
node tests/skills/expected-skips.mjs --list # + поимённо, с причиной
```
Совпало со `Skipped: N` из прогона — норма. Разошлось — разбираться: появился новый источник
скипа либо кейс скипается не по той причине, что заявлена.
### Платформенная верификация снапшотов
```bash
+63
View File
@@ -0,0 +1,63 @@
#!/usr/bin/env node
// Сколько кейсов ДОЛЖНО быть пропущено на этой ОС и этом порте.
//
// «skipped» в прогоне — норма, а не падение: часть кейсов гейтится по ОС, по порту или по
// наличию внешней выгрузки. Но само число ни о чём не говорит, пока не с чем сверить, а
// запоминать его нельзя — оно растёт с набором кейсов. Отсюда этот скрипт: он считает
// ожидаемое значение по тем же правилам, что и раннер, и его надо сверять с `Skipped: N`.
//
// Совпало — норма. Разошлось — разбираться: появился новый источник скипа либо кейс
// скипается не по той причине, что заявлена.
//
// Запуск: node tests/skills/expected-skips.mjs [--runtime python] [--list]
import { readFileSync, readdirSync, existsSync, statSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { dirname, join } from 'node:path';
const HERE = dirname(fileURLToPath(import.meta.url));
const CASES = join(HERE, 'cases');
const argv = process.argv.slice(2);
const runtime = argv.includes('--runtime') ? argv[argv.indexOf('--runtime') + 1] : 'powershell';
const listMode = argv.includes('--list');
const os = process.platform;
const reasons = { external: [], runtimeOnly: [], osOnly: [] };
for (const skill of readdirSync(CASES)) {
const dir = join(CASES, skill);
if (!statSync(dir).isDirectory()) continue;
const skillCfgPath = join(dir, '_skill.json');
const skillCfg = existsSync(skillCfgPath) ? JSON.parse(readFileSync(skillCfgPath, 'utf8')) : {};
for (const file of readdirSync(dir)) {
if (!file.endsWith('.json') || file === '_skill.json') continue;
const id = `${skill}/${file.replace(/\.json$/, '')}`;
const c = JSON.parse(readFileSync(join(dir, file), 'utf8'));
// Порядок совпадает с раннером: сначала ОС, потом порт, потом фикстура.
if (c.osOnly && ![].concat(c.osOnly).includes(os)) { reasons.osOnly.push(id); continue; }
if (c.runtimeOnly && c.runtimeOnly !== runtime) { reasons.runtimeOnly.push(id); continue; }
const setup = String(c.setup || skillCfg.setup || '');
if (setup.startsWith('external:') && !existsSync(setup.slice('external:'.length))) {
reasons.external.push(id);
}
}
}
const total = reasons.external.length + reasons.runtimeOnly.length + reasons.osOnly.length;
if (listMode) {
for (const [key, ids] of Object.entries(reasons)) {
if (!ids.length) continue;
console.log(`\n${key} (${ids.length}):`);
for (const id of ids) console.log(` ${id}`);
}
console.log('');
}
console.log(
`Ожидается пропущенных: ${total} [${os}, runtime: ${runtime}] ` +
`= external ${reasons.external.length} + runtimeOnly ${reasons.runtimeOnly.length} + osOnly ${reasons.osOnly.length}`,
);
console.log('Сверить с "Skipped: N" из прогона runner.mjs с теми же --runtime.');