70 lines
4.0 KiB
JavaScript
70 lines
4.0 KiB
JavaScript
import fs from 'node:fs';
|
|
import assert from 'node:assert/strict';
|
|
import { chromium, expect } from '@playwright/test';
|
|
|
|
const output = 'tmp/rest262-soak.json';
|
|
assert(!fs.existsSync(output), 'Never overwrite acceptance evidence');
|
|
const credentials = JSON.parse(fs.readFileSync('tmp/portal195-private.json'));
|
|
const browser = await chromium.launch({ channel: 'msedge', headless: true });
|
|
let token;
|
|
try {
|
|
const page = await browser.newPage();
|
|
await page.goto('http://localhost:3000/certificates');
|
|
await page.getByLabel('Email', { exact: true }).fill(credentials.email);
|
|
await page.getByLabel('Пароль', { exact: true }).fill(credentials.password);
|
|
await page.getByRole('button', { name: 'Войти', exact: true }).click();
|
|
await expect(page.getByRole('navigation', { name: 'Путь страницы' })).toContainText('Сертификаты', { timeout: 30000 });
|
|
token = await page.evaluate(() => localStorage.getItem('mmapp_token'));
|
|
assert(token);
|
|
} finally { await browser.close(); }
|
|
|
|
const report = {
|
|
date: '2026-10-15', block: '12.5.262', realPortalLogin: true, mocks: false,
|
|
backend: 'ux-certificates258', endpoint: '/api/certificates/state', directBackendTunnel: true,
|
|
concurrentRequestsPerWave: 12, waveIntervalMs: 30000, plannedWindowMs: 600000,
|
|
nonAuthApiWrites: 0, nativeConfigurationWrites: 0, waves: [],
|
|
bounds: ['One backend, two CHR over HTTP REST; not a global cap for external clients or multiple backend instances', 'Concurrent normal collectors and UI acceptance may run; not maximum throughput', 'A finite successful window does not prove all historical 401/500 causes or indefinite stability'],
|
|
};
|
|
const start = Date.now();
|
|
const lastReceipts = new Map();
|
|
try {
|
|
for (let wave = 0; wave <= 20; wave++) {
|
|
const wait = start + wave * report.waveIntervalMs - Date.now();
|
|
if (wait > 0) await new Promise(resolve => setTimeout(resolve, wait));
|
|
const began = Date.now();
|
|
const samples = await Promise.all(Array.from({ length: 12 }, async () => {
|
|
const requested = Date.now();
|
|
try {
|
|
const response = await fetch('http://localhost:8000/api/certificates/state', {
|
|
headers: { Authorization: 'Bearer ' + token }, signal: AbortSignal.timeout(20000),
|
|
});
|
|
const body = await response.json();
|
|
return { status: response.status, durationMs: Date.now() - requested, servers: body.servers?.map(s => ({ serverId: s.serverId, collectedAt: s.collectedAt, error: s.error !== null })) ?? [] };
|
|
} catch (error) { return { status: null, durationMs: Date.now() - requested, failure: error.name, servers: [] }; }
|
|
}));
|
|
const item = { wave, startedAt: new Date(began).toISOString(), elapsedMs: Date.now() - start, durationMs: Date.now() - began, samples };
|
|
report.waves.push(item);
|
|
fs.writeFileSync(output, JSON.stringify(report, null, 2) + '\n');
|
|
assert(samples.every(s => s.status === 200 && s.servers.length === 2 && s.servers.every(row => !row.error) && s.servers.map(row => row.serverId).sort().join(',') === '1,2'), 'Request or device read failed; stopping without automatic replay');
|
|
for (const id of ['1', '2']) {
|
|
const receipts = samples.flatMap(s => s.servers.filter(row => row.serverId === id).map(row => Date.parse(row.collectedAt)));
|
|
assert(receipts.every(Number.isFinite));
|
|
const earliest = Math.min(...receipts);
|
|
if (lastReceipts.has(id)) assert(earliest > lastReceipts.get(id), 'A later wave returned an old receipt');
|
|
lastReceipts.set(id, Math.max(...receipts));
|
|
}
|
|
console.log(JSON.stringify({ wave, durationMs: item.durationMs, requests: samples.length, errors: 0 }));
|
|
}
|
|
report.completed = true;
|
|
report.durationMs = Date.now() - start;
|
|
assert(report.durationMs >= report.plannedWindowMs);
|
|
report.passed = true;
|
|
} catch (error) {
|
|
report.passed = false;
|
|
report.failure = { name: error.name, message: error.message.slice(0, 200) };
|
|
process.exitCode = 1;
|
|
} finally {
|
|
token = undefined;
|
|
fs.writeFileSync(output, JSON.stringify(report, null, 2) + '\n');
|
|
}
|