import { expect } from '@playwright/test'; import type { TimingCollector } from '@levkin/playkit'; /** * Perf-budget gate for a `timings.measure(name, ...)` step. * * `timings.measure()` on its own only *records* durations (for the * Pushgateway export, see ROADMAP "Timing -> Pushgateway") — nothing ever * failed CI for a step getting slow. This asserts the most recently * recorded sample for `name` against `maxMs`, so a regression shows up as a * red test instead of only a graph nobody's watching. * * Budgets are intentionally generous (shared CI runner + DEV LXC under * concurrent PR load — see docs/guides/ci-runners-and-control.md in the * ansible repo) — this catches order-of-magnitude regressions, not * micro-optimization. Pick the closest `BUDGET_MS` bucket rather than * inventing a new one-off number per call site. */ export function expectWithinBudget(timings: TimingCollector, name: string, maxMs: number): void { const samples = timings.getSamples().filter((sample) => sample.name === name); const sample = samples[samples.length - 1]; expect(sample, `no timing sample recorded for "${name}" — call timings.measure() first`).toBeTruthy(); expect( sample!.durationMs, `"${name}" took ${Math.round(sample!.durationMs)}ms, budget is ${maxMs}ms`, ).toBeLessThanOrEqual(maxMs); } /** Shared budget buckets — see `expectWithinBudget` for rationale. */ export const BUDGET_MS = { /** Direct FastAPI/NextAuth-route calls (LAN, no browser). */ api: 8_000, /** Single UI interaction: open a page/panel, apply a filter, sign out. */ uiAction: 20_000, /** Full login flow (navigate + submit + redirect wait). */ uiLogin: 45_000, /** Multi-step UI CRUD (create/edit/deactivate + list refetch). */ heavyCrud: 60_000, } as const;