Files
portal/app/playwright/refresh-session.test.js
T
Дмитрий e8e5c82b86
Accessibility (Pa11y live) / a11y (push) Has been cancelled
SAST — Semgrep / Semgrep SAST scan (push) Has been cancelled
fix(приёмка): FN-RESET + FN-LOGIN-ROUTE + диагностируемость FN-SESSION
FN-RESET: письмо сброса строило именованный роут password.reset которого нет в SPA.
ResetPassword::createUrlUsing → /reset/{token}?email= в AppServiceProvider boot.

FN-LOGIN-ROUTE: гость без Accept json на auth:sanctum уводил в именованный роут
login которого нет → 500. redirectGuestsTo /login + render AuthenticationException
→ 401 JSON для api/*.

FN-SESSION: chromium.launch стоял вне try/catch — отказ запуска браузера маскировался
unhandled-rejection в opaque exit 1 двойник login-rejected. launch в try + top-level
catch → чистый exit 4 + JSON stderr в refresh-session.js и manage-project.js.

Тесты: PasswordResetUrlTest, UnauthenticatedApiResponseTest, node:test launch-failure
в обоих playwright-скриптах. Разбор FN-SESSION + ops-долг playwright install под
www-data + поправки отчёта приёмки + новая находка FN-INN-LOOKUP.

Прод не трогался. Накат — позже вместе с остальным.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 08:49:19 +03:00

62 lines
3.2 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.
/**
* Тест refresh-session.js — поведение при ОТКАЗЕ запуска браузера.
*
* FN-SESSION (приёмка 22.06.2026): на проде browserType.launch падал (не было
* исполняемого файла headless-shell в кэше www-data). Из-за того, что
* chromium.launch() стоял ВНЕ try/catch, отказ становился unhandled promise
* rejection → Node выходил с кодом 1 + сырой стек в stderr — неотличимо от
* честного «login rejected» (тоже exit 1). Этот тест фиксирует контракт:
* отказ запуска браузера = exit 4 + структурированный JSON {error} в stderr.
*
* Runner: встроенный node:test (Node 18+). Запуск: `node --test refresh-session.test.js`.
*/
const { test } = require('node:test');
const assert = require('node:assert');
const { execFile } = require('node:child_process');
const path = require('node:path');
const SCRIPT = path.resolve(__dirname, 'refresh-session.js');
/** Спавнить refresh-session.js с заданным env, подать JSON на stdin, вернуть {code, stdout, stderr}. */
function runScript(input, extraEnv) {
return new Promise((resolve, reject) => {
const child = execFile(
'node',
[SCRIPT],
{ timeout: 90_000, env: { ...process.env, ...extraEnv } },
(err, stdout, stderr) => {
if (err && err.killed) return reject(new Error('Process killed / timed out'));
resolve({
code: err ? err.code : 0,
stdout: stdout.toString(),
stderr: stderr.toString(),
});
},
);
child.stdin.write(JSON.stringify(input));
child.stdin.end();
});
}
test('отказ запуска браузера → чистый exit 4 + JSON {error} в stderr (не опасный exit 1)', async () => {
// Форсируем отказ chromium.launch: указываем кэш браузеров в несуществующий
// путь — Playwright не найдёт исполняемый файл (та же ошибка, что была на проде).
const result = await runScript(
{ login: 'x', password: 'y', url: 'http://127.0.0.1:1/' },
{ PLAYWRIGHT_BROWSERS_PATH: path.resolve(__dirname, '__nonexistent_browsers__') },
);
// Отказ запуска должен классифицироваться как exit 4 (другая ошибка),
// а НЕ как exit 1 (login rejected) и не как unhandled-rejection exit 1.
assert.strictEqual(result.code, 4, `Expected exit 4, got ${result.code}. stderr: ${result.stderr}`);
// stderr должен быть валидным JSON с ключом error (а не сырым стеком Node).
let parsed;
try {
parsed = JSON.parse(result.stderr.trim());
} catch (e) {
assert.fail(`stderr не валидный JSON (сырой стек?): ${result.stderr}`);
}
assert.ok(typeof parsed.error === 'string' && parsed.error.length > 0, `expected {error}, got ${result.stderr}`);
});