food-market/tests/regression/lib/worker-org.ts
nns cf760fab10
Some checks are pending
Auto-tag / Create date-tag (push) Waiting to run
CI / Backend (.NET 8) (push) Waiting to run
CI / Web (React + Vite) (push) Waiting to run
CI / POS (WPF, Windows) (push) Waiting to run
feat(s26): flaky-test detection + observability dashboards (8/8 ✓ 10/10 cert)
После 24 спринтов regress-suite разросся; нестабильность блокирует
доверие. Этот спринт: ловит flaky тесты, добавляет observability
(Grafana + Prometheus alerts + RUNBOOK), сертифицирует 10× cert-прогон.

1. tests/regression/find-flaky.sh — 10× прогон + JSON-агрегатор →
   docs/flaky-tests.md (per-test pass/fail sequence + reproduce).
2. OrgFactory.signupWithRetry теперь honors Retry-After header
   (api-client.ts:ApiError.retryAfterSec). Stage rate-limit поднят:
   RATE_SIGNUP_HOUR=5000, RATE_PER_IP_MIN=5000 (~/food-market-stage/deploy/.env).
3. fullyParallel=true + workers=4 = тесты идут в недетерминированном
   порядке; isolation работает (OrgFactory per-test).
4. workers=4 даёт **2.4× ускорение** (66.6s → 27.7s). Worker-scoped
   фикстура lib/worker-org.ts добавлена как opt-in.
5. deploy/grafana/dashboards/quality-watchdog.json (10 панелей:
   smoke success ratio 7d, incidents, multi-tenant violations,
   current emoji, p95 by endpoint, step failures, RPS, DB p95,
   docs posted, disk free) + dashboards/README.md.
   quality-watchdog.sh пишет Prometheus textfile экспорт в
   ~/.fm-watchdog/textfile/quality_watchdog.prom для node_exporter.
6. deploy/prometheus/alerts.yml — 10 правил, 4 группы (uptime,
   errors, database, quality-watchdog). MultiTenantViolation = P0.
   deploy/prometheus/prometheus.yml — reference config.
7. docs/RUNBOOK.md +178 строк: action per alert (api-down,
   rps-drop, http-errors-spike/growing, doc-posting-errors,
   db-p95-high, disk-free-low, watchdog-red, multi-tenant-violation,
   watchdog-incident). Junior-friendly с конкретными командами.

**Cert-прогон (10× workers=4):** 420/420 passed, 0 flaky, avg 30.1s/run,
total 300.6s (< 5min budget).

Изменения вне репо:
- ~/food-market-stage/deploy/.env — RATE_* limits bumped.
- ~/quality-watchdog.sh — добавлен .prom textfile экспорт.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-08 14:44:19 +05:00

60 lines
2.9 KiB
TypeScript
Raw Permalink 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.

/**
* Sprint 26: worker-scoped org fixture.
*
* Каждый Playwright worker = свой long-lived org. Тесты, которые не
* требуют изоляции (а это большинство — catalog/reports/i18n/realtime/
* onboarding/wizard), переиспользуют этот org через `test.extend`
* вместо вызова `OrgFactory.build()` per-test.
*
* Выгода:
* - 1 signup на worker × 4 workers = 4 signup per cert-run
* (вместо 42 signup'ов per-test).
* - Меньше нагрузка на signup rate-limit (per-IP).
* - Быстрее: фикстура переиспользуется, нет setup-overhead.
*
* Изоляция данных НЕ страдает: каждый test всё равно создаёт свои
* сущности с уникальными именами (через `Date.now()` суффикс), и
* `pageSize=N` ограничивает list-запросы — другие тесты в той же org
* не мешают.
*
* Кому НЕ подходит:
* - 06-multi-tenant.spec.ts — нужно ДВЕ свежие org per-test, иначе
* тест становится бессмысленным (используем OrgFactory напрямую).
* - 02-auth.spec.ts — тестирует сам signup, нужна fresh org.
* - 09-onboarding-wizard.spec.ts — тестирует онбординг свежей org'и.
*
* Использование:
* import { test } from '../lib/worker-org.js'
* test('foo', async ({ workerOrg }) => {
* const products = await request(Endpoints.products, { token: workerOrg.session.accessToken })
* ...
* })
*/
import { test as baseTest } from '@playwright/test'
import { OrgFactory } from '../factories/OrgFactory.js'
import type { BuiltOrg } from '../factories/OrgFactory.js'
interface WorkerFixtures {
workerOrg: BuiltOrg
}
export const test = baseTest.extend<{}, WorkerFixtures>({
workerOrg: [async ({}, use, workerInfo) => {
// Одна org на весь worker. Имя содержит workerIndex чтобы
// параллельные workers получали разные org'и (и поэтому
// не конкурировали за уникальные суффиксы).
const slug = `w${workerInfo.workerIndex}`
const org = await OrgFactory.for(slug)
.withProducts(3) // базовый каталог чтобы reports что-то видели
.withCounterparties(1)
.build()
await use(org)
// После всех тестов worker'a — org остаётся (cleanup делает
// Hangfire-job `prune-quality-test-orgs`, см. [[sprint25_done]]).
// Можно делать явный DELETE здесь, но это требует SuperAdmin-токена
// и cascade-обвязки — пока не реализуем.
}, { scope: 'worker' }],
})
export { expect } from '@playwright/test'