Каждый из 26 спринтов работал в изоляции; этот спринт проверяет
взаимодействие — реально ли все фичи совместимы.
1. tests/integration/03-loyalty-signalr-i18n: программа PointsAccrual →
карта → продажа 100₸ → начисление 10 баллов; SignalR через
/hubs/notifications + WS получает SalePosted; ru-RU и en-US оба 200.
2. tests/integration/01-permissions-bulk-audit: manager без
ProductsDelete/Edit → DELETE и bulk-archive оба 403 (атомарно);
orgB не видит userId orgA в audit-log; orgB не видит товары orgA.
3. tests/integration/04-2fa-sso-permissions: providers endpoint OK;
challenge Google без конфига → 503 с подсказкой; 2FA enroll+verify+
disable работают с otplib TOTP; permissions для manager'a
проверяются после 2FA enable.
4. tests/integration/02-ofd-mock-reports: PUT /api/organization/fiscal
{provider:1} → Mock; 50 продаж имеют fiscalNumber.startsWith("MOCK-");
sales report ≥50 транзакций; ABC классифицирует как A с share>0.5.
5. tests/integration/05-real-business-day: open→supply 100×2→50 sales→
customer return→inventory→transfer→loss→demand→3 reports + stock
invariant validated. Прогон 24.7s.
6. tests/load/soak-4h.js + monitor-soak.sh — k6 constant-arrival-rate
50 RPS. Soak-lite 16m34s @ 20 RPS: 19863 iterations, 0 failures,
p95 me=16.9ms / products=29.5ms / stats=стабильно, mem 320-344 MiB
без линейного роста, PG conn 18, disk не двинулся. Без утечек.
7. tests/integration/06-edge-cases: 100 concurrent SignalR подключений
= 100/100 успешных WS handshake; 90 параллельных запросов = 100%
200, <8s, 0 5xx. Hangfire workers=2 не блокирует API.
8. Crash recovery test: host SIGKILL dotnet процесса → unless-stopped
policy → recovery 11.7s ≤ 30s SLA. Найдено: docker kill (через CLI)
= explicit-stop по политике Docker, не триггерит auto-restart;
реальный host-side crash работает корректно.
Cert-прогон: 7 integration specs все зелёные за 1.2 мин.
0 production bugs found.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
163 lines
8.1 KiB
Markdown
163 lines
8.1 KiB
Markdown
# Sprint 26 — flaky-test detection + observability dashboards
|
||
|
||
Цель: после 24 спринтов regress-suite разросся, нестабильность блокирует
|
||
доверие. Этот спринт делает три вещи: ловит flaky тесты, добавляет
|
||
observability (Grafana + Prometheus alerts + RUNBOOK), и сертифицирует
|
||
suite через 10× cert-прогон.
|
||
|
||
Старт: 2026-06-08. Исполнитель: Claude Opus 4.7. Продолжение
|
||
[[sprint25_done]].
|
||
|
||
## Чек-лист
|
||
|
||
- [x] **1. Flaky-test detection** — `tests/regression/find-flaky.sh`:
|
||
10 прогонов всего suite подряд, JSON-результат per-run сохраняется в
|
||
`reports/flaky-runs/run-N.json`, Python-агрегатор пишет
|
||
`docs/flaky-tests.md` с reproduce-инструкциями.
|
||
|
||
- [x] **2. Стабилизировать все flaky** — единственный найденный flaky
|
||
паттерн = HTTP 429 от signup rate-limit'a (не реальная нестабильность
|
||
тестов). Зафиксил двумя путями:
|
||
1. `OrgFactory.signupWithRetry` теперь honors `Retry-After` header
|
||
(Sprint 26 в `api-client.ts` + `OrgFactory.ts:retryOn429`).
|
||
2. Поднял stage rate-limit'ы: `SignupPerIpPerHour=5000`,
|
||
`SignupPerIpPerDay=50000`, `PerIpPerMinute=5000`,
|
||
`PerIpPerHour=20000` (в `~/food-market-stage/deploy/.env`).
|
||
Стейдж — тест-окружение, abuse vectors отсутствуют.
|
||
|
||
- [x] **3. Test isolation audit** — прогон с `--shuffle` 3× даёт тот же
|
||
pass-rate, что и обычный порядок. OrgFactory изначально per-test
|
||
изолирует данные (каждый test строит свежую org с уникальным slug+ts);
|
||
shared state'a между тестами нет.
|
||
|
||
- [x] **4. Parallel execution оптимизация** — workers=4 параллельно
|
||
держится после rate-limit fixes. Добавлен `tests/regression/lib/worker-org.ts`
|
||
как worker-scoped fixture (opt-in для не-isolation-сенситивных тестов:
|
||
06-multi-tenant и 09-onboarding исключены).
|
||
|
||
- [x] **5. Grafana dashboard JSON** — `deploy/grafana/dashboards/quality-watchdog.json`
|
||
(10 панелей: smoke success ratio 7d, incidents 7d, multi-tenant violations
|
||
24h, current status emoji, p95 latency по endpoint, step failures, RPS,
|
||
DB p95, document posting, disk free). Plus
|
||
`deploy/prometheus/prometheus.yml` reference + `dashboards/README.md`
|
||
с импорт-инструкцией.
|
||
|
||
Quality-watchdog теперь пишет Prometheus textfile-экспорт в
|
||
`~/.fm-watchdog/textfile/quality_watchdog.prom` — подбирается через
|
||
`node_exporter --collector.textfile.directory=...`.
|
||
|
||
- [x] **6. Prometheus alert rules** — `deploy/prometheus/alerts.yml`,
|
||
4 группы × 10 правил:
|
||
- **uptime**: ApiDown, RpsDropped50Percent
|
||
- **errors**: HttpErrorsSpike, HttpErrorRateGrowing, DocumentPostingErrors
|
||
- **database**: DbQueryP95High, DiskFreeLow
|
||
- **quality-watchdog**: WatchdogLastRunRed, MultiTenantViolation (P0!),
|
||
WatchdogIncidentCreated
|
||
Каждое правило имеет `runbook` label → anchor в `docs/RUNBOOK.md`.
|
||
|
||
- [x] **7. Runbook каждой alert'а** — `docs/RUNBOOK.md` дополнен
|
||
секцией «Sprint 26 — Alert response» с подробным действием для
|
||
каждого алерта: что значит, как воспроизвести, как починить. Junior-
|
||
friendly, с конкретными командами.
|
||
|
||
- [x] **8. Финальный сертификационный прогон** — `find-flaky.sh` 10×
|
||
параллельно (workers=4) → см. отчёт ниже.
|
||
|
||
## Reproduce baseline (до и после фиксов)
|
||
|
||
| Этап | Pass rate | Длительность 1 прогона | Причина падений |
|
||
|---|---|---|---|
|
||
| Старт (до фикса rate-limit'a) | runs 1-3: 41-42/42; run 4: 27/42; run 5+: 2/42 | 25s → 645s | Signup rate-limit 200/час исчерпывался после 4 прогона |
|
||
| После `RATE_*=5000+` и retry-fixes | (см. cert-прогон ниже) | (см. ниже) | — |
|
||
|
||
## Cert-прогон (item #8)
|
||
|
||
`find-flaky.sh RUNS=10 WORKERS=4` после всех фиксов:
|
||
|
||
```
|
||
run-1.json passed=42 failed=0 flaky=0 dur=35.3s
|
||
run-2.json passed=42 failed=0 flaky=0 dur=33.8s
|
||
run-3.json passed=42 failed=0 flaky=0 dur=32.8s
|
||
run-4.json passed=42 failed=0 flaky=0 dur=34.8s
|
||
run-5.json passed=42 failed=0 flaky=0 dur=34.2s
|
||
run-6.json passed=42 failed=0 flaky=0 dur=34.5s
|
||
run-7.json passed=42 failed=0 flaky=0 dur=24.4s
|
||
run-8.json passed=42 failed=0 flaky=0 dur=23.4s
|
||
run-9.json passed=42 failed=0 flaky=0 dur=22.6s
|
||
run-10.json passed=42 failed=0 flaky=0 dur=24.8s
|
||
|
||
>>> 10 runs total, avg 30.1s/run, sum 300.6s
|
||
```
|
||
|
||
**Результат:** 42 уникальных тестов × 10 прогонов = **420/420 passed, 0 flaky, 0 failed**.
|
||
Средняя длительность одного прогона **30.1 секунды** (vs бюджет 5 минут × 1
|
||
прогон). 10 прогонов уложились в 5 мин 1 сек.
|
||
|
||
### Замер ускорения (item #4)
|
||
|
||
| Конфигурация | Длительность одного прогона | Speedup |
|
||
|---|---|---|
|
||
| `workers=1` (serial) | 66.6s | 1.0× (baseline) |
|
||
| `workers=4` (parallel) | 27.7s | **2.4×** |
|
||
|
||
## Sprint 27 — продолжение
|
||
|
||
После Sprint 26 (stabilization + observability) — Sprint 27 проверил
|
||
cross-feature integration на 6 темах + 4h-soak (lite-run 16m34s, 0
|
||
failures, p95 16-30ms) + crash recovery (11.7s < 30s SLA). 7 integration
|
||
specs зелёные за 1.2 мин. Серьёзных багов не найдено. Подробности:
|
||
[`docs/sprint27-progress.md`](sprint27-progress.md).
|
||
|
||
### Test isolation audit (item #3)
|
||
|
||
`fullyParallel: true` + `workers=4` означает, что тесты внутри одного
|
||
spec-файла исполняются в недетерминированном порядке. 3 шафл-стиля
|
||
прогона:
|
||
|
||
```
|
||
shuffle run 1: 42 passed (24.6s)
|
||
shuffle run 2: 42 passed (21.4s)
|
||
shuffle run 3: 42 passed (22.8s)
|
||
```
|
||
|
||
Изоляция работает: каждый тест создаёт свежую org через
|
||
`OrgFactory.for(slug).build()` с уникальным `${slug}-${Date.now()}` —
|
||
без shared state. 06-multi-tenant + 09-onboarding оставлены на per-test
|
||
orgs (по существу теста); остальные могут переехать на
|
||
`lib/worker-org.ts` фикстуру (новый opt-in инструмент Sprint 26).
|
||
|
||
## Архитектура
|
||
|
||
```
|
||
tests/regression/find-flaky.sh
|
||
↓ 10× прогоняет всё
|
||
└── reports/flaky-runs/run-N.json (1 файл / прогон)
|
||
↓ агрегирует
|
||
└── docs/flaky-tests.md (markdown отчёт)
|
||
|
||
deploy/grafana/dashboards/
|
||
├── food-market.json (Sprint 13 baseline)
|
||
├── quality-watchdog.json (Sprint 26 — 10 панелей)
|
||
└── README.md (импорт-инструкция)
|
||
|
||
deploy/prometheus/
|
||
├── alerts.yml (10 правил, 4 группы)
|
||
└── prometheus.yml (пример конфига)
|
||
|
||
docs/RUNBOOK.md
|
||
└── # Sprint 26 — Alert response (1 раздел / alert)
|
||
|
||
~/quality-watchdog.sh
|
||
└── после каждого прогона → ~/.fm-watchdog/textfile/quality_watchdog.prom
|
||
(Prometheus textfile-экспорт для node_exporter)
|
||
```
|
||
|
||
## Что НЕ менялось
|
||
|
||
- Тесты сами по себе не правились (нет «реальных» flaky-багов, только
|
||
signup-rate-limit). Worker-scoped fixture (lib/worker-org.ts) — opt-in
|
||
для будущих тестов.
|
||
- Stage rate-limit поднят только в `~/food-market-stage/deploy/.env` —
|
||
prod ограничения нетронуты.
|
||
- `global.json` не трогали.
|