test(qa): qualify route matrix and document stale-write failure

This commit is contained in:
cesnimda
2026-10-22 22:27:51 +02:00
parent 322b025350
commit 82be698dcf
69 changed files with 41364 additions and 1 deletions
@@ -0,0 +1,99 @@
import { test, expect, type APIRequestContext } from '../../../../../job-tracker-ui/node_modules/@playwright/test';
import os from 'node:os';
const api = 'http://localhost:5302/api';
const password = 'E2ePassword123!';
async function login(request: APIRequestContext, email: string) {
expect((await request.post(`${api}/auth/login`, { data: { email, password } })).status()).toBe(200);
const me = await (await request.get(`${api}/auth/me`)).json();
const csrf = (await request.storageState()).cookies.find(cookie => cookie.name === 'XSRF-TOKEN')!.value;
return { 'X-CSRF-TOKEN': csrf, 'X-Session-Context': me.sessionContext as string };
}
test('P9 bounded sustained reads with 250 jobs and long CV while using native workspaces', async ({ page, context, playwright }, info) => {
test.setTimeout(240000);
const admin = await playwright.request.newContext();
const latency: { worker: number; endpoint: string; ms: number; bytes: number; status: number }[] = [];
const navigation: object[] = [];
const cdp = await context.newCDPSession(page);
await cdp.send('Performance.enable');
try {
const adminHeaders = await login(admin, 'e2e@example.test');
const email = `p9-sustained-${Date.now()}@example.test`;
expect((await admin.post(`${api}/users`, { headers: adminHeaders, data: { email, password, roles: ['Premium'] } })).status()).toBe(200);
const headers = await login(page.request, email);
const company = await page.request.post(`${api}/companies`, { headers, data: { name: 'P9 bounded workload' } });
expect(company.status()).toBe(201); const companyId = (await company.json()).id;
for (let index = 0; index < 250; index++) expect((await page.request.post(`${api}/jobapplications`, { headers,
data: { companyId, jobTitle: `P9 bounded role ${index}`, status: 'Applied', dateApplied: new Date().toISOString() } })).status()).toBe(201);
const profile = { version: '1', contact: { fullName: 'Åse Synthetic', email: 'synthetic@example.test' }, summary: ['Documented accessible workflows.'],
jobs: Array.from({ length: 8 }, (_, index) => ({ id: `p9-role-${index}`, title: `Platform engineer ${index}`, company: 'Nordlys synthetic',
start: '2017-01', end: '2025-12', bullets: Array.from({ length: 6 }, (_, bullet) => `Evidence ${index}.${bullet}: ${'Documented reliable workflows. Dokumenterte pålitelige arbeidsflyter. '.repeat(3).trim()}`) })) };
expect((await page.request.put(`${api}/career/profile`, { headers, data: { profile } })).status()).toBe(200);
const savedProfile = (await (await page.request.get(`${api}/career/profile`)).json()).profile;
expect(savedProfile.jobs).toHaveLength(8);
expect(savedProfile.jobs.map((job: { start: string; end: string; bullets: string[] }) => ({ start: job.start, end: job.end, bullets: job.bullets })))
.toEqual(profile.jobs.map(job => ({ start: job.start, end: job.end, bullets: job.bullets })));
const cv = await page.request.post(`${api}/cv/variants`, { headers, data: { name: 'P9 long workload CV', settings: { themeId: 'minimal', language: 'nb' } } });
expect(cv.status()).toBe(200); const cvId = (await cv.json()).id;
await context.route('**/*', route => {
const url = new URL(route.request().url());
return ['localhost', '127.0.0.1'].includes(url.hostname) || ['data:', 'blob:', 'about:'].includes(url.protocol) ? route.continue() : route.abort();
});
const endpoints = ['/jobapplications/stats', '/jobapplications/board', '/career/profile', '/documents'];
const started = Date.now();
const readWorker = async (worker: number) => {
for (let index = 0; index < 120; index++) {
const endpoint = endpoints[index % endpoints.length]; const begin = Date.now();
const response = await page.request.get(`${api}${endpoint}`);
latency.push({ worker, endpoint, ms: Date.now() - begin, bytes: (await response.body()).length, status: response.status() });
expect(response.status(), endpoint).toBe(200);
if (endpoint.endsWith('/board')) expect(await response.json()).toHaveLength(250);
await new Promise(resolve => setTimeout(resolve, 500));
}
};
const interact = async () => {
for (let cycle = 0; cycle < 5; cycle++) for (const route of ['/dashboard', '/jobs?view=table', '/jobs?view=board', `/career/builder/${cvId}`]) {
const begin = Date.now(); await page.goto(route);
if (route === '/dashboard') await expect(page.getByRole('link', { name: 'Tracked jobs: 250', exact: true })).toBeVisible();
else if (route.endsWith('board')) await expect(page.locator('[data-kanban-card-id]')).toHaveCount(250);
else if (route.endsWith('table')) await expect(page.locator('[data-job-row-id]').first()).toBeVisible();
else {
await expect(page.getByRole('region', { name: 'Live preview', exact: true })).toHaveAttribute('aria-busy', 'false');
const measured = await page.getByTitle('CV preview', { exact: true }).evaluate(element => {
const doc = (element as HTMLIFrameElement).contentDocument!;
const pages = Number(doc.documentElement.dataset.cvPreviewPages);
const width = Number(doc.documentElement.dataset.cvPreviewWidth);
const walker = doc.createTreeWalker(doc.body, NodeFilter.SHOW_TEXT);
let node; let finalMarkerPage = 0;
while ((node = walker.nextNode())) {
const index = node.textContent?.indexOf('Evidence 7.5:') ?? -1;
if (index < 0) continue;
const range = doc.createRange(); range.setStart(node, index); range.setEnd(node, index + 'Evidence 7.5:'.length);
finalMarkerPage = Math.floor(range.getClientRects()[0].x / width) + 1;
}
return { pages, finalMarkerPage, ready: doc.documentElement.dataset.cvPreviewReady };
});
expect(measured.ready).toBe('true'); expect(measured.pages).toBeGreaterThan(1);
expect(measured.finalMarkerPage).toBe(measured.pages);
await expect(page.locator('[data-cv-page]')).toHaveCount(measured.pages);
await expect(page.getByTitle('CV preview page 1', { exact: true })).toBeVisible();
}
navigation.push({ cycle, route, readyMs: Date.now() - begin,
metrics: (await cdp.send('Performance.getMetrics')).metrics,
domElements: await page.locator('*').count() });
}
};
await Promise.all([readWorker(0), readWorker(1), interact()]);
const durationMs = Date.now() - started;
expect(durationMs).toBeGreaterThanOrEqual(60000);
expect(latency).toHaveLength(240);
expect((await (await page.request.get(`${api}/jobapplications/board`)).json())).toHaveLength(250);
expect((await (await page.request.get(`${api}/career/profile`)).json()).profile).toEqual(savedProfile);
const sorted = latency.map(row => row.ms).sort((a, b) => a - b);
await info.attach('phase9-sustained-performance', { body: JSON.stringify({ durationMs, fixtureJobs: 250, fixtureCareerRoles: 8,
readConcurrency: 2, reads: latency.length, p50Ms: sorted[Math.floor(sorted.length * .5)], p95Ms: sorted[Math.floor(sorted.length * .95)], maxMs: sorted.at(-1),
environment: { platform: os.platform(), cpus: os.cpus().length, totalMemoryBytes: os.totalmem(), browser: context.browser()?.version(), database: 'isolated SQLite', frontend: 'production static export' },
latency, navigation, limits: 'Finite paced two-reader workload plus 20 native navigations; no SLA/capacity, physical mobile, deployed Linux, model inference or server CPU attribution.' }, null, 2), contentType: 'application/json' });
} finally { await cdp.detach(); await admin.dispose(); }
});