Files
houseplan-card/demo/guard/verify-guard.mjs
T
Codex d4dd027b0a build: prepare v1.71.0-beta.2 candidate
Issue: #426
Issue: #427
Issue: #428
Issue: #431
Issue: #432
Issue: #434
User-Visible: no
2026-09-03 15:23:40 +03:00

92 lines
4.9 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env node
/**
* Отрицательный прогон гарда исключений (#404).
*
* Гард объявлен в `demo/serve.mjs` с 2026-07-27 и до этой задачи не срабатывал
* ни разу: счётчик читался раньше, чем Playwright доставлял `pageerror`. Такое
* ловится только запуском — «проверка, которая не умеет падать» выглядит
* идентично работающей во всём, кроме исхода.
*
* Почему не тест в `test/`: пробам нужен настоящий Chromium, а job «Фронтенд»,
* где идёт `npm test`, браузеры не ставит. Тест там молча скипался бы — то есть
* ровно тот тихий успех, против которого всё это и делается. Поэтому проверка
* живёт в job «Смоки в браузере», где браузер есть, и вызывается один раз.
*/
import { spawnSync } from 'node:child_process';
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
const HERE = dirname(fileURLToPath(import.meta.url));
/**
* Каждая проба: чего ждём от кода возврата и что обязано быть в выводе.
*
* `args` — необязательные аргументы запуска; `file` может указывать и выше
* этого каталога (`../benchmark_*.mjs`), потому что benchmark в `demo/guard/`
* не переселить: его гоняют руками при рекалибровке порогов (#430).
*/
const PROBES = [
{
file: 'guard_tail_exception.mjs',
expectExit: 1,
expectOutput: /uncaught exception\(s\) inside the card/,
because: 'исключение в хвосте — та самая слепая зона, из-за которой заводился #404',
},
{
file: 'guard_tail_rejection.mjs',
expectExit: 1,
expectOutput: /uncaught exception\(s\) inside the card/,
because: 'отклонение промиса приходит тем же каналом; на этом держится связь с #405',
},
{
file: 'guard_closed_page.mjs',
expectExit: 0,
expectOutput: /OK/,
because: 'round-trip к закрытой странице не имеет права ронять вердикт',
},
{
// #430: до этой задачи гард benchmark доказывался тестом, который искал
// в тексте те самые подстроки, которые сам же и вырезал у мутанта, —
// доказано было, что регулярка не пуста. Режим `--guard-probe` в
// benchmark существовал с #423 и не вызывался ни одним прогоном; теперь
// вызывается здесь, в единственной job с настоящим браузером.
file: '../benchmark_backdrop_decode.mjs',
args: ['--guard-probe'],
expectExit: 1,
expectOutput: /uncaught exception\(s\) inside the card/,
because: 'benchmark открывает страницу Playwright и обязан выносить тот же вердикт,'
+ ' что и смоки: исключение внутри карточки во время замера иначе не увидит никто',
},
{
file: 'guard_report_page_errors.mjs',
expectExit: 1,
expectOutput: /uncaught exception\(s\) inside the card/,
because: 'reportPageErrors() обязан сам дождаться хвостового pageerror, не полагаясь на finish()',
},
];
let failed = 0;
for (const probe of PROBES) {
const run = spawnSync(process.execPath, [resolve(HERE, probe.file), ...probe.args || []], {
encoding: 'utf8', cwd: resolve(HERE, '../..'), timeout: 90_000,
});
const output = `${run.stdout || ''}${run.stderr || ''}`;
const exitOk = run.status === probe.expectExit;
const textOk = probe.expectOutput.test(output);
if (exitOk && textOk) {
console.log(`ok ${[probe.file, ...probe.args || []].join(' ')} → exit ${run.status}`);
continue;
}
failed += 1;
console.error(`FAIL ${probe.file}: ${probe.because}`);
console.error(` ожидался exit ${probe.expectExit}, получен ${run.status}`);
if (!textOk) console.error(` в выводе нет ${probe.expectOutput}`);
console.error(output.split('\n').slice(-12).map((line) => ` | ${line}`).join('\n'));
}
if (failed) {
console.error(`\nгард исключений не доказан: проб провалено ${failed} из ${PROBES.length}`);
process.exit(1);
}
console.log(`\nгард исключений доказан на ${PROBES.length} пробах`);